summary refs log tree commit diff
path: root/Crypto/src
diff options
context:
space:
mode:
Diffstat (limited to 'Crypto/src')
-rw-r--r--Crypto/src/AssemblyInfo.cs82
-rw-r--r--Crypto/src/asn1/ASN1Generator.cs27
-rw-r--r--Crypto/src/asn1/ASN1OctetStringParser.cs10
-rw-r--r--Crypto/src/asn1/ASN1SequenceParser.cs8
-rw-r--r--Crypto/src/asn1/ASN1SetParser.cs8
-rw-r--r--Crypto/src/asn1/ASN1StreamParser.cs232
-rw-r--r--Crypto/src/asn1/ASN1TaggedObjectParser.cs10
-rw-r--r--Crypto/src/asn1/Asn1Encodable.cs78
-rw-r--r--Crypto/src/asn1/Asn1EncodableVector.cs93
-rw-r--r--Crypto/src/asn1/Asn1Exception.cs27
-rw-r--r--Crypto/src/asn1/Asn1InputStream.cs339
-rw-r--r--Crypto/src/asn1/Asn1Null.cs18
-rw-r--r--Crypto/src/asn1/Asn1Object.cs63
-rw-r--r--Crypto/src/asn1/Asn1OctetString.cs119
-rw-r--r--Crypto/src/asn1/Asn1OutputStream.cs35
-rw-r--r--Crypto/src/asn1/Asn1ParsingException.cs26
-rw-r--r--Crypto/src/asn1/Asn1Sequence.cs255
-rw-r--r--Crypto/src/asn1/Asn1Set.cs326
-rw-r--r--Crypto/src/asn1/Asn1TaggedObject.cs178
-rw-r--r--Crypto/src/asn1/Asn1Tags.cs36
-rw-r--r--Crypto/src/asn1/BERGenerator.cs102
-rw-r--r--Crypto/src/asn1/BEROctetStringGenerator.cs121
-rw-r--r--Crypto/src/asn1/BEROctetStringParser.cs36
-rw-r--r--Crypto/src/asn1/BERSequenceGenerator.cs24
-rw-r--r--Crypto/src/asn1/BERSequenceParser.cs24
-rw-r--r--Crypto/src/asn1/BERSetGenerator.cs24
-rw-r--r--Crypto/src/asn1/BERSetParser.cs24
-rw-r--r--Crypto/src/asn1/BERTaggedObjectParser.cs71
-rw-r--r--Crypto/src/asn1/BerApplicationSpecific.cs15
-rw-r--r--Crypto/src/asn1/BerApplicationSpecificParser.cs29
-rw-r--r--Crypto/src/asn1/BerNull.cs35
-rw-r--r--Crypto/src/asn1/BerOctetString.cs135
-rw-r--r--Crypto/src/asn1/BerOutputStream.cs36
-rw-r--r--Crypto/src/asn1/BerSequence.cs69
-rw-r--r--Crypto/src/asn1/BerSet.cs70
-rw-r--r--Crypto/src/asn1/BerTaggedObject.cs108
-rw-r--r--Crypto/src/asn1/ConstructedOctetStream.cs102
-rw-r--r--Crypto/src/asn1/DERExternal.cs207
-rw-r--r--Crypto/src/asn1/DERExternalParser.cs26
-rw-r--r--Crypto/src/asn1/DERGenerator.cs107
-rw-r--r--Crypto/src/asn1/DEROctetStringParser.cs36
-rw-r--r--Crypto/src/asn1/DERSequenceGenerator.cs40
-rw-r--r--Crypto/src/asn1/DERSequenceParser.cs24
-rw-r--r--Crypto/src/asn1/DERSetGenerator.cs40
-rw-r--r--Crypto/src/asn1/DERSetParser.cs24
-rw-r--r--Crypto/src/asn1/DefiniteLengthInputStream.cs90
-rw-r--r--Crypto/src/asn1/DerApplicationSpecific.cs237
-rw-r--r--Crypto/src/asn1/DerBMPString.cs115
-rw-r--r--Crypto/src/asn1/DerBitString.cs248
-rw-r--r--Crypto/src/asn1/DerBoolean.cs110
-rw-r--r--Crypto/src/asn1/DerEnumerated.cs100
-rw-r--r--Crypto/src/asn1/DerGeneralString.cs81
-rw-r--r--Crypto/src/asn1/DerGeneralizedTime.cs305
-rw-r--r--Crypto/src/asn1/DerIA5String.cs145
-rw-r--r--Crypto/src/asn1/DerInteger.cs117
-rw-r--r--Crypto/src/asn1/DerNull.cs41
-rw-r--r--Crypto/src/asn1/DerNumericString.cs138
-rw-r--r--Crypto/src/asn1/DerObjectIdentifier.cs242
-rw-r--r--Crypto/src/asn1/DerOctetString.cs34
-rw-r--r--Crypto/src/asn1/DerOutputStream.cs160
-rw-r--r--Crypto/src/asn1/DerPrintableString.cs163
-rw-r--r--Crypto/src/asn1/DerSequence.cs85
-rw-r--r--Crypto/src/asn1/DerSet.cs108
-rw-r--r--Crypto/src/asn1/DerStringBase.cs22
-rw-r--r--Crypto/src/asn1/DerT61String.cs102
-rw-r--r--Crypto/src/asn1/DerTaggedObject.cs72
-rw-r--r--Crypto/src/asn1/DerUTCTime.cs263
-rw-r--r--Crypto/src/asn1/DerUTF8String.cs96
-rw-r--r--Crypto/src/asn1/DerUniversalString.cs107
-rw-r--r--Crypto/src/asn1/DerUnknownTag.cs80
-rw-r--r--Crypto/src/asn1/DerVisibleString.cs111
-rw-r--r--Crypto/src/asn1/IAsn1ApplicationSpecificParser.cs10
-rw-r--r--Crypto/src/asn1/IAsn1Choice.cs17
-rw-r--r--Crypto/src/asn1/IAsn1Convertible.cs7
-rw-r--r--Crypto/src/asn1/IAsn1String.cs10
-rw-r--r--Crypto/src/asn1/IndefiniteLengthInputStream.cs166
-rw-r--r--Crypto/src/asn1/LazyASN1InputStream.cs33
-rw-r--r--Crypto/src/asn1/LazyDERSequence.cs82
-rw-r--r--Crypto/src/asn1/LazyDERSet.cs82
-rw-r--r--Crypto/src/asn1/LimitedInputStream.cs35
-rw-r--r--Crypto/src/asn1/OidTokenizer.cs45
-rw-r--r--Crypto/src/asn1/bc/BCObjectIdentifiers.cs39
-rw-r--r--Crypto/src/asn1/cmp/CAKeyUpdAnnContent.cs60
-rw-r--r--Crypto/src/asn1/cmp/CertConfirmContent.cs47
-rw-r--r--Crypto/src/asn1/cmp/CertOrEncCert.cs85
-rw-r--r--Crypto/src/asn1/cmp/CertRepMessage.cs94
-rw-r--r--Crypto/src/asn1/cmp/CertResponse.cs115
-rw-r--r--Crypto/src/asn1/cmp/CertStatus.cs84
-rw-r--r--Crypto/src/asn1/cmp/CertifiedKeyPair.cs114
-rw-r--r--Crypto/src/asn1/cmp/Challenge.cs79
-rw-r--r--Crypto/src/asn1/cmp/CmpCertificate.cs80
-rw-r--r--Crypto/src/asn1/cmp/CmpObjectIdentifiers.cs106
-rw-r--r--Crypto/src/asn1/cmp/CrlAnnContent.cs49
-rw-r--r--Crypto/src/asn1/cmp/ErrorMsgContent.cs94
-rw-r--r--Crypto/src/asn1/cmp/GenMsgContent.cs52
-rw-r--r--Crypto/src/asn1/cmp/GenRepContent.cs52
-rw-r--r--Crypto/src/asn1/cmp/InfoTypeAndValue.cs121
-rw-r--r--Crypto/src/asn1/cmp/KeyRecRepContent.cs115
-rw-r--r--Crypto/src/asn1/cmp/OobCertHash.cs87
-rw-r--r--Crypto/src/asn1/cmp/PKIBody.cs186
-rw-r--r--Crypto/src/asn1/cmp/PKIConfirmContent.cs34
-rw-r--r--Crypto/src/asn1/cmp/PKIFailureInfo.cs73
-rw-r--r--Crypto/src/asn1/cmp/PKIFreeText.cs97
-rw-r--r--Crypto/src/asn1/cmp/PKIHeader.cs237
-rw-r--r--Crypto/src/asn1/cmp/PKIHeaderBuilder.cs223
-rw-r--r--Crypto/src/asn1/cmp/PKIMessage.cs140
-rw-r--r--Crypto/src/asn1/cmp/PKIMessages.cs52
-rw-r--r--Crypto/src/asn1/cmp/PKIStatus.cs62
-rw-r--r--Crypto/src/asn1/cmp/PKIStatusInfo.cs165
-rw-r--r--Crypto/src/asn1/cmp/PbmParameter.cs100
-rw-r--r--Crypto/src/asn1/cmp/PollRepContent.cs66
-rw-r--r--Crypto/src/asn1/cmp/PollReqContent.cs59
-rw-r--r--Crypto/src/asn1/cmp/PopoDecKeyChallContent.cs47
-rw-r--r--Crypto/src/asn1/cmp/PopoDecKeyRespContent.cs47
-rw-r--r--Crypto/src/asn1/cmp/ProtectedPart.cs58
-rw-r--r--Crypto/src/asn1/cmp/RevAnnContent.cs86
-rw-r--r--Crypto/src/asn1/cmp/RevDetails.cs75
-rw-r--r--Crypto/src/asn1/cmp/RevRepContent.cs112
-rw-r--r--Crypto/src/asn1/cmp/RevRepContentBuilder.cs55
-rw-r--r--Crypto/src/asn1/cmp/RevReqContent.cs52
-rw-r--r--Crypto/src/asn1/cms/Attribute.cs70
-rw-r--r--Crypto/src/asn1/cms/AttributeTable.cs231
-rw-r--r--Crypto/src/asn1/cms/Attributes.cs43
-rw-r--r--Crypto/src/asn1/cms/AuthEnvelopedData.cs203
-rw-r--r--Crypto/src/asn1/cms/AuthEnvelopedDataParser.cs145
-rw-r--r--Crypto/src/asn1/cms/AuthenticatedData.cs270
-rw-r--r--Crypto/src/asn1/cms/AuthenticatedDataParser.cs182
-rw-r--r--Crypto/src/asn1/cms/CMSAttributes.cs14
-rw-r--r--Crypto/src/asn1/cms/CMSObjectIdentifiers.cs18
-rw-r--r--Crypto/src/asn1/cms/CompressedData.cs96
-rw-r--r--Crypto/src/asn1/cms/CompressedDataParser.cs47
-rw-r--r--Crypto/src/asn1/cms/ContentInfo.cs83
-rw-r--r--Crypto/src/asn1/cms/ContentInfoParser.cs40
-rw-r--r--Crypto/src/asn1/cms/EncryptedContentInfo.cs94
-rw-r--r--Crypto/src/asn1/cms/EncryptedContentInfoParser.cs46
-rw-r--r--Crypto/src/asn1/cms/EncryptedData.cs95
-rw-r--r--Crypto/src/asn1/cms/EnvelopedData.cs167
-rw-r--r--Crypto/src/asn1/cms/EnvelopedDataParser.cs107
-rw-r--r--Crypto/src/asn1/cms/Evidence.cs47
-rw-r--r--Crypto/src/asn1/cms/IssuerAndSerialNumber.cs66
-rw-r--r--Crypto/src/asn1/cms/KEKIdentifier.cs119
-rw-r--r--Crypto/src/asn1/cms/KEKRecipientInfo.cs106
-rw-r--r--Crypto/src/asn1/cms/KeyAgreeRecipientIdentifier.cs92
-rw-r--r--Crypto/src/asn1/cms/KeyAgreeRecipientInfo.cs141
-rw-r--r--Crypto/src/asn1/cms/KeyTransRecipientInfo.cs99
-rw-r--r--Crypto/src/asn1/cms/MetaData.cs94
-rw-r--r--Crypto/src/asn1/cms/OriginatorIdentifierOrKey.cs168
-rw-r--r--Crypto/src/asn1/cms/OriginatorInfo.cs121
-rw-r--r--Crypto/src/asn1/cms/OriginatorPublicKey.cs87
-rw-r--r--Crypto/src/asn1/cms/OtherKeyAttribute.cs70
-rw-r--r--Crypto/src/asn1/cms/OtherRecipientInfo.cs85
-rw-r--r--Crypto/src/asn1/cms/PasswordRecipientInfo.cs133
-rw-r--r--Crypto/src/asn1/cms/RecipientEncryptedKey.cs88
-rw-r--r--Crypto/src/asn1/cms/RecipientIdentifier.cs89
-rw-r--r--Crypto/src/asn1/cms/RecipientInfo.cs145
-rw-r--r--Crypto/src/asn1/cms/RecipientKeyIdentifier.cs137
-rw-r--r--Crypto/src/asn1/cms/SignedData.cs282
-rw-r--r--Crypto/src/asn1/cms/SignedDataParser.cs112
-rw-r--r--Crypto/src/asn1/cms/SignerIdentifier.cs89
-rw-r--r--Crypto/src/asn1/cms/SignerInfo.cs175
-rw-r--r--Crypto/src/asn1/cms/Time.cs118
-rw-r--r--Crypto/src/asn1/cms/TimeStampAndCRL.cs62
-rw-r--r--Crypto/src/asn1/cms/TimeStampTokenEvidence.cs65
-rw-r--r--Crypto/src/asn1/cms/TimeStampedData.cs95
-rw-r--r--Crypto/src/asn1/cms/TimeStampedDataParser.cs76
-rw-r--r--Crypto/src/asn1/cms/ecc/MQVuserKeyingMaterial.cs103
-rw-r--r--Crypto/src/asn1/crmf/AttributeTypeAndValue.cs66
-rw-r--r--Crypto/src/asn1/crmf/CertId.cs58
-rw-r--r--Crypto/src/asn1/crmf/CertReqMessages.cs52
-rw-r--r--Crypto/src/asn1/crmf/CertReqMsg.cs106
-rw-r--r--Crypto/src/asn1/crmf/CertRequest.cs82
-rw-r--r--Crypto/src/asn1/crmf/CertTemplate.cs149
-rw-r--r--Crypto/src/asn1/crmf/CertTemplateBuilder.cs125
-rw-r--r--Crypto/src/asn1/crmf/Controls.cs52
-rw-r--r--Crypto/src/asn1/crmf/CrmfObjectIdentifiers.cs23
-rw-r--r--Crypto/src/asn1/crmf/EncKeyWithID.cs103
-rw-r--r--Crypto/src/asn1/crmf/EncryptedKey.cs78
-rw-r--r--Crypto/src/asn1/crmf/EncryptedValue.cs154
-rw-r--r--Crypto/src/asn1/crmf/OptionalValidity.cs64
-rw-r--r--Crypto/src/asn1/crmf/PKIArchiveOptions.cs105
-rw-r--r--Crypto/src/asn1/crmf/PKIPublicationInfo.cs64
-rw-r--r--Crypto/src/asn1/crmf/PKMacValue.cs89
-rw-r--r--Crypto/src/asn1/crmf/PopoPrivKey.cs84
-rw-r--r--Crypto/src/asn1/crmf/PopoSigningKey.cs115
-rw-r--r--Crypto/src/asn1/crmf/PopoSigningKeyInput.cs115
-rw-r--r--Crypto/src/asn1/crmf/ProofOfPossession.cs98
-rw-r--r--Crypto/src/asn1/crmf/SinglePubInfo.cs58
-rw-r--r--Crypto/src/asn1/crmf/SubsequentMessage.cs27
-rw-r--r--Crypto/src/asn1/cryptopro/CryptoProObjectIdentifiers.cs48
-rw-r--r--Crypto/src/asn1/cryptopro/ECGOST3410NamedCurves.cs179
-rw-r--r--Crypto/src/asn1/cryptopro/ECGOST3410ParamSetParameters.cs86
-rw-r--r--Crypto/src/asn1/cryptopro/GOST28147Parameters.cs63
-rw-r--r--Crypto/src/asn1/cryptopro/GOST3410NamedParameters.cs123
-rw-r--r--Crypto/src/asn1/cryptopro/GOST3410ParamSetParameters.cs87
-rw-r--r--Crypto/src/asn1/cryptopro/GOST3410PublicKeyAlgParameters.cs99
-rw-r--r--Crypto/src/asn1/eac/EACObjectIdentifiers.cs50
-rw-r--r--Crypto/src/asn1/esf/CertificateValues.cs85
-rw-r--r--Crypto/src/asn1/esf/CommitmentTypeIdentifier.cs17
-rw-r--r--Crypto/src/asn1/esf/CommitmentTypeIndication.cs95
-rw-r--r--Crypto/src/asn1/esf/CommitmentTypeQualifier.cs119
-rw-r--r--Crypto/src/asn1/esf/CompleteCertificateRefs.cs84
-rw-r--r--Crypto/src/asn1/esf/CompleteRevocationRefs.cs84
-rw-r--r--Crypto/src/asn1/esf/CrlIdentifier.cs110
-rw-r--r--Crypto/src/asn1/esf/CrlListID.cs89
-rw-r--r--Crypto/src/asn1/esf/CrlOcspRef.cs111
-rw-r--r--Crypto/src/asn1/esf/CrlValidatedID.cs89
-rw-r--r--Crypto/src/asn1/esf/ESFAttributes.cs25
-rw-r--r--Crypto/src/asn1/esf/OcspIdentifier.cs77
-rw-r--r--Crypto/src/asn1/esf/OcspListID.cs88
-rw-r--r--Crypto/src/asn1/esf/OcspResponsesID.cs92
-rw-r--r--Crypto/src/asn1/esf/OtherCertID.cs93
-rw-r--r--Crypto/src/asn1/esf/OtherHash.cs88
-rw-r--r--Crypto/src/asn1/esf/OtherHashAlgAndValue.cs94
-rw-r--r--Crypto/src/asn1/esf/OtherRevRefs.cs78
-rw-r--r--Crypto/src/asn1/esf/OtherRevVals.cs78
-rw-r--r--Crypto/src/asn1/esf/OtherSigningCertificate.cs138
-rw-r--r--Crypto/src/asn1/esf/RevocationValues.cs165
-rw-r--r--Crypto/src/asn1/esf/SigPolicyQualifierInfo.cs71
-rw-r--r--Crypto/src/asn1/esf/SignaturePolicyId.cs145
-rw-r--r--Crypto/src/asn1/esf/SignaturePolicyIdentifier.cs64
-rw-r--r--Crypto/src/asn1/esf/SignerAttribute.cs96
-rw-r--r--Crypto/src/asn1/esf/SignerLocation.cs144
-rw-r--r--Crypto/src/asn1/ess/ContentHints.cs92
-rw-r--r--Crypto/src/asn1/ess/ContentIdentifier.cs65
-rw-r--r--Crypto/src/asn1/ess/ESSCertID.cs93
-rw-r--r--Crypto/src/asn1/ess/ESSCertIDv2.cs138
-rw-r--r--Crypto/src/asn1/ess/OtherCertID.cs132
-rw-r--r--Crypto/src/asn1/ess/OtherSigningCertificate.cs109
-rw-r--r--Crypto/src/asn1/ess/SigningCertificate.cs108
-rw-r--r--Crypto/src/asn1/ess/SigningCertificateV2.cs106
-rw-r--r--Crypto/src/asn1/gnu/GNUObjectIdentifiers.cs31
-rw-r--r--Crypto/src/asn1/iana/IANAObjectIdentifiers.cs18
-rw-r--r--Crypto/src/asn1/icao/CscaMasterList.cs83
-rw-r--r--Crypto/src/asn1/icao/DataGroupHash.cs86
-rw-r--r--Crypto/src/asn1/icao/ICAOObjectIdentifiers.cs34
-rw-r--r--Crypto/src/asn1/icao/LDSSecurityObject.cs145
-rw-r--r--Crypto/src/asn1/icao/LDSVersionInfo.cs61
-rw-r--r--Crypto/src/asn1/isismtt/ISISMTTObjectIdentifiers.cs177
-rw-r--r--Crypto/src/asn1/isismtt/ocsp/CertHash.cs121
-rw-r--r--Crypto/src/asn1/isismtt/ocsp/RequestedCertificate.cs186
-rw-r--r--Crypto/src/asn1/isismtt/x509/AdditionalInformationSyntax.cs70
-rw-r--r--Crypto/src/asn1/isismtt/x509/AdmissionSyntax.csbin0 -> 20844 bytes
-rw-r--r--Crypto/src/asn1/isismtt/x509/Admissions.cs186
-rw-r--r--Crypto/src/asn1/isismtt/x509/DeclarationOfMajority.cs170
-rw-r--r--Crypto/src/asn1/isismtt/x509/MonetaryLimit.cs121
-rw-r--r--Crypto/src/asn1/isismtt/x509/NamingAuthority.cs214
-rw-r--r--Crypto/src/asn1/isismtt/x509/ProcurationSyntax.cs232
-rw-r--r--Crypto/src/asn1/isismtt/x509/ProfessionInfo.cs386
-rw-r--r--Crypto/src/asn1/isismtt/x509/Restriction.cs81
-rw-r--r--Crypto/src/asn1/kisa/KISAObjectIdentifiers.cs8
-rw-r--r--Crypto/src/asn1/microsoft/MicrosoftObjectIdentifiers.cs18
-rw-r--r--Crypto/src/asn1/misc/CAST5CBCParameters.cs74
-rw-r--r--Crypto/src/asn1/misc/IDEACBCPar.cs68
-rw-r--r--Crypto/src/asn1/misc/MiscObjectIdentifiers.cs48
-rw-r--r--Crypto/src/asn1/misc/NetscapeCertType.cs54
-rw-r--r--Crypto/src/asn1/misc/NetscapeRevocationURL.cs18
-rw-r--r--Crypto/src/asn1/misc/VerisignCzagExtension.cs18
-rw-r--r--Crypto/src/asn1/mozilla/PublicKeyAndChallenge.cs67
-rw-r--r--Crypto/src/asn1/nist/NISTNamedCurves.cs102
-rw-r--r--Crypto/src/asn1/nist/NISTObjectIdentifiers.cs61
-rw-r--r--Crypto/src/asn1/ntt/NTTObjectIdentifiers.cs14
-rw-r--r--Crypto/src/asn1/ocsp/BasicOCSPResponse.cs131
-rw-r--r--Crypto/src/asn1/ocsp/CertID.cs98
-rw-r--r--Crypto/src/asn1/ocsp/CertStatus.cs94
-rw-r--r--Crypto/src/asn1/ocsp/CrlID.cs82
-rw-r--r--Crypto/src/asn1/ocsp/OCSPObjectIdentifiers.cs23
-rw-r--r--Crypto/src/asn1/ocsp/OCSPRequest.cs89
-rw-r--r--Crypto/src/asn1/ocsp/OCSPResponse.cs90
-rw-r--r--Crypto/src/asn1/ocsp/OCSPResponseStatus.cs41
-rw-r--r--Crypto/src/asn1/ocsp/Request.cs90
-rw-r--r--Crypto/src/asn1/ocsp/ResponderID.cs107
-rw-r--r--Crypto/src/asn1/ocsp/ResponseBytes.cs82
-rw-r--r--Crypto/src/asn1/ocsp/ResponseData.cs158
-rw-r--r--Crypto/src/asn1/ocsp/RevokedInfo.cs96
-rw-r--r--Crypto/src/asn1/ocsp/ServiceLocator.cs95
-rw-r--r--Crypto/src/asn1/ocsp/Signature.cs110
-rw-r--r--Crypto/src/asn1/ocsp/SingleResponse.cs137
-rw-r--r--Crypto/src/asn1/ocsp/TBSRequest.cs151
-rw-r--r--Crypto/src/asn1/oiw/ElGamalParameter.cs47
-rw-r--r--Crypto/src/asn1/oiw/OIWObjectIdentifiers.cs29
-rw-r--r--Crypto/src/asn1/pkcs/Attribute.cs79
-rw-r--r--Crypto/src/asn1/pkcs/AuthenticatedSafe.cs37
-rw-r--r--Crypto/src/asn1/pkcs/CertBag.cs46
-rw-r--r--Crypto/src/asn1/pkcs/CertificationRequest.cs81
-rw-r--r--Crypto/src/asn1/pkcs/CertificationRequestInfo.cs123
-rw-r--r--Crypto/src/asn1/pkcs/ContentInfo.cs80
-rw-r--r--Crypto/src/asn1/pkcs/DHParameter.cs72
-rw-r--r--Crypto/src/asn1/pkcs/EncryptedData.cs104
-rw-r--r--Crypto/src/asn1/pkcs/EncryptedPrivateKeyInfo.cs78
-rw-r--r--Crypto/src/asn1/pkcs/EncryptionScheme.cs49
-rw-r--r--Crypto/src/asn1/pkcs/IssuerAndSerialNumber.cs71
-rw-r--r--Crypto/src/asn1/pkcs/KeyDerivationFunc.cs21
-rw-r--r--Crypto/src/asn1/pkcs/MacData.cs96
-rw-r--r--Crypto/src/asn1/pkcs/PBEParameter.cs60
-rw-r--r--Crypto/src/asn1/pkcs/PBES2Parameters.cs61
-rw-r--r--Crypto/src/asn1/pkcs/PBKDF2Params.cs86
-rw-r--r--Crypto/src/asn1/pkcs/PKCS12PBEParams.cs63
-rw-r--r--Crypto/src/asn1/pkcs/PKCSObjectIdentifiers.cs256
-rw-r--r--Crypto/src/asn1/pkcs/Pfx.cs65
-rw-r--r--Crypto/src/asn1/pkcs/PrivateKeyInfo.cs126
-rw-r--r--Crypto/src/asn1/pkcs/RC2CBCParameter.cs81
-rw-r--r--Crypto/src/asn1/pkcs/RSAESOAEPparams.cs145
-rw-r--r--Crypto/src/asn1/pkcs/RSAPrivateKeyStructure.cs131
-rw-r--r--Crypto/src/asn1/pkcs/RSASSAPSSparams.cs165
-rw-r--r--Crypto/src/asn1/pkcs/SafeBag.cs70
-rw-r--r--Crypto/src/asn1/pkcs/SignedData.cs163
-rw-r--r--Crypto/src/asn1/pkcs/SignerInfo.cs154
-rw-r--r--Crypto/src/asn1/sec/ECPrivateKeyStructure.cs118
-rw-r--r--Crypto/src/asn1/sec/SECNamedCurves.cs1192
-rw-r--r--Crypto/src/asn1/sec/SECObjectIdentifiers.cs52
-rw-r--r--Crypto/src/asn1/smime/SMIMEAttributes.cs11
-rw-r--r--Crypto/src/asn1/smime/SMIMECapabilities.cs128
-rw-r--r--Crypto/src/asn1/smime/SMIMECapabilitiesAttribute.cs16
-rw-r--r--Crypto/src/asn1/smime/SMIMECapability.cs101
-rw-r--r--Crypto/src/asn1/smime/SMIMECapabilityVector.cs37
-rw-r--r--Crypto/src/asn1/smime/SMIMEEncryptionKeyPreferenceAttribute.cs44
-rw-r--r--Crypto/src/asn1/teletrust/TeleTrusTNamedCurves.cs428
-rw-r--r--Crypto/src/asn1/teletrust/TeleTrusTObjectIdentifiers.cs45
-rw-r--r--Crypto/src/asn1/tsp/Accuracy.cs149
-rw-r--r--Crypto/src/asn1/tsp/MessageImprint.cs74
-rw-r--r--Crypto/src/asn1/tsp/TSTInfo.cs249
-rw-r--r--Crypto/src/asn1/tsp/TimeStampReq.cs164
-rw-r--r--Crypto/src/asn1/tsp/TimeStampResp.cs80
-rw-r--r--Crypto/src/asn1/util/Asn1Dump.cs378
-rw-r--r--Crypto/src/asn1/util/Dump.cs30
-rw-r--r--Crypto/src/asn1/util/FilterStream.cs72
-rw-r--r--Crypto/src/asn1/x500/DirectoryString.cs75
-rw-r--r--Crypto/src/asn1/x509/AccessDescription.cs83
-rw-r--r--Crypto/src/asn1/x509/AlgorithmIdentifier.cs110
-rw-r--r--Crypto/src/asn1/x509/AttCertIssuer.cs86
-rw-r--r--Crypto/src/asn1/x509/AttCertValidityPeriod.cs78
-rw-r--r--Crypto/src/asn1/x509/Attribute.cs82
-rw-r--r--Crypto/src/asn1/x509/AttributeCertificate.cs81
-rw-r--r--Crypto/src/asn1/x509/AttributeCertificateInfo.cs156
-rw-r--r--Crypto/src/asn1/x509/AttributeTable.cs73
-rw-r--r--Crypto/src/asn1/x509/AuthorityInformationAccess.cs105
-rw-r--r--Crypto/src/asn1/x509/AuthorityKeyIdentifier.cs211
-rw-r--r--Crypto/src/asn1/x509/BasicConstraints.cs133
-rw-r--r--Crypto/src/asn1/x509/CRLDistPoint.cs93
-rw-r--r--Crypto/src/asn1/x509/CRLNumber.cs30
-rw-r--r--Crypto/src/asn1/x509/CRLReason.cs61
-rw-r--r--Crypto/src/asn1/x509/CertPolicyId.cs20
-rw-r--r--Crypto/src/asn1/x509/CertificateList.cs108
-rw-r--r--Crypto/src/asn1/x509/CertificatePair.cs160
-rw-r--r--Crypto/src/asn1/x509/DSAParameter.cs77
-rw-r--r--Crypto/src/asn1/x509/DigestInfo.cs76
-rw-r--r--Crypto/src/asn1/x509/DisplayText.cs172
-rw-r--r--Crypto/src/asn1/x509/DistributionPoint.cs161
-rw-r--r--Crypto/src/asn1/x509/DistributionPointName.cs130
-rw-r--r--Crypto/src/asn1/x509/ExtendedKeyUsage.cs131
-rw-r--r--Crypto/src/asn1/x509/GeneralName.cs418
-rw-r--r--Crypto/src/asn1/x509/GeneralNames.cs95
-rw-r--r--Crypto/src/asn1/x509/GeneralSubtree.cs189
-rw-r--r--Crypto/src/asn1/x509/Holder.cs257
-rw-r--r--Crypto/src/asn1/x509/IetfAttrSyntax.cs161
-rw-r--r--Crypto/src/asn1/x509/IssuerSerial.cs98
-rw-r--r--Crypto/src/asn1/x509/IssuingDistributionPoint.cs247
-rw-r--r--Crypto/src/asn1/x509/KeyPurposeId.cs36
-rw-r--r--Crypto/src/asn1/x509/KeyUsage.cs79
-rw-r--r--Crypto/src/asn1/x509/NameConstraints.cs118
-rw-r--r--Crypto/src/asn1/x509/NoticeReference.cs138
-rw-r--r--Crypto/src/asn1/x509/ObjectDigestInfo.cs177
-rw-r--r--Crypto/src/asn1/x509/PolicyInformation.cs80
-rw-r--r--Crypto/src/asn1/x509/PolicyMappings.cs70
-rw-r--r--Crypto/src/asn1/x509/PolicyQualifierId.cs28
-rw-r--r--Crypto/src/asn1/x509/PolicyQualifierInfo.cs91
-rw-r--r--Crypto/src/asn1/x509/PrivateKeyUsagePeriod.cs82
-rw-r--r--Crypto/src/asn1/x509/RSAPublicKeyStructure.cs92
-rw-r--r--Crypto/src/asn1/x509/ReasonFlags.cs46
-rw-r--r--Crypto/src/asn1/x509/RoleSyntax.cs230
-rw-r--r--Crypto/src/asn1/x509/SubjectDirectoryAttributes.cs142
-rw-r--r--Crypto/src/asn1/x509/SubjectKeyIdentifier.cs141
-rw-r--r--Crypto/src/asn1/x509/SubjectPublicKeyInfo.cs102
-rw-r--r--Crypto/src/asn1/x509/TBSCertList.cs274
-rw-r--r--Crypto/src/asn1/x509/TBSCertificateStructure.cs185
-rw-r--r--Crypto/src/asn1/x509/Target.cs139
-rw-r--r--Crypto/src/asn1/x509/TargetInformation.cs123
-rw-r--r--Crypto/src/asn1/x509/Targets.cs121
-rw-r--r--Crypto/src/asn1/x509/Time.cs120
-rw-r--r--Crypto/src/asn1/x509/UserNotice.cs104
-rw-r--r--Crypto/src/asn1/x509/V1TBSCertificateGenerator.cs108
-rw-r--r--Crypto/src/asn1/x509/V2AttributeCertificateInfoGenerator.cs137
-rw-r--r--Crypto/src/asn1/x509/V2Form.cs125
-rw-r--r--Crypto/src/asn1/x509/V2TBSCertListGenerator.cs201
-rw-r--r--Crypto/src/asn1/x509/V3TBSCertificateGenerator.cs168
-rw-r--r--Crypto/src/asn1/x509/X509Attributes.cs9
-rw-r--r--Crypto/src/asn1/x509/X509CertificateStructure.cs129
-rw-r--r--Crypto/src/asn1/x509/X509DefaultEntryConverter.cs63
-rw-r--r--Crypto/src/asn1/x509/X509Extension.cs79
-rw-r--r--Crypto/src/asn1/x509/X509Extensions.cs451
-rw-r--r--Crypto/src/asn1/x509/X509ExtensionsGenerator.cs81
-rw-r--r--Crypto/src/asn1/x509/X509Name.cs1189
-rw-r--r--Crypto/src/asn1/x509/X509NameEntryConverter.cs89
-rw-r--r--Crypto/src/asn1/x509/X509NameTokenizer.cs104
-rw-r--r--Crypto/src/asn1/x509/X509ObjectIdentifiers.cs59
-rw-r--r--Crypto/src/asn1/x509/qualified/BiometricData.cs112
-rw-r--r--Crypto/src/asn1/x509/qualified/ETSIQCObjectIdentifiers.cs19
-rw-r--r--Crypto/src/asn1/x509/qualified/Iso4217CurrencyCode.cs84
-rw-r--r--Crypto/src/asn1/x509/qualified/MonetaryValue.cs83
-rw-r--r--Crypto/src/asn1/x509/qualified/QCStatement.cs85
-rw-r--r--Crypto/src/asn1/x509/qualified/RFC3739QCObjectIdentifiers.cs21
-rw-r--r--Crypto/src/asn1/x509/qualified/SemanticsInformation.cs124
-rw-r--r--Crypto/src/asn1/x509/qualified/TypeOfBiometricData.cs91
-rw-r--r--Crypto/src/asn1/x509/sigi/NameOrPseudonym.cs177
-rw-r--r--Crypto/src/asn1/x509/sigi/PersonalData.cs210
-rw-r--r--Crypto/src/asn1/x509/sigi/SigIObjectIdentifiers.cs49
-rw-r--r--Crypto/src/asn1/x9/DHDomainParameters.cs116
-rw-r--r--Crypto/src/asn1/x9/DHPublicKey.cs44
-rw-r--r--Crypto/src/asn1/x9/DHValidationParms.cs62
-rw-r--r--Crypto/src/asn1/x9/KeySpecificInfo.cs58
-rw-r--r--Crypto/src/asn1/x9/OtherInfo.cs88
-rw-r--r--Crypto/src/asn1/x9/X962NamedCurves.cs733
-rw-r--r--Crypto/src/asn1/x9/X962Parameters.cs53
-rw-r--r--Crypto/src/asn1/x9/X9Curve.cs147
-rw-r--r--Crypto/src/asn1/x9/X9ECParameters.cs170
-rw-r--r--Crypto/src/asn1/x9/X9ECParametersHolder.cs22
-rw-r--r--Crypto/src/asn1/x9/X9ECPoint.cs44
-rw-r--r--Crypto/src/asn1/x9/X9FieldElement.cs69
-rw-r--r--Crypto/src/asn1/x9/X9FieldID.cs102
-rw-r--r--Crypto/src/asn1/x9/X9IntegerConverter.cs48
-rw-r--r--Crypto/src/asn1/x9/X9ObjectIdentifiers.cs135
-rw-r--r--Crypto/src/bcpg/ArmoredInputStream.cs517
-rw-r--r--Crypto/src/bcpg/ArmoredOutputStream.cs333
-rw-r--r--Crypto/src/bcpg/BcpgInputStream.cs359
-rw-r--r--Crypto/src/bcpg/BcpgObject.cs22
-rw-r--r--Crypto/src/bcpg/BcpgOutputStream.cs394
-rw-r--r--Crypto/src/bcpg/CompressedDataPacket.cs24
-rw-r--r--Crypto/src/bcpg/CompressionAlgorithmTags.cs11
-rw-r--r--Crypto/src/bcpg/ContainedPacket.cs22
-rw-r--r--Crypto/src/bcpg/Crc24.cs46
-rw-r--r--Crypto/src/bcpg/DsaPublicBcpgKey.cs80
-rw-r--r--Crypto/src/bcpg/DsaSecretBcpgKey.cs61
-rw-r--r--Crypto/src/bcpg/ElGamalPublicBcpgKey.cs71
-rw-r--r--Crypto/src/bcpg/ElGamalSecretBcpgKey.cs61
-rw-r--r--Crypto/src/bcpg/ExperimentalPacket.cs38
-rw-r--r--Crypto/src/bcpg/HashAlgorithmTags.cs19
-rw-r--r--Crypto/src/bcpg/IBcpgKey.cs16
-rw-r--r--Crypto/src/bcpg/InputStreamPacket.cs20
-rw-r--r--Crypto/src/bcpg/LiteralDataPacket.cs57
-rw-r--r--Crypto/src/bcpg/MPInteger.cs59
-rw-r--r--Crypto/src/bcpg/MarkerPacket.cs24
-rw-r--r--Crypto/src/bcpg/ModDetectionCodePacket.cs42
-rw-r--r--Crypto/src/bcpg/OnePassSignaturePacket.cs93
-rw-r--r--Crypto/src/bcpg/OutputStreamPacket.cs24
-rw-r--r--Crypto/src/bcpg/Packet.cs7
-rw-r--r--Crypto/src/bcpg/PacketTags.cs30
-rw-r--r--Crypto/src/bcpg/PublicKeyAlgorithmTags.cs28
-rw-r--r--Crypto/src/bcpg/PublicKeyEncSessionPacket.cs103
-rw-r--r--Crypto/src/bcpg/PublicKeyPacket.cs115
-rw-r--r--Crypto/src/bcpg/PublicSubkeyPacket.cs30
-rw-r--r--Crypto/src/bcpg/RsaPublicBcpgKey.cs66
-rw-r--r--Crypto/src/bcpg/RsaSecretBcpgKey.cs114
-rw-r--r--Crypto/src/bcpg/S2k.cs147
-rw-r--r--Crypto/src/bcpg/SecretKeyPacket.cs170
-rw-r--r--Crypto/src/bcpg/SecretSubkeyPacket.cs43
-rw-r--r--Crypto/src/bcpg/SignaturePacket.cs472
-rw-r--r--Crypto/src/bcpg/SignatureSubpacket.cs76
-rw-r--r--Crypto/src/bcpg/SignatureSubpacketTags.cs33
-rw-r--r--Crypto/src/bcpg/SignatureSubpacketsReader.cs88
-rw-r--r--Crypto/src/bcpg/SymmetricEncDataPacket.cs15
-rw-r--r--Crypto/src/bcpg/SymmetricEncIntegrityPacket.cs18
-rw-r--r--Crypto/src/bcpg/SymmetricKeyAlgorithmTags.cs20
-rw-r--r--Crypto/src/bcpg/SymmetricKeyEncSessionPacket.cs91
-rw-r--r--Crypto/src/bcpg/TrustPacket.cs43
-rw-r--r--Crypto/src/bcpg/UserAttributePacket.cs61
-rw-r--r--Crypto/src/bcpg/UserAttributeSubpacket.cs86
-rw-r--r--Crypto/src/bcpg/UserAttributeSubpacketTags.cs10
-rw-r--r--Crypto/src/bcpg/UserAttributeSubpacketsReader.cs63
-rw-r--r--Crypto/src/bcpg/UserIdPacket.cs37
-rw-r--r--Crypto/src/bcpg/attr/ImageAttrib.cs68
-rw-r--r--Crypto/src/bcpg/sig/EmbeddedSignature.cs18
-rw-r--r--Crypto/src/bcpg/sig/Exportable.cs47
-rw-r--r--Crypto/src/bcpg/sig/IssuerKeyId.cs61
-rw-r--r--Crypto/src/bcpg/sig/KeyExpirationTime.cs56
-rw-r--r--Crypto/src/bcpg/sig/KeyFlags.cs74
-rw-r--r--Crypto/src/bcpg/sig/NotationData.cs112
-rw-r--r--Crypto/src/bcpg/sig/PreferredAlgorithms.cs54
-rw-r--r--Crypto/src/bcpg/sig/PrimaryUserId.cs48
-rw-r--r--Crypto/src/bcpg/sig/Revocable.cs48
-rw-r--r--Crypto/src/bcpg/sig/RevocationKey.cs63
-rw-r--r--Crypto/src/bcpg/sig/RevocationKeyTags.cs9
-rw-r--r--Crypto/src/bcpg/sig/RevocationReason.cs60
-rw-r--r--Crypto/src/bcpg/sig/RevocationReasonTags.cs14
-rw-r--r--Crypto/src/bcpg/sig/SignatureCreationTime.cs47
-rw-r--r--Crypto/src/bcpg/sig/SignatureExpirationTime.cs54
-rw-r--r--Crypto/src/bcpg/sig/SignerUserId.cs52
-rw-r--r--Crypto/src/bcpg/sig/TrustSignature.cs43
-rw-r--r--Crypto/src/cms/BaseDigestCalculator.cs23
-rw-r--r--Crypto/src/cms/CMSAttributeTableGenerationException.cs25
-rw-r--r--Crypto/src/cms/CMSAttributeTableGenerator.cs25
-rw-r--r--Crypto/src/cms/CMSAuthEnvelopedData.cs112
-rw-r--r--Crypto/src/cms/CMSAuthEnvelopedGenerator.cs16
-rw-r--r--Crypto/src/cms/CMSAuthenticatedData.cs137
-rw-r--r--Crypto/src/cms/CMSAuthenticatedDataGenerator.cs156
-rw-r--r--Crypto/src/cms/CMSAuthenticatedDataParser.cs214
-rw-r--r--Crypto/src/cms/CMSAuthenticatedDataStreamGenerator.cs275
-rw-r--r--Crypto/src/cms/CMSAuthenticatedGenerator.cs35
-rw-r--r--Crypto/src/cms/CMSCompressedData.cs107
-rw-r--r--Crypto/src/cms/CMSCompressedDataGenerator.cs66
-rw-r--r--Crypto/src/cms/CMSCompressedDataParser.cs57
-rw-r--r--Crypto/src/cms/CMSCompressedDataStreamGenerator.cs144
-rw-r--r--Crypto/src/cms/CMSContentInfoParser.cs47
-rw-r--r--Crypto/src/cms/CMSEnvelopedData.cs115
-rw-r--r--Crypto/src/cms/CMSEnvelopedDataGenerator.cs178
-rw-r--r--Crypto/src/cms/CMSEnvelopedDataParser.cs161
-rw-r--r--Crypto/src/cms/CMSEnvelopedDataStreamGenerator.cs285
-rw-r--r--Crypto/src/cms/CMSEnvelopedGenerator.cs331
-rw-r--r--Crypto/src/cms/CMSEnvelopedHelper.cs311
-rw-r--r--Crypto/src/cms/CMSException.cs25
-rw-r--r--Crypto/src/cms/CMSPBEKey.cs109
-rw-r--r--Crypto/src/cms/CMSProcessable.cs19
-rw-r--r--Crypto/src/cms/CMSProcessableByteArray.cs36
-rw-r--r--Crypto/src/cms/CMSProcessableFile.cs57
-rw-r--r--Crypto/src/cms/CMSProcessableInputStream.cs52
-rw-r--r--Crypto/src/cms/CMSReadable.cs10
-rw-r--r--Crypto/src/cms/CMSSecureReadable.cs14
-rw-r--r--Crypto/src/cms/CMSSignedData.cs425
-rw-r--r--Crypto/src/cms/CMSSignedDataGenerator.cs551
-rw-r--r--Crypto/src/cms/CMSSignedDataParser.cs455
-rw-r--r--Crypto/src/cms/CMSSignedDataStreamGenerator.cs917
-rw-r--r--Crypto/src/cms/CMSSignedGenerator.cs261
-rw-r--r--Crypto/src/cms/CMSSignedHelper.cs319
-rw-r--r--Crypto/src/cms/CMSStreamException.cs26
-rw-r--r--Crypto/src/cms/CMSTypedStream.cs72
-rw-r--r--Crypto/src/cms/CMSUtils.cs186
-rw-r--r--Crypto/src/cms/CounterSignatureDigestCalculator.cs28
-rw-r--r--Crypto/src/cms/DefaultAuthenticatedAttributeTableGenerator.cs90
-rw-r--r--Crypto/src/cms/DefaultSignedAttributeTableGenerator.cs124
-rw-r--r--Crypto/src/cms/DigOutputStream.cs28
-rw-r--r--Crypto/src/cms/IDigestCalculator.cs9
-rw-r--r--Crypto/src/cms/KEKRecipientInfoGenerator.cs137
-rw-r--r--Crypto/src/cms/KEKRecipientInformation.cs62
-rw-r--r--Crypto/src/cms/KeyAgreeRecipientInfoGenerator.cs171
-rw-r--r--Crypto/src/cms/KeyAgreeRecipientInformation.cs226
-rw-r--r--Crypto/src/cms/KeyTransRecipientInfoGenerator.cs87
-rw-r--r--Crypto/src/cms/KeyTransRecipientInformation.cs113
-rw-r--r--Crypto/src/cms/MacOutputStream.cs28
-rw-r--r--Crypto/src/cms/NullOutputStream.cs20
-rw-r--r--Crypto/src/cms/OriginatorId.cs51
-rw-r--r--Crypto/src/cms/PKCS5Scheme2PBEKey.cs64
-rw-r--r--Crypto/src/cms/PKCS5Scheme2UTF8PBEKey.cs64
-rw-r--r--Crypto/src/cms/PasswordRecipientInfoGenerator.cs69
-rw-r--r--Crypto/src/cms/PasswordRecipientInformation.cs79
-rw-r--r--Crypto/src/cms/RecipientId.cs58
-rw-r--r--Crypto/src/cms/RecipientInfoGenerator.cs26
-rw-r--r--Crypto/src/cms/RecipientInformation.cs126
-rw-r--r--Crypto/src/cms/RecipientInformationStore.cs86
-rw-r--r--Crypto/src/cms/SigOutputStream.cs43
-rw-r--r--Crypto/src/cms/SignerId.cs51
-rw-r--r--Crypto/src/cms/SignerInfoGenerator.cs14
-rw-r--r--Crypto/src/cms/SignerInformation.cs758
-rw-r--r--Crypto/src/cms/SignerInformationStore.cs74
-rw-r--r--Crypto/src/cms/SimpleAttributeTableGenerator.cs28
-rw-r--r--Crypto/src/crypto/AsymmetricCipherKeyPair.cs52
-rw-r--r--Crypto/src/crypto/AsymmetricKeyParameter.cs47
-rw-r--r--Crypto/src/crypto/BufferedAeadBlockCipher.cs259
-rw-r--r--Crypto/src/crypto/BufferedAsymmetricBlockCipher.cs152
-rw-r--r--Crypto/src/crypto/BufferedBlockCipher.cs378
-rw-r--r--Crypto/src/crypto/BufferedCipherBase.cs113
-rw-r--r--Crypto/src/crypto/BufferedIesCipher.cs113
-rw-r--r--Crypto/src/crypto/BufferedStreamCipher.cs131
-rw-r--r--Crypto/src/crypto/CipherKeyGenerator.cs83
-rw-r--r--Crypto/src/crypto/CryptoException.cs25
-rw-r--r--Crypto/src/crypto/DataLengthException.cs39
-rw-r--r--Crypto/src/crypto/IAsymmetricBlockCipher.cs30
-rw-r--r--Crypto/src/crypto/IAsymmetricCipherKeyPairGenerator.cs24
-rw-r--r--Crypto/src/crypto/IBasicAgreement.cs24
-rw-r--r--Crypto/src/crypto/IBlockCipher.cs36
-rw-r--r--Crypto/src/crypto/IBufferedCipher.cs44
-rw-r--r--Crypto/src/crypto/ICipherParameters.cs11
-rw-r--r--Crypto/src/crypto/IDSA.cs40
-rw-r--r--Crypto/src/crypto/IDerivationFunction.cs24
-rw-r--r--Crypto/src/crypto/IDerivationParameters.cs11
-rw-r--r--Crypto/src/crypto/IDigest.cs61
-rw-r--r--Crypto/src/crypto/IMac.cs69
-rw-r--r--Crypto/src/crypto/ISigner.cs50
-rw-r--r--Crypto/src/crypto/ISignerWithRecovery.cs37
-rw-r--r--Crypto/src/crypto/IStreamCipher.cs45
-rw-r--r--Crypto/src/crypto/IWrapper.cs18
-rw-r--r--Crypto/src/crypto/InvalidCipherTextException.cs37
-rw-r--r--Crypto/src/crypto/KeyGenerationParameters.cs55
-rw-r--r--Crypto/src/crypto/MaxBytesExceededException.cs29
-rw-r--r--Crypto/src/crypto/PbeParametersGenerator.cs190
-rw-r--r--Crypto/src/crypto/StreamBlockCipher.cs109
-rw-r--r--Crypto/src/crypto/agreement/DHAgreement.cs93
-rw-r--r--Crypto/src/crypto/agreement/DHBasicAgreement.cs60
-rw-r--r--Crypto/src/crypto/agreement/ECDHBasicAgreement.cs50
-rw-r--r--Crypto/src/crypto/agreement/ECDHCBasicAgreement.cs58
-rw-r--r--Crypto/src/crypto/agreement/ECDHWithKdfBasicAgreement.cs64
-rw-r--r--Crypto/src/crypto/agreement/ECMqvBasicAgreement.cs85
-rw-r--r--Crypto/src/crypto/agreement/ECMqvWithKdfBasicAgreement.cs64
-rw-r--r--Crypto/src/crypto/agreement/kdf/DHKdfParameters.cs57
-rw-r--r--Crypto/src/crypto/agreement/kdf/DHKekGenerator.cs129
-rw-r--r--Crypto/src/crypto/agreement/kdf/ECDHKekGenerator.cs71
-rw-r--r--Crypto/src/crypto/agreement/srp/SRP6Client.cs93
-rw-r--r--Crypto/src/crypto/agreement/srp/SRP6Server.cs90
-rw-r--r--Crypto/src/crypto/agreement/srp/SRP6Utilities.cs85
-rw-r--r--Crypto/src/crypto/agreement/srp/SRP6VerifierGenerator.cs49
-rw-r--r--Crypto/src/crypto/digests/GOST3411Digest.cs343
-rw-r--r--Crypto/src/crypto/digests/GeneralDigest.cs118
-rw-r--r--Crypto/src/crypto/digests/LongDigest.cs346
-rw-r--r--Crypto/src/crypto/digests/MD2Digest.cs247
-rw-r--r--Crypto/src/crypto/digests/MD4Digest.cs271
-rw-r--r--Crypto/src/crypto/digests/MD5Digest.cs301
-rw-r--r--Crypto/src/crypto/digests/NullDigest.cs49
-rw-r--r--Crypto/src/crypto/digests/RipeMD128Digest.cs462
-rw-r--r--Crypto/src/crypto/digests/RipeMD160Digest.cs423
-rw-r--r--Crypto/src/crypto/digests/RipeMD256Digest.cs409
-rw-r--r--Crypto/src/crypto/digests/RipeMD320Digest.cs438
-rw-r--r--Crypto/src/crypto/digests/Sha1Digest.cs263
-rw-r--r--Crypto/src/crypto/digests/Sha224Digest.cs268
-rw-r--r--Crypto/src/crypto/digests/Sha256Digest.cs309
-rw-r--r--Crypto/src/crypto/digests/Sha384Digest.cs87
-rw-r--r--Crypto/src/crypto/digests/Sha512Digest.cs90
-rw-r--r--Crypto/src/crypto/digests/ShortenedDigest.cs82
-rw-r--r--Crypto/src/crypto/digests/TigerDigest.cs868
-rw-r--r--Crypto/src/crypto/digests/WhirlpoolDigest.cs397
-rw-r--r--Crypto/src/crypto/encodings/ISO9796d1Encoding.cs273
-rw-r--r--Crypto/src/crypto/encodings/OaepEncoding.cs345
-rw-r--r--Crypto/src/crypto/encodings/Pkcs1Encoding.cs232
-rw-r--r--Crypto/src/crypto/engines/AesEngine.cs525
-rw-r--r--Crypto/src/crypto/engines/AesFastEngine.cs853
-rw-r--r--Crypto/src/crypto/engines/AesLightEngine.cs419
-rw-r--r--Crypto/src/crypto/engines/AesWrapEngine.cs16
-rw-r--r--Crypto/src/crypto/engines/BlowfishEngine.cs561
-rw-r--r--Crypto/src/crypto/engines/CamelliaEngine.cs669
-rw-r--r--Crypto/src/crypto/engines/CamelliaLightEngine.cs581
-rw-r--r--Crypto/src/crypto/engines/CamelliaWrapEngine.cs16
-rw-r--r--Crypto/src/crypto/engines/Cast5Engine.cs802
-rw-r--r--Crypto/src/crypto/engines/Cast6Engine.cs279
-rw-r--r--Crypto/src/crypto/engines/DesEdeEngine.cs100
-rw-r--r--Crypto/src/crypto/engines/DesEdeWrapEngine.cs322
-rw-r--r--Crypto/src/crypto/engines/DesEngine.cs475
-rw-r--r--Crypto/src/crypto/engines/ElGamalEngine.cs178
-rw-r--r--Crypto/src/crypto/engines/GOST28147Engine.cs377
-rw-r--r--Crypto/src/crypto/engines/HC128Engine.cs235
-rw-r--r--Crypto/src/crypto/engines/HC256Engine.cs224
-rw-r--r--Crypto/src/crypto/engines/ISAACEngine.cs252
-rw-r--r--Crypto/src/crypto/engines/IdeaEngine.cs341
-rw-r--r--Crypto/src/crypto/engines/IesEngine.cs236
-rw-r--r--Crypto/src/crypto/engines/NaccacheSternEngine.cs432
-rw-r--r--Crypto/src/crypto/engines/NoekeonEngine.cs240
-rw-r--r--Crypto/src/crypto/engines/NullEngine.cs70
-rw-r--r--Crypto/src/crypto/engines/RC2Engine.cs312
-rw-r--r--Crypto/src/crypto/engines/RC2WrapEngine.cs370
-rw-r--r--Crypto/src/crypto/engines/RC4Engine.cs147
-rw-r--r--Crypto/src/crypto/engines/RC532Engine.cs294
-rw-r--r--Crypto/src/crypto/engines/RC564Engine.cs295
-rw-r--r--Crypto/src/crypto/engines/RC6Engine.cs362
-rw-r--r--Crypto/src/crypto/engines/RFC3211WrapEngine.cs168
-rw-r--r--Crypto/src/crypto/engines/RFC3394WrapEngine.cs178
-rw-r--r--Crypto/src/crypto/engines/RSABlindedEngine.cs124
-rw-r--r--Crypto/src/crypto/engines/RSABlindingEngine.cs139
-rw-r--r--Crypto/src/crypto/engines/RSACoreEngine.cs156
-rw-r--r--Crypto/src/crypto/engines/RijndaelEngine.cs747
-rw-r--r--Crypto/src/crypto/engines/RsaEngine.cs78
-rw-r--r--Crypto/src/crypto/engines/SEEDEngine.cs361
-rw-r--r--Crypto/src/crypto/engines/SEEDWrapEngine.cs16
-rw-r--r--Crypto/src/crypto/engines/Salsa20Engine.cs299
-rw-r--r--Crypto/src/crypto/engines/SerpentEngine.cs779
-rw-r--r--Crypto/src/crypto/engines/SkipjackEngine.cs255
-rw-r--r--Crypto/src/crypto/engines/TEAEngine.cs168
-rw-r--r--Crypto/src/crypto/engines/TwofishEngine.cs675
-rw-r--r--Crypto/src/crypto/engines/VMPCEngine.cs139
-rw-r--r--Crypto/src/crypto/engines/VMPCKSA3Engine.cs51
-rw-r--r--Crypto/src/crypto/engines/XTEAEngine.cs168
-rw-r--r--Crypto/src/crypto/generators/BaseKdfBytesGenerator.cs141
-rw-r--r--Crypto/src/crypto/generators/DHBasicKeyPairGenerator.cs38
-rw-r--r--Crypto/src/crypto/generators/DHKeyGeneratorHelper.cs53
-rw-r--r--Crypto/src/crypto/generators/DHKeyPairGenerator.cs38
-rw-r--r--Crypto/src/crypto/generators/DHParametersGenerator.cs45
-rw-r--r--Crypto/src/crypto/generators/DHParametersHelper.cs234
-rw-r--r--Crypto/src/crypto/generators/DesEdeKeyGenerator.cs67
-rw-r--r--Crypto/src/crypto/generators/DesKeyGenerator.cs57
-rw-r--r--Crypto/src/crypto/generators/DsaKeyPairGenerator.cs61
-rw-r--r--Crypto/src/crypto/generators/DsaParametersGenerator.cs355
-rw-r--r--Crypto/src/crypto/generators/ECKeyPairGenerator.cs174
-rw-r--r--Crypto/src/crypto/generators/ElGamalKeyPairGenerator.cs40
-rw-r--r--Crypto/src/crypto/generators/ElGamalParametersGenerator.cs46
-rw-r--r--Crypto/src/crypto/generators/GOST3410KeyPairGenerator.cs73
-rw-r--r--Crypto/src/crypto/generators/GOST3410ParametersGenerator.cs530
-rw-r--r--Crypto/src/crypto/generators/Kdf1BytesGenerator.cs27
-rw-r--r--Crypto/src/crypto/generators/Kdf2BytesGenerator.cs28
-rw-r--r--Crypto/src/crypto/generators/Mgf1BytesGenerator.cs117
-rw-r--r--Crypto/src/crypto/generators/NaccacheSternKeyPairGenerator.cs333
-rw-r--r--Crypto/src/crypto/generators/OpenSSLPBEParametersGenerator.cs167
-rw-r--r--Crypto/src/crypto/generators/Pkcs12ParametersGenerator.cs245
-rw-r--r--Crypto/src/crypto/generators/Pkcs5S1ParametersGenerator.cs162
-rw-r--r--Crypto/src/crypto/generators/Pkcs5S2ParametersGenerator.cs172
-rw-r--r--Crypto/src/crypto/generators/RSABlindingFactorGenerator.cs69
-rw-r--r--Crypto/src/crypto/generators/RsaKeyPairGenerator.cs139
-rw-r--r--Crypto/src/crypto/generators/SCrypt.cs140
-rw-r--r--Crypto/src/crypto/io/CipherStream.cs237
-rw-r--r--Crypto/src/crypto/io/DigestStream.cs140
-rw-r--r--Crypto/src/crypto/io/MacStream.cs139
-rw-r--r--Crypto/src/crypto/io/SignerStream.cs140
-rw-r--r--Crypto/src/crypto/macs/CMac.cs240
-rw-r--r--Crypto/src/crypto/macs/CbcBlockCipherMac.cs209
-rw-r--r--Crypto/src/crypto/macs/CfbBlockCipherMac.cs368
-rw-r--r--Crypto/src/crypto/macs/GOST28147Mac.cs296
-rw-r--r--Crypto/src/crypto/macs/HMac.cs134
-rw-r--r--Crypto/src/crypto/macs/ISO9797Alg3Mac.cs275
-rw-r--r--Crypto/src/crypto/macs/VMPCMac.cs173
-rw-r--r--Crypto/src/crypto/modes/CbcBlockCipher.cs231
-rw-r--r--Crypto/src/crypto/modes/CcmBlockCipher.cs345
-rw-r--r--Crypto/src/crypto/modes/CfbBlockCipher.cs218
-rw-r--r--Crypto/src/crypto/modes/CtsBlockCipher.cs253
-rw-r--r--Crypto/src/crypto/modes/EAXBlockCipher.cs302
-rw-r--r--Crypto/src/crypto/modes/GCMBlockCipher.cs400
-rw-r--r--Crypto/src/crypto/modes/GOFBBlockCipher.cs223
-rw-r--r--Crypto/src/crypto/modes/IAeadBlockCipher.cs90
-rw-r--r--Crypto/src/crypto/modes/OfbBlockCipher.cs178
-rw-r--r--Crypto/src/crypto/modes/OpenPgpCfbBlockCipher.cs337
-rw-r--r--Crypto/src/crypto/modes/SicBlockCipher.cs110
-rw-r--r--Crypto/src/crypto/modes/gcm/BasicGcmExponentiator.cs40
-rw-r--r--Crypto/src/crypto/modes/gcm/BasicGcmMultiplier.cs20
-rw-r--r--Crypto/src/crypto/modes/gcm/GcmUtilities.cs149
-rw-r--r--Crypto/src/crypto/modes/gcm/IGcmExponentiator.cs10
-rw-r--r--Crypto/src/crypto/modes/gcm/IGcmMultiplier.cs10
-rw-r--r--Crypto/src/crypto/modes/gcm/Tables1kGcmExponentiator.cs44
-rw-r--r--Crypto/src/crypto/modes/gcm/Tables64kGcmMultiplier.cs64
-rw-r--r--Crypto/src/crypto/modes/gcm/Tables8kGcmMultiplier.cs90
-rw-r--r--Crypto/src/crypto/paddings/BlockCipherPadding.cs43
-rw-r--r--Crypto/src/crypto/paddings/ISO10126d2Padding.cs76
-rw-r--r--Crypto/src/crypto/paddings/ISO7816d4Padding.cs79
-rw-r--r--Crypto/src/crypto/paddings/PaddedBufferedBlockCipher.cs288
-rw-r--r--Crypto/src/crypto/paddings/Pkcs7Padding.cs79
-rw-r--r--Crypto/src/crypto/paddings/TbcPadding.cs79
-rw-r--r--Crypto/src/crypto/paddings/X923Padding.cs82
-rw-r--r--Crypto/src/crypto/paddings/ZeroBytePadding.cs68
-rw-r--r--Crypto/src/crypto/parameters/AEADParameters.cs53
-rw-r--r--Crypto/src/crypto/parameters/CcmParameters.cs25
-rw-r--r--Crypto/src/crypto/parameters/DHKeyGenerationParameters.cs31
-rw-r--r--Crypto/src/crypto/parameters/DHKeyParameters.cs76
-rw-r--r--Crypto/src/crypto/parameters/DHParameters.cs184
-rw-r--r--Crypto/src/crypto/parameters/DHPrivateKeyParameters.cs60
-rw-r--r--Crypto/src/crypto/parameters/DHPublicKeyParameters.cs66
-rw-r--r--Crypto/src/crypto/parameters/DHValidationParameters.cs59
-rw-r--r--Crypto/src/crypto/parameters/DesEdeParameters.cs95
-rw-r--r--Crypto/src/crypto/parameters/DesParameters.cs130
-rw-r--r--Crypto/src/crypto/parameters/DsaKeyGenerationParameters.cs26
-rw-r--r--Crypto/src/crypto/parameters/DsaKeyParameters.cs59
-rw-r--r--Crypto/src/crypto/parameters/DsaParameters.cs85
-rw-r--r--Crypto/src/crypto/parameters/DsaPrivateKeyParameters.cs53
-rw-r--r--Crypto/src/crypto/parameters/DsaPublicKeyParameters.cs52
-rw-r--r--Crypto/src/crypto/parameters/DsaValidationParameters.cs59
-rw-r--r--Crypto/src/crypto/parameters/ECDomainParameters.cs116
-rw-r--r--Crypto/src/crypto/parameters/ECKeyGenerationParameters.cs41
-rw-r--r--Crypto/src/crypto/parameters/ECKeyParameters.cs145
-rw-r--r--Crypto/src/crypto/parameters/ECPrivateKeyParameters.cs87
-rw-r--r--Crypto/src/crypto/parameters/ECPublicKeyParameters.cs86
-rw-r--r--Crypto/src/crypto/parameters/ElGamalKeyGenerationParameters.cs31
-rw-r--r--Crypto/src/crypto/parameters/ElGamalKeyParameters.cs59
-rw-r--r--Crypto/src/crypto/parameters/ElGamalParameters.cs81
-rw-r--r--Crypto/src/crypto/parameters/ElGamalPrivateKeyParameters.cs53
-rw-r--r--Crypto/src/crypto/parameters/ElGamalPublicKeyParameters.cs53
-rw-r--r--Crypto/src/crypto/parameters/GOST3410KeyGenerationParameters.cs55
-rw-r--r--Crypto/src/crypto/parameters/GOST3410KeyParameters.cs58
-rw-r--r--Crypto/src/crypto/parameters/GOST3410Parameters.cs86
-rw-r--r--Crypto/src/crypto/parameters/GOST3410PrivateKeyParameters.cs41
-rw-r--r--Crypto/src/crypto/parameters/GOST3410PublicKeyParameters.cs40
-rw-r--r--Crypto/src/crypto/parameters/GOST3410ValidationParameters.cs51
-rw-r--r--Crypto/src/crypto/parameters/ISO18033KDFParameters.cs25
-rw-r--r--Crypto/src/crypto/parameters/IesParameters.cs49
-rw-r--r--Crypto/src/crypto/parameters/IesWithCipherParameters.cs33
-rw-r--r--Crypto/src/crypto/parameters/KdfParameters.cs33
-rw-r--r--Crypto/src/crypto/parameters/KeyParameter.cs43
-rw-r--r--Crypto/src/crypto/parameters/MgfParameters.cs31
-rw-r--r--Crypto/src/crypto/parameters/MqvPrivateParameters.cs44
-rw-r--r--Crypto/src/crypto/parameters/MqvPublicParameters.cs29
-rw-r--r--Crypto/src/crypto/parameters/NaccacheSternKeyGenerationParameters.cs101
-rw-r--r--Crypto/src/crypto/parameters/NaccacheSternKeyParameters.cs44
-rw-r--r--Crypto/src/crypto/parameters/NaccacheSternPrivateKeyParameters.cs79
-rw-r--r--Crypto/src/crypto/parameters/ParametersWithIV.cs44
-rw-r--r--Crypto/src/crypto/parameters/ParametersWithRandom.cs48
-rw-r--r--Crypto/src/crypto/parameters/ParametersWithSBox.cs24
-rw-r--r--Crypto/src/crypto/parameters/ParametersWithSalt.cs39
-rw-r--r--Crypto/src/crypto/parameters/RC2Parameters.cs47
-rw-r--r--Crypto/src/crypto/parameters/RC5Parameters.cs27
-rw-r--r--Crypto/src/crypto/parameters/RSABlindingParameters.cs34
-rw-r--r--Crypto/src/crypto/parameters/RsaKeyGenerationParameters.cs55
-rw-r--r--Crypto/src/crypto/parameters/RsaKeyParameters.cs63
-rw-r--r--Crypto/src/crypto/parameters/RsaPrivateCrtKeyParameters.cs104
-rw-r--r--Crypto/src/crypto/prng/CryptoApiRandomGenerator.cs66
-rw-r--r--Crypto/src/crypto/prng/DigestRandomGenerator.cs129
-rw-r--r--Crypto/src/crypto/prng/IRandomGenerator.cs26
-rw-r--r--Crypto/src/crypto/prng/ReversedWindowGenerator.cs98
-rw-r--r--Crypto/src/crypto/prng/ThreadedSeedGenerator.cs99
-rw-r--r--Crypto/src/crypto/prng/VMPCRandomGenerator.cs115
-rw-r--r--Crypto/src/crypto/signers/DsaDigestSigner.cs145
-rw-r--r--Crypto/src/crypto/signers/DsaSigner.cs136
-rw-r--r--Crypto/src/crypto/signers/ECDsaSigner.cs156
-rw-r--r--Crypto/src/crypto/signers/ECGOST3410Signer.cs154
-rw-r--r--Crypto/src/crypto/signers/ECNRSigner.cs186
-rw-r--r--Crypto/src/crypto/signers/GOST3410DigestSigner.cs145
-rw-r--r--Crypto/src/crypto/signers/GOST3410Signer.cs132
-rw-r--r--Crypto/src/crypto/signers/GenericSigner.cs129
-rw-r--r--Crypto/src/crypto/signers/Iso9796d2PssSigner.cs576
-rw-r--r--Crypto/src/crypto/signers/Iso9796d2Signer.cs557
-rw-r--r--Crypto/src/crypto/signers/PssSigner.cs345
-rw-r--r--Crypto/src/crypto/signers/RsaDigestSigner.cs228
-rw-r--r--Crypto/src/crypto/tls/AlertDescription.cs47
-rw-r--r--Crypto/src/crypto/tls/AlertLevel.cs11
-rw-r--r--Crypto/src/crypto/tls/AlwaysValidVerifyer.cs24
-rw-r--r--Crypto/src/crypto/tls/ByteQueue.cs125
-rw-r--r--Crypto/src/crypto/tls/Certificate.cs111
-rw-r--r--Crypto/src/crypto/tls/CertificateRequest.cs28
-rw-r--r--Crypto/src/crypto/tls/CipherSuite.cs136
-rw-r--r--Crypto/src/crypto/tls/ClientCertificateType.cs20
-rw-r--r--Crypto/src/crypto/tls/CombinedHash.cs82
-rw-r--r--Crypto/src/crypto/tls/CompressionMethod.cs20
-rw-r--r--Crypto/src/crypto/tls/ContentType.cs13
-rw-r--r--Crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs67
-rw-r--r--Crypto/src/crypto/tls/DefaultTlsCipherFactory.cs73
-rw-r--r--Crypto/src/crypto/tls/DefaultTlsClient.cs259
-rw-r--r--Crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs76
-rw-r--r--Crypto/src/crypto/tls/DigestAlgorithm.cs21
-rw-r--r--Crypto/src/crypto/tls/ECCurveType.cs29
-rw-r--r--Crypto/src/crypto/tls/ECPointFormat.cs16
-rw-r--r--Crypto/src/crypto/tls/EncryptionAlgorithm.cs32
-rw-r--r--Crypto/src/crypto/tls/ExtensionType.cs31
-rw-r--r--Crypto/src/crypto/tls/HandshakeType.cs19
-rw-r--r--Crypto/src/crypto/tls/ICertificateVerifyer.cs18
-rw-r--r--Crypto/src/crypto/tls/KeyExchangeAlgorithm.cs36
-rw-r--r--Crypto/src/crypto/tls/LegacyTlsAuthentication.cs30
-rw-r--r--Crypto/src/crypto/tls/LegacyTlsClient.cs26
-rw-r--r--Crypto/src/crypto/tls/NamedCurve.cs72
-rw-r--r--Crypto/src/crypto/tls/PskTlsClient.cs182
-rw-r--r--Crypto/src/crypto/tls/RecordStream.cs166
-rw-r--r--Crypto/src/crypto/tls/SecurityParameters.cs26
-rw-r--r--Crypto/src/crypto/tls/SrpTlsClient.cs188
-rw-r--r--Crypto/src/crypto/tls/Ssl3Mac.cs114
-rw-r--r--Crypto/src/crypto/tls/TlsAgreementCredentials.cs11
-rw-r--r--Crypto/src/crypto/tls/TlsAuthentication.cs31
-rw-r--r--Crypto/src/crypto/tls/TlsBlockCipher.cs248
-rw-r--r--Crypto/src/crypto/tls/TlsCipher.cs14
-rw-r--r--Crypto/src/crypto/tls/TlsCipherFactory.cs12
-rw-r--r--Crypto/src/crypto/tls/TlsClient.cs129
-rw-r--r--Crypto/src/crypto/tls/TlsClientContext.cs15
-rw-r--r--Crypto/src/crypto/tls/TlsClientContextImpl.cs37
-rw-r--r--Crypto/src/crypto/tls/TlsCompression.cs12
-rw-r--r--Crypto/src/crypto/tls/TlsCredentials.cs9
-rw-r--r--Crypto/src/crypto/tls/TlsDHKeyExchange.cs201
-rw-r--r--Crypto/src/crypto/tls/TlsDHUtilities.cs70
-rw-r--r--Crypto/src/crypto/tls/TlsDeflateCompression.cs45
-rw-r--r--Crypto/src/crypto/tls/TlsDheKeyExchange.cs56
-rw-r--r--Crypto/src/crypto/tls/TlsDsaSigner.cs51
-rw-r--r--Crypto/src/crypto/tls/TlsDssSigner.cs21
-rw-r--r--Crypto/src/crypto/tls/TlsECDHKeyExchange.cs230
-rw-r--r--Crypto/src/crypto/tls/TlsECDheKeyExchange.cs110
-rw-r--r--Crypto/src/crypto/tls/TlsECDsaSigner.cs21
-rw-r--r--Crypto/src/crypto/tls/TlsException.cs11
-rw-r--r--Crypto/src/crypto/tls/TlsFatalAlert.cs21
-rw-r--r--Crypto/src/crypto/tls/TlsKeyExchange.cs38
-rw-r--r--Crypto/src/crypto/tls/TlsMac.cs106
-rw-r--r--Crypto/src/crypto/tls/TlsNullCipher.cs28
-rw-r--r--Crypto/src/crypto/tls/TlsNullCompression.cs19
-rw-r--r--Crypto/src/crypto/tls/TlsProtocolHandler.cs1259
-rw-r--r--Crypto/src/crypto/tls/TlsPskIdentity.cs15
-rw-r--r--Crypto/src/crypto/tls/TlsPskKeyExchange.cs149
-rw-r--r--Crypto/src/crypto/tls/TlsRsaKeyExchange.cs165
-rw-r--r--Crypto/src/crypto/tls/TlsRsaSigner.cs53
-rw-r--r--Crypto/src/crypto/tls/TlsRsaUtilities.cs42
-rw-r--r--Crypto/src/crypto/tls/TlsSigner.cs18
-rw-r--r--Crypto/src/crypto/tls/TlsSignerCredentials.cs11
-rw-r--r--Crypto/src/crypto/tls/TlsSrpKeyExchange.cs203
-rw-r--r--Crypto/src/crypto/tls/TlsStream.cs89
-rw-r--r--Crypto/src/crypto/tls/TlsUtilities.cs286
-rw-r--r--Crypto/src/crypto/util/Pack.cs219
-rw-r--r--Crypto/src/math/BigInteger.cs3141
-rw-r--r--Crypto/src/math/ec/ECAlgorithms.cs93
-rw-r--r--Crypto/src/math/ec/ECCurve.cs661
-rw-r--r--Crypto/src/math/ec/ECFieldElement.cs1253
-rw-r--r--Crypto/src/math/ec/ECPoint.cs567
-rw-r--r--Crypto/src/math/ec/IntArray.cs485
-rw-r--r--Crypto/src/math/ec/abc/SimpleBigDecimal.cs241
-rw-r--r--Crypto/src/math/ec/abc/Tnaf.cs834
-rw-r--r--Crypto/src/math/ec/abc/ZTauElement.cs36
-rw-r--r--Crypto/src/math/ec/multiplier/ECMultiplier.cs18
-rw-r--r--Crypto/src/math/ec/multiplier/FpNafMultiplier.cs39
-rw-r--r--Crypto/src/math/ec/multiplier/PreCompInfo.cs11
-rw-r--r--Crypto/src/math/ec/multiplier/ReferenceMultiplier.cs30
-rw-r--r--Crypto/src/math/ec/multiplier/WNafMultiplier.cs241
-rw-r--r--Crypto/src/math/ec/multiplier/WNafPreCompInfo.cs46
-rw-r--r--Crypto/src/math/ec/multiplier/WTauNafMultiplier.cs120
-rw-r--r--Crypto/src/math/ec/multiplier/WTauNafPreCompInfo.cs41
-rw-r--r--Crypto/src/ocsp/BasicOCSPResp.cs220
-rw-r--r--Crypto/src/ocsp/BasicOCSPRespGenerator.cs318
-rw-r--r--Crypto/src/ocsp/CertificateID.cs141
-rw-r--r--Crypto/src/ocsp/CertificateStatus.cs9
-rw-r--r--Crypto/src/ocsp/OCSPException.cs25
-rw-r--r--Crypto/src/ocsp/OCSPReq.cs268
-rw-r--r--Crypto/src/ocsp/OCSPReqGenerator.cs243
-rw-r--r--Crypto/src/ocsp/OCSPResp.cs100
-rw-r--r--Crypto/src/ocsp/OCSPRespGenerator.cs54
-rw-r--r--Crypto/src/ocsp/OCSPRespStatus.cs22
-rw-r--r--Crypto/src/ocsp/OCSPUtil.cs133
-rw-r--r--Crypto/src/ocsp/Req.cs38
-rw-r--r--Crypto/src/ocsp/RespData.cs60
-rw-r--r--Crypto/src/ocsp/RespID.cs72
-rw-r--r--Crypto/src/ocsp/RevokedStatus.cs58
-rw-r--r--Crypto/src/ocsp/SingleResp.cs81
-rw-r--r--Crypto/src/ocsp/UnknownStatus.cs15
-rw-r--r--Crypto/src/openpgp/IStreamGenerator.cs7
-rw-r--r--Crypto/src/openpgp/PGPKeyRing.cs79
-rw-r--r--Crypto/src/openpgp/PGPObject.cs9
-rw-r--r--Crypto/src/openpgp/PGPUserAttributeSubpacketVectorGenerator.cs33
-rw-r--r--Crypto/src/openpgp/PgpCompressedData.cs50
-rw-r--r--Crypto/src/openpgp/PgpCompressedDataGenerator.cs203
-rw-r--r--Crypto/src/openpgp/PgpDataValidationException.cs15
-rw-r--r--Crypto/src/openpgp/PgpEncryptedData.cs151
-rw-r--r--Crypto/src/openpgp/PgpEncryptedDataGenerator.cs506
-rw-r--r--Crypto/src/openpgp/PgpEncryptedDataList.cs72
-rw-r--r--Crypto/src/openpgp/PgpException.cs19
-rw-r--r--Crypto/src/openpgp/PgpExperimental.cs16
-rw-r--r--Crypto/src/openpgp/PgpKeyFlags.cs13
-rw-r--r--Crypto/src/openpgp/PgpKeyPair.cs67
-rw-r--r--Crypto/src/openpgp/PgpKeyRingGenerator.cs167
-rw-r--r--Crypto/src/openpgp/PgpKeyValidationException.cs15
-rw-r--r--Crypto/src/openpgp/PgpLiteralData.cs63
-rw-r--r--Crypto/src/openpgp/PgpLiteralDataGenerator.cs180
-rw-r--r--Crypto/src/openpgp/PgpMarker.cs18
-rw-r--r--Crypto/src/openpgp/PgpObjectFactory.cs143
-rw-r--r--Crypto/src/openpgp/PgpOnePassSignature.cs179
-rw-r--r--Crypto/src/openpgp/PgpOnePassSignatureList.cs51
-rw-r--r--Crypto/src/openpgp/PgpPbeEncryptedData.cs135
-rw-r--r--Crypto/src/openpgp/PgpPrivateKey.cs42
-rw-r--r--Crypto/src/openpgp/PgpPublicKey.cs890
-rw-r--r--Crypto/src/openpgp/PgpPublicKeyEncryptedData.cs252
-rw-r--r--Crypto/src/openpgp/PgpPublicKeyRing.cs200
-rw-r--r--Crypto/src/openpgp/PgpPublicKeyRingBundle.cs280
-rw-r--r--Crypto/src/openpgp/PgpSecretKey.cs666
-rw-r--r--Crypto/src/openpgp/PgpSecretKeyRing.cs301
-rw-r--r--Crypto/src/openpgp/PgpSecretKeyRingBundle.cs281
-rw-r--r--Crypto/src/openpgp/PgpSignature.cs422
-rw-r--r--Crypto/src/openpgp/PgpSignatureGenerator.cs393
-rw-r--r--Crypto/src/openpgp/PgpSignatureList.cs51
-rw-r--r--Crypto/src/openpgp/PgpSignatureSubpacketGenerator.cs193
-rw-r--r--Crypto/src/openpgp/PgpSignatureSubpacketVector.cs229
-rw-r--r--Crypto/src/openpgp/PgpUserAttributeSubpacketVector.cs81
-rw-r--r--Crypto/src/openpgp/PgpUtilities.cs428
-rw-r--r--Crypto/src/openpgp/PgpV3SignatureGenerator.cs199
-rw-r--r--Crypto/src/openpgp/WrappedGeneratorStream.cs28
-rw-r--r--Crypto/src/openssl/EncryptionException.cs22
-rw-r--r--Crypto/src/openssl/IPasswordFinder.cs9
-rw-r--r--Crypto/src/openssl/MiscPemGenerator.cs277
-rw-r--r--Crypto/src/openssl/PEMException.cs22
-rw-r--r--Crypto/src/openssl/PEMReader.cs407
-rw-r--r--Crypto/src/openssl/PEMUtilities.cs158
-rw-r--r--Crypto/src/openssl/PEMWriter.cs61
-rw-r--r--Crypto/src/openssl/PasswordException.cs22
-rw-r--r--Crypto/src/openssl/Pkcs8Generator.cs111
-rw-r--r--Crypto/src/pkcs/AsymmetricKeyEntry.cs60
-rw-r--r--Crypto/src/pkcs/EncryptedPrivateKeyInfoFactory.cs75
-rw-r--r--Crypto/src/pkcs/PKCS12StoreBuilder.cs41
-rw-r--r--Crypto/src/pkcs/Pkcs10CertificationRequest.cs466
-rw-r--r--Crypto/src/pkcs/Pkcs10CertificationRequestDelaySigned.cs150
-rw-r--r--Crypto/src/pkcs/Pkcs12Entry.cs64
-rw-r--r--Crypto/src/pkcs/Pkcs12Store.cs1231
-rw-r--r--Crypto/src/pkcs/Pkcs12Utilities.cs77
-rw-r--r--Crypto/src/pkcs/PrivateKeyInfoFactory.cs214
-rw-r--r--Crypto/src/pkcs/X509CertificateEntry.cs60
-rw-r--r--Crypto/src/pkix/CertStatus.cs35
-rw-r--r--Crypto/src/pkix/PkixAttrCertChecker.cs57
-rw-r--r--Crypto/src/pkix/PkixAttrCertPathBuilder.cs215
-rw-r--r--Crypto/src/pkix/PkixAttrCertPathValidator.cs76
-rw-r--r--Crypto/src/pkix/PkixBuilderParameters.cs140
-rw-r--r--Crypto/src/pkix/PkixCertPath.cs460
-rw-r--r--Crypto/src/pkix/PkixCertPathBuilder.cs205
-rw-r--r--Crypto/src/pkix/PkixCertPathBuilderException.cs19
-rw-r--r--Crypto/src/pkix/PkixCertPathBuilderResult.cs45
-rw-r--r--Crypto/src/pkix/PkixCertPathChecker.cs101
-rw-r--r--Crypto/src/pkix/PkixCertPathValidator.cs420
-rw-r--r--Crypto/src/pkix/PkixCertPathValidatorException.cs218
-rw-r--r--Crypto/src/pkix/PkixCertPathValidatorResult.cs69
-rw-r--r--Crypto/src/pkix/PkixCertPathValidatorUtilities.cs1194
-rw-r--r--Crypto/src/pkix/PkixCrlUtilities.cs114
-rw-r--r--Crypto/src/pkix/PkixNameConstraintValidator.cs1937
-rw-r--r--Crypto/src/pkix/PkixNameConstraintValidatorException.cs12
-rw-r--r--Crypto/src/pkix/PkixParameters.cs893
-rw-r--r--Crypto/src/pkix/PkixPolicyNode.cs158
-rw-r--r--Crypto/src/pkix/ReasonsMask.cs96
-rw-r--r--Crypto/src/pkix/Rfc3280CertPathUtilities.cs2448
-rw-r--r--Crypto/src/pkix/Rfc3281CertPathUtilities.cs608
-rw-r--r--Crypto/src/pkix/TrustAnchor.cs259
-rw-r--r--Crypto/src/security/AgreementUtilities.cs106
-rw-r--r--Crypto/src/security/CipherUtilities.cs696
-rw-r--r--Crypto/src/security/DigestUtilities.cs189
-rw-r--r--Crypto/src/security/DotNetUtilities.cs222
-rw-r--r--Crypto/src/security/GeneralSecurityException.cs26
-rw-r--r--Crypto/src/security/GeneratorUtilities.cs341
-rw-r--r--Crypto/src/security/InvalidKeyException.cs11
-rw-r--r--Crypto/src/security/InvalidParameterException.cs11
-rw-r--r--Crypto/src/security/KeyException.cs11
-rw-r--r--Crypto/src/security/MacUtilities.cs239
-rw-r--r--Crypto/src/security/NoSuchAlgorithmException.cs12
-rw-r--r--Crypto/src/security/ParameterUtilities.cs327
-rw-r--r--Crypto/src/security/PbeUtilities.cs658
-rw-r--r--Crypto/src/security/PrivateKeyFactory.cs221
-rw-r--r--Crypto/src/security/PublicKeyFactory.cs253
-rw-r--r--Crypto/src/security/SecureRandom.cs228
-rw-r--r--Crypto/src/security/SecurityUtilityException.cs33
-rw-r--r--Crypto/src/security/SignatureException.cs11
-rw-r--r--Crypto/src/security/SignerUtilities.cs550
-rw-r--r--Crypto/src/security/WrapperUtilities.cs153
-rw-r--r--Crypto/src/security/cert/CertificateEncodingException.cs11
-rw-r--r--Crypto/src/security/cert/CertificateException.cs11
-rw-r--r--Crypto/src/security/cert/CertificateExpiredException.cs11
-rw-r--r--Crypto/src/security/cert/CertificateNotYetValidException.cs11
-rw-r--r--Crypto/src/security/cert/CertificateParsingException.cs11
-rw-r--r--Crypto/src/security/cert/CrlException.cs11
-rw-r--r--Crypto/src/tsp/GenTimeAccuracy.cs33
-rw-r--r--Crypto/src/tsp/TSPAlgorithms.cs48
-rw-r--r--Crypto/src/tsp/TSPException.cs25
-rw-r--r--Crypto/src/tsp/TSPUtil.cs202
-rw-r--r--Crypto/src/tsp/TSPValidationException.cs39
-rw-r--r--Crypto/src/tsp/TimeStampRequest.cs196
-rw-r--r--Crypto/src/tsp/TimeStampRequestGenerator.cs139
-rw-r--r--Crypto/src/tsp/TimeStampResponse.cs184
-rw-r--r--Crypto/src/tsp/TimeStampResponseGenerator.cs210
-rw-r--r--Crypto/src/tsp/TimeStampToken.cs305
-rw-r--r--Crypto/src/tsp/TimeStampTokenGenerator.cs245
-rw-r--r--Crypto/src/tsp/TimeStampTokenInfo.cs107
-rw-r--r--Crypto/src/util/Arrays.cs226
-rw-r--r--Crypto/src/util/BigIntegers.cs72
-rw-r--r--Crypto/src/util/Enums.cs72
-rw-r--r--Crypto/src/util/Platform.cs171
-rw-r--r--Crypto/src/util/Strings.cs107
-rw-r--r--Crypto/src/util/collections/CollectionUtilities.cs71
-rw-r--r--Crypto/src/util/collections/EmptyEnumerable.cs44
-rw-r--r--Crypto/src/util/collections/EnumerableProxy.cs25
-rw-r--r--Crypto/src/util/collections/HashSet.cs99
-rw-r--r--Crypto/src/util/collections/ISet.cs19
-rw-r--r--Crypto/src/util/collections/LinkedDictionary.cs178
-rw-r--r--Crypto/src/util/collections/UnmodifiableDictionary.cs64
-rw-r--r--Crypto/src/util/collections/UnmodifiableDictionaryProxy.cs66
-rw-r--r--Crypto/src/util/collections/UnmodifiableList.cs67
-rw-r--r--Crypto/src/util/collections/UnmodifiableListProxy.cs61
-rw-r--r--Crypto/src/util/collections/UnmodifiableSet.cs59
-rw-r--r--Crypto/src/util/collections/UnmodifiableSetProxy.cs56
-rw-r--r--Crypto/src/util/date/DateTimeObject.cs25
-rw-r--r--Crypto/src/util/date/DateTimeUtilities.cs47
-rw-r--r--Crypto/src/util/encoders/Base64.cs95
-rw-r--r--Crypto/src/util/encoders/Base64Encoder.cs307
-rw-r--r--Crypto/src/util/encoders/BufferedDecoder.cs117
-rw-r--r--Crypto/src/util/encoders/BufferedEncoder.cs117
-rw-r--r--Crypto/src/util/encoders/Hex.cs131
-rw-r--r--Crypto/src/util/encoders/HexEncoder.cs164
-rw-r--r--Crypto/src/util/encoders/HexTranslator.cs108
-rw-r--r--Crypto/src/util/encoders/IEncoder.cs18
-rw-r--r--Crypto/src/util/encoders/Translator.cs19
-rw-r--r--Crypto/src/util/encoders/UrlBase64.cs127
-rw-r--r--Crypto/src/util/encoders/UrlBase64Encoder.cs31
-rw-r--r--Crypto/src/util/io/BaseInputStream.cs54
-rw-r--r--Crypto/src/util/io/BaseOutputStream.cs54
-rw-r--r--Crypto/src/util/io/PushbackStream.cs52
-rw-r--r--Crypto/src/util/io/StreamOverflowException.cs27
-rw-r--r--Crypto/src/util/io/Streams.cs94
-rw-r--r--Crypto/src/util/io/TeeInputStream.cs54
-rw-r--r--Crypto/src/util/io/TeeOutputStream.cs42
-rw-r--r--Crypto/src/util/io/pem/PemGenerationException.cs26
-rw-r--r--Crypto/src/util/io/pem/PemHeader.cs55
-rw-r--r--Crypto/src/util/io/pem/PemObject.cs47
-rw-r--r--Crypto/src/util/io/pem/PemObjectGenerator.cs13
-rw-r--r--Crypto/src/util/io/pem/PemObjectParser.cs17
-rw-r--r--Crypto/src/util/io/pem/PemReader.cs94
-rw-r--r--Crypto/src/util/io/pem/PemWriter.cs120
-rw-r--r--Crypto/src/util/net/IPAddress.cs197
-rw-r--r--Crypto/src/util/zlib/Adler32.cs88
-rw-r--r--Crypto/src/util/zlib/Deflate.cs1640
-rw-r--r--Crypto/src/util/zlib/InfBlocks.cs618
-rw-r--r--Crypto/src/util/zlib/InfCodes.cs611
-rw-r--r--Crypto/src/util/zlib/InfTree.cs523
-rw-r--r--Crypto/src/util/zlib/Inflate.cs387
-rw-r--r--Crypto/src/util/zlib/JZlib.cs73
-rw-r--r--Crypto/src/util/zlib/StaticTree.cs152
-rw-r--r--Crypto/src/util/zlib/Tree.cs367
-rw-r--r--Crypto/src/util/zlib/ZDeflaterOutputStream.cs155
-rw-r--r--Crypto/src/util/zlib/ZInflaterInputStream.cs131
-rw-r--r--Crypto/src/util/zlib/ZInputStream.cs196
-rw-r--r--Crypto/src/util/zlib/ZOutputStream.cs220
-rw-r--r--Crypto/src/util/zlib/ZStream.cs214
-rw-r--r--Crypto/src/x509/AttributeCertificateHolder.cs442
-rw-r--r--Crypto/src/x509/AttributeCertificateIssuer.cs199
-rw-r--r--Crypto/src/x509/IX509AttributeCertificate.cs57
-rw-r--r--Crypto/src/x509/IX509Extension.cs27
-rw-r--r--Crypto/src/x509/PEMParser.cs94
-rw-r--r--Crypto/src/x509/PrincipalUtil.cs70
-rw-r--r--Crypto/src/x509/SubjectPublicKeyInfoFactory.cs187
-rw-r--r--Crypto/src/x509/X509AttrCertParser.cs173
-rw-r--r--Crypto/src/x509/X509Attribute.cs76
-rw-r--r--Crypto/src/x509/X509CertPairParser.cs95
-rw-r--r--Crypto/src/x509/X509Certificate.cs595
-rw-r--r--Crypto/src/x509/X509CertificatePair.cs123
-rw-r--r--Crypto/src/x509/X509CertificateParser.cs183
-rw-r--r--Crypto/src/x509/X509Crl.cs403
-rw-r--r--Crypto/src/x509/X509CrlEntry.cs201
-rw-r--r--Crypto/src/x509/X509CrlParser.cs195
-rw-r--r--Crypto/src/x509/X509ExtensionBase.cs82
-rw-r--r--Crypto/src/x509/X509KeyUsage.cs59
-rw-r--r--Crypto/src/x509/X509SignatureUtil.cs128
-rw-r--r--Crypto/src/x509/X509Utilities.cs188
-rw-r--r--Crypto/src/x509/X509V1CertificateGenerator.cs205
-rw-r--r--Crypto/src/x509/X509V2AttributeCertificate.cs255
-rw-r--r--Crypto/src/x509/X509V2AttributeCertificateGenerator.cs180
-rw-r--r--Crypto/src/x509/X509V2CRLGenerator.cs261
-rw-r--r--Crypto/src/x509/X509V3CertificateGenerator.cs346
-rw-r--r--Crypto/src/x509/extension/AuthorityKeyIdentifierStructure.cs102
-rw-r--r--Crypto/src/x509/extension/SubjectKeyIdentifierStructure.cs49
-rw-r--r--Crypto/src/x509/extension/X509ExtensionUtil.cs89
-rw-r--r--Crypto/src/x509/store/IX509Selector.cs15
-rw-r--r--Crypto/src/x509/store/IX509Store.cs11
-rw-r--r--Crypto/src/x509/store/IX509StoreParameters.cs8
-rw-r--r--Crypto/src/x509/store/NoSuchStoreException.cs25
-rw-r--r--Crypto/src/x509/store/X509AttrCertStoreSelector.cs376
-rw-r--r--Crypto/src/x509/store/X509CertPairStoreSelector.cs92
-rw-r--r--Crypto/src/x509/store/X509CertStoreSelector.cs337
-rw-r--r--Crypto/src/x509/store/X509CollectionStore.cs51
-rw-r--r--Crypto/src/x509/store/X509CollectionStoreParameters.cs60
-rw-r--r--Crypto/src/x509/store/X509CrlStoreSelector.cs283
-rw-r--r--Crypto/src/x509/store/X509StoreException.cs25
-rw-r--r--Crypto/src/x509/store/X509StoreFactory.cs61
1122 files changed, 158139 insertions, 0 deletions
diff --git a/Crypto/src/AssemblyInfo.cs b/Crypto/src/AssemblyInfo.cs
new file mode 100644

index 000000000..7064bf82e --- /dev/null +++ b/Crypto/src/AssemblyInfo.cs
@@ -0,0 +1,82 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +//using System.Security.Permissions; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +#if INCLUDE_IDEA +[assembly: AssemblyTitle("BouncyCastle.CryptoExt")] +[assembly: AssemblyDescription("Bouncy Castle Cryptography API (Extended)")] +#else +[assembly: AssemblyTitle("BouncyCastle.Crypto")] +[assembly: AssemblyDescription("Bouncy Castle Cryptography API")] +#endif +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("The Legion of the Bouncy Castle")] +[assembly: AssemblyProduct("Bouncy Castle for .NET")] +[assembly: AssemblyCopyright("Copyright (C) 2000-2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision + +[assembly: AssemblyVersion(AssemblyInfo.Version)] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\<configuration>. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +#if STRONG_NAME +[assembly: AssemblyKeyFile(@"../BouncyCastle.snk")] +#else +[assembly: AssemblyKeyFile("")] +#endif +[assembly: AssemblyKeyName("")] + +[assembly: CLSCompliant(true)] + +// Start with no permissions +//[assembly: PermissionSet(SecurityAction.RequestOptional, Unrestricted=false)] +//...and explicitly add those we need + +// see Org.BouncyCastle.Crypto.Encodings.Pkcs1Encoding.StrictLengthEnabledProperty +//[assembly: EnvironmentPermission(SecurityAction.RequestOptional, Read="Org.BouncyCastle.Pkcs1.Strict")] + +internal class AssemblyInfo +{ + public const string Version = @"1.7.0.0"; +} + diff --git a/Crypto/src/asn1/ASN1Generator.cs b/Crypto/src/asn1/ASN1Generator.cs new file mode 100644
index 000000000..e56051736 --- /dev/null +++ b/Crypto/src/asn1/ASN1Generator.cs
@@ -0,0 +1,27 @@ +using System.Collections; +using System.IO; + +namespace Org.BouncyCastle.Asn1 +{ + public abstract class Asn1Generator + { + private Stream _out; + + protected Asn1Generator( + Stream outStream) + { + _out = outStream; + } + + protected Stream Out + { + get { return _out; } + } + + public abstract void AddObject(Asn1Encodable obj); + + public abstract Stream GetRawOutputStream(); + + public abstract void Close(); + } +} diff --git a/Crypto/src/asn1/ASN1OctetStringParser.cs b/Crypto/src/asn1/ASN1OctetStringParser.cs new file mode 100644
index 000000000..5815aa42f --- /dev/null +++ b/Crypto/src/asn1/ASN1OctetStringParser.cs
@@ -0,0 +1,10 @@ +using System.IO; + +namespace Org.BouncyCastle.Asn1 +{ + public interface Asn1OctetStringParser + : IAsn1Convertible + { + Stream GetOctetStream(); + } +} diff --git a/Crypto/src/asn1/ASN1SequenceParser.cs b/Crypto/src/asn1/ASN1SequenceParser.cs new file mode 100644
index 000000000..9e88ac788 --- /dev/null +++ b/Crypto/src/asn1/ASN1SequenceParser.cs
@@ -0,0 +1,8 @@ +namespace Org.BouncyCastle.Asn1 +{ + public interface Asn1SequenceParser + : IAsn1Convertible + { + IAsn1Convertible ReadObject(); + } +} diff --git a/Crypto/src/asn1/ASN1SetParser.cs b/Crypto/src/asn1/ASN1SetParser.cs new file mode 100644
index 000000000..d1b9c64e2 --- /dev/null +++ b/Crypto/src/asn1/ASN1SetParser.cs
@@ -0,0 +1,8 @@ +namespace Org.BouncyCastle.Asn1 +{ + public interface Asn1SetParser + : IAsn1Convertible + { + IAsn1Convertible ReadObject(); + } +} diff --git a/Crypto/src/asn1/ASN1StreamParser.cs b/Crypto/src/asn1/ASN1StreamParser.cs new file mode 100644
index 000000000..6c256db53 --- /dev/null +++ b/Crypto/src/asn1/ASN1StreamParser.cs
@@ -0,0 +1,232 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Asn1 +{ + public class Asn1StreamParser + { + private readonly Stream _in; + private readonly int _limit; + + public Asn1StreamParser( + Stream inStream) + : this(inStream, Asn1InputStream.FindLimit(inStream)) + { + } + + public Asn1StreamParser( + Stream inStream, + int limit) + { + if (!inStream.CanRead) + throw new ArgumentException("Expected stream to be readable", "inStream"); + + this._in = inStream; + this._limit = limit; + } + + public Asn1StreamParser( + byte[] encoding) + : this(new MemoryStream(encoding, false), encoding.Length) + { + } + + internal IAsn1Convertible ReadIndef(int tagValue) + { + // Note: INDEF => CONSTRUCTED + + // TODO There are other tags that may be constructed (e.g. BIT_STRING) + switch (tagValue) + { + case Asn1Tags.External: + return new DerExternalParser(this); + case Asn1Tags.OctetString: + return new BerOctetStringParser(this); + case Asn1Tags.Sequence: + return new BerSequenceParser(this); + case Asn1Tags.Set: + return new BerSetParser(this); + default: + throw new Asn1Exception("unknown BER object encountered: 0x" + tagValue.ToString("X")); + } + } + + internal IAsn1Convertible ReadImplicit(bool constructed, int tag) + { + if (_in is IndefiniteLengthInputStream) + { + if (!constructed) + throw new IOException("indefinite length primitive encoding encountered"); + + return ReadIndef(tag); + } + + if (constructed) + { + switch (tag) + { + case Asn1Tags.Set: + return new DerSetParser(this); + case Asn1Tags.Sequence: + return new DerSequenceParser(this); + case Asn1Tags.OctetString: + return new BerOctetStringParser(this); + } + } + else + { + switch (tag) + { + case Asn1Tags.Set: + throw new Asn1Exception("sequences must use constructed encoding (see X.690 8.9.1/8.10.1)"); + case Asn1Tags.Sequence: + throw new Asn1Exception("sets must use constructed encoding (see X.690 8.11.1/8.12.1)"); + case Asn1Tags.OctetString: + return new DerOctetStringParser((DefiniteLengthInputStream)_in); + } + } + + throw new Asn1Exception("implicit tagging not implemented"); + } + + internal Asn1Object ReadTaggedObject(bool constructed, int tag) + { + if (!constructed) + { + // Note: !CONSTRUCTED => IMPLICIT + DefiniteLengthInputStream defIn = (DefiniteLengthInputStream)_in; + return new DerTaggedObject(false, tag, new DerOctetString(defIn.ToArray())); + } + + Asn1EncodableVector v = ReadVector(); + + if (_in is IndefiniteLengthInputStream) + { + return v.Count == 1 + ? new BerTaggedObject(true, tag, v[0]) + : new BerTaggedObject(false, tag, BerSequence.FromVector(v)); + } + + return v.Count == 1 + ? new DerTaggedObject(true, tag, v[0]) + : new DerTaggedObject(false, tag, DerSequence.FromVector(v)); + } + + public virtual IAsn1Convertible ReadObject() + { + int tag = _in.ReadByte(); + 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); + + bool isConstructed = (tag & Asn1Tags.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 & Asn1Tags.Application) != 0) + { + return new BerApplicationSpecificParser(tagNo, sp); + } + + if ((tag & Asn1Tags.Tagged) != 0) + { + return new BerTaggedObjectParser(true, tagNo, sp); + } + + return sp.ReadIndef(tagNo); + } + else + { + DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(_in, length); + + if ((tag & Asn1Tags.Application) != 0) + { + return new DerApplicationSpecific(isConstructed, tagNo, defIn.ToArray()); + } + + if ((tag & Asn1Tags.Tagged) != 0) + { + return new BerTaggedObjectParser(isConstructed, tagNo, new Asn1StreamParser(defIn)); + } + + if (isConstructed) + { + // TODO There are other tags that may be constructed (e.g. BitString) + switch (tagNo) + { + case Asn1Tags.OctetString: + // + // yes, people actually do this... + // + return new BerOctetStringParser(new Asn1StreamParser(defIn)); + case Asn1Tags.Sequence: + return new DerSequenceParser(new Asn1StreamParser(defIn)); + case Asn1Tags.Set: + return new DerSetParser(new Asn1StreamParser(defIn)); + case Asn1Tags.External: + return new DerExternalParser(new Asn1StreamParser(defIn)); + default: + // TODO Add DerUnknownTagParser class? + return new DerUnknownTag(true, tagNo, defIn.ToArray()); + } + } + + // Some primitive encodings can be handled by parsers too... + switch (tagNo) + { + case Asn1Tags.OctetString: + return new DerOctetStringParser(defIn); + } + + try + { + return Asn1InputStream.CreatePrimitiveDerObject(tagNo, defIn.ToArray()); + } + catch (ArgumentException e) + { + throw new Asn1Exception("corrupted stream detected", e); + } + } + } + + private void Set00Check( + bool enabled) + { + if (_in is IndefiniteLengthInputStream) + { + ((IndefiniteLengthInputStream) _in).SetEofOn00(enabled); + } + } + + internal Asn1EncodableVector ReadVector() + { + Asn1EncodableVector v = new Asn1EncodableVector(); + + IAsn1Convertible obj; + while ((obj = ReadObject()) != null) + { + v.Add(obj.ToAsn1Object()); + } + + return v; + } + } +} diff --git a/Crypto/src/asn1/ASN1TaggedObjectParser.cs b/Crypto/src/asn1/ASN1TaggedObjectParser.cs new file mode 100644
index 000000000..32327a269 --- /dev/null +++ b/Crypto/src/asn1/ASN1TaggedObjectParser.cs
@@ -0,0 +1,10 @@ +namespace Org.BouncyCastle.Asn1 +{ + public interface Asn1TaggedObjectParser + : IAsn1Convertible + { + int TagNo { get; } + + IAsn1Convertible GetObjectParser(int tag, bool isExplicit); + } +} diff --git a/Crypto/src/asn1/Asn1Encodable.cs b/Crypto/src/asn1/Asn1Encodable.cs new file mode 100644
index 000000000..e3dd9a14c --- /dev/null +++ b/Crypto/src/asn1/Asn1Encodable.cs
@@ -0,0 +1,78 @@ +using System.IO; + +namespace Org.BouncyCastle.Asn1 +{ + public abstract class Asn1Encodable + : IAsn1Convertible + { + public const string Der = "DER"; + public const string Ber = "BER"; + + public byte[] GetEncoded() + { + MemoryStream bOut = new MemoryStream(); + Asn1OutputStream aOut = new Asn1OutputStream(bOut); + + aOut.WriteObject(this); + + return bOut.ToArray(); + } + + public byte[] GetEncoded( + string encoding) + { + if (encoding.Equals(Der)) + { + MemoryStream bOut = new MemoryStream(); + DerOutputStream dOut = new DerOutputStream(bOut); + + dOut.WriteObject(this); + + return bOut.ToArray(); + } + + return GetEncoded(); + } + + /** + * Return the DER encoding of the object, null if the DER encoding can not be made. + * + * @return a DER byte array, null otherwise. + */ + public byte[] GetDerEncoded() + { + try + { + return GetEncoded(Der); + } + catch (IOException) + { + return null; + } + } + + public sealed override int GetHashCode() + { + return ToAsn1Object().CallAsn1GetHashCode(); + } + + public sealed override bool Equals( + object obj) + { + if (obj == this) + return true; + + IAsn1Convertible other = obj as IAsn1Convertible; + + if (other == null) + return false; + + Asn1Object o1 = ToAsn1Object(); + Asn1Object o2 = other.ToAsn1Object(); + + return o1 == o2 || o1.CallAsn1Equals(o2); + } + + public abstract Asn1Object ToAsn1Object(); + } +} diff --git a/Crypto/src/asn1/Asn1EncodableVector.cs b/Crypto/src/asn1/Asn1EncodableVector.cs new file mode 100644
index 000000000..49532fe57 --- /dev/null +++ b/Crypto/src/asn1/Asn1EncodableVector.cs
@@ -0,0 +1,93 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1 +{ + public class Asn1EncodableVector + : IEnumerable + { + private IList v = Platform.CreateArrayList(); + + public static Asn1EncodableVector FromEnumerable( + IEnumerable e) + { + Asn1EncodableVector v = new Asn1EncodableVector(); + foreach (Asn1Encodable obj in e) + { + v.Add(obj); + } + return v; + } + +// public Asn1EncodableVector() +// { +// } + + public Asn1EncodableVector( + params Asn1Encodable[] v) + { + Add(v); + } + +// public void Add( +// Asn1Encodable obj) +// { +// v.Add(obj); +// } + + public void Add( + params Asn1Encodable[] objs) + { + foreach (Asn1Encodable obj in objs) + { + v.Add(obj); + } + } + + public void AddOptional( + params Asn1Encodable[] objs) + { + if (objs != null) + { + foreach (Asn1Encodable obj in objs) + { + if (obj != null) + { + v.Add(obj); + } + } + } + } + + public Asn1Encodable this[ + int index] + { + get { return (Asn1Encodable) v[index]; } + } + + [Obsolete("Use 'object[index]' syntax instead")] + public Asn1Encodable Get( + int index) + { + return this[index]; + } + + [Obsolete("Use 'Count' property instead")] + public int Size + { + get { return v.Count; } + } + + public int Count + { + get { return v.Count; } + } + + public IEnumerator GetEnumerator() + { + return v.GetEnumerator(); + } + } +} diff --git a/Crypto/src/asn1/Asn1Exception.cs b/Crypto/src/asn1/Asn1Exception.cs new file mode 100644
index 000000000..dfc1641a4 --- /dev/null +++ b/Crypto/src/asn1/Asn1Exception.cs
@@ -0,0 +1,27 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Asn1 +{ + public class Asn1Exception + : IOException + { + public Asn1Exception() + : base() + { + } + + public Asn1Exception( + string message) + : base(message) + { + } + + public Asn1Exception( + string message, + Exception exception) + : base(message, exception) + { + } + } +} diff --git a/Crypto/src/asn1/Asn1InputStream.cs b/Crypto/src/asn1/Asn1InputStream.cs new file mode 100644
index 000000000..9a9761653 --- /dev/null +++ b/Crypto/src/asn1/Asn1InputStream.cs
@@ -0,0 +1,339 @@ +using System; +using System.Diagnostics; +using System.IO; + +using Org.BouncyCastle.Asn1.Utilities; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Asn1 +{ + /** + * 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 + : FilterStream + { + private readonly int limit; + + internal static int FindLimit(Stream input) + { + if (input is LimitedInputStream) + { + return ((LimitedInputStream)input).GetRemaining(); + } + else if (input is MemoryStream) + { + MemoryStream mem = (MemoryStream)input; + return (int)(mem.Length - mem.Position); + } + + return int.MaxValue; + } + + public Asn1InputStream( + Stream inputStream) + : this(inputStream, FindLimit(inputStream)) + { + } + + /** + * 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( + Stream inputStream, + int limit) + : base(inputStream) + { + this.limit = limit; + } + + /** + * 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 MemoryStream(input, false), input.Length) + { + } + + /** + * build an object given its tag and the number of bytes to construct it from. + */ + private Asn1Object BuildObject( + int tag, + int tagNo, + int length) + { + bool isConstructed = (tag & Asn1Tags.Constructed) != 0; + + DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(this.s, length); + + if ((tag & Asn1Tags.Application) != 0) + { + return new DerApplicationSpecific(isConstructed, tagNo, defIn.ToArray()); + } + + if ((tag & Asn1Tags.Tagged) != 0) + { + return new Asn1StreamParser(defIn).ReadTaggedObject(isConstructed, tagNo); + } + + if (isConstructed) + { + // TODO There are other tags that may be constructed (e.g. BitString) + switch (tagNo) + { + case Asn1Tags.OctetString: + // + // yes, people actually do this... + // + return new BerOctetString(BuildDerEncodableVector(defIn)); + case Asn1Tags.Sequence: + return CreateDerSequence(defIn); + case Asn1Tags.Set: + return CreateDerSet(defIn); + case Asn1Tags.External: + return new DerExternal(BuildDerEncodableVector(defIn)); + default: + return new DerUnknownTag(true, tagNo, defIn.ToArray()); + } + } + + return CreatePrimitiveDerObject(tagNo, defIn.ToArray()); + } + + internal Asn1EncodableVector BuildEncodableVector() + { + Asn1EncodableVector v = new Asn1EncodableVector(); + + Asn1Object o; + while ((o = ReadObject()) != null) + { + v.Add(o); + } + + return v; + } + + internal virtual Asn1EncodableVector BuildDerEncodableVector( + DefiniteLengthInputStream dIn) + { + return new Asn1InputStream(dIn).BuildEncodableVector(); + } + + internal virtual DerSequence CreateDerSequence( + DefiniteLengthInputStream dIn) + { + return DerSequence.FromVector(BuildDerEncodableVector(dIn)); + } + + internal virtual DerSet CreateDerSet( + DefiniteLengthInputStream dIn) + { + return DerSet.FromVector(BuildDerEncodableVector(dIn), false); + } + + public Asn1Object ReadObject() + { + int tag = ReadByte(); + if (tag <= 0) + { + if (tag == 0) + throw new IOException("unexpected end-of-contents marker"); + + return null; + } + + // + // calculate tag number + // + int tagNo = ReadTagNumber(this.s, tag); + + bool isConstructed = (tag & Asn1Tags.Constructed) != 0; + + // + // calculate length + // + int length = ReadLength(this.s, limit); + + if (length < 0) // indefinite length method + { + if (!isConstructed) + throw new IOException("indefinite length primitive encoding encountered"); + + IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(this.s, limit); + Asn1StreamParser sp = new Asn1StreamParser(indIn, limit); + + if ((tag & Asn1Tags.Application) != 0) + { + return new BerApplicationSpecificParser(tagNo, sp).ToAsn1Object(); + } + + if ((tag & Asn1Tags.Tagged) != 0) + { + return new BerTaggedObjectParser(true, tagNo, sp).ToAsn1Object(); + } + + // TODO There are other tags that may be constructed (e.g. BitString) + switch (tagNo) + { + case Asn1Tags.OctetString: + return new BerOctetStringParser(sp).ToAsn1Object(); + case Asn1Tags.Sequence: + return new BerSequenceParser(sp).ToAsn1Object(); + case Asn1Tags.Set: + return new BerSetParser(sp).ToAsn1Object(); + case Asn1Tags.External: + return new DerExternalParser(sp).ToAsn1Object(); + default: + throw new IOException("unknown BER object encountered"); + } + } + else + { + try + { + return BuildObject(tag, tagNo, length); + } + catch (ArgumentException e) + { + throw new Asn1Exception("corrupted stream detected", e); + } + } + } + + internal static int ReadTagNumber( + Stream s, + int tag) + { + 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.ReadByte(); + + // 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.ReadByte(); + } + + if (b < 0) + throw new EndOfStreamException("EOF found inside tag value."); + + tagNo |= (b & 0x7f); + } + + return tagNo; + } + + internal static int ReadLength( + Stream s, + int limit) + { + int length = s.ReadByte(); + if (length < 0) + throw new EndOfStreamException("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.ReadByte(); + + if (next < 0) + throw new EndOfStreamException("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; + } + + internal static Asn1Object CreatePrimitiveDerObject( + int tagNo, + byte[] bytes) + { + switch (tagNo) + { + case Asn1Tags.BitString: + return DerBitString.FromAsn1Octets(bytes); + case Asn1Tags.BmpString: + return new DerBmpString(bytes); + case Asn1Tags.Boolean: + return new DerBoolean(bytes); + case Asn1Tags.Enumerated: + return new DerEnumerated(bytes); + case Asn1Tags.GeneralizedTime: + return new DerGeneralizedTime(bytes); + case Asn1Tags.GeneralString: + return new DerGeneralString(bytes); + case Asn1Tags.IA5String: + return new DerIA5String(bytes); + case Asn1Tags.Integer: + return new DerInteger(bytes); + case Asn1Tags.Null: + return DerNull.Instance; // actual content is ignored (enforce 0 length?) + case Asn1Tags.NumericString: + return new DerNumericString(bytes); + case Asn1Tags.ObjectIdentifier: + return new DerObjectIdentifier(bytes); + case Asn1Tags.OctetString: + return new DerOctetString(bytes); + case Asn1Tags.PrintableString: + return new DerPrintableString(bytes); + case Asn1Tags.T61String: + return new DerT61String(bytes); + case Asn1Tags.UniversalString: + return new DerUniversalString(bytes); + case Asn1Tags.UtcTime: + return new DerUtcTime(bytes); + case Asn1Tags.Utf8String: + return new DerUtf8String(bytes); + case Asn1Tags.VisibleString: + return new DerVisibleString(bytes); + default: + return new DerUnknownTag(false, tagNo, bytes); + } + } + } +} diff --git a/Crypto/src/asn1/Asn1Null.cs b/Crypto/src/asn1/Asn1Null.cs new file mode 100644
index 000000000..d54019f67 --- /dev/null +++ b/Crypto/src/asn1/Asn1Null.cs
@@ -0,0 +1,18 @@ +namespace Org.BouncyCastle.Asn1 +{ + /** + * A Null object. + */ + public abstract class Asn1Null + : Asn1Object + { + internal Asn1Null() + { + } + + public override string ToString() + { + return "NULL"; + } + } +} diff --git a/Crypto/src/asn1/Asn1Object.cs b/Crypto/src/asn1/Asn1Object.cs new file mode 100644
index 000000000..08bd599c1 --- /dev/null +++ b/Crypto/src/asn1/Asn1Object.cs
@@ -0,0 +1,63 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Asn1 +{ + public abstract class Asn1Object + : Asn1Encodable + { + /// <summary>Create a base ASN.1 object from a byte array.</summary> + /// <param name="data">The byte array to parse.</param> + /// <returns>The base ASN.1 object represented by the byte array.</returns> + /// <exception cref="IOException">If there is a problem parsing the data.</exception> + public static Asn1Object FromByteArray( + byte[] data) + { + try + { + return new Asn1InputStream(data).ReadObject(); + } + catch (InvalidCastException) + { + throw new IOException("cannot recognise object in stream"); + } + } + + /// <summary>Read a base ASN.1 object from a stream.</summary> + /// <param name="inStr">The stream to parse.</param> + /// <returns>The base ASN.1 object represented by the byte array.</returns> + /// <exception cref="IOException">If there is a problem parsing the data.</exception> + public static Asn1Object FromStream( + Stream inStr) + { + try + { + return new Asn1InputStream(inStr).ReadObject(); + } + catch (InvalidCastException) + { + throw new IOException("cannot recognise object in stream"); + } + } + + public sealed override Asn1Object ToAsn1Object() + { + return this; + } + + internal abstract void Encode(DerOutputStream derOut); + + protected abstract bool Asn1Equals(Asn1Object asn1Object); + protected abstract int Asn1GetHashCode(); + + internal bool CallAsn1Equals(Asn1Object obj) + { + return Asn1Equals(obj); + } + + internal int CallAsn1GetHashCode() + { + return Asn1GetHashCode(); + } + } +} diff --git a/Crypto/src/asn1/Asn1OctetString.cs b/Crypto/src/asn1/Asn1OctetString.cs new file mode 100644
index 000000000..9c738a8f2 --- /dev/null +++ b/Crypto/src/asn1/Asn1OctetString.cs
@@ -0,0 +1,119 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Asn1 +{ + public abstract class Asn1OctetString + : Asn1Object, Asn1OctetStringParser + { + internal byte[] str; + + /** + * return an Octet string from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param explicitly true if the object is meant to be explicitly + * tagged false otherwise. + * @exception ArgumentException if the tagged object cannot + * be converted. + */ + public static Asn1OctetString GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + Asn1Object o = obj.GetObject(); + + if (isExplicit || o is Asn1OctetString) + { + return GetInstance(o); + } + + return BerOctetString.FromSequence(Asn1Sequence.GetInstance(o)); + } + + /** + * return an Octet string from the given object. + * + * @param obj the object we want converted. + * @exception ArgumentException if the object cannot be converted. + */ + public static Asn1OctetString GetInstance(object obj) + { + if (obj == null || obj is Asn1OctetString) + { + return (Asn1OctetString)obj; + } + + // TODO: this needs to be deleted in V2 + if (obj is Asn1TaggedObject) + return GetInstance(((Asn1TaggedObject)obj).GetObject()); + + throw new ArgumentException("illegal object in GetInstance: " + obj.GetType().Name); + } + + /** + * @param string the octets making up the octet string. + */ + internal Asn1OctetString( + byte[] str) + { + if (str == null) + throw new ArgumentNullException("str"); + + this.str = str; + } + + internal Asn1OctetString( + Asn1Encodable obj) + { + try + { + this.str = obj.GetEncoded(Asn1Encodable.Der); + } + catch (IOException e) + { + throw new ArgumentException("Error processing object : " + e.ToString()); + } + } + + public Stream GetOctetStream() + { + return new MemoryStream(str, false); + } + + public Asn1OctetStringParser Parser + { + get { return this; } + } + + public virtual byte[] GetOctets() + { + return str; + } + + protected override int Asn1GetHashCode() + { + return Arrays.GetHashCode(GetOctets()); + } + + protected override bool Asn1Equals( + Asn1Object asn1Object) + { + DerOctetString other = asn1Object as DerOctetString; + + if (other == null) + return false; + + return Arrays.AreEqual(GetOctets(), other.GetOctets()); + } + + public override string ToString() + { + return "#" + Hex.ToHexString(str); + } + } +} diff --git a/Crypto/src/asn1/Asn1OutputStream.cs b/Crypto/src/asn1/Asn1OutputStream.cs new file mode 100644
index 000000000..39c8b1e5e --- /dev/null +++ b/Crypto/src/asn1/Asn1OutputStream.cs
@@ -0,0 +1,35 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Asn1 +{ + public class Asn1OutputStream + : DerOutputStream + { + public Asn1OutputStream(Stream os) : base(os) + { + } + + [Obsolete("Use version taking an Asn1Encodable arg instead")] + public override void WriteObject( + object obj) + { + if (obj == null) + { + WriteNull(); + } + else if (obj is Asn1Object) + { + ((Asn1Object)obj).Encode(this); + } + else if (obj is Asn1Encodable) + { + ((Asn1Encodable)obj).ToAsn1Object().Encode(this); + } + else + { + throw new IOException("object not Asn1Encodable"); + } + } + } +} diff --git a/Crypto/src/asn1/Asn1ParsingException.cs b/Crypto/src/asn1/Asn1ParsingException.cs new file mode 100644
index 000000000..8827d8329 --- /dev/null +++ b/Crypto/src/asn1/Asn1ParsingException.cs
@@ -0,0 +1,26 @@ +using System; + +namespace Org.BouncyCastle.Asn1 +{ + public class Asn1ParsingException + : InvalidOperationException + { + public Asn1ParsingException() + : base() + { + } + + public Asn1ParsingException( + string message) + : base(message) + { + } + + public Asn1ParsingException( + string message, + Exception exception) + : base(message, exception) + { + } + } +} diff --git a/Crypto/src/asn1/Asn1Sequence.cs b/Crypto/src/asn1/Asn1Sequence.cs new file mode 100644
index 000000000..3131ead84 --- /dev/null +++ b/Crypto/src/asn1/Asn1Sequence.cs
@@ -0,0 +1,255 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Asn1 +{ + public abstract class Asn1Sequence + : Asn1Object, IEnumerable + { + private readonly IList seq; + + /** + * return an Asn1Sequence from the given object. + * + * @param obj the object we want converted. + * @exception ArgumentException if the object cannot be converted. + */ + public static Asn1Sequence GetInstance( + object obj) + { + if (obj == null || obj is Asn1Sequence) + { + return (Asn1Sequence)obj; + } + else if (obj is byte[]) + { + try + { + return Asn1Sequence.GetInstance(Asn1Object.FromByteArray((byte[])obj)); + } + catch (IOException e) + { + throw new ArgumentException("Failed to construct sequence from byte[]", e); + } + } + + throw new ArgumentException("Unknown object in GetInstance: " + obj.GetType().FullName, "obj"); + } + + /** + * 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 explicitly true if the object is meant to be explicitly tagged, + * false otherwise. + * @exception ArgumentException if the tagged object cannot + * be converted. + */ + public static Asn1Sequence GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + Asn1Object inner = obj.GetObject(); + + if (explicitly) + { + if (!obj.IsExplicit()) + throw new ArgumentException("object implicit - explicit expected."); + + return (Asn1Sequence) inner; + } + + // + // 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 is BerTaggedObject) + { + return new BerSequence(inner); + } + + return new DerSequence(inner); + } + + if (inner is Asn1Sequence) + { + return (Asn1Sequence) inner; + } + + throw new ArgumentException("Unknown object in GetInstance: " + obj.GetType().FullName, "obj"); + } + + protected internal Asn1Sequence( + int capacity) + { + seq = Platform.CreateArrayList(capacity); + } + + public virtual IEnumerator GetEnumerator() + { + return seq.GetEnumerator(); + } + + [Obsolete("Use GetEnumerator() instead")] + public IEnumerator GetObjects() + { + return GetEnumerator(); + } + + private class Asn1SequenceParserImpl + : Asn1SequenceParser + { + private readonly Asn1Sequence outer; + private readonly int max; + private int index; + + public Asn1SequenceParserImpl( + Asn1Sequence outer) + { + this.outer = outer; + this.max = outer.Count; + } + + public IAsn1Convertible ReadObject() + { + if (index == max) + return null; + + Asn1Encodable obj = outer[index++]; + + if (obj is Asn1Sequence) + return ((Asn1Sequence)obj).Parser; + + if (obj is Asn1Set) + return ((Asn1Set)obj).Parser; + + // NB: Asn1OctetString implements Asn1OctetStringParser directly +// if (obj is Asn1OctetString) +// return ((Asn1OctetString)obj).Parser; + + return obj; + } + + public Asn1Object ToAsn1Object() + { + return outer; + } + } + + public virtual Asn1SequenceParser Parser + { + get { return new Asn1SequenceParserImpl(this); } + } + + /** + * 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 virtual Asn1Encodable this[int index] + { + get { return (Asn1Encodable) seq[index]; } + } + + [Obsolete("Use 'object[index]' syntax instead")] + public Asn1Encodable GetObjectAt( + int index) + { + return this[index]; + } + + [Obsolete("Use 'Count' property instead")] + public int Size + { + get { return Count; } + } + + public virtual int Count + { + get { return seq.Count; } + } + + protected override int Asn1GetHashCode() + { + int hc = Count; + + foreach (object o in this) + { + hc *= 17; + if (o == null) + { + hc ^= DerNull.Instance.GetHashCode(); + } + else + { + hc ^= o.GetHashCode(); + } + } + + return hc; + } + + protected override bool Asn1Equals( + Asn1Object asn1Object) + { + Asn1Sequence other = asn1Object as Asn1Sequence; + + if (other == null) + return false; + + if (Count != other.Count) + return false; + + IEnumerator s1 = GetEnumerator(); + IEnumerator s2 = other.GetEnumerator(); + + while (s1.MoveNext() && s2.MoveNext()) + { + Asn1Object o1 = GetCurrent(s1).ToAsn1Object(); + Asn1Object o2 = GetCurrent(s2).ToAsn1Object(); + + if (!o1.Equals(o2)) + return false; + } + + return true; + } + + private Asn1Encodable GetCurrent(IEnumerator e) + { + Asn1Encodable encObj = (Asn1Encodable)e.Current; + + // unfortunately null was allowed as a substitute for DER null + if (encObj == null) + return DerNull.Instance; + + return encObj; + } + + protected internal void AddObject( + Asn1Encodable obj) + { + seq.Add(obj); + } + + public override string ToString() + { + return CollectionUtilities.ToString(seq); + } + } +} diff --git a/Crypto/src/asn1/Asn1Set.cs b/Crypto/src/asn1/Asn1Set.cs new file mode 100644
index 000000000..f5b66495c --- /dev/null +++ b/Crypto/src/asn1/Asn1Set.cs
@@ -0,0 +1,326 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Asn1 +{ + abstract public class Asn1Set + : Asn1Object, IEnumerable + { + private readonly IList _set; + + /** + * return an ASN1Set from the given object. + * + * @param obj the object we want converted. + * @exception ArgumentException if the object cannot be converted. + */ + public static Asn1Set GetInstance( + object obj) + { + if (obj == null || obj is Asn1Set) + { + return (Asn1Set)obj; + } + + throw new ArgumentException("Unknown object in GetInstance: " + obj.GetType().FullName, "obj"); + } + + /** + * 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 explicitly true if the object is meant to be explicitly tagged + * false otherwise. + * @exception ArgumentException if the tagged object cannot + * be converted. + */ + public static Asn1Set GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + Asn1Object inner = obj.GetObject(); + + if (explicitly) + { + if (!obj.IsExplicit()) + throw new ArgumentException("object implicit - explicit expected."); + + return (Asn1Set) inner; + } + + // + // constructed object which appears to be explicitly tagged + // and it's really implicit means we have to add the + // surrounding sequence. + // + if (obj.IsExplicit()) + { + return new DerSet(inner); + } + + if (inner is Asn1Set) + { + return (Asn1Set) inner; + } + + // + // in this case the parser returns a sequence, convert it + // into a set. + // + if (inner is Asn1Sequence) + { + Asn1EncodableVector v = new Asn1EncodableVector(); + Asn1Sequence s = (Asn1Sequence) inner; + + foreach (Asn1Encodable ae in s) + { + v.Add(ae); + } + + // TODO Should be able to construct set directly from sequence? + return new DerSet(v, false); + } + + throw new ArgumentException("Unknown object in GetInstance: " + obj.GetType().FullName, "obj"); + } + + protected internal Asn1Set( + int capacity) + { + _set = Platform.CreateArrayList(capacity); + } + + public virtual IEnumerator GetEnumerator() + { + return _set.GetEnumerator(); + } + + [Obsolete("Use GetEnumerator() instead")] + public IEnumerator GetObjects() + { + return GetEnumerator(); + } + + /** + * 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 virtual Asn1Encodable this[int index] + { + get { return (Asn1Encodable) _set[index]; } + } + + [Obsolete("Use 'object[index]' syntax instead")] + public Asn1Encodable GetObjectAt( + int index) + { + return this[index]; + } + + [Obsolete("Use 'Count' property instead")] + public int Size + { + get { return Count; } + } + + public virtual int Count + { + get { return _set.Count; } + } + + public virtual Asn1Encodable[] ToArray() + { + Asn1Encodable[] values = new Asn1Encodable[this.Count]; + for (int i = 0; i < this.Count; ++i) + { + values[i] = this[i]; + } + return values; + } + + private class Asn1SetParserImpl + : Asn1SetParser + { + private readonly Asn1Set outer; + private readonly int max; + private int index; + + public Asn1SetParserImpl( + Asn1Set outer) + { + this.outer = outer; + this.max = outer.Count; + } + + public IAsn1Convertible ReadObject() + { + if (index == max) + return null; + + Asn1Encodable obj = outer[index++]; + if (obj is Asn1Sequence) + return ((Asn1Sequence)obj).Parser; + + if (obj is Asn1Set) + return ((Asn1Set)obj).Parser; + + // NB: Asn1OctetString implements Asn1OctetStringParser directly +// if (obj is Asn1OctetString) +// return ((Asn1OctetString)obj).Parser; + + return obj; + } + + public virtual Asn1Object ToAsn1Object() + { + return outer; + } + } + + public Asn1SetParser Parser + { + get { return new Asn1SetParserImpl(this); } + } + + protected override int Asn1GetHashCode() + { + int hc = Count; + + foreach (object o in this) + { + hc *= 17; + if (o == null) + { + hc ^= DerNull.Instance.GetHashCode(); + } + else + { + hc ^= o.GetHashCode(); + } + } + + return hc; + } + + protected override bool Asn1Equals( + Asn1Object asn1Object) + { + Asn1Set other = asn1Object as Asn1Set; + + if (other == null) + return false; + + if (Count != other.Count) + { + return false; + } + + IEnumerator s1 = GetEnumerator(); + IEnumerator s2 = other.GetEnumerator(); + + while (s1.MoveNext() && s2.MoveNext()) + { + Asn1Object o1 = GetCurrent(s1).ToAsn1Object(); + Asn1Object o2 = GetCurrent(s2).ToAsn1Object(); + + if (!o1.Equals(o2)) + return false; + } + + return true; + } + + private Asn1Encodable GetCurrent(IEnumerator e) + { + Asn1Encodable encObj = (Asn1Encodable)e.Current; + + // unfortunately null was allowed as a substitute for DER null + if (encObj == null) + return DerNull.Instance; + + return encObj; + } + + /** + * return true if a &lt;= b (arrays are assumed padded with zeros). + */ + private bool LessThanOrEqual( + byte[] a, + byte[] b) + { + int len = System.Math.Min(a.Length, b.Length); + for (int i = 0; i != len; ++i) + { + if (a[i] != b[i]) + { + return a[i] < b[i]; + } + } + return len == a.Length; + } + + protected internal void Sort() + { + if (_set.Count > 1) + { + bool swapped = true; + int lastSwap = _set.Count - 1; + + while (swapped) + { + int index = 0; + int swapIndex = 0; + byte[] a = ((Asn1Encodable) _set[0]).GetEncoded(); + + swapped = false; + + while (index != lastSwap) + { + byte[] b = ((Asn1Encodable) _set[index + 1]).GetEncoded(); + + if (LessThanOrEqual(a, b)) + { + a = b; + } + else + { + object o = _set[index]; + _set[index] = _set[index + 1]; + _set[index + 1] = o; + + swapped = true; + swapIndex = index; + } + + index++; + } + + lastSwap = swapIndex; + } + } + } + + protected internal void AddObject( + Asn1Encodable obj) + { + _set.Add(obj); + } + + public override string ToString() + { + return CollectionUtilities.ToString(_set); + } + } +} diff --git a/Crypto/src/asn1/Asn1TaggedObject.cs b/Crypto/src/asn1/Asn1TaggedObject.cs new file mode 100644
index 000000000..2e480738a --- /dev/null +++ b/Crypto/src/asn1/Asn1TaggedObject.cs
@@ -0,0 +1,178 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1 +{ + /** + * 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 + : Asn1Object, Asn1TaggedObjectParser + { + internal int tagNo; +// internal bool empty; + internal bool explicitly = true; + internal Asn1Encodable obj; + + static public Asn1TaggedObject GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + if (explicitly) + { + return (Asn1TaggedObject) obj.GetObject(); + } + + throw new ArgumentException("implicitly tagged tagged object"); + } + + static public Asn1TaggedObject GetInstance( + object obj) + { + if (obj == null || obj is Asn1TaggedObject) + { + return (Asn1TaggedObject) obj; + } + + throw new ArgumentException("Unknown object in GetInstance: " + obj.GetType().FullName, "obj"); + } + + /** + * @param tagNo the tag number for this object. + * @param obj the tagged object. + */ + protected Asn1TaggedObject( + int tagNo, + Asn1Encodable obj) + { + this.explicitly = true; + this.tagNo = tagNo; + this.obj = obj; + } + + /** + * @param explicitly true if the object is explicitly tagged. + * @param tagNo the tag number for this object. + * @param obj the tagged object. + */ + protected Asn1TaggedObject( + bool explicitly, + int tagNo, + Asn1Encodable obj) + { + // IAsn1Choice marker interface 'insists' on explicit tagging + this.explicitly = explicitly || (obj is IAsn1Choice); + this.tagNo = tagNo; + this.obj = obj; + } + + protected override bool Asn1Equals( + Asn1Object asn1Object) + { + Asn1TaggedObject other = asn1Object as Asn1TaggedObject; + + if (other == null) + return false; + + return this.tagNo == other.tagNo +// && this.empty == other.empty + && this.explicitly == other.explicitly // TODO Should this be part of equality? + && Platform.Equals(GetObject(), other.GetObject()); + } + + protected override int Asn1GetHashCode() + { + int code = tagNo.GetHashCode(); + + // 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... +// code ^= explicitly.GetHashCode(); + + if (obj != null) + { + code ^= obj.GetHashCode(); + } + + return code; + } + + public int TagNo + { + get { 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.</p> + */ + public bool IsExplicit() + { + return explicitly; + } + + public bool IsEmpty() + { + return false; //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.</p> + */ + public Asn1Object GetObject() + { + if (obj != null) + { + return obj.ToAsn1Object(); + } + + 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 IAsn1Convertible GetObjectParser( + int tag, + bool isExplicit) + { + switch (tag) + { + case Asn1Tags.Set: + return Asn1Set.GetInstance(this, isExplicit).Parser; + case Asn1Tags.Sequence: + return Asn1Sequence.GetInstance(this, isExplicit).Parser; + case Asn1Tags.OctetString: + return Asn1OctetString.GetInstance(this, isExplicit).Parser; + } + + if (isExplicit) + { + return GetObject(); + } + + throw Platform.CreateNotImplementedException("implicit tagging for tag: " + tag); + } + + public override string ToString() + { + return "[" + tagNo + "]" + obj; + } + } +} diff --git a/Crypto/src/asn1/Asn1Tags.cs b/Crypto/src/asn1/Asn1Tags.cs new file mode 100644
index 000000000..32ac6bc6c --- /dev/null +++ b/Crypto/src/asn1/Asn1Tags.cs
@@ -0,0 +1,36 @@ +namespace Org.BouncyCastle.Asn1 +{ + public class Asn1Tags + { + public const int Boolean = 0x01; + public const int Integer = 0x02; + public const int BitString = 0x03; + public const int OctetString = 0x04; + public const int Null = 0x05; + public const int ObjectIdentifier = 0x06; + public const int External = 0x08; + public const int Enumerated = 0x0a; + public const int Sequence = 0x10; + public const int SequenceOf = 0x10; // for completeness + public const int Set = 0x11; + public const int SetOf = 0x11; // for completeness + + public const int NumericString = 0x12; + public const int PrintableString = 0x13; + public const int T61String = 0x14; + public const int VideotexString = 0x15; + public const int IA5String = 0x16; + public const int UtcTime = 0x17; + public const int GeneralizedTime = 0x18; + public const int GraphicString = 0x19; + public const int VisibleString = 0x1a; + public const int GeneralString = 0x1b; + public const int UniversalString = 0x1c; + public const int BmpString = 0x1e; + public const int Utf8String = 0x0c; + + public const int Constructed = 0x20; + public const int Application = 0x40; + public const int Tagged = 0x80; + } +} diff --git a/Crypto/src/asn1/BERGenerator.cs b/Crypto/src/asn1/BERGenerator.cs new file mode 100644
index 000000000..271572c02 --- /dev/null +++ b/Crypto/src/asn1/BERGenerator.cs
@@ -0,0 +1,102 @@ +using System.IO; + +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Asn1 +{ + public class BerGenerator + : Asn1Generator + { + private bool _tagged = false; + private bool _isExplicit; + private int _tagNo; + + protected BerGenerator( + Stream outStream) + : base(outStream) + { + } + + public BerGenerator( + Stream outStream, + int tagNo, + bool isExplicit) + : base(outStream) + { + _tagged = true; + _isExplicit = isExplicit; + _tagNo = tagNo; + } + + public override void AddObject( + Asn1Encodable obj) + { + new BerOutputStream(Out).WriteObject(obj); + } + + public override Stream GetRawOutputStream() + { + return Out; + } + + public override void Close() + { + WriteBerEnd(); + } + + private void WriteHdr( + int tag) + { + Out.WriteByte((byte) tag); + Out.WriteByte(0x80); + } + + protected void WriteBerHeader( + int tag) + { + if (_tagged) + { + int tagNum = _tagNo | Asn1Tags.Tagged; + + if (_isExplicit) + { + WriteHdr(tagNum | Asn1Tags.Constructed); + WriteHdr(tag); + } + else + { + if ((tag & Asn1Tags.Constructed) != 0) + { + WriteHdr(tagNum | Asn1Tags.Constructed); + } + else + { + WriteHdr(tagNum); + } + } + } + else + { + WriteHdr(tag); + } + } + + protected void WriteBerBody( + Stream contentStream) + { + Streams.PipeAll(contentStream, Out); + } + + protected void WriteBerEnd() + { + Out.WriteByte(0x00); + Out.WriteByte(0x00); + + if (_tagged && _isExplicit) // write extra end for tag header + { + Out.WriteByte(0x00); + Out.WriteByte(0x00); + } + } + } +} diff --git a/Crypto/src/asn1/BEROctetStringGenerator.cs b/Crypto/src/asn1/BEROctetStringGenerator.cs new file mode 100644
index 000000000..644060765 --- /dev/null +++ b/Crypto/src/asn1/BEROctetStringGenerator.cs
@@ -0,0 +1,121 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Asn1 +{ + public class BerOctetStringGenerator + : BerGenerator + { + public BerOctetStringGenerator(Stream outStream) + : base(outStream) + { + WriteBerHeader(Asn1Tags.Constructed | Asn1Tags.OctetString); + } + + public BerOctetStringGenerator( + Stream outStream, + int tagNo, + bool isExplicit) + : base(outStream, tagNo, isExplicit) + { + WriteBerHeader(Asn1Tags.Constructed | Asn1Tags.OctetString); + } + + public Stream GetOctetOutputStream() + { + return GetOctetOutputStream(new byte[1000]); // limit for CER encoding. + } + + public Stream GetOctetOutputStream( + int bufSize) + { + return bufSize < 1 + ? GetOctetOutputStream() + : GetOctetOutputStream(new byte[bufSize]); + } + + public Stream GetOctetOutputStream( + byte[] buf) + { + return new BufferedBerOctetStream(this, buf); + } + + private class BufferedBerOctetStream + : BaseOutputStream + { + private byte[] _buf; + private int _off; + private readonly BerOctetStringGenerator _gen; + private readonly DerOutputStream _derOut; + + internal BufferedBerOctetStream( + BerOctetStringGenerator gen, + byte[] buf) + { + _gen = gen; + _buf = buf; + _off = 0; + _derOut = new DerOutputStream(_gen.Out); + } + + public override void WriteByte( + byte b) + { + _buf[_off++] = b; + + if (_off == _buf.Length) + { + DerOctetString.Encode(_derOut, _buf, 0, _off); + _off = 0; + } + } + + public override void Write( + byte[] buf, + int offset, + int len) + { + while (len > 0) + { + int numToCopy = System.Math.Min(len, _buf.Length - _off); + + if (numToCopy == _buf.Length) + { + DerOctetString.Encode(_derOut, buf, offset, numToCopy); + } + else + { + Array.Copy(buf, offset, _buf, _off, numToCopy); + + _off += numToCopy; + if (_off < _buf.Length) + break; + + DerOctetString.Encode(_derOut, _buf, 0, _off); + _off = 0; + } + + offset += numToCopy; + len -= numToCopy; + } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (_off != 0) + { + DerOctetString.Encode(_derOut, _buf, 0, _off); + } + + _gen.WriteBerEnd(); + } + + base.Dispose(disposing); + } + } + } +} diff --git a/Crypto/src/asn1/BEROctetStringParser.cs b/Crypto/src/asn1/BEROctetStringParser.cs new file mode 100644
index 000000000..3bfd2a98d --- /dev/null +++ b/Crypto/src/asn1/BEROctetStringParser.cs
@@ -0,0 +1,36 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Asn1 +{ + public class BerOctetStringParser + : Asn1OctetStringParser + { + private readonly Asn1StreamParser _parser; + + internal BerOctetStringParser( + Asn1StreamParser parser) + { + _parser = parser; + } + + public Stream GetOctetStream() + { + return new ConstructedOctetStream(_parser); + } + + public Asn1Object ToAsn1Object() + { + try + { + return new BerOctetString(Streams.ReadAll(GetOctetStream())); + } + catch (IOException e) + { + throw new Asn1ParsingException("IOException converting stream to byte array: " + e.Message, e); + } + } + } +} diff --git a/Crypto/src/asn1/BERSequenceGenerator.cs b/Crypto/src/asn1/BERSequenceGenerator.cs new file mode 100644
index 000000000..5ea2c9b82 --- /dev/null +++ b/Crypto/src/asn1/BERSequenceGenerator.cs
@@ -0,0 +1,24 @@ +using System.IO; + +namespace Org.BouncyCastle.Asn1 +{ + public class BerSequenceGenerator + : BerGenerator + { + public BerSequenceGenerator( + Stream outStream) + : base(outStream) + { + WriteBerHeader(Asn1Tags.Constructed | Asn1Tags.Sequence); + } + + public BerSequenceGenerator( + Stream outStream, + int tagNo, + bool isExplicit) + : base(outStream, tagNo, isExplicit) + { + WriteBerHeader(Asn1Tags.Constructed | Asn1Tags.Sequence); + } + } +} diff --git a/Crypto/src/asn1/BERSequenceParser.cs b/Crypto/src/asn1/BERSequenceParser.cs new file mode 100644
index 000000000..8474b8d24 --- /dev/null +++ b/Crypto/src/asn1/BERSequenceParser.cs
@@ -0,0 +1,24 @@ +namespace Org.BouncyCastle.Asn1 +{ + public class BerSequenceParser + : Asn1SequenceParser + { + private readonly Asn1StreamParser _parser; + + internal BerSequenceParser( + Asn1StreamParser parser) + { + this._parser = parser; + } + + public IAsn1Convertible ReadObject() + { + return _parser.ReadObject(); + } + + public Asn1Object ToAsn1Object() + { + return new BerSequence(_parser.ReadVector()); + } + } +} diff --git a/Crypto/src/asn1/BERSetGenerator.cs b/Crypto/src/asn1/BERSetGenerator.cs new file mode 100644
index 000000000..72b1f903a --- /dev/null +++ b/Crypto/src/asn1/BERSetGenerator.cs
@@ -0,0 +1,24 @@ +using System.IO; + +namespace Org.BouncyCastle.Asn1 +{ + public class BerSetGenerator + : BerGenerator + { + public BerSetGenerator( + Stream outStream) + : base(outStream) + { + WriteBerHeader(Asn1Tags.Constructed | Asn1Tags.Set); + } + + public BerSetGenerator( + Stream outStream, + int tagNo, + bool isExplicit) + : base(outStream, tagNo, isExplicit) + { + WriteBerHeader(Asn1Tags.Constructed | Asn1Tags.Set); + } + } +} diff --git a/Crypto/src/asn1/BERSetParser.cs b/Crypto/src/asn1/BERSetParser.cs new file mode 100644
index 000000000..aa9ccbc12 --- /dev/null +++ b/Crypto/src/asn1/BERSetParser.cs
@@ -0,0 +1,24 @@ +namespace Org.BouncyCastle.Asn1 +{ + public class BerSetParser + : Asn1SetParser + { + private readonly Asn1StreamParser _parser; + + internal BerSetParser( + Asn1StreamParser parser) + { + this._parser = parser; + } + + public IAsn1Convertible ReadObject() + { + return _parser.ReadObject(); + } + + public Asn1Object ToAsn1Object() + { + return new BerSet(_parser.ReadVector(), false); + } + } +} diff --git a/Crypto/src/asn1/BERTaggedObjectParser.cs b/Crypto/src/asn1/BERTaggedObjectParser.cs new file mode 100644
index 000000000..354437a6a --- /dev/null +++ b/Crypto/src/asn1/BERTaggedObjectParser.cs
@@ -0,0 +1,71 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1 +{ + public class BerTaggedObjectParser + : Asn1TaggedObjectParser + { + private bool _constructed; + private int _tagNumber; + private Asn1StreamParser _parser; + + [Obsolete] + internal BerTaggedObjectParser( + int baseTag, + int tagNumber, + Stream contentStream) + : this((baseTag & Asn1Tags.Constructed) != 0, tagNumber, new Asn1StreamParser(contentStream)) + { + } + + internal BerTaggedObjectParser( + bool constructed, + int tagNumber, + Asn1StreamParser parser) + { + _constructed = constructed; + _tagNumber = tagNumber; + _parser = parser; + } + + public bool IsConstructed + { + get { return _constructed; } + } + + public int TagNo + { + get { return _tagNumber; } + } + + public IAsn1Convertible GetObjectParser( + int tag, + bool isExplicit) + { + 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 Asn1Object ToAsn1Object() + { + try + { + return _parser.ReadTaggedObject(_constructed, _tagNumber); + } + catch (IOException e) + { + throw new Asn1ParsingException(e.Message); + } + } + } +} diff --git a/Crypto/src/asn1/BerApplicationSpecific.cs b/Crypto/src/asn1/BerApplicationSpecific.cs new file mode 100644
index 000000000..65fbecbe1 --- /dev/null +++ b/Crypto/src/asn1/BerApplicationSpecific.cs
@@ -0,0 +1,15 @@ +using System; + +namespace Org.BouncyCastle.Asn1 +{ + public class BerApplicationSpecific + : DerApplicationSpecific + { + public BerApplicationSpecific( + int tagNo, + Asn1EncodableVector vec) + : base(tagNo, vec) + { + } + } +} diff --git a/Crypto/src/asn1/BerApplicationSpecificParser.cs b/Crypto/src/asn1/BerApplicationSpecificParser.cs new file mode 100644
index 000000000..7d2c4b3e8 --- /dev/null +++ b/Crypto/src/asn1/BerApplicationSpecificParser.cs
@@ -0,0 +1,29 @@ +using System; + +namespace Org.BouncyCastle.Asn1 +{ + public class BerApplicationSpecificParser + : IAsn1ApplicationSpecificParser + { + private readonly int tag; + private readonly Asn1StreamParser parser; + + internal BerApplicationSpecificParser( + int tag, + Asn1StreamParser parser) + { + this.tag = tag; + this.parser = parser; + } + + public IAsn1Convertible ReadObject() + { + return parser.ReadObject(); + } + + public Asn1Object ToAsn1Object() + { + return new BerApplicationSpecific(tag, parser.ReadVector()); + } + } +} diff --git a/Crypto/src/asn1/BerNull.cs b/Crypto/src/asn1/BerNull.cs new file mode 100644
index 000000000..0751bbac3 --- /dev/null +++ b/Crypto/src/asn1/BerNull.cs
@@ -0,0 +1,35 @@ +using System; + +namespace Org.BouncyCastle.Asn1 +{ + /** + * A BER Null object. + */ + public class BerNull + : DerNull + { + public static new readonly BerNull Instance = new BerNull(0); + + [Obsolete("Use static Instance object")] + public BerNull() + { + } + + private BerNull(int dummy) : base(dummy) + { + } + + internal override void Encode( + DerOutputStream derOut) + { + if (derOut is Asn1OutputStream || derOut is BerOutputStream) + { + derOut.WriteByte(Asn1Tags.Null); + } + else + { + base.Encode(derOut); + } + } + } +} diff --git a/Crypto/src/asn1/BerOctetString.cs b/Crypto/src/asn1/BerOctetString.cs new file mode 100644
index 000000000..a7c8ad33e --- /dev/null +++ b/Crypto/src/asn1/BerOctetString.cs
@@ -0,0 +1,135 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1 +{ + public class BerOctetString + : DerOctetString, IEnumerable + { + public static BerOctetString FromSequence(Asn1Sequence seq) + { + IList v = Platform.CreateArrayList(); + + foreach (Asn1Encodable obj in seq) + { + v.Add(obj); + } + + return new BerOctetString(v); + } + + private const int MaxLength = 1000; + + /** + * convert a vector of octet strings into a single byte string + */ + private static byte[] ToBytes( + IEnumerable octs) + { + MemoryStream bOut = new MemoryStream(); + foreach (DerOctetString o in octs) + { + byte[] octets = o.GetOctets(); + bOut.Write(octets, 0, octets.Length); + } + return bOut.ToArray(); + } + + private readonly IEnumerable octs; + + /// <param name="str">The octets making up the octet string.</param> + public BerOctetString( + byte[] str) + : base(str) + { + } + + public BerOctetString( + IEnumerable octets) + : base(ToBytes(octets)) + { + this.octs = octets; + } + + public BerOctetString( + Asn1Object obj) + : base(obj) + { + } + + public BerOctetString( + Asn1Encodable obj) + : base(obj.ToAsn1Object()) + { + } + + public override byte[] GetOctets() + { + return str; + } + + /** + * return the DER octets that make up this string. + */ + public IEnumerator GetEnumerator() + { + if (octs == null) + { + return GenerateOcts().GetEnumerator(); + } + + return octs.GetEnumerator(); + } + + [Obsolete("Use GetEnumerator() instead")] + public IEnumerator GetObjects() + { + return GetEnumerator(); + } + + private IList GenerateOcts() + { + IList vec = Platform.CreateArrayList(); + for (int i = 0; i < str.Length; i += MaxLength) + { + int end = System.Math.Min(str.Length, i + MaxLength); + + byte[] nStr = new byte[end - i]; + + Array.Copy(str, i, nStr, 0, nStr.Length); + + vec.Add(new DerOctetString(nStr)); + } + return vec; + } + + internal override void Encode( + DerOutputStream derOut) + { + if (derOut is Asn1OutputStream || derOut is BerOutputStream) + { + derOut.WriteByte(Asn1Tags.Constructed | Asn1Tags.OctetString); + + derOut.WriteByte(0x80); + + // + // write out the octet array + // + foreach (DerOctetString oct in this) + { + derOut.WriteObject(oct); + } + + derOut.WriteByte(0x00); + derOut.WriteByte(0x00); + } + else + { + base.Encode(derOut); + } + } + } +} diff --git a/Crypto/src/asn1/BerOutputStream.cs b/Crypto/src/asn1/BerOutputStream.cs new file mode 100644
index 000000000..b3ece10d3 --- /dev/null +++ b/Crypto/src/asn1/BerOutputStream.cs
@@ -0,0 +1,36 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Asn1 +{ + // TODO Make Obsolete in favour of Asn1OutputStream? + public class BerOutputStream + : DerOutputStream + { + public BerOutputStream(Stream os) : base(os) + { + } + + [Obsolete("Use version taking an Asn1Encodable arg instead")] + public override void WriteObject( + object obj) + { + if (obj == null) + { + WriteNull(); + } + else if (obj is Asn1Object) + { + ((Asn1Object)obj).Encode(this); + } + else if (obj is Asn1Encodable) + { + ((Asn1Encodable)obj).ToAsn1Object().Encode(this); + } + else + { + throw new IOException("object not BerEncodable"); + } + } + } +} diff --git a/Crypto/src/asn1/BerSequence.cs b/Crypto/src/asn1/BerSequence.cs new file mode 100644
index 000000000..70b43fc79 --- /dev/null +++ b/Crypto/src/asn1/BerSequence.cs
@@ -0,0 +1,69 @@ +namespace Org.BouncyCastle.Asn1 +{ + public class BerSequence + : DerSequence + { + public static new readonly BerSequence Empty = new BerSequence(); + + public static new BerSequence FromVector( + Asn1EncodableVector v) + { + return v.Count < 1 ? Empty : new BerSequence(v); + } + + /** + * create an empty sequence + */ + public BerSequence() + { + } + + /** + * create a sequence containing one object + */ + public BerSequence( + Asn1Encodable obj) + : base(obj) + { + } + + public BerSequence( + params Asn1Encodable[] v) + : base(v) + { + } + + /** + * create a sequence containing a vector of objects. + */ + public BerSequence( + Asn1EncodableVector v) + : base(v) + { + } + + /* + */ + internal override void Encode( + DerOutputStream derOut) + { + if (derOut is Asn1OutputStream || derOut is BerOutputStream) + { + derOut.WriteByte(Asn1Tags.Sequence | Asn1Tags.Constructed); + derOut.WriteByte(0x80); + + foreach (Asn1Encodable o in this) + { + derOut.WriteObject(o); + } + + derOut.WriteByte(0x00); + derOut.WriteByte(0x00); + } + else + { + base.Encode(derOut); + } + } + } +} diff --git a/Crypto/src/asn1/BerSet.cs b/Crypto/src/asn1/BerSet.cs new file mode 100644
index 000000000..a181e172d --- /dev/null +++ b/Crypto/src/asn1/BerSet.cs
@@ -0,0 +1,70 @@ +namespace Org.BouncyCastle.Asn1 +{ + public class BerSet + : DerSet + { + public static new readonly BerSet Empty = new BerSet(); + + public static new BerSet FromVector( + Asn1EncodableVector v) + { + return v.Count < 1 ? Empty : new BerSet(v); + } + + internal static new BerSet FromVector( + Asn1EncodableVector v, + bool needsSorting) + { + return v.Count < 1 ? Empty : new BerSet(v, needsSorting); + } + + /** + * create an empty sequence + */ + public BerSet() + { + } + + /** + * create a set containing one object + */ + public BerSet(Asn1Encodable obj) : base(obj) + { + } + + /** + * create a set containing a vector of objects. + */ + public BerSet(Asn1EncodableVector v) : base(v, false) + { + } + + internal BerSet(Asn1EncodableVector v, bool needsSorting) : base(v, needsSorting) + { + } + + /* + */ + internal override void Encode( + DerOutputStream derOut) + { + if (derOut is Asn1OutputStream || derOut is BerOutputStream) + { + derOut.WriteByte(Asn1Tags.Set | Asn1Tags.Constructed); + derOut.WriteByte(0x80); + + foreach (Asn1Encodable o in this) + { + derOut.WriteObject(o); + } + + derOut.WriteByte(0x00); + derOut.WriteByte(0x00); + } + else + { + base.Encode(derOut); + } + } + } +} diff --git a/Crypto/src/asn1/BerTaggedObject.cs b/Crypto/src/asn1/BerTaggedObject.cs new file mode 100644
index 000000000..228b136cb --- /dev/null +++ b/Crypto/src/asn1/BerTaggedObject.cs
@@ -0,0 +1,108 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1 +{ + /** + * 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 + : DerTaggedObject + { + /** + * @param tagNo the tag number for this object. + * @param obj the tagged object. + */ + public BerTaggedObject( + int tagNo, + Asn1Encodable obj) + : base(tagNo, obj) + { + } + + /** + * @param explicitly true if an explicitly tagged object. + * @param tagNo the tag number for this object. + * @param obj the tagged object. + */ + public BerTaggedObject( + bool explicitly, + int tagNo, + Asn1Encodable obj) + : base(explicitly, tagNo, obj) + { + } + + /** + * create an implicitly tagged object that contains a zero + * length sequence. + */ + public BerTaggedObject( + int tagNo) + : base(false, tagNo, BerSequence.Empty) + { + } + + internal override void Encode( + DerOutputStream derOut) + { + if (derOut is Asn1OutputStream || derOut is BerOutputStream) + { + derOut.WriteTag((byte)(Asn1Tags.Constructed | Asn1Tags.Tagged), tagNo); + derOut.WriteByte(0x80); + + if (!IsEmpty()) + { + if (!explicitly) + { + IEnumerable eObj; + if (obj is Asn1OctetString) + { + if (obj is BerOctetString) + { + eObj = (BerOctetString) obj; + } + else + { + Asn1OctetString octs = (Asn1OctetString)obj; + eObj = new BerOctetString(octs.GetOctets()); + } + } + else if (obj is Asn1Sequence) + { + eObj = (Asn1Sequence) obj; + } + else if (obj is Asn1Set) + { + eObj = (Asn1Set) obj; + } + else + { + throw Platform.CreateNotImplementedException(obj.GetType().Name); + } + + foreach (Asn1Encodable o in eObj) + { + derOut.WriteObject(o); + } + } + else + { + derOut.WriteObject(obj); + } + } + + derOut.WriteByte(0x00); + derOut.WriteByte(0x00); + } + else + { + base.Encode(derOut); + } + } + } +} diff --git a/Crypto/src/asn1/ConstructedOctetStream.cs b/Crypto/src/asn1/ConstructedOctetStream.cs new file mode 100644
index 000000000..1773b22cc --- /dev/null +++ b/Crypto/src/asn1/ConstructedOctetStream.cs
@@ -0,0 +1,102 @@ +using System.IO; + +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Asn1 +{ + internal class ConstructedOctetStream + : BaseInputStream + { + private readonly Asn1StreamParser _parser; + + private bool _first = true; + private Stream _currentStream; + + internal ConstructedOctetStream( + Asn1StreamParser parser) + { + _parser = parser; + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (_currentStream == null) + { + if (!_first) + return 0; + + Asn1OctetStringParser s = (Asn1OctetStringParser)_parser.ReadObject(); + + if (s == null) + return 0; + + _first = false; + _currentStream = s.GetOctetStream(); + } + + int totalRead = 0; + + for (;;) + { + int numRead = _currentStream.Read(buffer, offset + totalRead, count - totalRead); + + if (numRead > 0) + { + totalRead += numRead; + + if (totalRead == count) + return totalRead; + } + else + { + Asn1OctetStringParser aos = (Asn1OctetStringParser)_parser.ReadObject(); + + if (aos == null) + { + _currentStream = null; + return totalRead; + } + + _currentStream = aos.GetOctetStream(); + } + } + } + + public override int ReadByte() + { + if (_currentStream == null) + { + if (!_first) + return 0; + + Asn1OctetStringParser s = (Asn1OctetStringParser)_parser.ReadObject(); + + if (s == null) + return 0; + + _first = false; + _currentStream = s.GetOctetStream(); + } + + for (;;) + { + int b = _currentStream.ReadByte(); + + if (b >= 0) + { + return b; + } + + Asn1OctetStringParser aos = (Asn1OctetStringParser)_parser.ReadObject(); + + if (aos == null) + { + _currentStream = null; + return -1; + } + + _currentStream = aos.GetOctetStream(); + } + } + } +} diff --git a/Crypto/src/asn1/DERExternal.cs b/Crypto/src/asn1/DERExternal.cs new file mode 100644
index 000000000..a342d6520 --- /dev/null +++ b/Crypto/src/asn1/DERExternal.cs
@@ -0,0 +1,207 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1 +{ + /** + * Class representing the DER-type External + */ + public class DerExternal + : Asn1Object + { + private DerObjectIdentifier directReference; + private DerInteger indirectReference; + private Asn1Object dataValueDescriptor; + private int encoding; + private Asn1Object externalContent; + + public DerExternal( + Asn1EncodableVector vector) + { + int offset = 0; + Asn1Object enc = GetObjFromVector(vector, offset); + if (enc is DerObjectIdentifier) + { + directReference = (DerObjectIdentifier)enc; + offset++; + enc = GetObjFromVector(vector, offset); + } + if (enc is DerInteger) + { + indirectReference = (DerInteger) enc; + offset++; + enc = GetObjFromVector(vector, offset); + } + if (!(enc is DerTaggedObject)) + { + dataValueDescriptor = (Asn1Object) enc; + offset++; + enc = GetObjFromVector(vector, offset); + } + if (!(enc is DerTaggedObject)) + { + throw new InvalidOperationException( + "No tagged object found in vector. Structure doesn't seem to be of type External"); + } + + if (vector.Count != offset + 1) + throw new ArgumentException("input vector too large", "vector"); + + if (!(enc is DerTaggedObject)) + throw new ArgumentException("No tagged object found in vector. Structure doesn't seem to be of type External", "vector"); + + DerTaggedObject obj = (DerTaggedObject)enc; + + // Use property accessor to include check on value + Encoding = obj.TagNo; + + if (encoding < 0 || encoding > 2) + throw new InvalidOperationException("invalid encoding value"); + + externalContent = obj.GetObject(); + } + + /** + * 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(DerObjectIdentifier directReference, DerInteger indirectReference, Asn1Object dataValueDescriptor, DerTaggedObject externalData) + : this(directReference, indirectReference, dataValueDescriptor, externalData.TagNo, externalData.ToAsn1Object()) + { + } + + /** + * 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(DerObjectIdentifier directReference, DerInteger indirectReference, Asn1Object dataValueDescriptor, int encoding, Asn1Object externalData) + { + DirectReference = directReference; + IndirectReference = indirectReference; + DataValueDescriptor = dataValueDescriptor; + Encoding = encoding; + ExternalContent = externalData.ToAsn1Object(); + } + + internal override void Encode(DerOutputStream derOut) + { + MemoryStream ms = new MemoryStream(); + WriteEncodable(ms, directReference); + WriteEncodable(ms, indirectReference); + WriteEncodable(ms, dataValueDescriptor); + WriteEncodable(ms, new DerTaggedObject(Asn1Tags.External, externalContent)); + + derOut.WriteEncoded(Asn1Tags.Constructed, Asn1Tags.External, ms.ToArray()); + } + + protected override int Asn1GetHashCode() + { + int ret = externalContent.GetHashCode(); + if (directReference != null) + { + ret ^= directReference.GetHashCode(); + } + if (indirectReference != null) + { + ret ^= indirectReference.GetHashCode(); + } + if (dataValueDescriptor != null) + { + ret ^= dataValueDescriptor.GetHashCode(); + } + return ret; + } + + protected override bool Asn1Equals( + Asn1Object asn1Object) + { + if (this == asn1Object) + return true; + + DerExternal other = asn1Object as DerExternal; + + if (other == null) + return false; + + return Platform.Equals(directReference, other.directReference) + && Platform.Equals(indirectReference, other.indirectReference) + && Platform.Equals(dataValueDescriptor, other.dataValueDescriptor) + && externalContent.Equals(other.externalContent); + } + + public Asn1Object DataValueDescriptor + { + get { return dataValueDescriptor; } + set { this.dataValueDescriptor = value; } + } + + public DerObjectIdentifier DirectReference + { + get { return directReference; } + set { this.directReference = value; } + } + + /** + * 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> + */ + public int Encoding + { + get + { + return encoding; + } + set + { + if (encoding < 0 || encoding > 2) + throw new InvalidOperationException("invalid encoding value: " + encoding); + + this.encoding = value; + } + } + + public Asn1Object ExternalContent + { + get { return externalContent; } + set { this.externalContent = value; } + } + + public DerInteger IndirectReference + { + get { return indirectReference; } + set { this.indirectReference = value; } + } + + private static Asn1Object GetObjFromVector(Asn1EncodableVector v, int index) + { + if (v.Count <= index) + throw new ArgumentException("too few objects in input vector", "v"); + + return v[index].ToAsn1Object(); + } + + private static void WriteEncodable(MemoryStream ms, Asn1Encodable e) + { + if (e != null) + { + byte[] bs = e.GetDerEncoded(); + ms.Write(bs, 0, bs.Length); + } + } + } +} diff --git a/Crypto/src/asn1/DERExternalParser.cs b/Crypto/src/asn1/DERExternalParser.cs new file mode 100644
index 000000000..70e426fed --- /dev/null +++ b/Crypto/src/asn1/DERExternalParser.cs
@@ -0,0 +1,26 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Asn1 +{ + public class DerExternalParser + : Asn1Encodable + { + private readonly Asn1StreamParser _parser; + + public DerExternalParser(Asn1StreamParser parser) + { + this._parser = parser; + } + + public IAsn1Convertible ReadObject() + { + return _parser.ReadObject(); + } + + public override Asn1Object ToAsn1Object() + { + return new DerExternal(_parser.ReadVector()); + } + } +} diff --git a/Crypto/src/asn1/DERGenerator.cs b/Crypto/src/asn1/DERGenerator.cs new file mode 100644
index 000000000..aab40fefa --- /dev/null +++ b/Crypto/src/asn1/DERGenerator.cs
@@ -0,0 +1,107 @@ +using System.IO; + +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Asn1 +{ + public abstract class DerGenerator + : Asn1Generator + { + private bool _tagged = false; + private bool _isExplicit; + private int _tagNo; + + protected DerGenerator( + Stream outStream) + : base(outStream) + { + } + + protected DerGenerator( + Stream outStream, + int tagNo, + bool isExplicit) + : base(outStream) + { + _tagged = true; + _isExplicit = isExplicit; + _tagNo = tagNo; + } + + private static void WriteLength( + Stream outStr, + int length) + { + if (length > 127) + { + int size = 1; + int val = length; + + while ((val >>= 8) != 0) + { + size++; + } + + outStr.WriteByte((byte)(size | 0x80)); + + for (int i = (size - 1) * 8; i >= 0; i -= 8) + { + outStr.WriteByte((byte)(length >> i)); + } + } + else + { + outStr.WriteByte((byte)length); + } + } + + internal static void WriteDerEncoded( + Stream outStream, + int tag, + byte[] bytes) + { + outStream.WriteByte((byte) tag); + WriteLength(outStream, bytes.Length); + outStream.Write(bytes, 0, bytes.Length); + } + + internal void WriteDerEncoded( + int tag, + byte[] bytes) + { + if (_tagged) + { + int tagNum = _tagNo | Asn1Tags.Tagged; + + if (_isExplicit) + { + int newTag = _tagNo | Asn1Tags.Constructed | Asn1Tags.Tagged; + MemoryStream bOut = new MemoryStream(); + WriteDerEncoded(bOut, tag, bytes); + WriteDerEncoded(Out, newTag, bOut.ToArray()); + } + else + { + if ((tag & Asn1Tags.Constructed) != 0) + { + tagNum |= Asn1Tags.Constructed; + } + + WriteDerEncoded(Out, tagNum, bytes); + } + } + else + { + WriteDerEncoded(Out, tag, bytes); + } + } + + internal static void WriteDerEncoded( + Stream outStr, + int tag, + Stream inStr) + { + WriteDerEncoded(outStr, tag, Streams.ReadAll(inStr)); + } + } +} diff --git a/Crypto/src/asn1/DEROctetStringParser.cs b/Crypto/src/asn1/DEROctetStringParser.cs new file mode 100644
index 000000000..b0d3ad8cf --- /dev/null +++ b/Crypto/src/asn1/DEROctetStringParser.cs
@@ -0,0 +1,36 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Asn1 +{ + public class DerOctetStringParser + : Asn1OctetStringParser + { + private readonly DefiniteLengthInputStream stream; + + internal DerOctetStringParser( + DefiniteLengthInputStream stream) + { + this.stream = stream; + } + + public Stream GetOctetStream() + { + return stream; + } + + public Asn1Object ToAsn1Object() + { + try + { + return new DerOctetString(stream.ToArray()); + } + catch (IOException e) + { + throw new InvalidOperationException("IOException converting stream to byte array: " + e.Message, e); + } + } + } +} diff --git a/Crypto/src/asn1/DERSequenceGenerator.cs b/Crypto/src/asn1/DERSequenceGenerator.cs new file mode 100644
index 000000000..4c2bfd012 --- /dev/null +++ b/Crypto/src/asn1/DERSequenceGenerator.cs
@@ -0,0 +1,40 @@ +using System.IO; + +namespace Org.BouncyCastle.Asn1 +{ + public class DerSequenceGenerator + : DerGenerator + { + private readonly MemoryStream _bOut = new MemoryStream(); + + public DerSequenceGenerator( + Stream outStream) + : base(outStream) + { + } + + public DerSequenceGenerator( + Stream outStream, + int tagNo, + bool isExplicit) + : base(outStream, tagNo, isExplicit) + { + } + + public override void AddObject( + Asn1Encodable obj) + { + new DerOutputStream(_bOut).WriteObject(obj); + } + + public override Stream GetRawOutputStream() + { + return _bOut; + } + + public override void Close() + { + WriteDerEncoded(Asn1Tags.Constructed | Asn1Tags.Sequence, _bOut.ToArray()); + } + } +} diff --git a/Crypto/src/asn1/DERSequenceParser.cs b/Crypto/src/asn1/DERSequenceParser.cs new file mode 100644
index 000000000..69c2b9b2d --- /dev/null +++ b/Crypto/src/asn1/DERSequenceParser.cs
@@ -0,0 +1,24 @@ +namespace Org.BouncyCastle.Asn1 +{ + public class DerSequenceParser + : Asn1SequenceParser + { + private readonly Asn1StreamParser _parser; + + internal DerSequenceParser( + Asn1StreamParser parser) + { + this._parser = parser; + } + + public IAsn1Convertible ReadObject() + { + return _parser.ReadObject(); + } + + public Asn1Object ToAsn1Object() + { + return new DerSequence(_parser.ReadVector()); + } + } +} diff --git a/Crypto/src/asn1/DERSetGenerator.cs b/Crypto/src/asn1/DERSetGenerator.cs new file mode 100644
index 000000000..455ca88ac --- /dev/null +++ b/Crypto/src/asn1/DERSetGenerator.cs
@@ -0,0 +1,40 @@ +using System.IO; + +namespace Org.BouncyCastle.Asn1 +{ + public class DerSetGenerator + : DerGenerator + { + private readonly MemoryStream _bOut = new MemoryStream(); + + public DerSetGenerator( + Stream outStream) + : base(outStream) + { + } + + public DerSetGenerator( + Stream outStream, + int tagNo, + bool isExplicit) + : base(outStream, tagNo, isExplicit) + { + } + + public override void AddObject( + Asn1Encodable obj) + { + new DerOutputStream(_bOut).WriteObject(obj); + } + + public override Stream GetRawOutputStream() + { + return _bOut; + } + + public override void Close() + { + WriteDerEncoded(Asn1Tags.Constructed | Asn1Tags.Set, _bOut.ToArray()); + } + } +} diff --git a/Crypto/src/asn1/DERSetParser.cs b/Crypto/src/asn1/DERSetParser.cs new file mode 100644
index 000000000..d67f135be --- /dev/null +++ b/Crypto/src/asn1/DERSetParser.cs
@@ -0,0 +1,24 @@ +namespace Org.BouncyCastle.Asn1 +{ + public class DerSetParser + : Asn1SetParser + { + private readonly Asn1StreamParser _parser; + + internal DerSetParser( + Asn1StreamParser parser) + { + this._parser = parser; + } + + public IAsn1Convertible ReadObject() + { + return _parser.ReadObject(); + } + + public Asn1Object ToAsn1Object() + { + return new DerSet(_parser.ReadVector(), false); + } + } +} diff --git a/Crypto/src/asn1/DefiniteLengthInputStream.cs b/Crypto/src/asn1/DefiniteLengthInputStream.cs new file mode 100644
index 000000000..cfea89f21 --- /dev/null +++ b/Crypto/src/asn1/DefiniteLengthInputStream.cs
@@ -0,0 +1,90 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Asn1 +{ + class DefiniteLengthInputStream + : LimitedInputStream + { + private static readonly byte[] EmptyBytes = new byte[0]; + + private readonly int _originalLength; + private int _remaining; + + internal DefiniteLengthInputStream( + Stream inStream, + int length) + : base(inStream, length) + { + if (length < 0) + throw new ArgumentException("negative lengths not allowed", "length"); + + this._originalLength = length; + this._remaining = length; + + if (length == 0) + { + SetParentEofDetect(true); + } + } + + internal int Remaining + { + get { return _remaining; } + } + + public override int ReadByte() + { + if (_remaining == 0) + return -1; + + int b = _in.ReadByte(); + + if (b < 0) + throw new EndOfStreamException("DEF length " + _originalLength + " object truncated by " + _remaining); + + if (--_remaining == 0) + { + SetParentEofDetect(true); + } + + return b; + } + + public override int Read( + byte[] buf, + int off, + int len) + { + if (_remaining == 0) + return 0; + + int toRead = System.Math.Min(len, _remaining); + int numRead = _in.Read(buf, off, toRead); + + if (numRead < 1) + throw new EndOfStreamException("DEF length " + _originalLength + " object truncated by " + _remaining); + + if ((_remaining -= numRead) == 0) + { + SetParentEofDetect(true); + } + + return numRead; + } + + internal byte[] ToArray() + { + if (_remaining == 0) + return EmptyBytes; + + byte[] bytes = new byte[_remaining]; + if ((_remaining -= Streams.ReadFully(_in, bytes)) != 0) + throw new EndOfStreamException("DEF length " + _originalLength + " object truncated by " + _remaining); + SetParentEofDetect(true); + return bytes; + } + } +} diff --git a/Crypto/src/asn1/DerApplicationSpecific.cs b/Crypto/src/asn1/DerApplicationSpecific.cs new file mode 100644
index 000000000..394c7431e --- /dev/null +++ b/Crypto/src/asn1/DerApplicationSpecific.cs
@@ -0,0 +1,237 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1 +{ + /** + * Base class for an application specific object + */ + public class DerApplicationSpecific + : Asn1Object + { + private readonly bool isConstructed; + private readonly int tag; + private readonly byte[] octets; + + internal DerApplicationSpecific( + bool 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 obj) + : this(true, tag, obj) + { + } + + public DerApplicationSpecific( + bool isExplicit, + int tag, + Asn1Encodable obj) + { + Asn1Object asn1Obj = obj.ToAsn1Object(); + + byte[] data = asn1Obj.GetDerEncoded(); + + this.isConstructed = isExplicit || asn1Obj is Asn1Set || asn1Obj is Asn1Sequence; + this.tag = tag; + + if (isExplicit) + { + this.octets = data; + } + else + { + int lenBytes = GetLengthOfHeader(data); + byte[] tmp = new byte[data.Length - lenBytes]; + Array.Copy(data, lenBytes, tmp, 0, tmp.Length); + this.octets = tmp; + } + } + + public DerApplicationSpecific( + int tagNo, + Asn1EncodableVector vec) + { + this.tag = tagNo; + this.isConstructed = true; + MemoryStream bOut = new MemoryStream(); + + for (int i = 0; i != vec.Count; i++) + { + try + { + byte[] bs = vec[i].GetDerEncoded(); + bOut.Write(bs, 0, bs.Length); + } + catch (IOException e) + { + throw new InvalidOperationException("malformed object", e); + } + } + this.octets = bOut.ToArray(); + } + + private int GetLengthOfHeader( + byte[] data) + { + int length = data[1]; // 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 InvalidOperationException("DER length more than 4 bytes: " + size); + } + + return size + 2; + } + + return 2; + } + + public bool IsConstructed() + { + return isConstructed; + } + + public byte[] GetContents() + { + return octets; + } + + public int ApplicationTag + { + get { return tag; } + } + + /** + * Return the enclosed object assuming explicit tagging. + * + * @return the resulting object + * @throws IOException if reconstruction fails. + */ + public Asn1Object GetObject() + { + return FromByteArray(GetContents()); + } + + /** + * 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 Asn1Object GetObject( + int derTagNo) + { + if (derTagNo >= 0x1f) + throw new IOException("unsupported tag number"); + + byte[] orig = this.GetEncoded(); + byte[] tmp = ReplaceTagNumber(derTagNo, orig); + + if ((orig[0] & Asn1Tags.Constructed) != 0) + { + tmp[0] |= Asn1Tags.Constructed; + } + + return FromByteArray(tmp);; + } + + internal override void Encode( + DerOutputStream derOut) + { + int classBits = Asn1Tags.Application; + if (isConstructed) + { + classBits |= Asn1Tags.Constructed; + } + + derOut.WriteEncoded(classBits, tag, octets); + } + + protected override bool Asn1Equals( + Asn1Object asn1Object) + { + DerApplicationSpecific other = asn1Object as DerApplicationSpecific; + + if (other == null) + return false; + + return this.isConstructed == other.isConstructed + && this.tag == other.tag + && Arrays.AreEqual(this.octets, other.octets); + } + + protected override int Asn1GetHashCode() + { + return isConstructed.GetHashCode() ^ tag.GetHashCode() ^ Arrays.GetHashCode(octets); + } + + private byte[] ReplaceTagNumber( + int newTag, + byte[] input) + { + 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 InvalidOperationException("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]; + + Array.Copy(input, index, tmp, 1, tmp.Length - 1); + + tmp[0] = (byte)newTag; + + return tmp; + } + } +} diff --git a/Crypto/src/asn1/DerBMPString.cs b/Crypto/src/asn1/DerBMPString.cs new file mode 100644
index 000000000..4f7e0a635 --- /dev/null +++ b/Crypto/src/asn1/DerBMPString.cs
@@ -0,0 +1,115 @@ +using System; + +namespace Org.BouncyCastle.Asn1 +{ + /** + * Der BMPString object. + */ + public class DerBmpString + : DerStringBase + { + private readonly string str; + + /** + * return a BMP string from the given object. + * + * @param obj the object we want converted. + * @exception ArgumentException if the object cannot be converted. + */ + public static DerBmpString GetInstance( + object obj) + { + if (obj == null || obj is DerBmpString) + { + return (DerBmpString)obj; + } + + throw new ArgumentException("illegal object in GetInstance: " + obj.GetType().Name); + } + + /** + * return a BMP string from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicitly true if the object is meant to be explicitly + * tagged false otherwise. + * @exception ArgumentException if the tagged object cannot + * be converted. + */ + public static DerBmpString GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + Asn1Object o = obj.GetObject(); + + if (isExplicit || o is DerBmpString) + { + return GetInstance(o); + } + + return new DerBmpString(Asn1OctetString.GetInstance(o).GetOctets()); + } + + /** + * basic constructor - byte encoded string. + */ + public DerBmpString( + byte[] str) + { + if (str == null) + throw new ArgumentNullException("str"); + + char[] cs = new char[str.Length / 2]; + + for (int i = 0; i != cs.Length; i++) + { + cs[i] = (char)((str[2 * i] << 8) | (str[2 * i + 1] & 0xff)); + } + + this.str = new string(cs); + } + + /** + * basic constructor + */ + public DerBmpString( + string str) + { + if (str == null) + throw new ArgumentNullException("str"); + + this.str = str; + } + + public override string GetString() + { + return str; + } + + protected override bool Asn1Equals( + Asn1Object asn1Object) + { + DerBmpString other = asn1Object as DerBmpString; + + if (other == null) + return false; + + return this.str.Equals(other.str); + } + + internal override void Encode( + DerOutputStream derOut) + { + char[] c = str.ToCharArray(); + byte[] b = new byte[c.Length * 2]; + + for (int i = 0; i != c.Length; i++) + { + b[2 * i] = (byte)(c[i] >> 8); + b[2 * i + 1] = (byte)c[i]; + } + + derOut.WriteEncoded(Asn1Tags.BmpString, b); + } + } +} diff --git a/Crypto/src/asn1/DerBitString.cs b/Crypto/src/asn1/DerBitString.cs new file mode 100644
index 000000000..d5cb872bc --- /dev/null +++ b/Crypto/src/asn1/DerBitString.cs
@@ -0,0 +1,248 @@ +using System; +using System.Text; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1 +{ + public class DerBitString + : DerStringBase + { + private static readonly char[] table + = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + + private readonly byte[] data; + private readonly int padBits; + + /** + * return the correct number of pad bits for a bit string defined in + * a 32 bit constant + */ + static internal 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 internal 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 ArgumentException if the object cannot be converted. + */ + public static DerBitString GetInstance( + object obj) + { + if (obj == null || obj is DerBitString) + { + return (DerBitString) obj; + } + + throw new ArgumentException("illegal object in GetInstance: " + obj.GetType().Name); + } + + /** + * return a Bit string from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicitly true if the object is meant to be explicitly + * tagged false otherwise. + * @exception ArgumentException if the tagged object cannot + * be converted. + */ + public static DerBitString GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + Asn1Object o = obj.GetObject(); + + if (isExplicit || o is DerBitString) + { + return GetInstance(o); + } + + return FromAsn1Octets(((Asn1OctetString)o).GetOctets()); + } + + internal DerBitString( + byte data, + int padBits) + { + this.data = new byte[]{ 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) + { + // TODO Deep copy? + this.data = data; + this.padBits = padBits; + } + + public DerBitString( + byte[] data) + { + // TODO Deep copy? + this.data = data; + } + + public DerBitString( + Asn1Encodable obj) + { + this.data = obj.GetDerEncoded(); + //this.padBits = 0; + } + + public byte[] GetBytes() + { + return data; + } + + public int PadBits + { + get { return padBits; } + } + + /** + * @return the value of the bit string as an int (truncating if necessary) + */ + public int IntValue + { + get + { + int value = 0; + + for (int i = 0; i != data.Length && i != 4; i++) + { + value |= (data[i] & 0xff) << (8 * i); + } + + return value; + } + } + + internal override void Encode( + DerOutputStream derOut) + { + byte[] bytes = new byte[GetBytes().Length + 1]; + + bytes[0] = (byte) PadBits; + Array.Copy(GetBytes(), 0, bytes, 1, bytes.Length - 1); + + derOut.WriteEncoded(Asn1Tags.BitString, bytes); + } + + protected override int Asn1GetHashCode() + { + return padBits.GetHashCode() ^ Arrays.GetHashCode(data); + } + + protected override bool Asn1Equals( + Asn1Object asn1Object) + { + DerBitString other = asn1Object as DerBitString; + + if (other == null) + return false; + + return this.padBits == other.padBits + && Arrays.AreEqual(this.data, other.data); + } + + public override string GetString() + { + StringBuilder buffer = new StringBuilder("#"); + + byte[] str = GetDerEncoded(); + + for (int i = 0; i != str.Length; i++) + { + uint ubyte = str[i]; + buffer.Append(table[(ubyte >> 4) & 0xf]); + buffer.Append(table[str[i] & 0xf]); + } + + return buffer.ToString(); + } + + internal static DerBitString FromAsn1Octets(byte[] octets) + { + if (octets.Length < 1) + throw new ArgumentException("truncated BIT STRING detected"); + + int padBits = octets[0]; + byte[] data = new byte[octets.Length - 1]; + Array.Copy(octets, 1, data, 0, data.Length); + return new DerBitString(data, padBits); + } + } +} + diff --git a/Crypto/src/asn1/DerBoolean.cs b/Crypto/src/asn1/DerBoolean.cs new file mode 100644
index 000000000..41ccae8a1 --- /dev/null +++ b/Crypto/src/asn1/DerBoolean.cs
@@ -0,0 +1,110 @@ +using System; + +namespace Org.BouncyCastle.Asn1 +{ + public class DerBoolean + : Asn1Object + { + private readonly byte value; + + public static readonly DerBoolean False = new DerBoolean(false); + public static readonly DerBoolean True = new DerBoolean(true); + + /** + * return a bool from the passed in object. + * + * @exception ArgumentException if the object cannot be converted. + */ + public static DerBoolean GetInstance( + object obj) + { + if (obj == null || obj is DerBoolean) + { + return (DerBoolean) obj; + } + + throw new ArgumentException("illegal object in GetInstance: " + obj.GetType().Name); + } + + /** + * return a DerBoolean from the passed in bool. + */ + public static DerBoolean GetInstance( + bool value) + { + return value ? True : False; + } + + /** + * return a Boolean from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicitly true if the object is meant to be explicitly + * tagged false otherwise. + * @exception ArgumentException if the tagged object cannot + * be converted. + */ + public static DerBoolean GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + Asn1Object o = obj.GetObject(); + + if (isExplicit || o is DerBoolean) + { + return GetInstance(o); + } + + return new DerBoolean(((Asn1OctetString)o).GetOctets()); + } + + public DerBoolean( + byte[] val) + { + if (val.Length != 1) + throw new ArgumentException("byte value should have 1 byte in it", "val"); + + // TODO Are there any constraints on the possible byte values? + this.value = val[0]; + } + + private DerBoolean( + bool value) + { + this.value = value ? (byte)0xff : (byte)0; + } + + public bool IsTrue + { + get { return value != 0; } + } + + internal override void Encode( + DerOutputStream derOut) + { + // TODO Should we make sure the byte value is one of '0' or '0xff' here? + derOut.WriteEncoded(Asn1Tags.Boolean, new byte[]{ value }); + } + + protected override bool Asn1Equals( + Asn1Object asn1Object) + { + DerBoolean other = asn1Object as DerBoolean; + + if (other == null) + return false; + + return IsTrue == other.IsTrue; + } + + protected override int Asn1GetHashCode() + { + return IsTrue.GetHashCode(); + } + + public override string ToString() + { + return IsTrue ? "TRUE" : "FALSE"; + } + } +} diff --git a/Crypto/src/asn1/DerEnumerated.cs b/Crypto/src/asn1/DerEnumerated.cs new file mode 100644
index 000000000..0e67e6dbe --- /dev/null +++ b/Crypto/src/asn1/DerEnumerated.cs
@@ -0,0 +1,100 @@ +using System; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1 +{ + public class DerEnumerated + : Asn1Object + { + private readonly byte[] bytes; + + /** + * return an integer from the passed in object + * + * @exception ArgumentException if the object cannot be converted. + */ + public static DerEnumerated GetInstance( + object obj) + { + if (obj == null || obj is DerEnumerated) + { + return (DerEnumerated)obj; + } + + throw new ArgumentException("illegal object in GetInstance: " + obj.GetType().Name); + } + + /** + * return an Enumerated from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicitly true if the object is meant to be explicitly + * tagged false otherwise. + * @exception ArgumentException if the tagged object cannot + * be converted. + */ + public static DerEnumerated GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + Asn1Object o = obj.GetObject(); + + if (isExplicit || o is DerEnumerated) + { + return GetInstance(o); + } + + return new DerEnumerated(((Asn1OctetString)o).GetOctets()); + } + + public DerEnumerated( + int val) + { + bytes = BigInteger.ValueOf(val).ToByteArray(); + } + + public DerEnumerated( + BigInteger val) + { + bytes = val.ToByteArray(); + } + + public DerEnumerated( + byte[] bytes) + { + this.bytes = bytes; + } + + public BigInteger Value + { + get + { + return new BigInteger(bytes); + } + } + + internal override void Encode( + DerOutputStream derOut) + { + derOut.WriteEncoded(Asn1Tags.Enumerated, bytes); + } + + protected override bool Asn1Equals( + Asn1Object asn1Object) + { + DerEnumerated other = asn1Object as DerEnumerated; + + if (other == null) + return false; + + return Arrays.AreEqual(this.bytes, other.bytes); + } + + protected override int Asn1GetHashCode() + { + return Arrays.GetHashCode(bytes); + } + } +} diff --git a/Crypto/src/asn1/DerGeneralString.cs b/Crypto/src/asn1/DerGeneralString.cs new file mode 100644
index 000000000..0e20b53bd --- /dev/null +++ b/Crypto/src/asn1/DerGeneralString.cs
@@ -0,0 +1,81 @@ +using System; +using System.Text; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1 +{ + public class DerGeneralString + : DerStringBase + { + private readonly string str; + + public static DerGeneralString GetInstance( + object obj) + { + if (obj == null || obj is DerGeneralString) + { + return (DerGeneralString) obj; + } + + throw new ArgumentException("illegal object in GetInstance: " + + obj.GetType().Name); + } + + public static DerGeneralString GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + Asn1Object o = obj.GetObject(); + + if (isExplicit || o is DerGeneralString) + { + return GetInstance(o); + } + + return new DerGeneralString(((Asn1OctetString)o).GetOctets()); + } + + public DerGeneralString( + byte[] str) + : this(Strings.FromAsciiByteArray(str)) + { + } + + public DerGeneralString( + string str) + { + if (str == null) + throw new ArgumentNullException("str"); + + this.str = str; + } + + public override string GetString() + { + return str; + } + + public byte[] GetOctets() + { + return Strings.ToAsciiByteArray(str); + } + + internal override void Encode( + DerOutputStream derOut) + { + derOut.WriteEncoded(Asn1Tags.GeneralString, GetOctets()); + } + + protected override bool Asn1Equals( + Asn1Object asn1Object) + { + DerGeneralString other = asn1Object as DerGeneralString; + + if (other == null) + return false; + + return this.str.Equals(other.str); + } + } +} diff --git a/Crypto/src/asn1/DerGeneralizedTime.cs b/Crypto/src/asn1/DerGeneralizedTime.cs new file mode 100644
index 000000000..0a0e6fd7c --- /dev/null +++ b/Crypto/src/asn1/DerGeneralizedTime.cs
@@ -0,0 +1,305 @@ +using System; +using System.Globalization; +using System.Text; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1 +{ + /** + * Generalized time object. + */ + public class DerGeneralizedTime + : Asn1Object + { + private readonly string time; + + /** + * return a generalized time from the passed in object + * + * @exception ArgumentException if the object cannot be converted. + */ + public static DerGeneralizedTime GetInstance( + object obj) + { + if (obj == null || obj is DerGeneralizedTime) + { + return (DerGeneralizedTime)obj; + } + + throw new ArgumentException("illegal object in GetInstance: " + obj.GetType().Name, "obj"); + } + + /** + * return a Generalized Time object from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicitly true if the object is meant to be explicitly + * tagged false otherwise. + * @exception ArgumentException if the tagged object cannot + * be converted. + */ + public static DerGeneralizedTime GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + Asn1Object o = obj.GetObject(); + + if (isExplicit || o is DerGeneralizedTime) + { + return GetInstance(o); + } + + return new DerGeneralizedTime(((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 ArgumentException if string is an illegal format. + */ + public DerGeneralizedTime( + string time) + { + this.time = time; + + try + { + ToDateTime(); + } + catch (FormatException e) + { + throw new ArgumentException("invalid date string: " + e.Message); + } + } + + /** + * base constructor from a local time object + */ + public DerGeneralizedTime( + DateTime time) + { + this.time = time.ToString(@"yyyyMMddHHmmss\Z"); + } + + internal DerGeneralizedTime( + byte[] bytes) + { + // + // explicitly convert to characters + // + this.time = Strings.FromAsciiByteArray(bytes); + } + + /** + * Return the time. + * @return The time string as it appeared in the encoded object. + */ + public string TimeString + { + get { return 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.</p> + */ + public string GetTime() + { + // + // standardise the format. + // + if (time[time.Length - 1] == 'Z') + { + return time.Substring(0, time.Length - 1) + "GMT+00:00"; + } + else + { + int signPos = time.Length - 5; + char sign = time[signPos]; + if (sign == '-' || sign == '+') + { + return time.Substring(0, signPos) + + "GMT" + + time.Substring(signPos, 3) + + ":" + + time.Substring(signPos + 3); + } + else + { + signPos = time.Length - 3; + sign = time[signPos]; + if (sign == '-' || sign == '+') + { + return time.Substring(0, signPos) + + "GMT" + + time.Substring(signPos) + + ":00"; + } + } + } + + return time + CalculateGmtOffset(); + } + + private string CalculateGmtOffset() + { + char sign = '+'; + DateTime time = ToDateTime(); + +#if (SILVERLIGHT || PORTABLE) + long offset = time.Ticks - time.ToUniversalTime().Ticks; + if (offset < 0) + { + sign = '-'; + offset = -offset; + } + int hours = (int)(offset / TimeSpan.TicksPerHour); + int minutes = (int)(offset / TimeSpan.TicksPerMinute) % 60; +#else + // Note: GetUtcOffset incorporates Daylight Savings offset + TimeSpan offset = TimeZone.CurrentTimeZone.GetUtcOffset(time); + if (offset.CompareTo(TimeSpan.Zero) < 0) + { + sign = '-'; + offset = offset.Duration(); + } + int hours = offset.Hours; + int minutes = offset.Minutes; +#endif + + return "GMT" + sign + Convert(hours) + ":" + Convert(minutes); + } + + private static string Convert( + int time) + { + if (time < 10) + { + return "0" + time; + } + + return time.ToString(); + } + + public DateTime ToDateTime() + { + string formatStr; + string d = time; + bool makeUniversal = false; + + if (d.EndsWith("Z")) + { + if (HasFractionalSeconds) + { + int fCount = d.Length - d.IndexOf('.') - 2; + formatStr = @"yyyyMMddHHmmss." + FString(fCount) + @"\Z"; + } + else + { + formatStr = @"yyyyMMddHHmmss\Z"; + } + } + else if (time.IndexOf('-') > 0 || time.IndexOf('+') > 0) + { + d = GetTime(); + makeUniversal = true; + + if (HasFractionalSeconds) + { + int fCount = d.IndexOf("GMT") - 1 - d.IndexOf('.'); + formatStr = @"yyyyMMddHHmmss." + FString(fCount) + @"'GMT'zzz"; + } + else + { + formatStr = @"yyyyMMddHHmmss'GMT'zzz"; + } + } + else + { + if (HasFractionalSeconds) + { + int fCount = d.Length - 1 - d.IndexOf('.'); + formatStr = @"yyyyMMddHHmmss." + FString(fCount); + } + else + { + formatStr = @"yyyyMMddHHmmss"; + } + + // TODO? +// dateF.setTimeZone(new SimpleTimeZone(0, TimeZone.getDefault().getID())); + } + + return ParseDateString(d, formatStr, makeUniversal); + } + + private string FString( + int count) + { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < count; ++i) + { + sb.Append('f'); + } + return sb.ToString(); + } + + private DateTime ParseDateString( + string dateStr, + string formatStr, + bool makeUniversal) + { + DateTime dt = DateTime.ParseExact( + dateStr, + formatStr, + DateTimeFormatInfo.InvariantInfo); + + return makeUniversal ? dt.ToUniversalTime() : dt; + } + + private bool HasFractionalSeconds + { + get { return time.IndexOf('.') == 14; } + } + + private byte[] GetOctets() + { + return Strings.ToAsciiByteArray(time); + } + + internal override void Encode( + DerOutputStream derOut) + { + derOut.WriteEncoded(Asn1Tags.GeneralizedTime, GetOctets()); + } + + protected override bool Asn1Equals( + Asn1Object asn1Object) + { + DerGeneralizedTime other = asn1Object as DerGeneralizedTime; + + if (other == null) + return false; + + return this.time.Equals(other.time); + } + + protected override int Asn1GetHashCode() + { + return time.GetHashCode(); + } + } +} diff --git a/Crypto/src/asn1/DerIA5String.cs b/Crypto/src/asn1/DerIA5String.cs new file mode 100644
index 000000000..9fa2cba3c --- /dev/null +++ b/Crypto/src/asn1/DerIA5String.cs
@@ -0,0 +1,145 @@ +using System; +using System.Text; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1 +{ + /** + * Der IA5String object - this is an ascii string. + */ + public class DerIA5String + : DerStringBase + { + private readonly string str; + + /** + * return a IA5 string from the passed in object + * + * @exception ArgumentException if the object cannot be converted. + */ + public static DerIA5String GetInstance( + object obj) + { + if (obj == null || obj is DerIA5String) + { + return (DerIA5String)obj; + } + + throw new ArgumentException("illegal object in GetInstance: " + obj.GetType().Name); + } + + /** + * return an IA5 string from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicitly true if the object is meant to be explicitly + * tagged false otherwise. + * @exception ArgumentException if the tagged object cannot + * be converted. + */ + public static DerIA5String GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + Asn1Object o = obj.GetObject(); + + if (isExplicit || o is DerIA5String) + { + return GetInstance(o); + } + + return new DerIA5String(((Asn1OctetString)o).GetOctets()); + } + + /** + * basic constructor - with bytes. + */ + public DerIA5String( + byte[] str) + : this(Strings.FromAsciiByteArray(str), false) + { + } + + /** + * basic constructor - without validation. + */ + public DerIA5String( + string str) + : this(str, false) + { + } + + /** + * Constructor with optional validation. + * + * @param string the base string to wrap. + * @param validate whether or not to check the string. + * @throws ArgumentException if validate is true and the string + * contains characters that should not be in an IA5String. + */ + public DerIA5String( + string str, + bool validate) + { + if (str == null) + throw new ArgumentNullException("str"); + if (validate && !IsIA5String(str)) + throw new ArgumentException("string contains illegal characters", "str"); + + this.str = str; + } + + public override string GetString() + { + return str; + } + + public byte[] GetOctets() + { + return Strings.ToAsciiByteArray(str); + } + + internal override void Encode( + DerOutputStream derOut) + { + derOut.WriteEncoded(Asn1Tags.IA5String, GetOctets()); + } + + protected override int Asn1GetHashCode() + { + return this.str.GetHashCode(); + } + + protected override bool Asn1Equals( + Asn1Object asn1Object) + { + DerIA5String other = asn1Object as DerIA5String; + + if (other == null) + return false; + + return this.str.Equals(other.str); + } + + /** + * 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 bool IsIA5String( + string str) + { + foreach (char ch in str) + { + if (ch > 0x007f) + { + return false; + } + } + + return true; + } + } +} diff --git a/Crypto/src/asn1/DerInteger.cs b/Crypto/src/asn1/DerInteger.cs new file mode 100644
index 000000000..eb0614515 --- /dev/null +++ b/Crypto/src/asn1/DerInteger.cs
@@ -0,0 +1,117 @@ +using System; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1 +{ + public class DerInteger + : Asn1Object + { + private readonly byte[] bytes; + + /** + * return an integer from the passed in object + * + * @exception ArgumentException if the object cannot be converted. + */ + public static DerInteger GetInstance( + object obj) + { + if (obj == null || obj is DerInteger) + { + return (DerInteger)obj; + } + + throw new ArgumentException("illegal object in GetInstance: " + obj.GetType().Name); + } + + /** + * return an Integer from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param isExplicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception ArgumentException if the tagged object cannot + * be converted. + */ + public static DerInteger GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + if (obj == null) + throw new ArgumentNullException("obj"); + + Asn1Object o = obj.GetObject(); + + if (isExplicit || o is DerInteger) + { + return GetInstance(o); + } + + return new DerInteger(Asn1OctetString.GetInstance(o).GetOctets()); + } + + public DerInteger( + int value) + { + bytes = BigInteger.ValueOf(value).ToByteArray(); + } + + public DerInteger( + BigInteger value) + { + if (value == null) + throw new ArgumentNullException("value"); + + bytes = value.ToByteArray(); + } + + public DerInteger( + byte[] bytes) + { + this.bytes = bytes; + } + + public BigInteger Value + { + get { return new BigInteger(bytes); } + } + + /** + * in some cases positive values Get crammed into a space, + * that's not quite big enough... + */ + public BigInteger PositiveValue + { + get { return new BigInteger(1, bytes); } + } + + internal override void Encode( + DerOutputStream derOut) + { + derOut.WriteEncoded(Asn1Tags.Integer, bytes); + } + + protected override int Asn1GetHashCode() + { + return Arrays.GetHashCode(bytes); + } + + protected override bool Asn1Equals( + Asn1Object asn1Object) + { + DerInteger other = asn1Object as DerInteger; + + if (other == null) + return false; + + return Arrays.AreEqual(this.bytes, other.bytes); + } + + public override string ToString() + { + return Value.ToString(); + } + } +} diff --git a/Crypto/src/asn1/DerNull.cs b/Crypto/src/asn1/DerNull.cs new file mode 100644
index 000000000..a802f6486 --- /dev/null +++ b/Crypto/src/asn1/DerNull.cs
@@ -0,0 +1,41 @@ +using System; + +namespace Org.BouncyCastle.Asn1 +{ + /** + * A Null object. + */ + public class DerNull + : Asn1Null + { + public static readonly DerNull Instance = new DerNull(0); + + byte[] zeroBytes = new byte[0]; + + [Obsolete("Use static Instance object")] + public DerNull() + { + } + + protected internal DerNull(int dummy) + { + } + + internal override void Encode( + DerOutputStream derOut) + { + derOut.WriteEncoded(Asn1Tags.Null, zeroBytes); + } + + protected override bool Asn1Equals( + Asn1Object asn1Object) + { + return asn1Object is DerNull; + } + + protected override int Asn1GetHashCode() + { + return -1; + } + } +} diff --git a/Crypto/src/asn1/DerNumericString.cs b/Crypto/src/asn1/DerNumericString.cs new file mode 100644
index 000000000..6e2715a4d --- /dev/null +++ b/Crypto/src/asn1/DerNumericString.cs
@@ -0,0 +1,138 @@ +using System; +using System.Text; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1 +{ + /** + * Der NumericString object - this is an ascii string of characters {0,1,2,3,4,5,6,7,8,9, }. + */ + public class DerNumericString + : DerStringBase + { + private readonly string str; + + /** + * return a Numeric string from the passed in object + * + * @exception ArgumentException if the object cannot be converted. + */ + public static DerNumericString GetInstance( + object obj) + { + if (obj == null || obj is DerNumericString) + { + return (DerNumericString)obj; + } + + throw new ArgumentException("illegal object in GetInstance: " + obj.GetType().Name); + } + + /** + * return an Numeric string from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicitly true if the object is meant to be explicitly + * tagged false otherwise. + * @exception ArgumentException if the tagged object cannot + * be converted. + */ + public static DerNumericString GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + Asn1Object o = obj.GetObject(); + + if (isExplicit || o is DerNumericString) + { + return GetInstance(o); + } + + return new DerNumericString(Asn1OctetString.GetInstance(o).GetOctets()); + } + + /** + * basic constructor - with bytes. + */ + public DerNumericString( + byte[] str) + : this(Strings.FromAsciiByteArray(str), false) + { + } + + /** + * basic constructor - without validation.. + */ + public DerNumericString( + string str) + : this(str, false) + { + } + + /** + * Constructor with optional validation. + * + * @param string the base string to wrap. + * @param validate whether or not to check the string. + * @throws ArgumentException if validate is true and the string + * contains characters that should not be in a NumericString. + */ + public DerNumericString( + string str, + bool validate) + { + if (str == null) + throw new ArgumentNullException("str"); + if (validate && !IsNumericString(str)) + throw new ArgumentException("string contains illegal characters", "str"); + + this.str = str; + } + + public override string GetString() + { + return str; + } + + public byte[] GetOctets() + { + return Strings.ToAsciiByteArray(str); + } + + internal override void Encode( + DerOutputStream derOut) + { + derOut.WriteEncoded(Asn1Tags.NumericString, GetOctets()); + } + + protected override bool Asn1Equals( + Asn1Object asn1Object) + { + DerNumericString other = asn1Object as DerNumericString; + + if (other == null) + return false; + + return this.str.Equals(other.str); + } + + /** + * 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 bool IsNumericString( + string str) + { + foreach (char ch in str) + { + if (ch > 0x007f || (ch != ' ' && !char.IsDigit(ch))) + return false; + } + + return true; + } + } +} diff --git a/Crypto/src/asn1/DerObjectIdentifier.cs b/Crypto/src/asn1/DerObjectIdentifier.cs new file mode 100644
index 000000000..7dc963729 --- /dev/null +++ b/Crypto/src/asn1/DerObjectIdentifier.cs
@@ -0,0 +1,242 @@ +using System; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Asn1 +{ + public class DerObjectIdentifier + : Asn1Object + { + private static readonly Regex OidRegex = new Regex(@"\A[0-2](\.[0-9]+)+\z"); + + private readonly string identifier; + + /** + * return an Oid from the passed in object + * + * @exception ArgumentException if the object cannot be converted. + */ + public static DerObjectIdentifier GetInstance( + object obj) + { + if (obj == null || obj is DerObjectIdentifier) + { + return (DerObjectIdentifier) obj; + } + + throw new ArgumentException("illegal object in GetInstance: " + obj.GetType().Name, "obj"); + } + + /** + * return an object Identifier from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicitly true if the object is meant to be explicitly + * tagged false otherwise. + * @exception ArgumentException if the tagged object cannot + * be converted. + */ + public static DerObjectIdentifier GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(obj.GetObject()); + } + + public DerObjectIdentifier( + string identifier) + { + if (identifier == null) + throw new ArgumentNullException("identifier"); + if (!OidRegex.IsMatch(identifier)) + throw new FormatException("string " + identifier + " not an OID"); + + this.identifier = identifier; + } + + // TODO Change to ID? + public string Id + { + get { return identifier; } + } + + public virtual DerObjectIdentifier Branch(string branchID) + { + return new DerObjectIdentifier(identifier + "." + 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 virtual bool On(DerObjectIdentifier stem) + { + string id = Id, stemId = stem.Id; + return id.Length > stemId.Length && id[stemId.Length] == '.' && id.StartsWith(stemId); + } + + internal DerObjectIdentifier( + byte[] bytes) + : this(MakeOidStringFromBytes(bytes)) + { + } + + private void WriteField( + Stream outputStream, + long fieldValue) + { + byte[] result = new byte[9]; + int pos = 8; + result[pos] = (byte)(fieldValue & 0x7f); + while (fieldValue >= (1L << 7)) + { + fieldValue >>= 7; + result[--pos] = (byte)((fieldValue & 0x7f) | 0x80); + } + outputStream.Write(result, pos, 9 - pos); + } + + private void WriteField( + Stream outputStream, + BigInteger fieldValue) + { + int byteCount = (fieldValue.BitLength + 6) / 7; + if (byteCount == 0) + { + outputStream.WriteByte(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; + outputStream.Write(tmp, 0, tmp.Length); + } + } + + internal override void Encode( + DerOutputStream derOut) + { + OidTokenizer tok = new OidTokenizer(identifier); + MemoryStream bOut = new MemoryStream(); + DerOutputStream dOut = new DerOutputStream(bOut); + + string token = tok.NextToken(); + int first = int.Parse(token); + + token = tok.NextToken(); + int second = int.Parse(token); + + WriteField(bOut, first * 40 + second); + + while (tok.HasMoreTokens) + { + token = tok.NextToken(); + if (token.Length < 18) + { + WriteField(bOut, Int64.Parse(token)); + } + else + { + WriteField(bOut, new BigInteger(token)); + } + } + + dOut.Dispose(); + + derOut.WriteEncoded(Asn1Tags.ObjectIdentifier, bOut.ToArray()); + } + + protected override int Asn1GetHashCode() + { + return identifier.GetHashCode(); + } + + protected override bool Asn1Equals( + Asn1Object asn1Object) + { + DerObjectIdentifier other = asn1Object as DerObjectIdentifier; + + if (other == null) + return false; + + return this.identifier.Equals(other.identifier); + } + + public override string ToString() + { + return identifier; + } + + private static string MakeOidStringFromBytes( + byte[] bytes) + { + StringBuilder objId = new StringBuilder(); + long value = 0; + BigInteger bigValue = null; + bool first = true; + + for (int i = 0; i != bytes.Length; i++) + { + int b = bytes[i]; + + if (value < 0x80000000000000L) + { + value = value * 128 + (b & 0x7f); + if ((b & 0x80) == 0) // end of number reached + { + if (first) + { + switch ((int)value / 40) + { + case 0: + objId.Append('0'); + break; + case 1: + objId.Append('1'); + value -= 40; + break; + default: + objId.Append('2'); + value -= 80; + break; + } + first = false; + } + + objId.Append('.'); + objId.Append(value); + value = 0; + } + } + else + { + if (bigValue == null) + { + bigValue = BigInteger.ValueOf(value); + } + bigValue = bigValue.ShiftLeft(7); + bigValue = bigValue.Or(BigInteger.ValueOf(b & 0x7f)); + if ((b & 0x80) == 0) + { + objId.Append('.'); + objId.Append(bigValue); + bigValue = null; + value = 0; + } + } + } + + return objId.ToString(); + } + } +} diff --git a/Crypto/src/asn1/DerOctetString.cs b/Crypto/src/asn1/DerOctetString.cs new file mode 100644
index 000000000..c046c9402 --- /dev/null +++ b/Crypto/src/asn1/DerOctetString.cs
@@ -0,0 +1,34 @@ +namespace Org.BouncyCastle.Asn1 +{ + public class DerOctetString + : Asn1OctetString + { + /// <param name="str">The octets making up the octet string.</param> + public DerOctetString( + byte[] str) + : base(str) + { + } + + public DerOctetString( + Asn1Encodable obj) + : base(obj) + { + } + + internal override void Encode( + DerOutputStream derOut) + { + derOut.WriteEncoded(Asn1Tags.OctetString, str); + } + + internal static void Encode( + DerOutputStream derOut, + byte[] bytes, + int offset, + int length) + { + derOut.WriteEncoded(Asn1Tags.OctetString, bytes, offset, length); + } + } +} diff --git a/Crypto/src/asn1/DerOutputStream.cs b/Crypto/src/asn1/DerOutputStream.cs new file mode 100644
index 000000000..f95d123f9 --- /dev/null +++ b/Crypto/src/asn1/DerOutputStream.cs
@@ -0,0 +1,160 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1.Utilities; + +namespace Org.BouncyCastle.Asn1 +{ + public class DerOutputStream + : FilterStream + { + public DerOutputStream(Stream os) + : base(os) + { + } + + private void WriteLength( + int length) + { + if (length > 127) + { + int size = 1; + uint val = (uint) length; + + while ((val >>= 8) != 0) + { + size++; + } + + WriteByte((byte)(size | 0x80)); + + for (int i = (size - 1) * 8; i >= 0; i -= 8) + { + WriteByte((byte)(length >> i)); + } + } + else + { + WriteByte((byte)length); + } + } + + internal void WriteEncoded( + int tag, + byte[] bytes) + { + WriteByte((byte) tag); + WriteLength(bytes.Length); + Write(bytes, 0, bytes.Length); + } + + internal void WriteEncoded( + int tag, + byte[] bytes, + int offset, + int length) + { + WriteByte((byte) tag); + WriteLength(length); + Write(bytes, offset, length); + } + + internal void WriteTag( + int flags, + int tagNo) + { + if (tagNo < 31) + { + WriteByte((byte)(flags | tagNo)); + } + else + { + WriteByte((byte)(flags | 0x1f)); + if (tagNo < 128) + { + WriteByte((byte)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); + } + } + } + + internal void WriteEncoded( + int flags, + int tagNo, + byte[] bytes) + { + WriteTag(flags, tagNo); + WriteLength(bytes.Length); + Write(bytes, 0, bytes.Length); + } + + protected void WriteNull() + { + WriteByte(Asn1Tags.Null); + WriteByte(0x00); + } + + [Obsolete("Use version taking an Asn1Encodable arg instead")] + public virtual void WriteObject( + object obj) + { + if (obj == null) + { + WriteNull(); + } + else if (obj is Asn1Object) + { + ((Asn1Object)obj).Encode(this); + } + else if (obj is Asn1Encodable) + { + ((Asn1Encodable)obj).ToAsn1Object().Encode(this); + } + else + { + throw new IOException("object not Asn1Object"); + } + } + + public virtual void WriteObject( + Asn1Encodable obj) + { + if (obj == null) + { + WriteNull(); + } + else + { + obj.ToAsn1Object().Encode(this); + } + } + + public virtual void WriteObject( + Asn1Object obj) + { + if (obj == null) + { + WriteNull(); + } + else + { + obj.Encode(this); + } + } + } +} diff --git a/Crypto/src/asn1/DerPrintableString.cs b/Crypto/src/asn1/DerPrintableString.cs new file mode 100644
index 000000000..cd2f46b48 --- /dev/null +++ b/Crypto/src/asn1/DerPrintableString.cs
@@ -0,0 +1,163 @@ +using System; +using System.Text; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1 +{ + /** + * Der PrintableString object. + */ + public class DerPrintableString + : DerStringBase + { + private readonly string str; + + /** + * return a printable string from the passed in object. + * + * @exception ArgumentException if the object cannot be converted. + */ + public static DerPrintableString GetInstance( + object obj) + { + if (obj == null || obj is DerPrintableString) + { + return (DerPrintableString)obj; + } + + throw new ArgumentException("illegal object in GetInstance: " + obj.GetType().Name); + } + + /** + * return a Printable string from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicitly true if the object is meant to be explicitly + * tagged false otherwise. + * @exception ArgumentException if the tagged object cannot + * be converted. + */ + public static DerPrintableString GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + Asn1Object o = obj.GetObject(); + + if (isExplicit || o is DerPrintableString) + { + return GetInstance(o); + } + + return new DerPrintableString(Asn1OctetString.GetInstance(o).GetOctets()); + } + + /** + * basic constructor - byte encoded string. + */ + public DerPrintableString( + byte[] str) + : this(Strings.FromAsciiByteArray(str), false) + { + } + + /** + * basic constructor - this does not validate the string + */ + public DerPrintableString( + string str) + : this(str, false) + { + } + + /** + * Constructor with optional validation. + * + * @param string the base string to wrap. + * @param validate whether or not to check the string. + * @throws ArgumentException if validate is true and the string + * contains characters that should not be in a PrintableString. + */ + public DerPrintableString( + string str, + bool validate) + { + if (str == null) + throw new ArgumentNullException("str"); + if (validate && !IsPrintableString(str)) + throw new ArgumentException("string contains illegal characters", "str"); + + this.str = str; + } + + public override string GetString() + { + return str; + } + + public byte[] GetOctets() + { + return Strings.ToAsciiByteArray(str); + } + + internal override void Encode( + DerOutputStream derOut) + { + derOut.WriteEncoded(Asn1Tags.PrintableString, GetOctets()); + } + + protected override bool Asn1Equals( + Asn1Object asn1Object) + { + DerPrintableString other = asn1Object as DerPrintableString; + + if (other == null) + return false; + + return this.str.Equals(other.str); + } + + /** + * 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 bool IsPrintableString( + string str) + { + foreach (char ch in str) + { + if (ch > 0x007f) + return false; + + if (char.IsLetterOrDigit(ch)) + continue; + +// if (char.IsPunctuation(ch)) +// continue; + + switch (ch) + { + case ' ': + case '\'': + case '(': + case ')': + case '+': + case '-': + case '.': + case ':': + case '=': + case '?': + case '/': + case ',': + continue; + } + + return false; + } + + return true; + } + } +} diff --git a/Crypto/src/asn1/DerSequence.cs b/Crypto/src/asn1/DerSequence.cs new file mode 100644
index 000000000..dd67d2fed --- /dev/null +++ b/Crypto/src/asn1/DerSequence.cs
@@ -0,0 +1,85 @@ +using System.Collections; +using System.IO; + +namespace Org.BouncyCastle.Asn1 +{ + public class DerSequence + : Asn1Sequence + { + public static readonly DerSequence Empty = new DerSequence(); + + public static DerSequence FromVector( + Asn1EncodableVector v) + { + return v.Count < 1 ? Empty : new DerSequence(v); + } + + /** + * create an empty sequence + */ + public DerSequence() + : base(0) + { + } + + /** + * create a sequence containing one object + */ + public DerSequence( + Asn1Encodable obj) + : base(1) + { + AddObject(obj); + } + + public DerSequence( + params Asn1Encodable[] v) + : base(v.Length) + { + foreach (Asn1Encodable ae in v) + { + AddObject(ae); + } + } + + /** + * create a sequence containing a vector of objects. + */ + public DerSequence( + Asn1EncodableVector v) + : base(v.Count) + { + foreach (Asn1Encodable ae in v) + { + AddObject(ae); + } + } + + /* + * 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 outputing Sequence, + * we also have to specify Constructed, and the objects length. + */ + internal override void Encode( + DerOutputStream derOut) + { + // TODO Intermediate buffer could be avoided if we could calculate expected length + MemoryStream bOut = new MemoryStream(); + DerOutputStream dOut = new DerOutputStream(bOut); + + foreach (Asn1Encodable obj in this) + { + dOut.WriteObject(obj); + } + + dOut.Dispose(); + + byte[] bytes = bOut.ToArray(); + + derOut.WriteEncoded(Asn1Tags.Sequence | Asn1Tags.Constructed, bytes); + } + } +} diff --git a/Crypto/src/asn1/DerSet.cs b/Crypto/src/asn1/DerSet.cs new file mode 100644
index 000000000..6d3f438bd --- /dev/null +++ b/Crypto/src/asn1/DerSet.cs
@@ -0,0 +1,108 @@ +using System.IO; + +namespace Org.BouncyCastle.Asn1 +{ + /** + * A Der encoded set object + */ + public class DerSet + : Asn1Set + { + public static readonly DerSet Empty = new DerSet(); + + public static DerSet FromVector( + Asn1EncodableVector v) + { + return v.Count < 1 ? Empty : new DerSet(v); + } + + internal static DerSet FromVector( + Asn1EncodableVector v, + bool needsSorting) + { + return v.Count < 1 ? Empty : new DerSet(v, needsSorting); + } + + /** + * create an empty set + */ + public DerSet() + : base(0) + { + } + + /** + * @param obj - a single object that makes up the set. + */ + public DerSet( + Asn1Encodable obj) + : base(1) + { + AddObject(obj); + } + + public DerSet( + params Asn1Encodable[] v) + : base(v.Length) + { + foreach (Asn1Encodable o in v) + { + AddObject(o); + } + + Sort(); + } + + /** + * @param v - a vector of objects making up the set. + */ + public DerSet( + Asn1EncodableVector v) + : this(v, true) + { + } + + internal DerSet( + Asn1EncodableVector v, + bool needsSorting) + : base(v.Count) + { + foreach (Asn1Encodable o in v) + { + AddObject(o); + } + + if (needsSorting) + { + Sort(); + } + } + + /* + * 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 outputing Set, + * we also have to specify Constructed, and the objects length. + */ + internal override void Encode( + DerOutputStream derOut) + { + // TODO Intermediate buffer could be avoided if we could calculate expected length + MemoryStream bOut = new MemoryStream(); + DerOutputStream dOut = new DerOutputStream(bOut); + + foreach (Asn1Encodable obj in this) + { + dOut.WriteObject(obj); + } + + dOut.Dispose(); + + byte[] bytes = bOut.ToArray(); + + derOut.WriteEncoded(Asn1Tags.Set | Asn1Tags.Constructed, bytes); + } + } +} diff --git a/Crypto/src/asn1/DerStringBase.cs b/Crypto/src/asn1/DerStringBase.cs new file mode 100644
index 000000000..2a5fb041e --- /dev/null +++ b/Crypto/src/asn1/DerStringBase.cs
@@ -0,0 +1,22 @@ +namespace Org.BouncyCastle.Asn1 +{ + public abstract class DerStringBase + : Asn1Object, IAsn1String + { + protected DerStringBase() + { + } + + public abstract string GetString(); + + public override string ToString() + { + return GetString(); + } + + protected override int Asn1GetHashCode() + { + return GetString().GetHashCode(); + } + } +} diff --git a/Crypto/src/asn1/DerT61String.cs b/Crypto/src/asn1/DerT61String.cs new file mode 100644
index 000000000..4dee6f30c --- /dev/null +++ b/Crypto/src/asn1/DerT61String.cs
@@ -0,0 +1,102 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1 +{ + /** + * Der T61String (also the teletex string) - 8-bit characters + */ + public class DerT61String + : DerStringBase + { + private readonly string str; + + /** + * return a T61 string from the passed in object. + * + * @exception ArgumentException if the object cannot be converted. + */ + public static DerT61String GetInstance( + object obj) + { + if (obj == null || obj is DerT61String) + { + return (DerT61String)obj; + } + + throw new ArgumentException("illegal object in GetInstance: " + obj.GetType().Name); + } + + /** + * return an T61 string from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicitly true if the object is meant to be explicitly + * tagged false otherwise. + * @exception ArgumentException if the tagged object cannot + * be converted. + */ + public static DerT61String GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + Asn1Object o = obj.GetObject(); + + if (isExplicit || o is DerT61String) + { + return GetInstance(o); + } + + return new DerT61String(Asn1OctetString.GetInstance(o).GetOctets()); + } + + /** + * basic constructor - with bytes. + */ + public DerT61String( + byte[] str) + : this(Strings.FromByteArray(str)) + { + } + + /** + * basic constructor - with string. + */ + public DerT61String( + string str) + { + if (str == null) + throw new ArgumentNullException("str"); + + this.str = str; + } + + public override string GetString() + { + return str; + } + + internal override void Encode( + DerOutputStream derOut) + { + derOut.WriteEncoded(Asn1Tags.T61String, GetOctets()); + } + + public byte[] GetOctets() + { + return Strings.ToByteArray(str); + } + + protected override bool Asn1Equals( + Asn1Object asn1Object) + { + DerT61String other = asn1Object as DerT61String; + + if (other == null) + return false; + + return this.str.Equals(other.str); + } + } +} diff --git a/Crypto/src/asn1/DerTaggedObject.cs b/Crypto/src/asn1/DerTaggedObject.cs new file mode 100644
index 000000000..717d724b6 --- /dev/null +++ b/Crypto/src/asn1/DerTaggedObject.cs
@@ -0,0 +1,72 @@ +namespace Org.BouncyCastle.Asn1 +{ + /** + * 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 + : Asn1TaggedObject + { + /** + * @param tagNo the tag number for this object. + * @param obj the tagged object. + */ + public DerTaggedObject( + int tagNo, + Asn1Encodable obj) + : base(tagNo, obj) + { + } + + /** + * @param explicitly true if an explicitly tagged object. + * @param tagNo the tag number for this object. + * @param obj the tagged object. + */ + public DerTaggedObject( + bool explicitly, + int tagNo, + Asn1Encodable obj) + : base(explicitly, tagNo, obj) + { + } + + /** + * create an implicitly tagged object that contains a zero + * length sequence. + */ + public DerTaggedObject( + int tagNo) + : base(false, tagNo, DerSequence.Empty) + { + } + + internal override void Encode( + DerOutputStream derOut) + { + if (!IsEmpty()) + { + byte[] bytes = obj.GetDerEncoded(); + + if (explicitly) + { + derOut.WriteEncoded(Asn1Tags.Constructed | Asn1Tags.Tagged, tagNo, bytes); + } + else + { + // + // need to mark constructed types... (preserve Constructed tag) + // + int flags = (bytes[0] & Asn1Tags.Constructed) | Asn1Tags.Tagged; + derOut.WriteTag(flags, tagNo); + derOut.Write(bytes, 1, bytes.Length - 1); + } + } + else + { + derOut.WriteEncoded(Asn1Tags.Constructed | Asn1Tags.Tagged, tagNo, new byte[0]); + } + } + } +} diff --git a/Crypto/src/asn1/DerUTCTime.cs b/Crypto/src/asn1/DerUTCTime.cs new file mode 100644
index 000000000..56fabeb47 --- /dev/null +++ b/Crypto/src/asn1/DerUTCTime.cs
@@ -0,0 +1,263 @@ +using System; +using System.Globalization; +using System.Text; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1 +{ + /** + * UTC time object. + */ + public class DerUtcTime + : Asn1Object + { + private readonly string time; + + /** + * return an UTC Time from the passed in object. + * + * @exception ArgumentException if the object cannot be converted. + */ + public static DerUtcTime GetInstance( + object obj) + { + if (obj == null || obj is DerUtcTime) + { + return (DerUtcTime)obj; + } + + throw new ArgumentException("illegal object in GetInstance: " + obj.GetType().Name); + } + + /** + * return an UTC Time from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicitly true if the object is meant to be explicitly + * tagged false otherwise. + * @exception ArgumentException if the tagged object cannot + * be converted. + */ + public static DerUtcTime GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + Asn1Object o = obj.GetObject(); + + if (isExplicit || o is DerUtcTime) + { + return GetInstance(o); + } + + return new DerUtcTime(((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.</p> + */ + public DerUtcTime( + string time) + { + if (time == null) + throw new ArgumentNullException("time"); + + this.time = time; + + try + { + ToDateTime(); + } + catch (FormatException e) + { + throw new ArgumentException("invalid date string: " + e.Message); + } + } + + /** + * base constructor from a DateTime object + */ + public DerUtcTime( + DateTime time) + { + this.time = time.ToString("yyMMddHHmmss") + "Z"; + } + + internal DerUtcTime( + byte[] bytes) + { + // + // explicitly convert to characters + // + this.time = Strings.FromAsciiByteArray(bytes); + } + +// public DateTime ToDateTime() +// { +// string tm = this.AdjustedTimeString; +// +// return new DateTime( +// Int16.Parse(tm.Substring(0, 4)), +// Int16.Parse(tm.Substring(4, 2)), +// Int16.Parse(tm.Substring(6, 2)), +// Int16.Parse(tm.Substring(8, 2)), +// Int16.Parse(tm.Substring(10, 2)), +// Int16.Parse(tm.Substring(12, 2))); +// } + + /** + * return the time as a date based on whatever a 2 digit year will return. For + * standardised processing use ToAdjustedDateTime(). + * + * @return the resulting date + * @exception ParseException if the date string cannot be parsed. + */ + public DateTime ToDateTime() + { + return ParseDateString(TimeString, @"yyMMddHHmmss'GMT'zzz"); + } + + /** + * 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 DateTime ToAdjustedDateTime() + { + return ParseDateString(AdjustedTimeString, @"yyyyMMddHHmmss'GMT'zzz"); + } + + private DateTime ParseDateString( + string dateStr, + string formatStr) + { + DateTime dt = DateTime.ParseExact( + dateStr, + formatStr, + DateTimeFormatInfo.InvariantInfo); + + return dt.ToUniversalTime(); + } + + /** + * 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> + * <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.</p> + */ + public string TimeString + { + get + { + // + // standardise the format. + // + if (time.IndexOf('-') < 0 && time.IndexOf('+') < 0) + { + if (time.Length == 11) + { + return time.Substring(0, 10) + "00GMT+00:00"; + } + else + { + return time.Substring(0, 12) + "GMT+00:00"; + } + } + else + { + int index = time.IndexOf('-'); + if (index < 0) + { + index = time.IndexOf('+'); + } + string d = time; + + if (index == time.Length - 3) + { + d += "00"; + } + + if (index == 10) + { + return d.Substring(0, 10) + "00GMT" + d.Substring(10, 3) + ":" + d.Substring(13, 2); + } + else + { + return d.Substring(0, 12) + "GMT" + d.Substring(12, 3) + ":" + d.Substring(15, 2); + } + } + } + } + + [Obsolete("Use 'AdjustedTimeString' property instead")] + public string AdjustedTime + { + get { return AdjustedTimeString; } + } + + /// <summary> + /// Return a time string as an adjusted date with a 4 digit year. + /// This goes in the range of 1950 - 2049. + /// </summary> + public string AdjustedTimeString + { + get + { + string d = TimeString; + string c = d[0] < '5' ? "20" : "19"; + + return c + d; + } + } + + private byte[] GetOctets() + { + return Strings.ToAsciiByteArray(time); + } + + internal override void Encode( + DerOutputStream derOut) + { + derOut.WriteEncoded(Asn1Tags.UtcTime, GetOctets()); + } + + protected override bool Asn1Equals( + Asn1Object asn1Object) + { + DerUtcTime other = asn1Object as DerUtcTime; + + if (other == null) + return false; + + return this.time.Equals(other.time); + } + + protected override int Asn1GetHashCode() + { + return time.GetHashCode(); + } + + public override string ToString() + { + return time; + } + } +} diff --git a/Crypto/src/asn1/DerUTF8String.cs b/Crypto/src/asn1/DerUTF8String.cs new file mode 100644
index 000000000..92a50e824 --- /dev/null +++ b/Crypto/src/asn1/DerUTF8String.cs
@@ -0,0 +1,96 @@ +using System; +using System.Text; + +namespace Org.BouncyCastle.Asn1 +{ + /** + * Der UTF8String object. + */ + public class DerUtf8String + : DerStringBase + { + private readonly string str; + + /** + * return an UTF8 string from the passed in object. + * + * @exception ArgumentException if the object cannot be converted. + */ + public static DerUtf8String GetInstance( + object obj) + { + if (obj == null || obj is DerUtf8String) + { + return (DerUtf8String)obj; + } + + throw new ArgumentException("illegal object in GetInstance: " + obj.GetType().Name); + } + + /** + * return an UTF8 string from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicitly true if the object is meant to be explicitly + * tagged false otherwise. + * @exception ArgumentException if the tagged object cannot + * be converted. + */ + public static DerUtf8String GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + Asn1Object o = obj.GetObject(); + + if (isExplicit || o is DerUtf8String) + { + return GetInstance(o); + } + + return new DerUtf8String(Asn1OctetString.GetInstance(o).GetOctets()); + } + + /** + * basic constructor - byte encoded string. + */ + public DerUtf8String( + byte[] str) + : this(Encoding.UTF8.GetString(str, 0, str.Length)) + { + } + + /** + * basic constructor + */ + public DerUtf8String( + string str) + { + if (str == null) + throw new ArgumentNullException("str"); + + this.str = str; + } + + public override string GetString() + { + return str; + } + + protected override bool Asn1Equals( + Asn1Object asn1Object) + { + DerUtf8String other = asn1Object as DerUtf8String; + + if (other == null) + return false; + + return this.str.Equals(other.str); + } + + internal override void Encode( + DerOutputStream derOut) + { + derOut.WriteEncoded(Asn1Tags.Utf8String, Encoding.UTF8.GetBytes(str)); + } + } +} diff --git a/Crypto/src/asn1/DerUniversalString.cs b/Crypto/src/asn1/DerUniversalString.cs new file mode 100644
index 000000000..305102f2f --- /dev/null +++ b/Crypto/src/asn1/DerUniversalString.cs
@@ -0,0 +1,107 @@ +using System; +using System.Text; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1 +{ + /** + * Der UniversalString object. + */ + public class DerUniversalString + : DerStringBase + { + private static readonly char[] table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + + private readonly byte[] str; + + /** + * return a Universal string from the passed in object. + * + * @exception ArgumentException if the object cannot be converted. + */ + public static DerUniversalString GetInstance( + object obj) + { + if (obj == null || obj is DerUniversalString) + { + return (DerUniversalString)obj; + } + + throw new ArgumentException("illegal object in GetInstance: " + obj.GetType().Name); + } + + /** + * return a Universal string from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicitly true if the object is meant to be explicitly + * tagged false otherwise. + * @exception ArgumentException if the tagged object cannot + * be converted. + */ + public static DerUniversalString GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + Asn1Object o = obj.GetObject(); + + if (isExplicit || o is DerUniversalString) + { + return GetInstance(o); + } + + return new DerUniversalString(Asn1OctetString.GetInstance(o).GetOctets()); + } + + /** + * basic constructor - byte encoded string. + */ + public DerUniversalString( + byte[] str) + { + if (str == null) + throw new ArgumentNullException("str"); + + this.str = str; + } + + public override string GetString() + { + StringBuilder buffer = new StringBuilder("#"); + byte[] enc = GetDerEncoded(); + + for (int i = 0; i != enc.Length; i++) + { + uint ubyte = enc[i]; + buffer.Append(table[(ubyte >> 4) & 0xf]); + buffer.Append(table[enc[i] & 0xf]); + } + + return buffer.ToString(); + } + + public byte[] GetOctets() + { + return (byte[]) str.Clone(); + } + + internal override void Encode( + DerOutputStream derOut) + { + derOut.WriteEncoded(Asn1Tags.UniversalString, this.str); + } + + protected override bool Asn1Equals( + Asn1Object asn1Object) + { + DerUniversalString other = asn1Object as DerUniversalString; + + if (other == null) + return false; + +// return this.GetString().Equals(other.GetString()); + return Arrays.AreEqual(this.str, other.str); + } + } +} diff --git a/Crypto/src/asn1/DerUnknownTag.cs b/Crypto/src/asn1/DerUnknownTag.cs new file mode 100644
index 000000000..1e0e61495 --- /dev/null +++ b/Crypto/src/asn1/DerUnknownTag.cs
@@ -0,0 +1,80 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1 +{ + /** + * We insert one of these when we find a tag we don't recognise. + */ + public class DerUnknownTag + : Asn1Object + { + private readonly bool isConstructed; + private readonly int tag; + private readonly byte[] data; + + /** + * @param tag the tag value. + * @param data the contents octets. + */ + public DerUnknownTag( + int tag, + byte[] data) + : this(false, tag, data) + { + } + + public DerUnknownTag( + bool isConstructed, + int tag, + byte[] data) + { + if (data == null) + throw new ArgumentNullException("data"); + + this.isConstructed = isConstructed; + this.tag = tag; + this.data = data; + } + + public bool IsConstructed + { + get { return isConstructed; } + } + + public int Tag + { + get { return tag; } + } + + public byte[] GetData() + { + return data; + } + + internal override void Encode( + DerOutputStream derOut) + { + derOut.WriteEncoded(isConstructed ? Asn1Tags.Constructed : 0, tag, data); + } + + protected override bool Asn1Equals( + Asn1Object asn1Object) + { + DerUnknownTag other = asn1Object as DerUnknownTag; + + if (other == null) + return false; + + return this.isConstructed == other.isConstructed + && this.tag == other.tag + && Arrays.AreEqual(this.data, other.data); + } + + protected override int Asn1GetHashCode() + { + return isConstructed.GetHashCode() ^ tag.GetHashCode() ^ Arrays.GetHashCode(data); + } + } +} diff --git a/Crypto/src/asn1/DerVisibleString.cs b/Crypto/src/asn1/DerVisibleString.cs new file mode 100644
index 000000000..84c9caade --- /dev/null +++ b/Crypto/src/asn1/DerVisibleString.cs
@@ -0,0 +1,111 @@ +using System; +using System.Text; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1 +{ + /** + * Der VisibleString object. + */ + public class DerVisibleString + : DerStringBase + { + private readonly string str; + + /** + * return a Visible string from the passed in object. + * + * @exception ArgumentException if the object cannot be converted. + */ + public static DerVisibleString GetInstance( + object obj) + { + if (obj == null || obj is DerVisibleString) + { + return (DerVisibleString)obj; + } + + if (obj is Asn1OctetString) + { + return new DerVisibleString(((Asn1OctetString)obj).GetOctets()); + } + + if (obj is Asn1TaggedObject) + { + return GetInstance(((Asn1TaggedObject)obj).GetObject()); + } + + throw new ArgumentException("illegal object in GetInstance: " + obj.GetType().Name); + } + + /** + * return a Visible string from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicitly true if the object is meant to be explicitly + * tagged false otherwise. + * @exception ArgumentException if the tagged object cannot + * be converted. + */ + public static DerVisibleString GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(obj.GetObject()); + } + + /** + * basic constructor - byte encoded string. + */ + public DerVisibleString( + byte[] str) + : this(Strings.FromAsciiByteArray(str)) + { + } + + /** + * basic constructor + */ + public DerVisibleString( + string str) + { + if (str == null) + throw new ArgumentNullException("str"); + + this.str = str; + } + + public override string GetString() + { + return str; + } + + public byte[] GetOctets() + { + return Strings.ToAsciiByteArray(str); + } + + internal override void Encode( + DerOutputStream derOut) + { + derOut.WriteEncoded(Asn1Tags.VisibleString, GetOctets()); + } + + protected override bool Asn1Equals( + Asn1Object asn1Object) + { + DerVisibleString other = asn1Object as DerVisibleString; + + if (other == null) + return false; + + return this.str.Equals(other.str); + } + + protected override int Asn1GetHashCode() + { + return this.str.GetHashCode(); + } + } +} diff --git a/Crypto/src/asn1/IAsn1ApplicationSpecificParser.cs b/Crypto/src/asn1/IAsn1ApplicationSpecificParser.cs new file mode 100644
index 000000000..89cf64c70 --- /dev/null +++ b/Crypto/src/asn1/IAsn1ApplicationSpecificParser.cs
@@ -0,0 +1,10 @@ +using System; + +namespace Org.BouncyCastle.Asn1 +{ + public interface IAsn1ApplicationSpecificParser + : IAsn1Convertible + { + IAsn1Convertible ReadObject(); + } +} diff --git a/Crypto/src/asn1/IAsn1Choice.cs b/Crypto/src/asn1/IAsn1Choice.cs new file mode 100644
index 000000000..ecd76e427 --- /dev/null +++ b/Crypto/src/asn1/IAsn1Choice.cs
@@ -0,0 +1,17 @@ + +namespace Org.BouncyCastle.Asn1 +{ + /** + * Marker interface for CHOICE objects - if you implement this in a roll-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. + * </p> + */ + public interface IAsn1Choice + { + // marker interface + } +} diff --git a/Crypto/src/asn1/IAsn1Convertible.cs b/Crypto/src/asn1/IAsn1Convertible.cs new file mode 100644
index 000000000..d3f83afc9 --- /dev/null +++ b/Crypto/src/asn1/IAsn1Convertible.cs
@@ -0,0 +1,7 @@ +namespace Org.BouncyCastle.Asn1 +{ + public interface IAsn1Convertible + { + Asn1Object ToAsn1Object(); + } +} diff --git a/Crypto/src/asn1/IAsn1String.cs b/Crypto/src/asn1/IAsn1String.cs new file mode 100644
index 000000000..cbc2635ff --- /dev/null +++ b/Crypto/src/asn1/IAsn1String.cs
@@ -0,0 +1,10 @@ +namespace Org.BouncyCastle.Asn1 +{ + /** + * basic interface for Der string objects. + */ + public interface IAsn1String + { + string GetString(); + } +} diff --git a/Crypto/src/asn1/IndefiniteLengthInputStream.cs b/Crypto/src/asn1/IndefiniteLengthInputStream.cs new file mode 100644
index 000000000..56c1bdfbc --- /dev/null +++ b/Crypto/src/asn1/IndefiniteLengthInputStream.cs
@@ -0,0 +1,166 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Asn1 +{ + class IndefiniteLengthInputStream + : LimitedInputStream + { + private int _lookAhead; + private bool _eofOn00 = true; + + internal IndefiniteLengthInputStream( + Stream inStream, + int limit) + : base(inStream, limit) + { + _lookAhead = RequireByte(); + CheckForEof(); + } + + internal void SetEofOn00( + bool eofOn00) + { + _eofOn00 = eofOn00; + CheckForEof(); + } + + private bool CheckForEof() + { + if (_lookAhead == 0x00 && _eofOn00) + { + int extra = RequireByte(); + if (extra != 0) + { + throw new IOException("malformed end-of-contents marker"); + } + + _lookAhead = -1; + SetParentEofDetect(true); + } + return _lookAhead < 0; + } + + public override int Read( + byte[] buffer, + int offset, + int count) + { + // Only use this optimisation if we aren't checking for 00 + if (_eofOn00 || count <= 1) + return base.Read(buffer, offset, count); + + if (_lookAhead < 0) + return 0; + + int numRead = _in.Read(buffer, offset + 1, count - 1); + + if (numRead <= 0) + { + // Corrupted stream + throw new EndOfStreamException(); + } + + buffer[offset] = (byte)_lookAhead; + _lookAhead = RequireByte(); + + return numRead + 1; + } + + public override int ReadByte() + { + if (CheckForEof()) + return -1; + + int result = _lookAhead; + _lookAhead = RequireByte(); + return result; + } + + private int RequireByte() + { + int b = _in.ReadByte(); + if (b < 0) + { + // Corrupted stream + throw new EndOfStreamException(); + } + return b; + } + } +} + +//using System; +//using System.IO; + +//namespace Org.BouncyCastle.Asn1 +//{ +// class IndefiniteLengthInputStream +// : LimitedInputStream +// { +// private bool _eofReached = false; +// private bool _eofOn00 = true; + +// internal IndefiniteLengthInputStream( +// Stream inStream, +// int limit) +// : base(inStream, limit) +// { +// } + +// internal void SetEofOn00( +// bool eofOn00) +// { +// _eofOn00 = eofOn00; +// } + +// public override int Read( +// byte[] buffer, +// int offset, +// int count) +// { +// if (_eofReached) +// return 0; + +// if (_eofOn00) +// return base.Read(buffer, offset, count); + +// int numRead = _in.Read(buffer, offset, count); + +// if (numRead <= 0) +// throw new EndOfStreamException(); + +// return numRead; +// } + +// public override int ReadByte() +// { +// if (_eofReached) +// return -1; + +// int b1 = _in.ReadByte(); + +// if (b1 < 0) +// throw new EndOfStreamException(); + +// if (b1 == 0 && _eofOn00) +// { +// int b2 = _in.ReadByte(); + +// if (b2 < 0) +// throw new EndOfStreamException(); + +// if (b2 == 0) +// { +// _eofReached = true; +// SetParentEofDetect(true); +// return -1; +// } + +// throw new InvalidDataException(); +// } + +// return b1; +// } +// } +//} diff --git a/Crypto/src/asn1/LazyASN1InputStream.cs b/Crypto/src/asn1/LazyASN1InputStream.cs new file mode 100644
index 000000000..4cf2305fd --- /dev/null +++ b/Crypto/src/asn1/LazyASN1InputStream.cs
@@ -0,0 +1,33 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Asn1 +{ + public class LazyAsn1InputStream + : Asn1InputStream + { + public LazyAsn1InputStream( + byte[] input) + : base(input) + { + } + + public LazyAsn1InputStream( + Stream inputStream) + : base(inputStream) + { + } + + internal override DerSequence CreateDerSequence( + DefiniteLengthInputStream dIn) + { + return new LazyDerSequence(dIn.ToArray()); + } + + internal override DerSet CreateDerSet( + DefiniteLengthInputStream dIn) + { + return new LazyDerSet(dIn.ToArray()); + } + } +} diff --git a/Crypto/src/asn1/LazyDERSequence.cs b/Crypto/src/asn1/LazyDERSequence.cs new file mode 100644
index 000000000..5e3dd076e --- /dev/null +++ b/Crypto/src/asn1/LazyDERSequence.cs
@@ -0,0 +1,82 @@ +using System; +using System.Collections; +using System.Diagnostics; + +namespace Org.BouncyCastle.Asn1 +{ + internal class LazyDerSequence + : DerSequence + { + private byte[] encoded; + private bool parsed = false; + + internal LazyDerSequence( + byte[] encoded) + { + this.encoded = encoded; + } + + private void Parse() + { + lock (this) + { + if (!parsed) + { + Asn1InputStream e = new LazyAsn1InputStream(encoded); + + Asn1Object o; + while ((o = e.ReadObject()) != null) + { + AddObject(o); + } + + encoded = null; + parsed = true; + } + } + } + + public override Asn1Encodable this[int index] + { + get + { + Parse(); + + return base[index]; + } + } + + public override IEnumerator GetEnumerator() + { + Parse(); + + return base.GetEnumerator(); + } + + public override int Count + { + get + { + Parse(); + + return base.Count; + } + } + + internal override void Encode( + DerOutputStream derOut) + { + lock (this) + { + if (parsed) + { + base.Encode(derOut); + } + else + { + derOut.WriteEncoded(Asn1Tags.Sequence | Asn1Tags.Constructed, encoded); + } + } + } + } +} diff --git a/Crypto/src/asn1/LazyDERSet.cs b/Crypto/src/asn1/LazyDERSet.cs new file mode 100644
index 000000000..84fce4808 --- /dev/null +++ b/Crypto/src/asn1/LazyDERSet.cs
@@ -0,0 +1,82 @@ +using System; +using System.Collections; +using System.Diagnostics; + +namespace Org.BouncyCastle.Asn1 +{ + internal class LazyDerSet + : DerSet + { + private byte[] encoded; + private bool parsed = false; + + internal LazyDerSet( + byte[] encoded) + { + this.encoded = encoded; + } + + private void Parse() + { + lock (this) + { + if (!parsed) + { + Asn1InputStream e = new LazyAsn1InputStream(encoded); + + Asn1Object o; + while ((o = e.ReadObject()) != null) + { + AddObject(o); + } + + encoded = null; + parsed = true; + } + } + } + + public override Asn1Encodable this[int index] + { + get + { + Parse(); + + return base[index]; + } + } + + public override IEnumerator GetEnumerator() + { + Parse(); + + return base.GetEnumerator(); + } + + public override int Count + { + get + { + Parse(); + + return base.Count; + } + } + + internal override void Encode( + DerOutputStream derOut) + { + lock (this) + { + if (parsed) + { + base.Encode(derOut); + } + else + { + derOut.WriteEncoded(Asn1Tags.Set | Asn1Tags.Constructed, encoded); + } + } + } + } +} diff --git a/Crypto/src/asn1/LimitedInputStream.cs b/Crypto/src/asn1/LimitedInputStream.cs new file mode 100644
index 000000000..62486aa77 --- /dev/null +++ b/Crypto/src/asn1/LimitedInputStream.cs
@@ -0,0 +1,35 @@ +using System.IO; + +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Asn1 +{ + internal abstract class LimitedInputStream + : BaseInputStream + { + protected readonly Stream _in; + private int _limit; + + internal LimitedInputStream( + Stream inStream, + int limit) + { + this._in = inStream; + this._limit = limit; + } + + internal virtual int GetRemaining() + { + // TODO: maybe one day this can become more accurate + return _limit; + } + + protected virtual void SetParentEofDetect(bool on) + { + if (_in is IndefiniteLengthInputStream) + { + ((IndefiniteLengthInputStream)_in).SetEofOn00(on); + } + } + } +} diff --git a/Crypto/src/asn1/OidTokenizer.cs b/Crypto/src/asn1/OidTokenizer.cs new file mode 100644
index 000000000..6e76e8c8b --- /dev/null +++ b/Crypto/src/asn1/OidTokenizer.cs
@@ -0,0 +1,45 @@ +namespace 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; + } + + public bool HasMoreTokens + { + get { return index != -1; } + } + + public string NextToken() + { + if (index == -1) + { + return null; + } + + int end = oid.IndexOf('.', index); + if (end == -1) + { + string lastToken = oid.Substring(index); + index = -1; + return lastToken; + } + + string nextToken = oid.Substring(index, end - index); + index = end + 1; + return nextToken; + } + } +} diff --git a/Crypto/src/asn1/bc/BCObjectIdentifiers.cs b/Crypto/src/asn1/bc/BCObjectIdentifiers.cs new file mode 100644
index 000000000..075e5384c --- /dev/null +++ b/Crypto/src/asn1/bc/BCObjectIdentifiers.cs
@@ -0,0 +1,39 @@ +using System; + +namespace Org.BouncyCastle.Asn1.BC +{ + public abstract class BCObjectIdentifiers + { + // iso.org.dod.internet.private.enterprise.legion-of-the-bouncy-castle + // 1.3.6.1.4.1.22554 + public static readonly DerObjectIdentifier bc = new DerObjectIdentifier("1.3.6.1.4.1.22554"); + + // pbe(1) algorithms + public static readonly DerObjectIdentifier bc_pbe = new DerObjectIdentifier(bc + ".1"); + + // SHA-1(1) + public static readonly DerObjectIdentifier bc_pbe_sha1 = new DerObjectIdentifier(bc_pbe + ".1"); + + // SHA-2(2) . (SHA-256(1)|SHA-384(2)|SHA-512(3)|SHA-224(4)) + public static readonly DerObjectIdentifier bc_pbe_sha256 = new DerObjectIdentifier(bc_pbe + ".2.1"); + public static readonly DerObjectIdentifier bc_pbe_sha384 = new DerObjectIdentifier(bc_pbe + ".2.2"); + public static readonly DerObjectIdentifier bc_pbe_sha512 = new DerObjectIdentifier(bc_pbe + ".2.3"); + public static readonly DerObjectIdentifier bc_pbe_sha224 = new DerObjectIdentifier(bc_pbe + ".2.4"); + + // PKCS-5(1)|PKCS-12(2) + public static readonly DerObjectIdentifier bc_pbe_sha1_pkcs5 = new DerObjectIdentifier(bc_pbe_sha1 + ".1"); + public static readonly DerObjectIdentifier bc_pbe_sha1_pkcs12 = new DerObjectIdentifier(bc_pbe_sha1 + ".2"); + + public static readonly DerObjectIdentifier bc_pbe_sha256_pkcs5 = new DerObjectIdentifier(bc_pbe_sha256 + ".1"); + public static readonly DerObjectIdentifier bc_pbe_sha256_pkcs12 = new DerObjectIdentifier(bc_pbe_sha256 + ".2"); + + // AES(1) . (CBC-128(2)|CBC-192(22)|CBC-256(42)) + public static readonly DerObjectIdentifier bc_pbe_sha1_pkcs12_aes128_cbc = new DerObjectIdentifier(bc_pbe_sha1_pkcs12 + ".1.2"); + public static readonly DerObjectIdentifier bc_pbe_sha1_pkcs12_aes192_cbc = new DerObjectIdentifier(bc_pbe_sha1_pkcs12 + ".1.22"); + public static readonly DerObjectIdentifier bc_pbe_sha1_pkcs12_aes256_cbc = new DerObjectIdentifier(bc_pbe_sha1_pkcs12 + ".1.42"); + + public static readonly DerObjectIdentifier bc_pbe_sha256_pkcs12_aes128_cbc = new DerObjectIdentifier(bc_pbe_sha256_pkcs12 + ".1.2"); + public static readonly DerObjectIdentifier bc_pbe_sha256_pkcs12_aes192_cbc = new DerObjectIdentifier(bc_pbe_sha256_pkcs12 + ".1.22"); + public static readonly DerObjectIdentifier bc_pbe_sha256_pkcs12_aes256_cbc = new DerObjectIdentifier(bc_pbe_sha256_pkcs12 + ".1.42"); + } +} \ No newline at end of file diff --git a/Crypto/src/asn1/cmp/CAKeyUpdAnnContent.cs b/Crypto/src/asn1/cmp/CAKeyUpdAnnContent.cs new file mode 100644
index 000000000..3cdb128a6 --- /dev/null +++ b/Crypto/src/asn1/cmp/CAKeyUpdAnnContent.cs
@@ -0,0 +1,60 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class CAKeyUpdAnnContent + : Asn1Encodable + { + private readonly CmpCertificate oldWithNew; + private readonly CmpCertificate newWithOld; + private readonly CmpCertificate newWithNew; + + private CAKeyUpdAnnContent(Asn1Sequence seq) + { + oldWithNew = CmpCertificate.GetInstance(seq[0]); + newWithOld = CmpCertificate.GetInstance(seq[1]); + newWithNew = CmpCertificate.GetInstance(seq[2]); + } + + public static CAKeyUpdAnnContent GetInstance(object obj) + { + if (obj is CAKeyUpdAnnContent) + return (CAKeyUpdAnnContent)obj; + + if (obj is Asn1Sequence) + return new CAKeyUpdAnnContent((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public virtual CmpCertificate OldWithNew + { + get { return oldWithNew; } + } + + public virtual CmpCertificate NewWithOld + { + get { return newWithOld; } + } + + public virtual CmpCertificate NewWithNew + { + get { 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 override Asn1Object ToAsn1Object() + { + return new DerSequence(oldWithNew, newWithOld, newWithNew); + } + } +} diff --git a/Crypto/src/asn1/cmp/CertConfirmContent.cs b/Crypto/src/asn1/cmp/CertConfirmContent.cs new file mode 100644
index 000000000..f4016d8d8 --- /dev/null +++ b/Crypto/src/asn1/cmp/CertConfirmContent.cs
@@ -0,0 +1,47 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class CertConfirmContent + : Asn1Encodable + { + private readonly Asn1Sequence content; + + private CertConfirmContent(Asn1Sequence seq) + { + content = seq; + } + + public static CertConfirmContent GetInstance(object obj) + { + if (obj is CertConfirmContent) + return (CertConfirmContent)obj; + + if (obj is Asn1Sequence) + return new CertConfirmContent((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public virtual CertStatus[] ToCertStatusArray() + { + CertStatus[] result = new CertStatus[content.Count]; + for (int i = 0; i != result.Length; i++) + { + result[i] = CertStatus.GetInstance(content[i]); + } + return result; + } + + /** + * <pre> + * CertConfirmContent ::= SEQUENCE OF CertStatus + * </pre> + * @return a basic ASN.1 object representation. + */ + public override Asn1Object ToAsn1Object() + { + return content; + } + } +} diff --git a/Crypto/src/asn1/cmp/CertOrEncCert.cs b/Crypto/src/asn1/cmp/CertOrEncCert.cs new file mode 100644
index 000000000..4c049c180 --- /dev/null +++ b/Crypto/src/asn1/cmp/CertOrEncCert.cs
@@ -0,0 +1,85 @@ +using System; + +using Org.BouncyCastle.Asn1.Crmf; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class CertOrEncCert + : Asn1Encodable, IAsn1Choice + { + private readonly CmpCertificate certificate; + private readonly EncryptedValue encryptedCert; + + private CertOrEncCert(Asn1TaggedObject tagged) + { + if (tagged.TagNo == 0) + { + certificate = CmpCertificate.GetInstance(tagged.GetObject()); + } + else if (tagged.TagNo == 1) + { + encryptedCert = EncryptedValue.GetInstance(tagged.GetObject()); + } + else + { + throw new ArgumentException("unknown tag: " + tagged.TagNo, "tagged"); + } + } + + public static CertOrEncCert GetInstance(object obj) + { + if (obj is CertOrEncCert) + return (CertOrEncCert)obj; + + if (obj is Asn1TaggedObject) + return new CertOrEncCert((Asn1TaggedObject)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public CertOrEncCert(CmpCertificate certificate) + { + if (certificate == null) + throw new ArgumentNullException("certificate"); + + this.certificate = certificate; + } + + public CertOrEncCert(EncryptedValue encryptedCert) + { + if (encryptedCert == null) + throw new ArgumentNullException("encryptedCert"); + + this.encryptedCert = encryptedCert; + } + + public virtual CmpCertificate Certificate + { + get { return certificate; } + } + + public virtual EncryptedValue EncryptedCert + { + get { return encryptedCert; } + } + + /** + * <pre> + * CertOrEncCert ::= CHOICE { + * certificate [0] CMPCertificate, + * encryptedCert [1] EncryptedValue + * } + * </pre> + * @return a basic ASN.1 object representation. + */ + public override Asn1Object ToAsn1Object() + { + if (certificate != null) + { + return new DerTaggedObject(true, 0, certificate); + } + + return new DerTaggedObject(true, 1, encryptedCert); + } + } +} diff --git a/Crypto/src/asn1/cmp/CertRepMessage.cs b/Crypto/src/asn1/cmp/CertRepMessage.cs new file mode 100644
index 000000000..c22b079c8 --- /dev/null +++ b/Crypto/src/asn1/cmp/CertRepMessage.cs
@@ -0,0 +1,94 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class CertRepMessage + : Asn1Encodable + { + private readonly Asn1Sequence caPubs; + private readonly Asn1Sequence response; + + private CertRepMessage(Asn1Sequence seq) + { + int index = 0; + + if (seq.Count > 1) + { + caPubs = Asn1Sequence.GetInstance((Asn1TaggedObject)seq[index++], true); + } + + response = Asn1Sequence.GetInstance(seq[index]); + } + + public static CertRepMessage GetInstance(object obj) + { + if (obj is CertRepMessage) + return (CertRepMessage)obj; + + if (obj is Asn1Sequence) + return new CertRepMessage((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public CertRepMessage(CmpCertificate[] caPubs, CertResponse[] response) + { + if (response == null) + throw new ArgumentNullException("response"); + + if (caPubs != null) + { + this.caPubs = new DerSequence(caPubs); + } + + this.response = new DerSequence(response); + } + + public virtual CmpCertificate[] GetCAPubs() + { + if (caPubs == null) + return null; + + CmpCertificate[] results = new CmpCertificate[caPubs.Count]; + for (int i = 0; i != results.Length; ++i) + { + results[i] = CmpCertificate.GetInstance(caPubs[i]); + } + return results; + } + + public virtual CertResponse[] GetResponse() + { + CertResponse[] results = new CertResponse[response.Count]; + for (int i = 0; i != results.Length; ++i) + { + results[i] = CertResponse.GetInstance(response[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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(); + + if (caPubs != null) + { + v.Add(new DerTaggedObject(true, 1, caPubs)); + } + + v.Add(response); + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/cmp/CertResponse.cs b/Crypto/src/asn1/cmp/CertResponse.cs new file mode 100644
index 000000000..246b8ce70 --- /dev/null +++ b/Crypto/src/asn1/cmp/CertResponse.cs
@@ -0,0 +1,115 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class CertResponse + : Asn1Encodable + { + private readonly DerInteger certReqId; + private readonly PkiStatusInfo status; + private readonly CertifiedKeyPair certifiedKeyPair; + private readonly Asn1OctetString rspInfo; + + private CertResponse(Asn1Sequence seq) + { + certReqId = DerInteger.GetInstance(seq[0]); + status = PkiStatusInfo.GetInstance(seq[1]); + + if (seq.Count >= 3) + { + if (seq.Count == 3) + { + Asn1Encodable o = seq[2]; + if (o is Asn1OctetString) + { + rspInfo = Asn1OctetString.GetInstance(o); + } + else + { + certifiedKeyPair = CertifiedKeyPair.GetInstance(o); + } + } + else + { + certifiedKeyPair = CertifiedKeyPair.GetInstance(seq[2]); + rspInfo = Asn1OctetString.GetInstance(seq[3]); + } + } + } + + public static CertResponse GetInstance(object obj) + { + if (obj is CertResponse) + return (CertResponse)obj; + + if (obj is Asn1Sequence) + return new CertResponse((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public CertResponse( + DerInteger certReqId, + PkiStatusInfo status) + : this(certReqId, status, null, null) + { + } + + public CertResponse( + DerInteger certReqId, + PkiStatusInfo status, + CertifiedKeyPair certifiedKeyPair, + Asn1OctetString rspInfo) + { + if (certReqId == null) + throw new ArgumentNullException("certReqId"); + + if (status == null) + throw new ArgumentNullException("status"); + + this.certReqId = certReqId; + this.status = status; + this.certifiedKeyPair = certifiedKeyPair; + this.rspInfo = rspInfo; + } + + public virtual DerInteger CertReqID + { + get { return certReqId; } + } + + public virtual PkiStatusInfo Status + { + get { return status; } + } + + public virtual CertifiedKeyPair CertifiedKeyPair + { + get { 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(certReqId, status); + v.AddOptional(certifiedKeyPair); + v.AddOptional(rspInfo); + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/cmp/CertStatus.cs b/Crypto/src/asn1/cmp/CertStatus.cs new file mode 100644
index 000000000..52d5ac504 --- /dev/null +++ b/Crypto/src/asn1/cmp/CertStatus.cs
@@ -0,0 +1,84 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class CertStatus + : Asn1Encodable + { + private readonly Asn1OctetString certHash; + private readonly DerInteger certReqId; + private readonly PkiStatusInfo statusInfo; + + private CertStatus(Asn1Sequence seq) + { + certHash = Asn1OctetString.GetInstance(seq[0]); + certReqId = DerInteger.GetInstance(seq[1]); + + if (seq.Count > 2) + { + statusInfo = PkiStatusInfo.GetInstance(seq[2]); + } + } + + public CertStatus(byte[] certHash, BigInteger certReqId) + { + this.certHash = new DerOctetString(certHash); + this.certReqId = new DerInteger(certReqId); + } + + public CertStatus(byte[] certHash, BigInteger certReqId, PkiStatusInfo statusInfo) + { + this.certHash = new DerOctetString(certHash); + this.certReqId = new DerInteger(certReqId); + this.statusInfo = statusInfo; + } + + public static CertStatus GetInstance(object obj) + { + if (obj is CertStatus) + return (CertStatus)obj; + + if (obj is Asn1Sequence) + return new CertStatus((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public virtual Asn1OctetString CertHash + { + get { return certHash; } + } + + public virtual DerInteger CertReqID + { + get { return certReqId; } + } + + public virtual PkiStatusInfo StatusInfo + { + get { 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(certHash, certReqId); + v.AddOptional(statusInfo); + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/cmp/CertifiedKeyPair.cs b/Crypto/src/asn1/cmp/CertifiedKeyPair.cs new file mode 100644
index 000000000..655dde0c5 --- /dev/null +++ b/Crypto/src/asn1/cmp/CertifiedKeyPair.cs
@@ -0,0 +1,114 @@ +using System; + +using Org.BouncyCastle.Asn1.Crmf; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class CertifiedKeyPair + : Asn1Encodable + { + private readonly CertOrEncCert certOrEncCert; + private readonly EncryptedValue privateKey; + private readonly PkiPublicationInfo publicationInfo; + + private CertifiedKeyPair(Asn1Sequence seq) + { + certOrEncCert = CertOrEncCert.GetInstance(seq[0]); + + if (seq.Count >= 2) + { + if (seq.Count == 2) + { + Asn1TaggedObject tagged = Asn1TaggedObject.GetInstance(seq[1]); + if (tagged.TagNo == 0) + { + privateKey = EncryptedValue.GetInstance(tagged.GetObject()); + } + else + { + publicationInfo = PkiPublicationInfo.GetInstance(tagged.GetObject()); + } + } + else + { + privateKey = EncryptedValue.GetInstance(Asn1TaggedObject.GetInstance(seq[1])); + publicationInfo = PkiPublicationInfo.GetInstance(Asn1TaggedObject.GetInstance(seq[2])); + } + } + } + + public static CertifiedKeyPair GetInstance(object obj) + { + if (obj is CertifiedKeyPair) + return (CertifiedKeyPair)obj; + + if (obj is Asn1Sequence) + return new CertifiedKeyPair((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public CertifiedKeyPair( + CertOrEncCert certOrEncCert) + : this(certOrEncCert, null, null) + { + } + + public CertifiedKeyPair( + CertOrEncCert certOrEncCert, + EncryptedValue privateKey, + PkiPublicationInfo publicationInfo + ) + { + if (certOrEncCert == null) + throw new ArgumentNullException("certOrEncCert"); + + this.certOrEncCert = certOrEncCert; + this.privateKey = privateKey; + this.publicationInfo = publicationInfo; + } + + public virtual CertOrEncCert CertOrEncCert + { + get { return certOrEncCert; } + } + + public virtual EncryptedValue PrivateKey + { + get { return privateKey; } + } + + public virtual PkiPublicationInfo PublicationInfo + { + get { 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(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/Crypto/src/asn1/cmp/Challenge.cs b/Crypto/src/asn1/cmp/Challenge.cs new file mode 100644
index 000000000..bee5f96f5 --- /dev/null +++ b/Crypto/src/asn1/cmp/Challenge.cs
@@ -0,0 +1,79 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class Challenge + : Asn1Encodable + { + private readonly AlgorithmIdentifier owf; + private readonly Asn1OctetString witness; + private readonly Asn1OctetString challenge; + + private Challenge(Asn1Sequence seq) + { + int index = 0; + + if (seq.Count == 3) + { + owf = AlgorithmIdentifier.GetInstance(seq[index++]); + } + + witness = Asn1OctetString.GetInstance(seq[index++]); + challenge = Asn1OctetString.GetInstance(seq[index]); + } + + public static Challenge GetInstance(object obj) + { + if (obj is Challenge) + return (Challenge)obj; + + if (obj is Asn1Sequence) + return new Challenge((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public virtual AlgorithmIdentifier Owf + { + get { return owf; } + } + + /** + * <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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(); + v.AddOptional(owf); + v.Add(witness); + v.Add(challenge); + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/cmp/CmpCertificate.cs b/Crypto/src/asn1/cmp/CmpCertificate.cs new file mode 100644
index 000000000..16ee30059 --- /dev/null +++ b/Crypto/src/asn1/cmp/CmpCertificate.cs
@@ -0,0 +1,80 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class CmpCertificate + : Asn1Encodable, IAsn1Choice + { + private readonly X509CertificateStructure x509v3PKCert; + private readonly AttributeCertificate x509v2AttrCert; + + /** + * Note: the addition of attribute certificates is a BC extension. + */ + public CmpCertificate(AttributeCertificate x509v2AttrCert) + { + this.x509v2AttrCert = x509v2AttrCert; + } + + public CmpCertificate(X509CertificateStructure x509v3PKCert) + { + if (x509v3PKCert.Version != 3) + throw new ArgumentException("only version 3 certificates allowed", "x509v3PKCert"); + + this.x509v3PKCert = x509v3PKCert; + } + + public static CmpCertificate GetInstance(object obj) + { + if (obj is CmpCertificate) + return (CmpCertificate)obj; + + if (obj is Asn1Sequence) + return new CmpCertificate(X509CertificateStructure.GetInstance(obj)); + + if (obj is Asn1TaggedObject) + return new CmpCertificate(AttributeCertificate.GetInstance(((Asn1TaggedObject)obj).GetObject())); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public virtual bool IsX509v3PKCert + { + get { return x509v3PKCert != null; } + } + + public virtual X509CertificateStructure X509v3PKCert + { + get { return x509v3PKCert; } + } + + public virtual AttributeCertificate X509v2AttrCert + { + get { 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 override Asn1Object ToAsn1Object() + { + if (x509v2AttrCert != null) + { + // explicit following CMP conventions + return new DerTaggedObject(true, 1, x509v2AttrCert); + } + + return x509v3PKCert.ToAsn1Object(); + } + } +} diff --git a/Crypto/src/asn1/cmp/CmpObjectIdentifiers.cs b/Crypto/src/asn1/cmp/CmpObjectIdentifiers.cs new file mode 100644
index 000000000..7e8274175 --- /dev/null +++ b/Crypto/src/asn1/cmp/CmpObjectIdentifiers.cs
@@ -0,0 +1,106 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public abstract class CmpObjectIdentifiers + { + // RFC 4210 + + // id-PasswordBasedMac OBJECT IDENTIFIER ::= {1 2 840 113533 7 66 13} + public static readonly DerObjectIdentifier passwordBasedMac = new DerObjectIdentifier("1.2.840.113533.7.66.13"); + + // id-DHBasedMac OBJECT IDENTIFIER ::= {1 2 840 113533 7 66 30} + public static readonly DerObjectIdentifier dhBasedMac = new DerObjectIdentifier("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} + public static readonly DerObjectIdentifier it_caProtEncCert = new DerObjectIdentifier("1.3.6.1.5.5.7.4.1"); + public static readonly DerObjectIdentifier it_signKeyPairTypes = new DerObjectIdentifier("1.3.6.1.5.5.7.4.2"); + public static readonly DerObjectIdentifier it_encKeyPairTypes = new DerObjectIdentifier("1.3.6.1.5.5.7.4.3"); + public static readonly DerObjectIdentifier it_preferredSymAlg = new DerObjectIdentifier("1.3.6.1.5.5.7.4.4"); + public static readonly DerObjectIdentifier it_caKeyUpdateInfo = new DerObjectIdentifier("1.3.6.1.5.5.7.4.5"); + public static readonly DerObjectIdentifier it_currentCRL = new DerObjectIdentifier("1.3.6.1.5.5.7.4.6"); + public static readonly DerObjectIdentifier it_unsupportedOIDs = new DerObjectIdentifier("1.3.6.1.5.5.7.4.7"); + public static readonly DerObjectIdentifier it_keyPairParamReq = new DerObjectIdentifier("1.3.6.1.5.5.7.4.10"); + public static readonly DerObjectIdentifier it_keyPairParamRep = new DerObjectIdentifier("1.3.6.1.5.5.7.4.11"); + public static readonly DerObjectIdentifier it_revPassphrase = new DerObjectIdentifier("1.3.6.1.5.5.7.4.12"); + public static readonly DerObjectIdentifier it_implicitConfirm = new DerObjectIdentifier("1.3.6.1.5.5.7.4.13"); + public static readonly DerObjectIdentifier it_confirmWaitTime = new DerObjectIdentifier("1.3.6.1.5.5.7.4.14"); + public static readonly DerObjectIdentifier it_origPKIMessage = new DerObjectIdentifier("1.3.6.1.5.5.7.4.15"); + public static readonly DerObjectIdentifier it_suppLangTags = new DerObjectIdentifier("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) } + + public static readonly DerObjectIdentifier regCtrl_regToken = new DerObjectIdentifier("1.3.6.1.5.5.7.5.1.1"); + public static readonly DerObjectIdentifier regCtrl_authenticator = new DerObjectIdentifier("1.3.6.1.5.5.7.5.1.2"); + public static readonly DerObjectIdentifier regCtrl_pkiPublicationInfo = new DerObjectIdentifier("1.3.6.1.5.5.7.5.1.3"); + public static readonly DerObjectIdentifier regCtrl_pkiArchiveOptions = new DerObjectIdentifier("1.3.6.1.5.5.7.5.1.4"); + public static readonly DerObjectIdentifier regCtrl_oldCertID = new DerObjectIdentifier("1.3.6.1.5.5.7.5.1.5"); + public static readonly DerObjectIdentifier regCtrl_protocolEncrKey = new DerObjectIdentifier("1.3.6.1.5.5.7.5.1.6"); + + // From RFC4210: + // id-regCtrl-altCertTemplate OBJECT IDENTIFIER ::= {id-regCtrl 7} + public static readonly DerObjectIdentifier regCtrl_altCertTemplate = new DerObjectIdentifier("1.3.6.1.5.5.7.5.1.7"); + + public static readonly DerObjectIdentifier regInfo_utf8Pairs = new DerObjectIdentifier("1.3.6.1.5.5.7.5.2.1"); + public static readonly DerObjectIdentifier regInfo_certReq = new DerObjectIdentifier("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} + public static readonly DerObjectIdentifier ct_encKeyWithID = new DerObjectIdentifier("1.2.840.113549.1.9.16.1.21"); + } +} diff --git a/Crypto/src/asn1/cmp/CrlAnnContent.cs b/Crypto/src/asn1/cmp/CrlAnnContent.cs new file mode 100644
index 000000000..3dc11d32c --- /dev/null +++ b/Crypto/src/asn1/cmp/CrlAnnContent.cs
@@ -0,0 +1,49 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class CrlAnnContent + : Asn1Encodable + { + private readonly Asn1Sequence content; + + private CrlAnnContent(Asn1Sequence seq) + { + content = seq; + } + + public static CrlAnnContent GetInstance(object obj) + { + if (obj is CrlAnnContent) + return (CrlAnnContent)obj; + + if (obj is Asn1Sequence) + return new CrlAnnContent((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public virtual CertificateList[] ToCertificateListArray() + { + CertificateList[] result = new CertificateList[content.Count]; + for (int i = 0; i != result.Length; ++ i) + { + result[i] = CertificateList.GetInstance(content[i]); + } + return result; + } + + /** + * <pre> + * CrlAnnContent ::= SEQUENCE OF CertificateList + * </pre> + * @return a basic ASN.1 object representation. + */ + public override Asn1Object ToAsn1Object() + { + return content; + } + } +} diff --git a/Crypto/src/asn1/cmp/ErrorMsgContent.cs b/Crypto/src/asn1/cmp/ErrorMsgContent.cs new file mode 100644
index 000000000..f4dc584ea --- /dev/null +++ b/Crypto/src/asn1/cmp/ErrorMsgContent.cs
@@ -0,0 +1,94 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class ErrorMsgContent + : Asn1Encodable + { + private readonly PkiStatusInfo pkiStatusInfo; + private readonly DerInteger errorCode; + private readonly PkiFreeText errorDetails; + + private ErrorMsgContent(Asn1Sequence seq) + { + pkiStatusInfo = PkiStatusInfo.GetInstance(seq[0]); + + for (int pos = 1; pos < seq.Count; ++pos) + { + Asn1Encodable ae = seq[pos]; + if (ae is DerInteger) + { + errorCode = DerInteger.GetInstance(ae); + } + else + { + errorDetails = PkiFreeText.GetInstance(ae); + } + } + } + + public static ErrorMsgContent GetInstance(object obj) + { + if (obj is ErrorMsgContent) + return (ErrorMsgContent)obj; + + if (obj is Asn1Sequence) + return new ErrorMsgContent((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public ErrorMsgContent(PkiStatusInfo pkiStatusInfo) + : this(pkiStatusInfo, null, null) + { + } + + public ErrorMsgContent( + PkiStatusInfo pkiStatusInfo, + DerInteger errorCode, + PkiFreeText errorDetails) + { + if (pkiStatusInfo == null) + throw new ArgumentNullException("pkiStatusInfo"); + + this.pkiStatusInfo = pkiStatusInfo; + this.errorCode = errorCode; + this.errorDetails = errorDetails; + } + + public virtual PkiStatusInfo PkiStatusInfo + { + get { return pkiStatusInfo; } + } + + public virtual DerInteger ErrorCode + { + get { return errorCode; } + } + + public virtual PkiFreeText ErrorDetails + { + get { 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(pkiStatusInfo); + v.AddOptional(errorCode); + v.AddOptional(errorDetails); + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/cmp/GenMsgContent.cs b/Crypto/src/asn1/cmp/GenMsgContent.cs new file mode 100644
index 000000000..9f042491c --- /dev/null +++ b/Crypto/src/asn1/cmp/GenMsgContent.cs
@@ -0,0 +1,52 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class GenMsgContent + : Asn1Encodable + { + private readonly Asn1Sequence content; + + private GenMsgContent(Asn1Sequence seq) + { + content = seq; + } + + public static GenMsgContent GetInstance(object obj) + { + if (obj is GenMsgContent) + return (GenMsgContent)obj; + + if (obj is Asn1Sequence) + return new GenMsgContent((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public GenMsgContent(params InfoTypeAndValue[] itv) + { + content = new DerSequence(itv); + } + + public virtual InfoTypeAndValue[] ToInfoTypeAndValueArray() + { + InfoTypeAndValue[] result = new InfoTypeAndValue[content.Count]; + for (int i = 0; i != result.Length; ++i) + { + result[i] = InfoTypeAndValue.GetInstance(content[i]); + } + return result; + } + + /** + * <pre> + * GenMsgContent ::= SEQUENCE OF InfoTypeAndValue + * </pre> + * @return a basic ASN.1 object representation. + */ + public override Asn1Object ToAsn1Object() + { + return content; + } + } +} diff --git a/Crypto/src/asn1/cmp/GenRepContent.cs b/Crypto/src/asn1/cmp/GenRepContent.cs new file mode 100644
index 000000000..5bdc5550a --- /dev/null +++ b/Crypto/src/asn1/cmp/GenRepContent.cs
@@ -0,0 +1,52 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class GenRepContent + : Asn1Encodable + { + private readonly Asn1Sequence content; + + private GenRepContent(Asn1Sequence seq) + { + content = seq; + } + + public static GenRepContent GetInstance(object obj) + { + if (obj is GenRepContent) + return (GenRepContent)obj; + + if (obj is Asn1Sequence) + return new GenRepContent((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public GenRepContent(params InfoTypeAndValue[] itv) + { + content = new DerSequence(itv); + } + + public virtual InfoTypeAndValue[] ToInfoTypeAndValueArray() + { + InfoTypeAndValue[] result = new InfoTypeAndValue[content.Count]; + for (int i = 0; i != result.Length; ++i) + { + result[i] = InfoTypeAndValue.GetInstance(content[i]); + } + return result; + } + + /** + * <pre> + * GenRepContent ::= SEQUENCE OF InfoTypeAndValue + * </pre> + * @return a basic ASN.1 object representation. + */ + public override Asn1Object ToAsn1Object() + { + return content; + } + } +} diff --git a/Crypto/src/asn1/cmp/InfoTypeAndValue.cs b/Crypto/src/asn1/cmp/InfoTypeAndValue.cs new file mode 100644
index 000000000..9b51dba02 --- /dev/null +++ b/Crypto/src/asn1/cmp/InfoTypeAndValue.cs
@@ -0,0 +1,121 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + /** + * 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 + : Asn1Encodable + { + private readonly DerObjectIdentifier infoType; + private readonly Asn1Encodable infoValue; + + private InfoTypeAndValue(Asn1Sequence seq) + { + infoType = DerObjectIdentifier.GetInstance(seq[0]); + + if (seq.Count > 1) + { + infoValue = (Asn1Encodable)seq[1]; + } + } + + public static InfoTypeAndValue GetInstance(object obj) + { + if (obj is InfoTypeAndValue) + return (InfoTypeAndValue)obj; + + if (obj is Asn1Sequence) + return new InfoTypeAndValue((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public InfoTypeAndValue( + DerObjectIdentifier infoType) + { + this.infoType = infoType; + this.infoValue = null; + } + + public InfoTypeAndValue( + DerObjectIdentifier infoType, + Asn1Encodable optionalValue) + { + this.infoType = infoType; + this.infoValue = optionalValue; + } + + public virtual DerObjectIdentifier InfoType + { + get { return infoType; } + } + + public virtual Asn1Encodable InfoValue + { + get { return infoValue; } + } + + /** + * <pre> + * InfoTypeAndValue ::= SEQUENCE { + * infoType OBJECT IDENTIFIER, + * infoValue ANY DEFINED BY infoType OPTIONAL + * } + * </pre> + * @return a basic ASN.1 object representation. + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(infoType); + + if (infoValue != null) + { + v.Add(infoValue); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/cmp/KeyRecRepContent.cs b/Crypto/src/asn1/cmp/KeyRecRepContent.cs new file mode 100644
index 000000000..b0352f048 --- /dev/null +++ b/Crypto/src/asn1/cmp/KeyRecRepContent.cs
@@ -0,0 +1,115 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class KeyRecRepContent + : Asn1Encodable + { + private readonly PkiStatusInfo status; + private readonly CmpCertificate newSigCert; + private readonly Asn1Sequence caCerts; + private readonly Asn1Sequence keyPairHist; + + private KeyRecRepContent(Asn1Sequence seq) + { + status = PkiStatusInfo.GetInstance(seq[0]); + + for (int pos = 1; pos < seq.Count; ++pos) + { + Asn1TaggedObject tObj = Asn1TaggedObject.GetInstance(seq[pos]); + + switch (tObj.TagNo) + { + 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 ArgumentException("unknown tag number: " + tObj.TagNo, "seq"); + } + } + } + + public static KeyRecRepContent GetInstance(object obj) + { + if (obj is KeyRecRepContent) + return (KeyRecRepContent)obj; + + if (obj is Asn1Sequence) + return new KeyRecRepContent((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public virtual PkiStatusInfo Status + { + get { return status; } + } + + public virtual CmpCertificate NewSigCert + { + get { return newSigCert; } + } + + public virtual CmpCertificate[] GetCACerts() + { + if (caCerts == null) + return null; + + CmpCertificate[] results = new CmpCertificate[caCerts.Count]; + for (int i = 0; i != results.Length; ++i) + { + results[i] = CmpCertificate.GetInstance(caCerts[i]); + } + return results; + } + + public virtual CertifiedKeyPair[] GetKeyPairHist() + { + if (keyPairHist == null) + return null; + + CertifiedKeyPair[] results = new CertifiedKeyPair[keyPairHist.Count]; + for (int i = 0; i != results.Length; ++i) + { + results[i] = CertifiedKeyPair.GetInstance(keyPairHist[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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(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/Crypto/src/asn1/cmp/OobCertHash.cs b/Crypto/src/asn1/cmp/OobCertHash.cs new file mode 100644
index 000000000..63ddff7c4 --- /dev/null +++ b/Crypto/src/asn1/cmp/OobCertHash.cs
@@ -0,0 +1,87 @@ +using System; + +using Org.BouncyCastle.Asn1.Crmf; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class OobCertHash + : Asn1Encodable + { + private readonly AlgorithmIdentifier hashAlg; + private readonly CertId certId; + private readonly DerBitString hashVal; + + private OobCertHash(Asn1Sequence seq) + { + int index = seq.Count - 1; + + hashVal = DerBitString.GetInstance(seq[index--]); + + for (int i = index; i >= 0; i--) + { + Asn1TaggedObject tObj = (Asn1TaggedObject)seq[i]; + + if (tObj.TagNo == 0) + { + hashAlg = AlgorithmIdentifier.GetInstance(tObj, true); + } + else + { + certId = CertId.GetInstance(tObj, true); + } + } + } + + public static OobCertHash GetInstance(object obj) + { + if (obj is OobCertHash) + return (OobCertHash)obj; + + if (obj is Asn1Sequence) + return new OobCertHash((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public virtual AlgorithmIdentifier HashAlg + { + get { return hashAlg; } + } + + public virtual CertId CertID + { + get { return certId; } + } + + /** + * <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 override Asn1Object ToAsn1Object() + { + 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/Crypto/src/asn1/cmp/PKIBody.cs b/Crypto/src/asn1/cmp/PKIBody.cs new file mode 100644
index 000000000..3205a907e --- /dev/null +++ b/Crypto/src/asn1/cmp/PKIBody.cs
@@ -0,0 +1,186 @@ +using System; + +using Org.BouncyCastle.Asn1.Crmf; +using Org.BouncyCastle.Asn1.Pkcs; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class PkiBody + : Asn1Encodable, IAsn1Choice + { + public const int TYPE_INIT_REQ = 0; + public const int TYPE_INIT_REP = 1; + public const int TYPE_CERT_REQ = 2; + public const int TYPE_CERT_REP = 3; + public const int TYPE_P10_CERT_REQ = 4; + public const int TYPE_POPO_CHALL = 5; + public const int TYPE_POPO_REP = 6; + public const int TYPE_KEY_UPDATE_REQ = 7; + public const int TYPE_KEY_UPDATE_REP = 8; + public const int TYPE_KEY_RECOVERY_REQ = 9; + public const int TYPE_KEY_RECOVERY_REP = 10; + public const int TYPE_REVOCATION_REQ = 11; + public const int TYPE_REVOCATION_REP = 12; + public const int TYPE_CROSS_CERT_REQ = 13; + public const int TYPE_CROSS_CERT_REP = 14; + public const int TYPE_CA_KEY_UPDATE_ANN = 15; + public const int TYPE_CERT_ANN = 16; + public const int TYPE_REVOCATION_ANN = 17; + public const int TYPE_CRL_ANN = 18; + public const int TYPE_CONFIRM = 19; + public const int TYPE_NESTED = 20; + public const int TYPE_GEN_MSG = 21; + public const int TYPE_GEN_REP = 22; + public const int TYPE_ERROR = 23; + public const int TYPE_CERT_CONFIRM = 24; + public const int TYPE_POLL_REQ = 25; + public const int TYPE_POLL_REP = 26; + + private int tagNo; + private Asn1Encodable body; + + public static PkiBody GetInstance(object obj) + { + if (obj is PkiBody) + return (PkiBody)obj; + + if (obj is Asn1TaggedObject) + return new PkiBody((Asn1TaggedObject)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + private PkiBody(Asn1TaggedObject tagged) + { + tagNo = tagged.TagNo; + 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 ArgumentException("unknown tag number: " + type, "type"); + } + } + + public virtual int Type + { + get { return tagNo; } + } + + public virtual Asn1Encodable Content + { + get { 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 override Asn1Object ToAsn1Object() + { + return new DerTaggedObject(true, tagNo, body); + } + } +} diff --git a/Crypto/src/asn1/cmp/PKIConfirmContent.cs b/Crypto/src/asn1/cmp/PKIConfirmContent.cs new file mode 100644
index 000000000..98645766a --- /dev/null +++ b/Crypto/src/asn1/cmp/PKIConfirmContent.cs
@@ -0,0 +1,34 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class PkiConfirmContent + : Asn1Encodable + { + public static PkiConfirmContent GetInstance(object obj) + { + if (obj is PkiConfirmContent) + return (PkiConfirmContent)obj; + + if (obj is Asn1Null) + return new PkiConfirmContent(); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public PkiConfirmContent() + { + } + + /** + * <pre> + * PkiConfirmContent ::= NULL + * </pre> + * @return a basic ASN.1 object representation. + */ + public override Asn1Object ToAsn1Object() + { + return DerNull.Instance; + } + } +} diff --git a/Crypto/src/asn1/cmp/PKIFailureInfo.cs b/Crypto/src/asn1/cmp/PKIFailureInfo.cs new file mode 100644
index 000000000..1df0e0693 --- /dev/null +++ b/Crypto/src/asn1/cmp/PKIFailureInfo.cs
@@ -0,0 +1,73 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + /** + * <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 + * 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 + * systemFailure (25) + * -- the request cannot be handled due to system failure + * </pre> + */ + public class PkiFailureInfo + : DerBitString + { + public const int BadAlg = (1 << 7); // unrecognized or unsupported Algorithm Identifier + public const int BadMessageCheck = (1 << 6); // integrity check failed (e.g., signature did not verify) + public const int BadRequest = (1 << 5); + public const int BadTime = (1 << 4); // -- messageTime was not sufficiently close to the system time, as defined by local policy + public const int BadCertId = (1 << 3); // no certificate could be found matching the provided criteria + public const int BadDataFormat = (1 << 2); + public const int WrongAuthority = (1 << 1); // the authority indicated in the request is different from the one creating the response token + public const int IncorrectData = 1; // the requester's data is incorrect (for notary services) + public const int MissingTimeStamp = (1 << 15); // when the timestamp is missing but should be there (by policy) + public const int BadPop = (1 << 14); // the proof-of-possession failed + public const int TimeNotAvailable = (1 << 9); // the TSA's time source is not available + public const int UnacceptedPolicy = (1 << 8); // the requested TSA policy is not supported by the TSA + public const int UnacceptedExtension = (1 << 23); //the requested extension is not supported by the TSA + public const int AddInfoNotAvailable = (1 << 22); //the additional information requested could not be understood or is not available + public const int SystemFailure = (1 << 30); //the request cannot be handled due to system failure + + /** + * Basic constructor. + */ + public PkiFailureInfo( + int info) + : base(GetBytes(info), GetPadBits(info)) + { + } + + public PkiFailureInfo( + DerBitString info) + : base(info.GetBytes(), info.PadBits) + { + } + + public override string ToString() + { + return "PkiFailureInfo: 0x" + this.IntValue.ToString("X"); + } + } +} diff --git a/Crypto/src/asn1/cmp/PKIFreeText.cs b/Crypto/src/asn1/cmp/PKIFreeText.cs new file mode 100644
index 000000000..571c8d93a --- /dev/null +++ b/Crypto/src/asn1/cmp/PKIFreeText.cs
@@ -0,0 +1,97 @@ +using System; +using System.Collections; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class PkiFreeText + : Asn1Encodable + { + internal Asn1Sequence strings; + + public static PkiFreeText GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit)); + } + + public static PkiFreeText GetInstance( + object obj) + { + if (obj is PkiFreeText) + { + return (PkiFreeText)obj; + } + else if (obj is Asn1Sequence) + { + return new PkiFreeText((Asn1Sequence)obj); + } + + throw new ArgumentException("Unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public PkiFreeText( + Asn1Sequence seq) + { + foreach (object o in seq) + { + if (!(o is DerUtf8String)) + { + throw new ArgumentException("attempt to insert non UTF8 STRING into PkiFreeText"); + } + } + + this.strings = seq; + } + + public PkiFreeText( + DerUtf8String p) + { + strings = new DerSequence(p); + } + + /** + * Return the number of string elements present. + * + * @return number of elements present. + */ + [Obsolete("Use 'Count' property instead")] + public int Size + { + get { return strings.Count; } + } + + public int Count + { + get { return strings.Count; } + } + + /** + * Return the UTF8STRING at index. + * + * @param index index of the string of interest + * @return the string at index. + */ + public DerUtf8String this[int index] + { + get { return (DerUtf8String) strings[index]; } + } + + [Obsolete("Use 'object[index]' syntax instead")] + public DerUtf8String GetStringAt( + int index) + { + return this[index]; + } + + /** + * <pre> + * PkiFreeText ::= SEQUENCE SIZE (1..MAX) OF UTF8String + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + return strings; + } + } +} diff --git a/Crypto/src/asn1/cmp/PKIHeader.cs b/Crypto/src/asn1/cmp/PKIHeader.cs new file mode 100644
index 000000000..e758e9f16 --- /dev/null +++ b/Crypto/src/asn1/cmp/PKIHeader.cs
@@ -0,0 +1,237 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class PkiHeader + : Asn1Encodable + { + /** + * Value for a "null" recipient or sender. + */ + public static readonly GeneralName NULL_NAME = new GeneralName(X509Name.GetInstance(new DerSequence())); + + public static readonly int CMP_1999 = 1; + public static readonly int CMP_2000 = 2; + + private readonly DerInteger pvno; + private readonly GeneralName sender; + private readonly GeneralName recipient; + private readonly DerGeneralizedTime messageTime; + private readonly AlgorithmIdentifier protectionAlg; + private readonly Asn1OctetString senderKID; // KeyIdentifier + private readonly Asn1OctetString recipKID; // KeyIdentifier + private readonly Asn1OctetString transactionID; + private readonly Asn1OctetString senderNonce; + private readonly Asn1OctetString recipNonce; + private readonly PkiFreeText freeText; + private readonly Asn1Sequence generalInfo; + + private PkiHeader(Asn1Sequence seq) + { + pvno = DerInteger.GetInstance(seq[0]); + sender = GeneralName.GetInstance(seq[1]); + recipient = GeneralName.GetInstance(seq[2]); + + for (int pos = 3; pos < seq.Count; ++pos) + { + Asn1TaggedObject tObj = (Asn1TaggedObject)seq[pos]; + + switch (tObj.TagNo) + { + 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 ArgumentException("unknown tag number: " + tObj.TagNo, "seq"); + } + } + } + + public static PkiHeader GetInstance(object obj) + { + if (obj is PkiHeader) + return (PkiHeader)obj; + + if (obj is Asn1Sequence) + return new PkiHeader((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public PkiHeader( + int pvno, + GeneralName sender, + GeneralName recipient) + : this(new DerInteger(pvno), sender, recipient) + { + } + + private PkiHeader( + DerInteger pvno, + GeneralName sender, + GeneralName recipient) + { + this.pvno = pvno; + this.sender = sender; + this.recipient = recipient; + } + + public virtual DerInteger Pvno + { + get { return pvno; } + } + + public virtual GeneralName Sender + { + get { return sender; } + } + + public virtual GeneralName Recipient + { + get { return recipient; } + } + + public virtual DerGeneralizedTime MessageTime + { + get { return messageTime; } + } + + public virtual AlgorithmIdentifier ProtectionAlg + { + get { return protectionAlg; } + } + + public virtual Asn1OctetString SenderKID + { + get { return senderKID; } + } + + public virtual Asn1OctetString RecipKID + { + get { return recipKID; } + } + + public virtual Asn1OctetString TransactionID + { + get { return transactionID; } + } + + public virtual Asn1OctetString SenderNonce + { + get { return senderNonce; } + } + + public virtual Asn1OctetString RecipNonce + { + get { return recipNonce; } + } + + public virtual PkiFreeText FreeText + { + get { return freeText; } + } + + public virtual InfoTypeAndValue[] GetGeneralInfo() + { + if (generalInfo == null) + { + return null; + } + InfoTypeAndValue[] results = new InfoTypeAndValue[generalInfo.Count]; + for (int i = 0; i < results.Length; i++) + { + results[i] = InfoTypeAndValue.GetInstance(generalInfo[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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(pvno, sender, 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 static void AddOptional(Asn1EncodableVector v, int tagNo, Asn1Encodable obj) + { + if (obj != null) + { + v.Add(new DerTaggedObject(true, tagNo, obj)); + } + } + } +} diff --git a/Crypto/src/asn1/cmp/PKIHeaderBuilder.cs b/Crypto/src/asn1/cmp/PKIHeaderBuilder.cs new file mode 100644
index 000000000..00073c062 --- /dev/null +++ b/Crypto/src/asn1/cmp/PKIHeaderBuilder.cs
@@ -0,0 +1,223 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class PkiHeaderBuilder + { + private DerInteger 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; + + public PkiHeaderBuilder( + int pvno, + GeneralName sender, + GeneralName recipient) + : this(new DerInteger(pvno), sender, recipient) + { + } + + private PkiHeaderBuilder( + DerInteger pvno, + GeneralName sender, + GeneralName recipient) + { + this.pvno = pvno; + this.sender = sender; + this.recipient = recipient; + } + + public virtual PkiHeaderBuilder SetMessageTime(DerGeneralizedTime time) + { + messageTime = time; + return this; + } + + public virtual PkiHeaderBuilder SetProtectionAlg(AlgorithmIdentifier aid) + { + protectionAlg = aid; + return this; + } + + public virtual PkiHeaderBuilder SetSenderKID(byte[] kid) + { + return SetSenderKID(kid == null ? null : new DerOctetString(kid)); + } + + public virtual PkiHeaderBuilder SetSenderKID(Asn1OctetString kid) + { + senderKID = kid; + return this; + } + + public virtual PkiHeaderBuilder SetRecipKID(byte[] kid) + { + return SetRecipKID(kid == null ? null : new DerOctetString(kid)); + } + + public virtual PkiHeaderBuilder SetRecipKID(DerOctetString kid) + { + recipKID = kid; + return this; + } + + public virtual PkiHeaderBuilder SetTransactionID(byte[] tid) + { + return SetTransactionID(tid == null ? null : new DerOctetString(tid)); + } + + public virtual PkiHeaderBuilder SetTransactionID(Asn1OctetString tid) + { + transactionID = tid; + return this; + } + + public virtual PkiHeaderBuilder SetSenderNonce(byte[] nonce) + { + return SetSenderNonce(nonce == null ? null : new DerOctetString(nonce)); + } + + public virtual PkiHeaderBuilder SetSenderNonce(Asn1OctetString nonce) + { + senderNonce = nonce; + return this; + } + + public virtual PkiHeaderBuilder SetRecipNonce(byte[] nonce) + { + return SetRecipNonce(nonce == null ? null : new DerOctetString(nonce)); + } + + public virtual PkiHeaderBuilder SetRecipNonce(Asn1OctetString nonce) + { + recipNonce = nonce; + return this; + } + + public virtual PkiHeaderBuilder SetFreeText(PkiFreeText text) + { + freeText = text; + return this; + } + + public virtual PkiHeaderBuilder SetGeneralInfo(InfoTypeAndValue genInfo) + { + return SetGeneralInfo(MakeGeneralInfoSeq(genInfo)); + } + + public virtual PkiHeaderBuilder SetGeneralInfo(InfoTypeAndValue[] genInfos) + { + return SetGeneralInfo(MakeGeneralInfoSeq(genInfos)); + } + + public virtual 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 virtual PkiHeader Build() + { + Asn1EncodableVector v = new Asn1EncodableVector(pvno, sender, 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/Crypto/src/asn1/cmp/PKIMessage.cs b/Crypto/src/asn1/cmp/PKIMessage.cs new file mode 100644
index 000000000..086a2d938 --- /dev/null +++ b/Crypto/src/asn1/cmp/PKIMessage.cs
@@ -0,0 +1,140 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class PkiMessage + : Asn1Encodable + { + private readonly PkiHeader header; + private readonly PkiBody body; + private readonly DerBitString protection; + private readonly Asn1Sequence extraCerts; + + private PkiMessage(Asn1Sequence seq) + { + header = PkiHeader.GetInstance(seq[0]); + body = PkiBody.GetInstance(seq[1]); + + for (int pos = 2; pos < seq.Count; ++pos) + { + Asn1TaggedObject tObj = (Asn1TaggedObject)seq[pos].ToAsn1Object(); + + if (tObj.TagNo == 0) + { + protection = DerBitString.GetInstance(tObj, true); + } + else + { + extraCerts = Asn1Sequence.GetInstance(tObj, true); + } + } + } + + public static PkiMessage GetInstance(object obj) + { + if (obj is PkiMessage) + return (PkiMessage)obj; + + if (obj != null) + return new PkiMessage(Asn1Sequence.GetInstance(obj)); + + 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) + { + this.extraCerts = new DerSequence(extraCerts); + } + } + + 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 virtual PkiHeader Header + { + get { return header; } + } + + public virtual PkiBody Body + { + get { return body; } + } + + public virtual DerBitString Protection + { + get { return protection; } + } + + public virtual CmpCertificate[] GetExtraCerts() + { + if (extraCerts == null) + return null; + + CmpCertificate[] results = new CmpCertificate[extraCerts.Count]; + for (int i = 0; i < results.Length; ++i) + { + results[i] = CmpCertificate.GetInstance(extraCerts[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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(header, body); + + AddOptional(v, 0, protection); + AddOptional(v, 1, extraCerts); + + return new DerSequence(v); + } + + private static void AddOptional(Asn1EncodableVector v, int tagNo, Asn1Encodable obj) + { + if (obj != null) + { + v.Add(new DerTaggedObject(true, tagNo, obj)); + } + } + } +} diff --git a/Crypto/src/asn1/cmp/PKIMessages.cs b/Crypto/src/asn1/cmp/PKIMessages.cs new file mode 100644
index 000000000..ddabdf4ae --- /dev/null +++ b/Crypto/src/asn1/cmp/PKIMessages.cs
@@ -0,0 +1,52 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class PkiMessages + : Asn1Encodable + { + private Asn1Sequence content; + + private PkiMessages(Asn1Sequence seq) + { + content = seq; + } + + public static PkiMessages GetInstance(object obj) + { + if (obj is PkiMessages) + return (PkiMessages)obj; + + if (obj is Asn1Sequence) + return new PkiMessages((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public PkiMessages(params PkiMessage[] msgs) + { + content = new DerSequence(msgs); + } + + public virtual PkiMessage[] ToPkiMessageArray() + { + PkiMessage[] result = new PkiMessage[content.Count]; + for (int i = 0; i != result.Length; ++i) + { + result[i] = PkiMessage.GetInstance(content[i]); + } + return result; + } + + /** + * <pre> + * PkiMessages ::= SEQUENCE SIZE (1..MAX) OF PkiMessage + * </pre> + * @return a basic ASN.1 object representation. + */ + public override Asn1Object ToAsn1Object() + { + return content; + } + } +} diff --git a/Crypto/src/asn1/cmp/PKIStatus.cs b/Crypto/src/asn1/cmp/PKIStatus.cs new file mode 100644
index 000000000..b03dd3d62 --- /dev/null +++ b/Crypto/src/asn1/cmp/PKIStatus.cs
@@ -0,0 +1,62 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public enum PkiStatus + { + Granted = 0, + GrantedWithMods = 1, + Rejection = 2, + Waiting = 3, + RevocationWarning = 4, + RevocationNotification = 5, + KeyUpdateWarning = 6, + } + + public class PkiStatusEncodable + : Asn1Encodable + { + public static readonly PkiStatusEncodable granted = new PkiStatusEncodable(PkiStatus.Granted); + public static readonly PkiStatusEncodable grantedWithMods = new PkiStatusEncodable(PkiStatus.GrantedWithMods); + public static readonly PkiStatusEncodable rejection = new PkiStatusEncodable(PkiStatus.Rejection); + public static readonly PkiStatusEncodable waiting = new PkiStatusEncodable(PkiStatus.Waiting); + public static readonly PkiStatusEncodable revocationWarning = new PkiStatusEncodable(PkiStatus.RevocationWarning); + public static readonly PkiStatusEncodable revocationNotification = new PkiStatusEncodable(PkiStatus.RevocationNotification); + public static readonly PkiStatusEncodable keyUpdateWaiting = new PkiStatusEncodable(PkiStatus.KeyUpdateWarning); + + private readonly DerInteger status; + + private PkiStatusEncodable(PkiStatus status) + : this(new DerInteger((int)status)) + { + } + + private PkiStatusEncodable(DerInteger status) + { + this.status = status; + } + + public static PkiStatusEncodable GetInstance(object obj) + { + if (obj is PkiStatusEncodable) + return (PkiStatusEncodable)obj; + + if (obj is DerInteger) + return new PkiStatusEncodable((DerInteger)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public virtual BigInteger Value + { + get { return status.Value; } + } + + public override Asn1Object ToAsn1Object() + { + return status; + } + } +} diff --git a/Crypto/src/asn1/cmp/PKIStatusInfo.cs b/Crypto/src/asn1/cmp/PKIStatusInfo.cs new file mode 100644
index 000000000..2463e0081 --- /dev/null +++ b/Crypto/src/asn1/cmp/PKIStatusInfo.cs
@@ -0,0 +1,165 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class PkiStatusInfo + : Asn1Encodable + { + DerInteger status; + PkiFreeText statusString; + DerBitString failInfo; + + public static PkiStatusInfo GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit)); + } + + public static PkiStatusInfo GetInstance( + object obj) + { + if (obj is PkiStatusInfo) + { + return (PkiStatusInfo)obj; + } + else if (obj is Asn1Sequence) + { + return new PkiStatusInfo((Asn1Sequence)obj); + } + + throw new ArgumentException("Unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public PkiStatusInfo( + Asn1Sequence seq) + { + this.status = DerInteger.GetInstance(seq[0]); + + this.statusString = null; + this.failInfo = null; + + if (seq.Count > 2) + { + this.statusString = PkiFreeText.GetInstance(seq[1]); + this.failInfo = DerBitString.GetInstance(seq[2]); + } + else if (seq.Count > 1) + { + object obj = seq[1]; + if (obj is DerBitString) + { + this.failInfo = DerBitString.GetInstance(obj); + } + else + { + this.statusString = PkiFreeText.GetInstance(obj); + } + } + } + + /** + * @param status + */ + public PkiStatusInfo(int status) + { + this.status = new DerInteger(status); + } + + /** + * @param status + * @param statusString + */ + public PkiStatusInfo( + int status, + PkiFreeText statusString) + { + this.status = new DerInteger(status); + this.statusString = statusString; + } + + public PkiStatusInfo( + int status, + PkiFreeText statusString, + PkiFailureInfo failInfo) + { + this.status = new DerInteger(status); + this.statusString = statusString; + this.failInfo = failInfo; + } + + public BigInteger Status + { + get + { + return status.Value; + } + } + + public PkiFreeText StatusString + { + get + { + return statusString; + } + } + + public DerBitString FailInfo + { + get + { + 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(status); + + if (statusString != null) + { + v.Add(statusString); + } + + if (failInfo!= null) + { + v.Add(failInfo); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/cmp/PbmParameter.cs b/Crypto/src/asn1/cmp/PbmParameter.cs new file mode 100644
index 000000000..59b1bd7bb --- /dev/null +++ b/Crypto/src/asn1/cmp/PbmParameter.cs
@@ -0,0 +1,100 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class PbmParameter + : Asn1Encodable + { + private Asn1OctetString salt; + private AlgorithmIdentifier owf; + private DerInteger iterationCount; + private AlgorithmIdentifier mac; + + private PbmParameter(Asn1Sequence seq) + { + salt = Asn1OctetString.GetInstance(seq[0]); + owf = AlgorithmIdentifier.GetInstance(seq[1]); + iterationCount = DerInteger.GetInstance(seq[2]); + mac = AlgorithmIdentifier.GetInstance(seq[3]); + } + + public static PbmParameter GetInstance(object obj) + { + if (obj is PbmParameter) + return (PbmParameter)obj; + + if (obj is Asn1Sequence) + return new PbmParameter((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public PbmParameter( + byte[] salt, + AlgorithmIdentifier owf, + int iterationCount, + AlgorithmIdentifier mac) + : this(new DerOctetString(salt), owf, new DerInteger(iterationCount), mac) + { + } + + public PbmParameter( + Asn1OctetString salt, + AlgorithmIdentifier owf, + DerInteger iterationCount, + AlgorithmIdentifier mac) + { + this.salt = salt; + this.owf = owf; + this.iterationCount = iterationCount; + this.mac = mac; + } + + public virtual Asn1OctetString Salt + { + get { return salt; } + } + + public virtual AlgorithmIdentifier Owf + { + get { return owf; } + } + + public virtual DerInteger IterationCount + { + get { return iterationCount; } + } + + public virtual AlgorithmIdentifier Mac + { + get { 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 override Asn1Object ToAsn1Object() + { + return new DerSequence(salt, owf, iterationCount, mac); + } + } +} diff --git a/Crypto/src/asn1/cmp/PollRepContent.cs b/Crypto/src/asn1/cmp/PollRepContent.cs new file mode 100644
index 000000000..4045ac7ed --- /dev/null +++ b/Crypto/src/asn1/cmp/PollRepContent.cs
@@ -0,0 +1,66 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class PollRepContent + : Asn1Encodable + { + private readonly DerInteger certReqId; + private readonly DerInteger checkAfter; + private readonly PkiFreeText reason; + + private PollRepContent(Asn1Sequence seq) + { + certReqId = DerInteger.GetInstance(seq[0]); + checkAfter = DerInteger.GetInstance(seq[1]); + + if (seq.Count > 2) + { + reason = PkiFreeText.GetInstance(seq[2]); + } + } + + public static PollRepContent GetInstance(object obj) + { + if (obj is PollRepContent) + return (PollRepContent)obj; + + if (obj is Asn1Sequence) + return new PollRepContent((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public virtual DerInteger CertReqID + { + get { return certReqId; } + } + + public virtual DerInteger CheckAfter + { + get { return checkAfter; } + } + + public virtual PkiFreeText Reason + { + get { return reason; } + } + + /** + * <pre> + * PollRepContent ::= SEQUENCE OF SEQUENCE { + * certReqId INTEGER, + * checkAfter INTEGER, -- time in seconds + * reason PKIFreeText OPTIONAL + * } + * </pre> + * @return a basic ASN.1 object representation. + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(certReqId, checkAfter); + v.AddOptional(reason); + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/cmp/PollReqContent.cs b/Crypto/src/asn1/cmp/PollReqContent.cs new file mode 100644
index 000000000..ca2164151 --- /dev/null +++ b/Crypto/src/asn1/cmp/PollReqContent.cs
@@ -0,0 +1,59 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class PollReqContent + : Asn1Encodable + { + private readonly Asn1Sequence content; + + private PollReqContent(Asn1Sequence seq) + { + content = seq; + } + + public static PollReqContent GetInstance(object obj) + { + if (obj is PollReqContent) + return (PollReqContent)obj; + + if (obj is Asn1Sequence) + return new PollReqContent((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public virtual DerInteger[][] GetCertReqIDs() + { + DerInteger[][] result = new DerInteger[content.Count][]; + for (int i = 0; i != result.Length; ++i) + { + result[i] = SequenceToDerIntegerArray((Asn1Sequence)content[i]); + } + return result; + } + + private static DerInteger[] SequenceToDerIntegerArray(Asn1Sequence seq) + { + DerInteger[] result = new DerInteger[seq.Count]; + for (int i = 0; i != result.Length; ++i) + { + result[i] = DerInteger.GetInstance(seq[i]); + } + return result; + } + + /** + * <pre> + * PollReqContent ::= SEQUENCE OF SEQUENCE { + * certReqId INTEGER + * } + * </pre> + * @return a basic ASN.1 object representation. + */ + public override Asn1Object ToAsn1Object() + { + return content; + } + } +} diff --git a/Crypto/src/asn1/cmp/PopoDecKeyChallContent.cs b/Crypto/src/asn1/cmp/PopoDecKeyChallContent.cs new file mode 100644
index 000000000..20b173b85 --- /dev/null +++ b/Crypto/src/asn1/cmp/PopoDecKeyChallContent.cs
@@ -0,0 +1,47 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class PopoDecKeyChallContent + : Asn1Encodable + { + private readonly Asn1Sequence content; + + private PopoDecKeyChallContent(Asn1Sequence seq) + { + content = seq; + } + + public static PopoDecKeyChallContent GetInstance(object obj) + { + if (obj is PopoDecKeyChallContent) + return (PopoDecKeyChallContent)obj; + + if (obj is Asn1Sequence) + return new PopoDecKeyChallContent((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public virtual Challenge[] ToChallengeArray() + { + Challenge[] result = new Challenge[content.Count]; + for (int i = 0; i != result.Length; ++i) + { + result[i] = Challenge.GetInstance(content[i]); + } + return result; + } + + /** + * <pre> + * PopoDecKeyChallContent ::= SEQUENCE OF Challenge + * </pre> + * @return a basic ASN.1 object representation. + */ + public override Asn1Object ToAsn1Object() + { + return content; + } + } +} diff --git a/Crypto/src/asn1/cmp/PopoDecKeyRespContent.cs b/Crypto/src/asn1/cmp/PopoDecKeyRespContent.cs new file mode 100644
index 000000000..8c322e4ec --- /dev/null +++ b/Crypto/src/asn1/cmp/PopoDecKeyRespContent.cs
@@ -0,0 +1,47 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class PopoDecKeyRespContent + : Asn1Encodable + { + private readonly Asn1Sequence content; + + private PopoDecKeyRespContent(Asn1Sequence seq) + { + content = seq; + } + + public static PopoDecKeyRespContent GetInstance(object obj) + { + if (obj is PopoDecKeyRespContent) + return (PopoDecKeyRespContent)obj; + + if (obj is Asn1Sequence) + return new PopoDecKeyRespContent((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public virtual DerInteger[] ToDerIntegerArray() + { + DerInteger[] result = new DerInteger[content.Count]; + for (int i = 0; i != result.Length; ++i) + { + result[i] = DerInteger.GetInstance(content[i]); + } + return result; + } + + /** + * <pre> + * PopoDecKeyRespContent ::= SEQUENCE OF INTEGER + * </pre> + * @return a basic ASN.1 object representation. + */ + public override Asn1Object ToAsn1Object() + { + return content; + } + } +} diff --git a/Crypto/src/asn1/cmp/ProtectedPart.cs b/Crypto/src/asn1/cmp/ProtectedPart.cs new file mode 100644
index 000000000..db6798fee --- /dev/null +++ b/Crypto/src/asn1/cmp/ProtectedPart.cs
@@ -0,0 +1,58 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class ProtectedPart + : Asn1Encodable + { + private readonly PkiHeader header; + private readonly PkiBody body; + + private ProtectedPart(Asn1Sequence seq) + { + header = PkiHeader.GetInstance(seq[0]); + body = PkiBody.GetInstance(seq[1]); + } + + public static ProtectedPart GetInstance(object obj) + { + if (obj is ProtectedPart) + return (ProtectedPart)obj; + + if (obj is Asn1Sequence) + return new ProtectedPart((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public ProtectedPart(PkiHeader header, PkiBody body) + { + this.header = header; + this.body = body; + } + + public virtual PkiHeader Header + { + get { return header; } + } + + public virtual PkiBody Body + { + get { return body; } + } + + /** + * <pre> + * ProtectedPart ::= SEQUENCE { + * header PKIHeader, + * body PKIBody + * } + * </pre> + * @return a basic ASN.1 object representation. + */ + public override Asn1Object ToAsn1Object() + { + return new DerSequence(header, body); + } + } +} diff --git a/Crypto/src/asn1/cmp/RevAnnContent.cs b/Crypto/src/asn1/cmp/RevAnnContent.cs new file mode 100644
index 000000000..2c3bd5f77 --- /dev/null +++ b/Crypto/src/asn1/cmp/RevAnnContent.cs
@@ -0,0 +1,86 @@ +using System; + +using Org.BouncyCastle.Asn1.Crmf; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class RevAnnContent + : Asn1Encodable + { + private readonly PkiStatusEncodable status; + private readonly CertId certId; + private readonly DerGeneralizedTime willBeRevokedAt; + private readonly DerGeneralizedTime badSinceDate; + private readonly X509Extensions crlDetails; + + private RevAnnContent(Asn1Sequence seq) + { + status = PkiStatusEncodable.GetInstance(seq[0]); + certId = CertId.GetInstance(seq[1]); + willBeRevokedAt = DerGeneralizedTime.GetInstance(seq[2]); + badSinceDate = DerGeneralizedTime.GetInstance(seq[3]); + + if (seq.Count > 4) + { + crlDetails = X509Extensions.GetInstance(seq[4]); + } + } + + public static RevAnnContent GetInstance(object obj) + { + if (obj is RevAnnContent) + return (RevAnnContent)obj; + + if (obj is Asn1Sequence) + return new RevAnnContent((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public virtual PkiStatusEncodable Status + { + get { return status; } + } + + public virtual CertId CertID + { + get { return certId; } + } + + public virtual DerGeneralizedTime WillBeRevokedAt + { + get { return willBeRevokedAt; } + } + + public virtual DerGeneralizedTime BadSinceDate + { + get { return badSinceDate; } + } + + public virtual X509Extensions CrlDetails + { + get { 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(status, certId, willBeRevokedAt, badSinceDate); + v.AddOptional(crlDetails); + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/cmp/RevDetails.cs b/Crypto/src/asn1/cmp/RevDetails.cs new file mode 100644
index 000000000..1bd95f1db --- /dev/null +++ b/Crypto/src/asn1/cmp/RevDetails.cs
@@ -0,0 +1,75 @@ +using System; + +using Org.BouncyCastle.Asn1.Crmf; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class RevDetails + : Asn1Encodable + { + private readonly CertTemplate certDetails; + private readonly X509Extensions crlEntryDetails; + + private RevDetails(Asn1Sequence seq) + { + certDetails = CertTemplate.GetInstance(seq[0]); + + if (seq.Count > 1) + { + crlEntryDetails = X509Extensions.GetInstance(seq[1]); + } + } + + public static RevDetails GetInstance(object obj) + { + if (obj is RevDetails) + return (RevDetails)obj; + + if (obj is Asn1Sequence) + return new RevDetails((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public RevDetails(CertTemplate certDetails) + { + this.certDetails = certDetails; + } + + public RevDetails(CertTemplate certDetails, X509Extensions crlEntryDetails) + { + this.crlEntryDetails = crlEntryDetails; + } + + public virtual CertTemplate CertDetails + { + get { return certDetails; } + } + + public virtual X509Extensions CrlEntryDetails + { + get { 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(certDetails); + v.AddOptional(crlEntryDetails); + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/cmp/RevRepContent.cs b/Crypto/src/asn1/cmp/RevRepContent.cs new file mode 100644
index 000000000..47987265a --- /dev/null +++ b/Crypto/src/asn1/cmp/RevRepContent.cs
@@ -0,0 +1,112 @@ +using System; + +using Org.BouncyCastle.Asn1.Crmf; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class RevRepContent + : Asn1Encodable + { + private readonly Asn1Sequence status; + private readonly Asn1Sequence revCerts; + private readonly Asn1Sequence crls; + + private RevRepContent(Asn1Sequence seq) + { + status = Asn1Sequence.GetInstance(seq[0]); + + for (int pos = 1; pos < seq.Count; ++pos) + { + Asn1TaggedObject tObj = Asn1TaggedObject.GetInstance(seq[pos]); + + if (tObj.TagNo == 0) + { + revCerts = Asn1Sequence.GetInstance(tObj, true); + } + else + { + crls = Asn1Sequence.GetInstance(tObj, true); + } + } + } + + public static RevRepContent GetInstance(object obj) + { + if (obj is RevRepContent) + return (RevRepContent)obj; + + if (obj is Asn1Sequence) + return new RevRepContent((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public virtual PkiStatusInfo[] GetStatus() + { + PkiStatusInfo[] results = new PkiStatusInfo[status.Count]; + for (int i = 0; i != results.Length; ++i) + { + results[i] = PkiStatusInfo.GetInstance(status[i]); + } + return results; + } + + public virtual CertId[] GetRevCerts() + { + if (revCerts == null) + return null; + + CertId[] results = new CertId[revCerts.Count]; + for (int i = 0; i != results.Length; ++i) + { + results[i] = CertId.GetInstance(revCerts[i]); + } + return results; + } + + public virtual CertificateList[] GetCrls() + { + if (crls == null) + return null; + + CertificateList[] results = new CertificateList[crls.Count]; + for (int i = 0; i != results.Length; ++i) + { + results[i] = CertificateList.GetInstance(crls[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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(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/Crypto/src/asn1/cmp/RevRepContentBuilder.cs b/Crypto/src/asn1/cmp/RevRepContentBuilder.cs new file mode 100644
index 000000000..cc17d1d4c --- /dev/null +++ b/Crypto/src/asn1/cmp/RevRepContentBuilder.cs
@@ -0,0 +1,55 @@ +using System; + +using Org.BouncyCastle.Asn1.Crmf; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class RevRepContentBuilder + { + private readonly Asn1EncodableVector status = new Asn1EncodableVector(); + private readonly Asn1EncodableVector revCerts = new Asn1EncodableVector(); + private readonly Asn1EncodableVector crls = new Asn1EncodableVector(); + + public virtual RevRepContentBuilder Add(PkiStatusInfo status) + { + this.status.Add(status); + return this; + } + + public virtual RevRepContentBuilder Add(PkiStatusInfo status, CertId certId) + { + if (this.status.Count != this.revCerts.Count) + throw new InvalidOperationException("status and revCerts sequence must be in common order"); + + this.status.Add(status); + this.revCerts.Add(certId); + return this; + } + + public virtual RevRepContentBuilder AddCrl(CertificateList crl) + { + this.crls.Add(crl); + return this; + } + + public virtual RevRepContent Build() + { + Asn1EncodableVector v = new Asn1EncodableVector(); + + v.Add(new DerSequence(status)); + + if (revCerts.Count != 0) + { + v.Add(new DerTaggedObject(true, 0, new DerSequence(revCerts))); + } + + if (crls.Count != 0) + { + v.Add(new DerTaggedObject(true, 1, new DerSequence(crls))); + } + + return RevRepContent.GetInstance(new DerSequence(v)); + } + } +} diff --git a/Crypto/src/asn1/cmp/RevReqContent.cs b/Crypto/src/asn1/cmp/RevReqContent.cs new file mode 100644
index 000000000..fbf869203 --- /dev/null +++ b/Crypto/src/asn1/cmp/RevReqContent.cs
@@ -0,0 +1,52 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class RevReqContent + : Asn1Encodable + { + private readonly Asn1Sequence content; + + private RevReqContent(Asn1Sequence seq) + { + content = seq; + } + + public static RevReqContent GetInstance(object obj) + { + if (obj is RevReqContent) + return (RevReqContent)obj; + + if (obj is Asn1Sequence) + return new RevReqContent((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public RevReqContent(params RevDetails[] revDetails) + { + this.content = new DerSequence(revDetails); + } + + public virtual RevDetails[] ToRevDetailsArray() + { + RevDetails[] result = new RevDetails[content.Count]; + for (int i = 0; i != result.Length; ++i) + { + result[i] = RevDetails.GetInstance(content[i]); + } + return result; + } + + /** + * <pre> + * RevReqContent ::= SEQUENCE OF RevDetails + * </pre> + * @return a basic ASN.1 object representation. + */ + public override Asn1Object ToAsn1Object() + { + return content; + } + } +} diff --git a/Crypto/src/asn1/cms/Attribute.cs b/Crypto/src/asn1/cms/Attribute.cs new file mode 100644
index 000000000..c4a104a3f --- /dev/null +++ b/Crypto/src/asn1/cms/Attribute.cs
@@ -0,0 +1,70 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class Attribute + : Asn1Encodable + { + private DerObjectIdentifier attrType; + private Asn1Set attrValues; + + /** + * return an Attribute object from the given object. + * + * @param o the object we want converted. + * @exception ArgumentException if the object cannot be converted. + */ + public static Attribute GetInstance( + object obj) + { + if (obj == null || obj is Attribute) + return (Attribute) obj; + + if (obj is Asn1Sequence) + return new Attribute((Asn1Sequence) obj); + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public Attribute( + Asn1Sequence seq) + { + attrType = (DerObjectIdentifier)seq[0]; + attrValues = (Asn1Set)seq[1]; + } + + public Attribute( + DerObjectIdentifier attrType, + Asn1Set attrValues) + { + this.attrType = attrType; + this.attrValues = attrValues; + } + + public DerObjectIdentifier AttrType + { + get { return attrType; } + } + + public Asn1Set AttrValues + { + get { return attrValues; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * Attribute ::= SEQUENCE { + * attrType OBJECT IDENTIFIER, + * attrValues SET OF AttributeValue + * } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + return new DerSequence(attrType, attrValues); + } + } +} diff --git a/Crypto/src/asn1/cms/AttributeTable.cs b/Crypto/src/asn1/cms/AttributeTable.cs new file mode 100644
index 000000000..3c0164ce8 --- /dev/null +++ b/Crypto/src/asn1/cms/AttributeTable.cs
@@ -0,0 +1,231 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class AttributeTable + { + private readonly IDictionary attributes; + +#if !(SILVERLIGHT || PORTABLE) + [Obsolete] + public AttributeTable( + Hashtable attrs) + { + this.attributes = Platform.CreateHashtable(attrs); + } +#endif + + public AttributeTable( + IDictionary attrs) + { + this.attributes = Platform.CreateHashtable(attrs); + } + + public AttributeTable( + Asn1EncodableVector v) + { + this.attributes = Platform.CreateHashtable(v.Count); + + foreach (Asn1Encodable o in v) + { + Attribute a = Attribute.GetInstance(o); + + AddAttribute(a); + } + } + + public AttributeTable( + Asn1Set s) + { + this.attributes = Platform.CreateHashtable(s.Count); + + for (int i = 0; i != s.Count; i++) + { + Attribute a = Attribute.GetInstance(s[i]); + + AddAttribute(a); + } + } + + public AttributeTable( + Attributes attrs) + : this(Asn1Set.GetInstance(attrs.ToAsn1Object())) + { + } + + private void AddAttribute( + Attribute a) + { + DerObjectIdentifier oid = a.AttrType; + object obj = attributes[oid]; + + if (obj == null) + { + attributes[oid] = a; + } + else + { + IList v; + + if (obj is Attribute) + { + v = Platform.CreateArrayList(); + + v.Add(obj); + v.Add(a); + } + else + { + v = (IList) obj; + + v.Add(a); + } + + attributes[oid] = v; + } + } + + /// <summary>Return the first attribute matching the given OBJECT IDENTIFIER</summary> + public Attribute this[DerObjectIdentifier oid] + { + get + { + object obj = attributes[oid]; + + if (obj is IList) + { + return (Attribute)((IList)obj)[0]; + } + + return (Attribute) obj; + } + } + + [Obsolete("Use 'object[oid]' syntax instead")] + public Attribute Get( + DerObjectIdentifier oid) + { + return this[oid]; + } + + /** + * 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( + DerObjectIdentifier oid) + { + Asn1EncodableVector v = new Asn1EncodableVector(); + + object obj = attributes[oid]; + + if (obj is IList) + { + foreach (Attribute a in (IList)obj) + { + v.Add(a); + } + } + else if (obj != null) + { + v.Add((Attribute) obj); + } + + return v; + } + + public int Count + { + get + { + int total = 0; + + foreach (object o in attributes.Values) + { + if (o is IList) + { + total += ((IList)o).Count; + } + else + { + ++total; + } + } + + return total; + } + } + + public IDictionary ToDictionary() + { + return Platform.CreateHashtable(attributes); + } + +#if !(SILVERLIGHT || PORTABLE) + [Obsolete("Use 'ToDictionary' instead")] + public Hashtable ToHashtable() + { + return new Hashtable(attributes); + } +#endif + + public Asn1EncodableVector ToAsn1EncodableVector() + { + Asn1EncodableVector v = new Asn1EncodableVector(); + + foreach (object obj in attributes.Values) + { + if (obj is IList) + { + foreach (object el in (IList)obj) + { + v.Add(Attribute.GetInstance(el)); + } + } + else + { + v.Add(Attribute.GetInstance(obj)); + } + } + + return v; + } + + public Attributes ToAttributes() + { + return new Attributes(this.ToAsn1EncodableVector()); + } + + /** + * Return a new table with the passed in attribute added. + * + * @param attrType + * @param attrValue + * @return + */ + public AttributeTable Add(DerObjectIdentifier attrType, Asn1Encodable attrValue) + { + AttributeTable newTable = new AttributeTable(attributes); + + newTable.AddAttribute(new Attribute(attrType, new DerSet(attrValue))); + + return newTable; + } + + public AttributeTable Remove(DerObjectIdentifier attrType) + { + AttributeTable newTable = new AttributeTable(attributes); + + newTable.attributes.Remove(attrType); + + return newTable; + } + } +} diff --git a/Crypto/src/asn1/cms/Attributes.cs b/Crypto/src/asn1/cms/Attributes.cs new file mode 100644
index 000000000..47cb66bdb --- /dev/null +++ b/Crypto/src/asn1/cms/Attributes.cs
@@ -0,0 +1,43 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class Attributes + : Asn1Encodable + { + private readonly Asn1Set attributes; + + private Attributes(Asn1Set attributes) + { + this.attributes = attributes; + } + + public Attributes(Asn1EncodableVector v) + { + attributes = new BerSet(v); + } + + public static Attributes GetInstance(object obj) + { + if (obj is Attributes) + return (Attributes)obj; + + if (obj != null) + return new Attributes(Asn1Set.GetInstance(obj)); + + return null; + } + + /** + * <pre> + * Attributes ::= + * SET SIZE(1..MAX) OF Attribute -- according to RFC 5652 + * </pre> + * @return + */ + public override Asn1Object ToAsn1Object() + { + return attributes; + } + } +} diff --git a/Crypto/src/asn1/cms/AuthEnvelopedData.cs b/Crypto/src/asn1/cms/AuthEnvelopedData.cs new file mode 100644
index 000000000..4260d80f9 --- /dev/null +++ b/Crypto/src/asn1/cms/AuthEnvelopedData.cs
@@ -0,0 +1,203 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class AuthEnvelopedData + : Asn1Encodable + { + private DerInteger 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 DerInteger(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; + } + + private AuthEnvelopedData( + Asn1Sequence seq) + { + int index = 0; + + // TODO + // "It MUST be set to 0." + Asn1Object tmp = seq[index++].ToAsn1Object(); + version = (DerInteger)tmp; + + tmp = seq[index++].ToAsn1Object(); + if (tmp is Asn1TaggedObject) + { + originatorInfo = OriginatorInfo.GetInstance((Asn1TaggedObject)tmp, false); + tmp = seq[index++].ToAsn1Object(); + } + + // TODO + // "There MUST be at least one element in the collection." + recipientInfos = Asn1Set.GetInstance(tmp); + + tmp = seq[index++].ToAsn1Object(); + authEncryptedContentInfo = EncryptedContentInfo.GetInstance(tmp); + + tmp = seq[index++].ToAsn1Object(); + if (tmp is Asn1TaggedObject) + { + authAttrs = Asn1Set.GetInstance((Asn1TaggedObject)tmp, false); + tmp = seq[index++].ToAsn1Object(); + } + else + { + // TODO + // "The authAttrs MUST be present if the content type carried in + // EncryptedContentInfo is not id-data." + } + + mac = Asn1OctetString.GetInstance(tmp); + + if (seq.Count > index) + { + tmp = seq[index++].ToAsn1Object(); + 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 isExplicit true if the object is meant to be explicitly + * tagged false otherwise. + * @throws ArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static AuthEnvelopedData GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit)); + } + + /** + * return an AuthEnvelopedData object from the given object. + * + * @param obj the object we want converted. + * @throws ArgumentException if the object cannot be converted. + */ + public static AuthEnvelopedData GetInstance( + object obj) + { + if (obj == null || obj is AuthEnvelopedData) + return (AuthEnvelopedData)obj; + + if (obj is Asn1Sequence) + return new AuthEnvelopedData((Asn1Sequence)obj); + + throw new ArgumentException("Invalid AuthEnvelopedData: " + obj.GetType().Name); + } + + public DerInteger Version + { + get { return version; } + } + + public OriginatorInfo OriginatorInfo + { + get { return originatorInfo; } + } + + public Asn1Set RecipientInfos + { + get { return recipientInfos; } + } + + public EncryptedContentInfo AuthEncryptedContentInfo + { + get { return authEncryptedContentInfo; } + } + + public Asn1Set AuthAttrs + { + get { return authAttrs; } + } + + public Asn1OctetString Mac + { + get { return mac; } + } + + public Asn1Set UnauthAttrs + { + get { 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(version); + + if (originatorInfo != null) + { + v.Add(new DerTaggedObject(false, 0, originatorInfo)); + } + + v.Add(recipientInfos, 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/Crypto/src/asn1/cms/AuthEnvelopedDataParser.cs b/Crypto/src/asn1/cms/AuthEnvelopedDataParser.cs new file mode 100644
index 000000000..35cb3bfcc --- /dev/null +++ b/Crypto/src/asn1/cms/AuthEnvelopedDataParser.cs
@@ -0,0 +1,145 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cms +{ + /** + * 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 DerInteger version; + private IAsn1Convertible nextObject; + private bool originatorInfoCalled; + + public AuthEnvelopedDataParser( + Asn1SequenceParser seq) + { + this.seq = seq; + + // TODO + // "It MUST be set to 0." + this.version = (DerInteger)seq.ReadObject(); + } + + public DerInteger Version + { + get { return version; } + } + + public OriginatorInfo GetOriginatorInfo() + { + originatorInfoCalled = true; + + if (nextObject == null) + { + nextObject = seq.ReadObject(); + } + + if (nextObject is Asn1TaggedObjectParser && ((Asn1TaggedObjectParser)nextObject).TagNo == 0) + { + Asn1SequenceParser originatorInfo = (Asn1SequenceParser) ((Asn1TaggedObjectParser)nextObject).GetObjectParser(Asn1Tags.Sequence, false); + nextObject = null; + return OriginatorInfo.GetInstance(originatorInfo.ToAsn1Object()); + } + + return null; + } + + public Asn1SetParser GetRecipientInfos() + { + if (!originatorInfoCalled) + { + GetOriginatorInfo(); + } + + if (nextObject == null) + { + nextObject = seq.ReadObject(); + } + + Asn1SetParser recipientInfos = (Asn1SetParser)nextObject; + nextObject = null; + return recipientInfos; + } + + public EncryptedContentInfoParser GetAuthEncryptedContentInfo() + { + if (nextObject == null) + { + nextObject = seq.ReadObject(); + } + + if (nextObject != null) + { + Asn1SequenceParser o = (Asn1SequenceParser) nextObject; + nextObject = null; + return new EncryptedContentInfoParser(o); + } + + return null; + } + + public Asn1SetParser GetAuthAttrs() + { + if (nextObject == null) + { + nextObject = seq.ReadObject(); + } + + if (nextObject is Asn1TaggedObjectParser) + { + IAsn1Convertible o = nextObject; + nextObject = null; + return (Asn1SetParser)((Asn1TaggedObjectParser)o).GetObjectParser(Asn1Tags.Set, false); + } + + // TODO + // "The authAttrs MUST be present if the content type carried in + // EncryptedContentInfo is not id-data." + + return null; + } + + public Asn1OctetString GetMac() + { + if (nextObject == null) + { + nextObject = seq.ReadObject(); + } + + IAsn1Convertible o = nextObject; + nextObject = null; + + return Asn1OctetString.GetInstance(o.ToAsn1Object()); + } + + public Asn1SetParser GetUnauthAttrs() + { + if (nextObject == null) + { + nextObject = seq.ReadObject(); + } + + if (nextObject != null) + { + IAsn1Convertible o = nextObject; + nextObject = null; + return (Asn1SetParser)((Asn1TaggedObjectParser)o).GetObjectParser(Asn1Tags.Set, false); + } + + return null; + } + } +} diff --git a/Crypto/src/asn1/cms/AuthenticatedData.cs b/Crypto/src/asn1/cms/AuthenticatedData.cs new file mode 100644
index 000000000..15286d1aa --- /dev/null +++ b/Crypto/src/asn1/cms/AuthenticatedData.cs
@@ -0,0 +1,270 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class AuthenticatedData + : Asn1Encodable + { + private DerInteger 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 ArgumentException("digestAlgorithm and authAttrs must be set together"); + } + } + + version = new DerInteger(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; + } + + private AuthenticatedData( + Asn1Sequence seq) + { + int index = 0; + + version = (DerInteger)seq[index++]; + + Asn1Encodable tmp = seq[index++]; + if (tmp is Asn1TaggedObject) + { + originatorInfo = OriginatorInfo.GetInstance((Asn1TaggedObject)tmp, false); + tmp = seq[index++]; + } + + recipientInfos = Asn1Set.GetInstance(tmp); + macAlgorithm = AlgorithmIdentifier.GetInstance(seq[index++]); + + tmp = seq[index++]; + if (tmp is Asn1TaggedObject) + { + digestAlgorithm = AlgorithmIdentifier.GetInstance((Asn1TaggedObject)tmp, false); + tmp = seq[index++]; + } + + encapsulatedContentInfo = ContentInfo.GetInstance(tmp); + + tmp = seq[index++]; + if (tmp is Asn1TaggedObject) + { + authAttrs = Asn1Set.GetInstance((Asn1TaggedObject)tmp, false); + tmp = seq[index++]; + } + + mac = Asn1OctetString.GetInstance(tmp); + + if (seq.Count > index) + { + unauthAttrs = Asn1Set.GetInstance((Asn1TaggedObject)seq[index], false); + } + } + + /** + * return an AuthenticatedData object from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param isExplicit true if the object is meant to be explicitly + * tagged false otherwise. + * @throws ArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static AuthenticatedData GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit)); + } + + /** + * return an AuthenticatedData object from the given object. + * + * @param obj the object we want converted. + * @throws ArgumentException if the object cannot be converted. + */ + public static AuthenticatedData GetInstance( + object obj) + { + if (obj == null || obj is AuthenticatedData) + { + return (AuthenticatedData)obj; + } + + if (obj is Asn1Sequence) + { + return new AuthenticatedData((Asn1Sequence)obj); + } + + throw new ArgumentException("Invalid AuthenticatedData: " + obj.GetType().Name); + } + + public DerInteger Version + { + get { return version; } + } + + public OriginatorInfo OriginatorInfo + { + get { return originatorInfo; } + } + + public Asn1Set RecipientInfos + { + get { return recipientInfos; } + } + + public AlgorithmIdentifier MacAlgorithm + { + get { return macAlgorithm; } + } + + public AlgorithmIdentifier DigestAlgorithm + { + get { return digestAlgorithm; } + } + + public ContentInfo EncapsulatedContentInfo + { + get { return encapsulatedContentInfo; } + } + + public Asn1Set AuthAttrs + { + get { return authAttrs; } + } + + public Asn1OctetString Mac + { + get { return mac; } + } + + public Asn1Set UnauthAttrs + { + get { 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(version); + + if (originatorInfo != null) + { + v.Add(new DerTaggedObject(false, 0, originatorInfo)); + } + + v.Add(recipientInfos, 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; + + int ver = 0; + + foreach (object obj in origInfo.Certificates) + { + if (obj is Asn1TaggedObject) + { + Asn1TaggedObject tag = (Asn1TaggedObject)obj; + + if (tag.TagNo == 2) + { + ver = 1; + } + else if (tag.TagNo == 3) + { + ver = 3; + break; + } + } + } + + foreach (object obj in origInfo.Crls) + { + if (obj is Asn1TaggedObject) + { + Asn1TaggedObject tag = (Asn1TaggedObject)obj; + + if (tag.TagNo == 1) + { + ver = 3; + break; + } + } + } + + return ver; + } + } +} diff --git a/Crypto/src/asn1/cms/AuthenticatedDataParser.cs b/Crypto/src/asn1/cms/AuthenticatedDataParser.cs new file mode 100644
index 000000000..4b80d1b02 --- /dev/null +++ b/Crypto/src/asn1/cms/AuthenticatedDataParser.cs
@@ -0,0 +1,182 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Cms +{ + /** + * 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 DerInteger version; + private IAsn1Convertible nextObject; + private bool originatorInfoCalled; + + public AuthenticatedDataParser( + Asn1SequenceParser seq) + { + this.seq = seq; + this.version = (DerInteger)seq.ReadObject(); + } + + public DerInteger Version + { + get { return version; } + } + + public OriginatorInfo GetOriginatorInfo() + { + originatorInfoCalled = true; + + if (nextObject == null) + { + nextObject = seq.ReadObject(); + } + + if (nextObject is Asn1TaggedObjectParser && ((Asn1TaggedObjectParser)nextObject).TagNo == 0) + { + Asn1SequenceParser originatorInfo = (Asn1SequenceParser) ((Asn1TaggedObjectParser)nextObject).GetObjectParser(Asn1Tags.Sequence, false); + nextObject = null; + return OriginatorInfo.GetInstance(originatorInfo.ToAsn1Object()); + } + + return null; + } + + public Asn1SetParser GetRecipientInfos() + { + if (!originatorInfoCalled) + { + GetOriginatorInfo(); + } + + if (nextObject == null) + { + nextObject = seq.ReadObject(); + } + + Asn1SetParser recipientInfos = (Asn1SetParser)nextObject; + nextObject = null; + return recipientInfos; + } + + public AlgorithmIdentifier GetMacAlgorithm() + { + if (nextObject == null) + { + nextObject = seq.ReadObject(); + } + + if (nextObject != null) + { + Asn1SequenceParser o = (Asn1SequenceParser)nextObject; + nextObject = null; + return AlgorithmIdentifier.GetInstance(o.ToAsn1Object()); + } + + return null; + } + + public AlgorithmIdentifier GetDigestAlgorithm() + { + if (nextObject == null) + { + nextObject = seq.ReadObject(); + } + + if (nextObject is Asn1TaggedObjectParser) + { + AlgorithmIdentifier obj = AlgorithmIdentifier.GetInstance( + (Asn1TaggedObject)nextObject.ToAsn1Object(), false); + nextObject = null; + return obj; + } + + return null; + } + + public ContentInfoParser GetEnapsulatedContentInfo() + { + if (nextObject == null) + { + nextObject = seq.ReadObject(); + } + + if (nextObject != null) + { + Asn1SequenceParser o = (Asn1SequenceParser)nextObject; + nextObject = null; + return new ContentInfoParser(o); + } + + return null; + } + + public Asn1SetParser GetAuthAttrs() + { + if (nextObject == null) + { + nextObject = seq.ReadObject(); + } + + if (nextObject is Asn1TaggedObjectParser) + { + IAsn1Convertible o = nextObject; + nextObject = null; + return (Asn1SetParser)((Asn1TaggedObjectParser)o).GetObjectParser(Asn1Tags.Set, false); + } + + return null; + } + + public Asn1OctetString GetMac() + { + if (nextObject == null) + { + nextObject = seq.ReadObject(); + } + + IAsn1Convertible o = nextObject; + nextObject = null; + + return Asn1OctetString.GetInstance(o.ToAsn1Object()); + } + + public Asn1SetParser GetUnauthAttrs() + { + if (nextObject == null) + { + nextObject = seq.ReadObject(); + } + + if (nextObject != null) + { + IAsn1Convertible o = nextObject; + nextObject = null; + return (Asn1SetParser)((Asn1TaggedObjectParser)o).GetObjectParser(Asn1Tags.Set, false); + } + + return null; + } + } +} diff --git a/Crypto/src/asn1/cms/CMSAttributes.cs b/Crypto/src/asn1/cms/CMSAttributes.cs new file mode 100644
index 000000000..fca2b6738 --- /dev/null +++ b/Crypto/src/asn1/cms/CMSAttributes.cs
@@ -0,0 +1,14 @@ +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Pkcs; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public abstract class CmsAttributes + { + public static readonly DerObjectIdentifier ContentType = PkcsObjectIdentifiers.Pkcs9AtContentType; + public static readonly DerObjectIdentifier MessageDigest = PkcsObjectIdentifiers.Pkcs9AtMessageDigest; + public static readonly DerObjectIdentifier SigningTime = PkcsObjectIdentifiers.Pkcs9AtSigningTime; + public static readonly DerObjectIdentifier CounterSignature = PkcsObjectIdentifiers.Pkcs9AtCounterSignature; + public static readonly DerObjectIdentifier ContentHint = PkcsObjectIdentifiers.IdAAContentHint; + } +} diff --git a/Crypto/src/asn1/cms/CMSObjectIdentifiers.cs b/Crypto/src/asn1/cms/CMSObjectIdentifiers.cs new file mode 100644
index 000000000..59009bda7 --- /dev/null +++ b/Crypto/src/asn1/cms/CMSObjectIdentifiers.cs
@@ -0,0 +1,18 @@ +using Org.BouncyCastle.Asn1.Pkcs; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public abstract class CmsObjectIdentifiers + { + public static readonly DerObjectIdentifier Data = PkcsObjectIdentifiers.Data; + public static readonly DerObjectIdentifier SignedData = PkcsObjectIdentifiers.SignedData; + public static readonly DerObjectIdentifier EnvelopedData = PkcsObjectIdentifiers.EnvelopedData; + public static readonly DerObjectIdentifier SignedAndEnvelopedData = PkcsObjectIdentifiers.SignedAndEnvelopedData; + public static readonly DerObjectIdentifier DigestedData = PkcsObjectIdentifiers.DigestedData; + public static readonly DerObjectIdentifier EncryptedData = PkcsObjectIdentifiers.EncryptedData; + public static readonly DerObjectIdentifier AuthenticatedData = PkcsObjectIdentifiers.IdCTAuthData; + public static readonly DerObjectIdentifier CompressedData = PkcsObjectIdentifiers.IdCTCompressedData; + public static readonly DerObjectIdentifier AuthEnvelopedData = PkcsObjectIdentifiers.IdCTAuthEnvelopedData; + public static readonly DerObjectIdentifier timestampedData = PkcsObjectIdentifiers.IdCTTimestampedData; + } +} diff --git a/Crypto/src/asn1/cms/CompressedData.cs b/Crypto/src/asn1/cms/CompressedData.cs new file mode 100644
index 000000000..5a2869b8c --- /dev/null +++ b/Crypto/src/asn1/cms/CompressedData.cs
@@ -0,0 +1,96 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Cms +{ + /** + * RFC 3274 - CMS Compressed Data. + * <pre> + * CompressedData ::= Sequence { + * version CMSVersion, + * compressionAlgorithm CompressionAlgorithmIdentifier, + * encapContentInfo EncapsulatedContentInfo + * } + * </pre> + */ + public class CompressedData + : Asn1Encodable + { + private DerInteger version; + private AlgorithmIdentifier compressionAlgorithm; + private ContentInfo encapContentInfo; + + public CompressedData( + AlgorithmIdentifier compressionAlgorithm, + ContentInfo encapContentInfo) + { + this.version = new DerInteger(0); + this.compressionAlgorithm = compressionAlgorithm; + this.encapContentInfo = encapContentInfo; + } + + public CompressedData( + Asn1Sequence seq) + { + this.version = (DerInteger) seq[0]; + this.compressionAlgorithm = AlgorithmIdentifier.GetInstance(seq[1]); + this.encapContentInfo = ContentInfo.GetInstance(seq[2]); + } + + /** + * return a CompressedData object from a tagged object. + * + * @param ato the tagged object holding the object we want. + * @param explicitly true if the object is meant to be explicitly + * tagged false otherwise. + * @exception ArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static CompressedData GetInstance( + Asn1TaggedObject ato, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(ato, explicitly)); + } + + /** + * return a CompressedData object from the given object. + * + * @param _obj the object we want converted. + * @exception ArgumentException if the object cannot be converted. + */ + public static CompressedData GetInstance( + object obj) + { + if (obj == null || obj is CompressedData) + return (CompressedData)obj; + + if (obj is Asn1Sequence) + return new CompressedData((Asn1Sequence) obj); + + throw new ArgumentException("Invalid CompressedData: " + obj.GetType().Name); + } + + public DerInteger Version + { + get { return version; } + } + + public AlgorithmIdentifier CompressionAlgorithmIdentifier + { + get { return compressionAlgorithm; } + } + + public ContentInfo EncapContentInfo + { + get { return encapContentInfo; } + } + + public override Asn1Object ToAsn1Object() + { + return new BerSequence(version, compressionAlgorithm, encapContentInfo); + } + } +} diff --git a/Crypto/src/asn1/cms/CompressedDataParser.cs b/Crypto/src/asn1/cms/CompressedDataParser.cs new file mode 100644
index 000000000..7c53453df --- /dev/null +++ b/Crypto/src/asn1/cms/CompressedDataParser.cs
@@ -0,0 +1,47 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Cms +{ + /** + * RFC 3274 - CMS Compressed Data. + * <pre> + * CompressedData ::= SEQUENCE { + * version CMSVersion, + * compressionAlgorithm CompressionAlgorithmIdentifier, + * encapContentInfo EncapsulatedContentInfo + * } + * </pre> + */ + public class CompressedDataParser + { + private DerInteger _version; + private AlgorithmIdentifier _compressionAlgorithm; + private ContentInfoParser _encapContentInfo; + + public CompressedDataParser( + Asn1SequenceParser seq) + { + this._version = (DerInteger)seq.ReadObject(); + this._compressionAlgorithm = AlgorithmIdentifier.GetInstance(seq.ReadObject().ToAsn1Object()); + this._encapContentInfo = new ContentInfoParser((Asn1SequenceParser)seq.ReadObject()); + } + + public DerInteger Version + { + get { return _version; } + } + + public AlgorithmIdentifier CompressionAlgorithmIdentifier + { + get { return _compressionAlgorithm; } + } + + public ContentInfoParser GetEncapContentInfo() + { + return _encapContentInfo; + } + } +} diff --git a/Crypto/src/asn1/cms/ContentInfo.cs b/Crypto/src/asn1/cms/ContentInfo.cs new file mode 100644
index 000000000..61a277535 --- /dev/null +++ b/Crypto/src/asn1/cms/ContentInfo.cs
@@ -0,0 +1,83 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class ContentInfo + : Asn1Encodable + { + private readonly DerObjectIdentifier contentType; + private readonly Asn1Encodable content; + + public static ContentInfo GetInstance( + object obj) + { + if (obj == null || obj is ContentInfo) + return (ContentInfo) obj; + + if (obj is Asn1Sequence) + return new ContentInfo((Asn1Sequence) obj); + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name); + } + + private ContentInfo( + Asn1Sequence seq) + { + if (seq.Count < 1 || seq.Count > 2) + throw new ArgumentException("Bad sequence size: " + seq.Count, "seq"); + + contentType = (DerObjectIdentifier) seq[0]; + + if (seq.Count > 1) + { + Asn1TaggedObject tagged = (Asn1TaggedObject) seq[1]; + if (!tagged.IsExplicit() || tagged.TagNo != 0) + throw new ArgumentException("Bad tag for 'content'", "seq"); + + content = tagged.GetObject(); + } + } + + public ContentInfo( + DerObjectIdentifier contentType, + Asn1Encodable content) + { + this.contentType = contentType; + this.content = content; + } + + public DerObjectIdentifier ContentType + { + get { return contentType; } + } + + public Asn1Encodable Content + { + get { return content; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * ContentInfo ::= Sequence { + * contentType ContentType, + * content + * [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(contentType); + + if (content != null) + { + v.Add(new BerTaggedObject(0, content)); + } + + return new BerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/cms/ContentInfoParser.cs b/Crypto/src/asn1/cms/ContentInfoParser.cs new file mode 100644
index 000000000..541cc0f59 --- /dev/null +++ b/Crypto/src/asn1/cms/ContentInfoParser.cs
@@ -0,0 +1,40 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cms +{ + /** + * 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 DerObjectIdentifier contentType; + private Asn1TaggedObjectParser content; + + public ContentInfoParser( + Asn1SequenceParser seq) + { + contentType = (DerObjectIdentifier)seq.ReadObject(); + content = (Asn1TaggedObjectParser)seq.ReadObject(); + } + + public DerObjectIdentifier ContentType + { + get { return contentType; } + } + + public IAsn1Convertible GetContent( + int tag) + { + if (content == null) + return null; + + return content.GetObjectParser(tag, true); + } + } +} diff --git a/Crypto/src/asn1/cms/EncryptedContentInfo.cs b/Crypto/src/asn1/cms/EncryptedContentInfo.cs new file mode 100644
index 000000000..4fdc47138 --- /dev/null +++ b/Crypto/src/asn1/cms/EncryptedContentInfo.cs
@@ -0,0 +1,94 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class EncryptedContentInfo + : Asn1Encodable + { + private DerObjectIdentifier contentType; + private AlgorithmIdentifier contentEncryptionAlgorithm; + private Asn1OctetString encryptedContent; + + public EncryptedContentInfo( + DerObjectIdentifier contentType, + AlgorithmIdentifier contentEncryptionAlgorithm, + Asn1OctetString encryptedContent) + { + this.contentType = contentType; + this.contentEncryptionAlgorithm = contentEncryptionAlgorithm; + this.encryptedContent = encryptedContent; + } + + public EncryptedContentInfo( + Asn1Sequence seq) + { + contentType = (DerObjectIdentifier) seq[0]; + contentEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(seq[1]); + + if (seq.Count > 2) + { + encryptedContent = Asn1OctetString.GetInstance( + (Asn1TaggedObject) seq[2], false); + } + } + + /** + * return an EncryptedContentInfo object from the given object. + * + * @param obj the object we want converted. + * @exception ArgumentException if the object cannot be converted. + */ + public static EncryptedContentInfo GetInstance( + object obj) + { + if (obj == null || obj is EncryptedContentInfo) + return (EncryptedContentInfo)obj; + + if (obj is Asn1Sequence) + return new EncryptedContentInfo((Asn1Sequence)obj); + + throw new ArgumentException("Invalid EncryptedContentInfo: " + obj.GetType().Name); + } + + public DerObjectIdentifier ContentType + { + get { return contentType; } + } + + public AlgorithmIdentifier ContentEncryptionAlgorithm + { + get { return contentEncryptionAlgorithm; } + } + + public Asn1OctetString EncryptedContent + { + get { return encryptedContent; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * EncryptedContentInfo ::= Sequence { + * contentType ContentType, + * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier, + * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL + * } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector( + contentType, contentEncryptionAlgorithm); + + if (encryptedContent != null) + { + v.Add(new BerTaggedObject(false, 0, encryptedContent)); + } + + return new BerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/cms/EncryptedContentInfoParser.cs b/Crypto/src/asn1/cms/EncryptedContentInfoParser.cs new file mode 100644
index 000000000..af748b1b3 --- /dev/null +++ b/Crypto/src/asn1/cms/EncryptedContentInfoParser.cs
@@ -0,0 +1,46 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Cms +{ + /** + * <pre> + * EncryptedContentInfo ::= SEQUENCE { + * contentType ContentType, + * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier, + * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL + * } + * </pre> + */ + public class EncryptedContentInfoParser + { + private DerObjectIdentifier _contentType; + private AlgorithmIdentifier _contentEncryptionAlgorithm; + private Asn1TaggedObjectParser _encryptedContent; + + public EncryptedContentInfoParser( + Asn1SequenceParser seq) + { + _contentType = (DerObjectIdentifier)seq.ReadObject(); + _contentEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(seq.ReadObject().ToAsn1Object()); + _encryptedContent = (Asn1TaggedObjectParser)seq.ReadObject(); + } + + public DerObjectIdentifier ContentType + { + get { return _contentType; } + } + + public AlgorithmIdentifier ContentEncryptionAlgorithm + { + get { return _contentEncryptionAlgorithm; } + } + + public IAsn1Convertible GetEncryptedContent( + int tag) + { + return _encryptedContent.GetObjectParser(tag, false); + } + } +} diff --git a/Crypto/src/asn1/cms/EncryptedData.cs b/Crypto/src/asn1/cms/EncryptedData.cs new file mode 100644
index 000000000..5b8378282 --- /dev/null +++ b/Crypto/src/asn1/cms/EncryptedData.cs
@@ -0,0 +1,95 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class EncryptedData + : Asn1Encodable + { + private readonly DerInteger version; + private readonly EncryptedContentInfo encryptedContentInfo; + private readonly Asn1Set unprotectedAttrs; + + public static EncryptedData GetInstance( + object obj) + { + if (obj is EncryptedData) + return (EncryptedData) obj; + + if (obj is Asn1Sequence) + return new EncryptedData((Asn1Sequence) obj); + + throw new ArgumentException("Invalid EncryptedData: " + obj.GetType().Name); + } + + public EncryptedData( + EncryptedContentInfo encInfo) + : this(encInfo, null) + { + } + + public EncryptedData( + EncryptedContentInfo encInfo, + Asn1Set unprotectedAttrs) + { + if (encInfo == null) + throw new ArgumentNullException("encInfo"); + + this.version = new DerInteger((unprotectedAttrs == null) ? 0 : 2); + this.encryptedContentInfo = encInfo; + this.unprotectedAttrs = unprotectedAttrs; + } + + private EncryptedData( + Asn1Sequence seq) + { + if (seq == null) + throw new ArgumentNullException("seq"); + if (seq.Count < 2 || seq.Count > 3) + throw new ArgumentException("Bad sequence size: " + seq.Count, "seq"); + + this.version = DerInteger.GetInstance(seq[0]); + this.encryptedContentInfo = EncryptedContentInfo.GetInstance(seq[1]); + + if (seq.Count > 2) + { + this.unprotectedAttrs = Asn1Set.GetInstance(seq[2]); + } + } + + public virtual DerInteger Version + { + get { return version; } + } + + public virtual EncryptedContentInfo EncryptedContentInfo + { + get { return encryptedContentInfo; } + } + + public virtual Asn1Set UnprotectedAttrs + { + get { return unprotectedAttrs; } + } + + /** + * <pre> + * EncryptedData ::= SEQUENCE { + * version CMSVersion, + * encryptedContentInfo EncryptedContentInfo, + * unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL } + * </pre> + * @return a basic ASN.1 object representation. + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(version, encryptedContentInfo); + + if (unprotectedAttrs != null) + { + v.Add(new BerTaggedObject(false, 1, unprotectedAttrs)); + } + + return new BerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/cms/EnvelopedData.cs b/Crypto/src/asn1/cms/EnvelopedData.cs new file mode 100644
index 000000000..47cd4a9f5 --- /dev/null +++ b/Crypto/src/asn1/cms/EnvelopedData.cs
@@ -0,0 +1,167 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class EnvelopedData + : Asn1Encodable + { + private DerInteger version; + private OriginatorInfo originatorInfo; + private Asn1Set recipientInfos; + private EncryptedContentInfo encryptedContentInfo; + private Asn1Set unprotectedAttrs; + + public EnvelopedData( + OriginatorInfo originatorInfo, + Asn1Set recipientInfos, + EncryptedContentInfo encryptedContentInfo, + Asn1Set unprotectedAttrs) + { + if (originatorInfo != null || unprotectedAttrs != null) + { + version = new DerInteger(2); + } + else + { + version = new DerInteger(0); + + foreach (object o in recipientInfos) + { + RecipientInfo ri = RecipientInfo.GetInstance(o); + + if (!ri.Version.Equals(version)) + { + version = new DerInteger(2); + break; + } + } + } + + this.originatorInfo = originatorInfo; + this.recipientInfos = recipientInfos; + this.encryptedContentInfo = encryptedContentInfo; + this.unprotectedAttrs = unprotectedAttrs; + } + + public EnvelopedData( + Asn1Sequence seq) + { + int index = 0; + + version = (DerInteger) seq[index++]; + + object tmp = seq[index++]; + + if (tmp is Asn1TaggedObject) + { + originatorInfo = OriginatorInfo.GetInstance((Asn1TaggedObject) tmp, false); + tmp = seq[index++]; + } + + recipientInfos = Asn1Set.GetInstance(tmp); + encryptedContentInfo = EncryptedContentInfo.GetInstance(seq[index++]); + + if (seq.Count > index) + { + unprotectedAttrs = Asn1Set.GetInstance((Asn1TaggedObject) seq[index], false); + } + } + + /** + * return an EnvelopedData object from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param explicitly true if the object is meant to be explicitly + * tagged false otherwise. + * @exception ArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static EnvelopedData GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + /** + * return an EnvelopedData object from the given object. + * + * @param obj the object we want converted. + * @exception ArgumentException if the object cannot be converted. + */ + public static EnvelopedData GetInstance( + object obj) + { + if (obj == null || obj is EnvelopedData) + { + return (EnvelopedData)obj; + } + + if (obj is Asn1Sequence) + { + return new EnvelopedData((Asn1Sequence)obj); + } + + throw new ArgumentException("Invalid EnvelopedData: " + obj.GetType().Name); + } + + public DerInteger Version + { + get { return version; } + } + + public OriginatorInfo OriginatorInfo + { + get { return originatorInfo; } + } + + public Asn1Set RecipientInfos + { + get { return recipientInfos; } + } + + public EncryptedContentInfo EncryptedContentInfo + { + get { return encryptedContentInfo; } + } + + public Asn1Set UnprotectedAttrs + { + get { 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(version); + + if (originatorInfo != null) + { + v.Add(new DerTaggedObject(false, 0, originatorInfo)); + } + + v.Add(recipientInfos, encryptedContentInfo); + + if (unprotectedAttrs != null) + { + v.Add(new DerTaggedObject(false, 1, unprotectedAttrs)); + } + + return new BerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/cms/EnvelopedDataParser.cs b/Crypto/src/asn1/cms/EnvelopedDataParser.cs new file mode 100644
index 000000000..599353791 --- /dev/null +++ b/Crypto/src/asn1/cms/EnvelopedDataParser.cs
@@ -0,0 +1,107 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cms +{ + /** + * 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 class EnvelopedDataParser + { + private Asn1SequenceParser _seq; + private DerInteger _version; + private IAsn1Convertible _nextObject; + private bool _originatorInfoCalled; + + public EnvelopedDataParser( + Asn1SequenceParser seq) + { + this._seq = seq; + this._version = (DerInteger)seq.ReadObject(); + } + + public DerInteger Version + { + get { return _version; } + } + + public OriginatorInfo GetOriginatorInfo() + { + _originatorInfoCalled = true; + + if (_nextObject == null) + { + _nextObject = _seq.ReadObject(); + } + + if (_nextObject is Asn1TaggedObjectParser && ((Asn1TaggedObjectParser)_nextObject).TagNo == 0) + { + Asn1SequenceParser originatorInfo = (Asn1SequenceParser) + ((Asn1TaggedObjectParser)_nextObject).GetObjectParser(Asn1Tags.Sequence, false); + _nextObject = null; + return OriginatorInfo.GetInstance(originatorInfo.ToAsn1Object()); + } + + return null; + } + + public Asn1SetParser GetRecipientInfos() + { + if (!_originatorInfoCalled) + { + GetOriginatorInfo(); + } + + if (_nextObject == null) + { + _nextObject = _seq.ReadObject(); + } + + Asn1SetParser recipientInfos = (Asn1SetParser)_nextObject; + _nextObject = null; + return recipientInfos; + } + + public EncryptedContentInfoParser GetEncryptedContentInfo() + { + if (_nextObject == null) + { + _nextObject = _seq.ReadObject(); + } + + if (_nextObject != null) + { + Asn1SequenceParser o = (Asn1SequenceParser) _nextObject; + _nextObject = null; + return new EncryptedContentInfoParser(o); + } + + return null; + } + + public Asn1SetParser GetUnprotectedAttrs() + { + if (_nextObject == null) + { + _nextObject = _seq.ReadObject(); + } + + if (_nextObject != null) + { + IAsn1Convertible o = _nextObject; + _nextObject = null; + return (Asn1SetParser)((Asn1TaggedObjectParser)o).GetObjectParser(Asn1Tags.Set, false); + } + + return null; + } + } +} diff --git a/Crypto/src/asn1/cms/Evidence.cs b/Crypto/src/asn1/cms/Evidence.cs new file mode 100644
index 000000000..4745e565b --- /dev/null +++ b/Crypto/src/asn1/cms/Evidence.cs
@@ -0,0 +1,47 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class Evidence + : Asn1Encodable, IAsn1Choice + { + private TimeStampTokenEvidence tstEvidence; + + public Evidence(TimeStampTokenEvidence tstEvidence) + { + this.tstEvidence = tstEvidence; + } + + private Evidence(Asn1TaggedObject tagged) + { + if (tagged.TagNo == 0) + { + this.tstEvidence = TimeStampTokenEvidence.GetInstance(tagged, false); + } + } + + public static Evidence GetInstance(object obj) + { + if (obj is Evidence) + return (Evidence)obj; + + if (obj is Asn1TaggedObject) + return new Evidence(Asn1TaggedObject.GetInstance(obj)); + + throw new ArgumentException("Unknown object in GetInstance: " + obj.GetType().FullName, "obj"); + } + + public virtual TimeStampTokenEvidence TstEvidence + { + get { return tstEvidence; } + } + + public override Asn1Object ToAsn1Object() + { + if (tstEvidence != null) + return new DerTaggedObject(false, 0, tstEvidence); + + return null; + } + } +} diff --git a/Crypto/src/asn1/cms/IssuerAndSerialNumber.cs b/Crypto/src/asn1/cms/IssuerAndSerialNumber.cs new file mode 100644
index 000000000..ac1af961f --- /dev/null +++ b/Crypto/src/asn1/cms/IssuerAndSerialNumber.cs
@@ -0,0 +1,66 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class IssuerAndSerialNumber + : Asn1Encodable + { + private X509Name name; + private DerInteger serialNumber; + + public static IssuerAndSerialNumber GetInstance( + object obj) + { + if (obj is IssuerAndSerialNumber) + return (IssuerAndSerialNumber)obj; + + if (obj is Asn1Sequence) + return new IssuerAndSerialNumber((Asn1Sequence)obj); + + throw new ArgumentException( + "Illegal object in IssuerAndSerialNumber: " + obj.GetType().Name); + } + + public IssuerAndSerialNumber( + Asn1Sequence seq) + { + this.name = X509Name.GetInstance(seq[0]); + this.serialNumber = (DerInteger) seq[1]; + } + + public IssuerAndSerialNumber( + X509Name name, + BigInteger serialNumber) + { + this.name = name; + this.serialNumber = new DerInteger(serialNumber); + } + + public IssuerAndSerialNumber( + X509Name name, + DerInteger serialNumber) + { + this.name = name; + this.serialNumber = serialNumber; + } + + public X509Name Name + { + get { return name; } + } + + public DerInteger SerialNumber + { + get { return serialNumber; } + } + + public override Asn1Object ToAsn1Object() + { + return new DerSequence(name, serialNumber); + } + } +} diff --git a/Crypto/src/asn1/cms/KEKIdentifier.cs b/Crypto/src/asn1/cms/KEKIdentifier.cs new file mode 100644
index 000000000..e5d1d9090 --- /dev/null +++ b/Crypto/src/asn1/cms/KEKIdentifier.cs
@@ -0,0 +1,119 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class KekIdentifier + : Asn1Encodable + { + private Asn1OctetString keyIdentifier; + private DerGeneralizedTime date; + private OtherKeyAttribute other; + + public KekIdentifier( + byte[] keyIdentifier, + DerGeneralizedTime date, + OtherKeyAttribute other) + { + this.keyIdentifier = new DerOctetString(keyIdentifier); + this.date = date; + this.other = other; + } + + public KekIdentifier( + Asn1Sequence seq) + { + keyIdentifier = (Asn1OctetString) seq[0]; + + switch (seq.Count) + { + case 1: + break; + case 2: + if (seq[1] is DerGeneralizedTime) + { + date = (DerGeneralizedTime) seq[1]; + } + else + { + other = OtherKeyAttribute.GetInstance(seq[2]); + } + break; + case 3: + date = (DerGeneralizedTime) seq[1]; + other = OtherKeyAttribute.GetInstance(seq[2]); + break; + default: + throw new ArgumentException("Invalid KekIdentifier"); + } + } + + /** + * return a KekIdentifier object from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param explicitly true if the object is meant to be explicitly + * tagged false otherwise. + * @exception ArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static KekIdentifier GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + /** + * return a KekIdentifier object from the given object. + * + * @param obj the object we want converted. + * @exception ArgumentException if the object cannot be converted. + */ + public static KekIdentifier GetInstance( + object obj) + { + if (obj == null || obj is KekIdentifier) + return (KekIdentifier)obj; + + if (obj is Asn1Sequence) + return new KekIdentifier((Asn1Sequence)obj); + + throw new ArgumentException("Invalid KekIdentifier: " + obj.GetType().Name); + } + + public Asn1OctetString KeyIdentifier + { + get { return keyIdentifier; } + } + + public DerGeneralizedTime Date + { + get { return date; } + } + + public OtherKeyAttribute Other + { + get { return other; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * KekIdentifier ::= Sequence { + * keyIdentifier OCTET STRING, + * date GeneralizedTime OPTIONAL, + * other OtherKeyAttribute OPTIONAL + * } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(keyIdentifier); + v.AddOptional(date, other); + return new DerSequence(v); + } + } +} + diff --git a/Crypto/src/asn1/cms/KEKRecipientInfo.cs b/Crypto/src/asn1/cms/KEKRecipientInfo.cs new file mode 100644
index 000000000..d847b50cc --- /dev/null +++ b/Crypto/src/asn1/cms/KEKRecipientInfo.cs
@@ -0,0 +1,106 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class KekRecipientInfo + : Asn1Encodable + { + private DerInteger version; + private KekIdentifier kekID; + private AlgorithmIdentifier keyEncryptionAlgorithm; + private Asn1OctetString encryptedKey; + + public KekRecipientInfo( + KekIdentifier kekID, + AlgorithmIdentifier keyEncryptionAlgorithm, + Asn1OctetString encryptedKey) + { + this.version = new DerInteger(4); + this.kekID = kekID; + this.keyEncryptionAlgorithm = keyEncryptionAlgorithm; + this.encryptedKey = encryptedKey; + } + + public KekRecipientInfo( + Asn1Sequence seq) + { + version = (DerInteger) seq[0]; + kekID = KekIdentifier.GetInstance(seq[1]); + keyEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(seq[2]); + encryptedKey = (Asn1OctetString) seq[3]; + } + + /** + * return a KekRecipientInfo object from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param explicitly true if the object is meant to be explicitly + * tagged false otherwise. + * @exception ArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static KekRecipientInfo GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + /** + * return a KekRecipientInfo object from the given object. + * + * @param obj the object we want converted. + * @exception ArgumentException if the object cannot be converted. + */ + public static KekRecipientInfo GetInstance( + object obj) + { + if (obj == null || obj is KekRecipientInfo) + return (KekRecipientInfo)obj; + + if(obj is Asn1Sequence) + return new KekRecipientInfo((Asn1Sequence)obj); + + throw new ArgumentException("Invalid KekRecipientInfo: " + obj.GetType().Name); + } + + public DerInteger Version + { + get { return version; } + } + + public KekIdentifier KekID + { + get { return kekID; } + } + + public AlgorithmIdentifier KeyEncryptionAlgorithm + { + get { return keyEncryptionAlgorithm; } + } + + public Asn1OctetString EncryptedKey + { + get { 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 override Asn1Object ToAsn1Object() + { + return new DerSequence(version, kekID, keyEncryptionAlgorithm, encryptedKey); + } + } +} diff --git a/Crypto/src/asn1/cms/KeyAgreeRecipientIdentifier.cs b/Crypto/src/asn1/cms/KeyAgreeRecipientIdentifier.cs new file mode 100644
index 000000000..fa6fdb0f3 --- /dev/null +++ b/Crypto/src/asn1/cms/KeyAgreeRecipientIdentifier.cs
@@ -0,0 +1,92 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class KeyAgreeRecipientIdentifier + : Asn1Encodable, IAsn1Choice + { + /** + * return an KeyAgreeRecipientIdentifier object from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param isExplicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception ArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static KeyAgreeRecipientIdentifier GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit)); + } + + /** + * return an KeyAgreeRecipientIdentifier object from the given object. + * + * @param obj the object we want converted. + * @exception ArgumentException if the object cannot be converted. + */ + public static KeyAgreeRecipientIdentifier GetInstance( + object obj) + { + if (obj == null || obj is KeyAgreeRecipientIdentifier) + return (KeyAgreeRecipientIdentifier)obj; + + if (obj is Asn1Sequence) + return new KeyAgreeRecipientIdentifier(IssuerAndSerialNumber.GetInstance(obj)); + + if (obj is Asn1TaggedObject && ((Asn1TaggedObject)obj).TagNo == 0) + { + return new KeyAgreeRecipientIdentifier(RecipientKeyIdentifier.GetInstance( + (Asn1TaggedObject)obj, false)); + } + + throw new ArgumentException("Invalid KeyAgreeRecipientIdentifier: " + obj.GetType().FullName, "obj"); + } + + private readonly IssuerAndSerialNumber issuerSerial; + private readonly RecipientKeyIdentifier rKeyID; + + public KeyAgreeRecipientIdentifier( + IssuerAndSerialNumber issuerSerial) + { + this.issuerSerial = issuerSerial; + } + + public KeyAgreeRecipientIdentifier( + RecipientKeyIdentifier rKeyID) + { + this.rKeyID = rKeyID; + } + + public IssuerAndSerialNumber IssuerAndSerialNumber + { + get { return issuerSerial; } + } + + public RecipientKeyIdentifier RKeyID + { + get { return rKeyID; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * KeyAgreeRecipientIdentifier ::= CHOICE { + * issuerAndSerialNumber IssuerAndSerialNumber, + * rKeyId [0] IMPLICIT RecipientKeyIdentifier + * } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + if (issuerSerial != null) + { + return issuerSerial.ToAsn1Object(); + } + + return new DerTaggedObject(false, 0, rKeyID); + } + } +} diff --git a/Crypto/src/asn1/cms/KeyAgreeRecipientInfo.cs b/Crypto/src/asn1/cms/KeyAgreeRecipientInfo.cs new file mode 100644
index 000000000..aafb008d4 --- /dev/null +++ b/Crypto/src/asn1/cms/KeyAgreeRecipientInfo.cs
@@ -0,0 +1,141 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class KeyAgreeRecipientInfo + : Asn1Encodable + { + private DerInteger 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 DerInteger(3); + this.originator = originator; + this.ukm = ukm; + this.keyEncryptionAlgorithm = keyEncryptionAlgorithm; + this.recipientEncryptedKeys = recipientEncryptedKeys; + } + + public KeyAgreeRecipientInfo( + Asn1Sequence seq) + { + int index = 0; + + version = (DerInteger) seq[index++]; + originator = OriginatorIdentifierOrKey.GetInstance( + (Asn1TaggedObject) seq[index++], true); + + if (seq[index] is Asn1TaggedObject) + { + ukm = Asn1OctetString.GetInstance( + (Asn1TaggedObject) seq[index++], true); + } + + keyEncryptionAlgorithm = AlgorithmIdentifier.GetInstance( + seq[index++]); + + recipientEncryptedKeys = (Asn1Sequence) seq[index++]; + } + + /** + * return a KeyAgreeRecipientInfo object from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param explicitly true if the object is meant to be explicitly + * tagged false otherwise. + * @exception ArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static KeyAgreeRecipientInfo GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + /** + * return a KeyAgreeRecipientInfo object from the given object. + * + * @param obj the object we want converted. + * @exception ArgumentException if the object cannot be converted. + */ + public static KeyAgreeRecipientInfo GetInstance( + object obj) + { + if (obj == null || obj is KeyAgreeRecipientInfo) + return (KeyAgreeRecipientInfo)obj; + + if (obj is Asn1Sequence) + return new KeyAgreeRecipientInfo((Asn1Sequence)obj); + + throw new ArgumentException( + "Illegal object in KeyAgreeRecipientInfo: " + obj.GetType().Name); + + } + + public DerInteger Version + { + get { return version; } + } + + public OriginatorIdentifierOrKey Originator + { + get { return originator; } + } + + public Asn1OctetString UserKeyingMaterial + { + get { return ukm; } + } + + public AlgorithmIdentifier KeyEncryptionAlgorithm + { + get { return keyEncryptionAlgorithm; } + } + + public Asn1Sequence RecipientEncryptedKeys + { + get { 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector( + version, new DerTaggedObject(true, 0, originator)); + + if (ukm != null) + { + v.Add(new DerTaggedObject(true, 1, ukm)); + } + + v.Add(keyEncryptionAlgorithm, recipientEncryptedKeys); + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/cms/KeyTransRecipientInfo.cs b/Crypto/src/asn1/cms/KeyTransRecipientInfo.cs new file mode 100644
index 000000000..aae18c59d --- /dev/null +++ b/Crypto/src/asn1/cms/KeyTransRecipientInfo.cs
@@ -0,0 +1,99 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class KeyTransRecipientInfo + : Asn1Encodable + { + private DerInteger version; + private RecipientIdentifier rid; + private AlgorithmIdentifier keyEncryptionAlgorithm; + private Asn1OctetString encryptedKey; + + public KeyTransRecipientInfo( + RecipientIdentifier rid, + AlgorithmIdentifier keyEncryptionAlgorithm, + Asn1OctetString encryptedKey) + { + if (rid.ToAsn1Object() is Asn1TaggedObject) + { + this.version = new DerInteger(2); + } + else + { + this.version = new DerInteger(0); + } + + this.rid = rid; + this.keyEncryptionAlgorithm = keyEncryptionAlgorithm; + this.encryptedKey = encryptedKey; + } + + public KeyTransRecipientInfo( + Asn1Sequence seq) + { + this.version = (DerInteger) seq[0]; + this.rid = RecipientIdentifier.GetInstance(seq[1]); + this.keyEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(seq[2]); + this.encryptedKey = (Asn1OctetString) seq[3]; + } + + /** + * return a KeyTransRecipientInfo object from the given object. + * + * @param obj the object we want converted. + * @exception ArgumentException if the object cannot be converted. + */ + public static KeyTransRecipientInfo GetInstance( + object obj) + { + if (obj == null || obj is KeyTransRecipientInfo) + return (KeyTransRecipientInfo) obj; + + if(obj is Asn1Sequence) + return new KeyTransRecipientInfo((Asn1Sequence) obj); + + throw new ArgumentException( + "Illegal object in KeyTransRecipientInfo: " + obj.GetType().Name); + } + + public DerInteger Version + { + get { return version; } + } + + public RecipientIdentifier RecipientIdentifier + { + get { return rid; } + } + + public AlgorithmIdentifier KeyEncryptionAlgorithm + { + get { return keyEncryptionAlgorithm; } + } + + public Asn1OctetString EncryptedKey + { + get { 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 override Asn1Object ToAsn1Object() + { + return new DerSequence(version, rid, keyEncryptionAlgorithm, encryptedKey); + } + } +} diff --git a/Crypto/src/asn1/cms/MetaData.cs b/Crypto/src/asn1/cms/MetaData.cs new file mode 100644
index 000000000..ad2b5c426 --- /dev/null +++ b/Crypto/src/asn1/cms/MetaData.cs
@@ -0,0 +1,94 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class MetaData + : Asn1Encodable + { + private DerBoolean hashProtected; + private DerUtf8String fileName; + private DerIA5String mediaType; + private Attributes otherMetaData; + + public MetaData( + DerBoolean 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 = DerBoolean.GetInstance(seq[0]); + + int index = 1; + + if (index < seq.Count && seq[index] is DerUtf8String) + { + this.fileName = DerUtf8String.GetInstance(seq[index++]); + } + if (index < seq.Count && seq[index] is DerIA5String) + { + this.mediaType = DerIA5String.GetInstance(seq[index++]); + } + if (index < seq.Count) + { + this.otherMetaData = Attributes.GetInstance(seq[index++]); + } + } + + public static MetaData GetInstance(object obj) + { + if (obj is MetaData) + return (MetaData)obj; + + 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(hashProtected); + v.AddOptional(fileName, mediaType, otherMetaData); + return new DerSequence(v); + } + + public virtual bool IsHashProtected + { + get { return hashProtected.IsTrue; } + } + + public virtual DerUtf8String FileName + { + get { return fileName; } + } + + public virtual DerIA5String MediaType + { + get { return mediaType; } + } + + public virtual Attributes OtherMetaData + { + get { return otherMetaData; } + } + } +} diff --git a/Crypto/src/asn1/cms/OriginatorIdentifierOrKey.cs b/Crypto/src/asn1/cms/OriginatorIdentifierOrKey.cs new file mode 100644
index 000000000..d33a11725 --- /dev/null +++ b/Crypto/src/asn1/cms/OriginatorIdentifierOrKey.cs
@@ -0,0 +1,168 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class OriginatorIdentifierOrKey + : Asn1Encodable, IAsn1Choice + { + private Asn1Encodable id; + + public OriginatorIdentifierOrKey( + IssuerAndSerialNumber id) + { + this.id = id; + } + + [Obsolete("Use version taking a 'SubjectKeyIdentifier'")] + public OriginatorIdentifierOrKey( + Asn1OctetString id) + : this(new SubjectKeyIdentifier(id)) + { + } + + public OriginatorIdentifierOrKey( + SubjectKeyIdentifier id) + { + this.id = new DerTaggedObject(false, 0, id); + } + + public OriginatorIdentifierOrKey( + OriginatorPublicKey id) + { + this.id = new DerTaggedObject(false, 1, id); + } + + [Obsolete("Use more specific version")] + public OriginatorIdentifierOrKey( + Asn1Object id) + { + this.id = id; + } + + private OriginatorIdentifierOrKey( + Asn1TaggedObject id) + { + // TODO Add validation + this.id = id; + } + + /** + * return an OriginatorIdentifierOrKey object from a tagged object. + * + * @param o the tagged object holding the object we want. + * @param explicitly true if the object is meant to be explicitly + * tagged false otherwise. + * @exception ArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static OriginatorIdentifierOrKey GetInstance( + Asn1TaggedObject o, + bool explicitly) + { + if (!explicitly) + { + throw new ArgumentException( + "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 ArgumentException if the object cannot be converted. + */ + public static OriginatorIdentifierOrKey GetInstance( + object o) + { + if (o == null || o is OriginatorIdentifierOrKey) + return (OriginatorIdentifierOrKey)o; + + if (o is IssuerAndSerialNumber) + return new OriginatorIdentifierOrKey((IssuerAndSerialNumber)o); + + if (o is SubjectKeyIdentifier) + return new OriginatorIdentifierOrKey((SubjectKeyIdentifier)o); + + if (o is OriginatorPublicKey) + return new OriginatorIdentifierOrKey((OriginatorPublicKey)o); + + if (o is Asn1TaggedObject) + return new OriginatorIdentifierOrKey((Asn1TaggedObject)o); + + throw new ArgumentException("Invalid OriginatorIdentifierOrKey: " + o.GetType().Name); + } + + public Asn1Encodable ID + { + get { return id; } + } + + public IssuerAndSerialNumber IssuerAndSerialNumber + { + get + { + if (id is IssuerAndSerialNumber) + { + return (IssuerAndSerialNumber)id; + } + + return null; + } + } + + public SubjectKeyIdentifier SubjectKeyIdentifier + { + get + { + if (id is Asn1TaggedObject && ((Asn1TaggedObject)id).TagNo == 0) + { + return SubjectKeyIdentifier.GetInstance((Asn1TaggedObject)id, false); + } + + return null; + } + } + + [Obsolete("Use 'OriginatorPublicKey' property")] + public OriginatorPublicKey OriginatorKey + { + get { return OriginatorPublicKey; } + } + + public OriginatorPublicKey OriginatorPublicKey + { + get + { + if (id is Asn1TaggedObject && ((Asn1TaggedObject)id).TagNo == 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 override Asn1Object ToAsn1Object() + { + return id.ToAsn1Object(); + } + } +} diff --git a/Crypto/src/asn1/cms/OriginatorInfo.cs b/Crypto/src/asn1/cms/OriginatorInfo.cs new file mode 100644
index 000000000..b4549bc36 --- /dev/null +++ b/Crypto/src/asn1/cms/OriginatorInfo.cs
@@ -0,0 +1,121 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class OriginatorInfo + : Asn1Encodable + { + private Asn1Set certs; + private Asn1Set crls; + + public OriginatorInfo( + Asn1Set certs, + Asn1Set crls) + { + this.certs = certs; + this.crls = crls; + } + + public OriginatorInfo( + Asn1Sequence seq) + { + switch (seq.Count) + { + case 0: // empty + break; + case 1: + Asn1TaggedObject o = (Asn1TaggedObject) seq[0]; + switch (o.TagNo) + { + case 0 : + certs = Asn1Set.GetInstance(o, false); + break; + case 1 : + crls = Asn1Set.GetInstance(o, false); + break; + default: + throw new ArgumentException("Bad tag in OriginatorInfo: " + o.TagNo); + } + break; + case 2: + certs = Asn1Set.GetInstance((Asn1TaggedObject) seq[0], false); + crls = Asn1Set.GetInstance((Asn1TaggedObject) seq[1], false); + break; + default: + throw new ArgumentException("OriginatorInfo too big"); + } + } + + /** + * return an OriginatorInfo object from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param explicitly true if the object is meant to be explicitly + * tagged false otherwise. + * @exception ArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static OriginatorInfo GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + /** + * return an OriginatorInfo object from the given object. + * + * @param obj the object we want converted. + * @exception ArgumentException if the object cannot be converted. + */ + public static OriginatorInfo GetInstance( + object obj) + { + if (obj == null || obj is OriginatorInfo) + return (OriginatorInfo)obj; + + if (obj is Asn1Sequence) + return new OriginatorInfo((Asn1Sequence)obj); + + throw new ArgumentException("Invalid OriginatorInfo: " + obj.GetType().Name); + } + + public Asn1Set Certificates + { + get { return certs; } + } + + public Asn1Set Crls + { + get { return crls; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * OriginatorInfo ::= Sequence { + * certs [0] IMPLICIT CertificateSet OPTIONAL, + * crls [1] IMPLICIT CertificateRevocationLists OPTIONAL + * } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + 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/Crypto/src/asn1/cms/OriginatorPublicKey.cs b/Crypto/src/asn1/cms/OriginatorPublicKey.cs new file mode 100644
index 000000000..aabaf4386 --- /dev/null +++ b/Crypto/src/asn1/cms/OriginatorPublicKey.cs
@@ -0,0 +1,87 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class OriginatorPublicKey + : Asn1Encodable + { + 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[0]); + publicKey = (DerBitString) seq[1]; + } + + /** + * return an OriginatorPublicKey object from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param explicitly true if the object is meant to be explicitly + * tagged false otherwise. + * @exception ArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static OriginatorPublicKey GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + /** + * return an OriginatorPublicKey object from the given object. + * + * @param obj the object we want converted. + * @exception ArgumentException if the object cannot be converted. + */ + public static OriginatorPublicKey GetInstance( + object obj) + { + if (obj == null || obj is OriginatorPublicKey) + return (OriginatorPublicKey)obj; + + if (obj is Asn1Sequence) + return new OriginatorPublicKey((Asn1Sequence) obj); + + throw new ArgumentException("Invalid OriginatorPublicKey: " + obj.GetType().Name); + } + + public AlgorithmIdentifier Algorithm + { + get { return algorithm; } + } + + public DerBitString PublicKey + { + get { return publicKey; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * OriginatorPublicKey ::= Sequence { + * algorithm AlgorithmIdentifier, + * publicKey BIT STRING + * } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + return new DerSequence(algorithm, publicKey); + } + } +} diff --git a/Crypto/src/asn1/cms/OtherKeyAttribute.cs b/Crypto/src/asn1/cms/OtherKeyAttribute.cs new file mode 100644
index 000000000..271059175 --- /dev/null +++ b/Crypto/src/asn1/cms/OtherKeyAttribute.cs
@@ -0,0 +1,70 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class OtherKeyAttribute + : Asn1Encodable + { + private DerObjectIdentifier keyAttrId; + private Asn1Encodable keyAttr; + + /** + * return an OtherKeyAttribute object from the given object. + * + * @param o the object we want converted. + * @exception ArgumentException if the object cannot be converted. + */ + public static OtherKeyAttribute GetInstance( + object obj) + { + if (obj == null || obj is OtherKeyAttribute) + return (OtherKeyAttribute) obj; + + if (obj is Asn1Sequence) + return new OtherKeyAttribute((Asn1Sequence) obj); + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public OtherKeyAttribute( + Asn1Sequence seq) + { + keyAttrId = (DerObjectIdentifier) seq[0]; + keyAttr = seq[1]; + } + + public OtherKeyAttribute( + DerObjectIdentifier keyAttrId, + Asn1Encodable keyAttr) + { + this.keyAttrId = keyAttrId; + this.keyAttr = keyAttr; + } + + public DerObjectIdentifier KeyAttrId + { + get { return keyAttrId; } + } + + public Asn1Encodable KeyAttr + { + get { return keyAttr; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * OtherKeyAttribute ::= Sequence { + * keyAttrId OBJECT IDENTIFIER, + * keyAttr ANY DEFINED BY keyAttrId OPTIONAL + * } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + return new DerSequence(keyAttrId, keyAttr); + } + } +} diff --git a/Crypto/src/asn1/cms/OtherRecipientInfo.cs b/Crypto/src/asn1/cms/OtherRecipientInfo.cs new file mode 100644
index 000000000..8b06b1946 --- /dev/null +++ b/Crypto/src/asn1/cms/OtherRecipientInfo.cs
@@ -0,0 +1,85 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class OtherRecipientInfo + : Asn1Encodable + { + private DerObjectIdentifier oriType; + private Asn1Encodable oriValue; + + public OtherRecipientInfo( + DerObjectIdentifier oriType, + Asn1Encodable oriValue) + { + this.oriType = oriType; + this.oriValue = oriValue; + } + + public OtherRecipientInfo( + Asn1Sequence seq) + { + oriType = DerObjectIdentifier.GetInstance(seq[0]); + oriValue = seq[1]; + } + + /** + * return a OtherRecipientInfo object from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param explicitly true if the object is meant to be explicitly + * tagged false otherwise. + * @exception ArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static OtherRecipientInfo GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + /** + * return a OtherRecipientInfo object from the given object. + * + * @param obj the object we want converted. + * @exception ArgumentException if the object cannot be converted. + */ + public static OtherRecipientInfo GetInstance( + object obj) + { + if (obj == null || obj is OtherRecipientInfo) + return (OtherRecipientInfo)obj; + + if (obj is Asn1Sequence) + return new OtherRecipientInfo((Asn1Sequence)obj); + + throw new ArgumentException("Invalid OtherRecipientInfo: " + obj.GetType().Name); + } + + public DerObjectIdentifier OriType + { + get { return oriType; } + } + + public Asn1Encodable OriValue + { + get { return oriValue; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * OtherRecipientInfo ::= Sequence { + * oriType OBJECT IDENTIFIER, + * oriValue ANY DEFINED BY oriType } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + return new DerSequence(oriType, oriValue); + } + } +} diff --git a/Crypto/src/asn1/cms/PasswordRecipientInfo.cs b/Crypto/src/asn1/cms/PasswordRecipientInfo.cs new file mode 100644
index 000000000..800b57951 --- /dev/null +++ b/Crypto/src/asn1/cms/PasswordRecipientInfo.cs
@@ -0,0 +1,133 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class PasswordRecipientInfo + : Asn1Encodable + { + private readonly DerInteger version; + private readonly AlgorithmIdentifier keyDerivationAlgorithm; + private readonly AlgorithmIdentifier keyEncryptionAlgorithm; + private readonly Asn1OctetString encryptedKey; + + public PasswordRecipientInfo( + AlgorithmIdentifier keyEncryptionAlgorithm, + Asn1OctetString encryptedKey) + { + this.version = new DerInteger(0); + this.keyEncryptionAlgorithm = keyEncryptionAlgorithm; + this.encryptedKey = encryptedKey; + } + + public PasswordRecipientInfo( + AlgorithmIdentifier keyDerivationAlgorithm, + AlgorithmIdentifier keyEncryptionAlgorithm, + Asn1OctetString encryptedKey) + { + this.version = new DerInteger(0); + this.keyDerivationAlgorithm = keyDerivationAlgorithm; + this.keyEncryptionAlgorithm = keyEncryptionAlgorithm; + this.encryptedKey = encryptedKey; + } + + public PasswordRecipientInfo( + Asn1Sequence seq) + { + version = (DerInteger) seq[0]; + + if (seq[1] is Asn1TaggedObject) + { + keyDerivationAlgorithm = AlgorithmIdentifier.GetInstance((Asn1TaggedObject) seq[1], false); + keyEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(seq[2]); + encryptedKey = (Asn1OctetString) seq[3]; + } + else + { + keyEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(seq[1]); + encryptedKey = (Asn1OctetString) seq[2]; + } + } + + /** + * return a PasswordRecipientInfo object from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param explicitly true if the object is meant to be explicitly + * tagged false otherwise. + * @exception ArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static PasswordRecipientInfo GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + /** + * return a PasswordRecipientInfo object from the given object. + * + * @param obj the object we want converted. + * @exception ArgumentException if the object cannot be converted. + */ + public static PasswordRecipientInfo GetInstance( + object obj) + { + if (obj == null || obj is PasswordRecipientInfo) + return (PasswordRecipientInfo) obj; + + if (obj is Asn1Sequence) + return new PasswordRecipientInfo((Asn1Sequence) obj); + + throw new ArgumentException("Invalid PasswordRecipientInfo: " + obj.GetType().Name); + } + + public DerInteger Version + { + get { return version; } + } + + public AlgorithmIdentifier KeyDerivationAlgorithm + { + get { return keyDerivationAlgorithm; } + } + + public AlgorithmIdentifier KeyEncryptionAlgorithm + { + get { return keyEncryptionAlgorithm; } + } + + public Asn1OctetString EncryptedKey + { + get { 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(version); + + if (keyDerivationAlgorithm != null) + { + v.Add(new DerTaggedObject(false, 0, keyDerivationAlgorithm)); + } + + v.Add(keyEncryptionAlgorithm, encryptedKey); + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/cms/RecipientEncryptedKey.cs b/Crypto/src/asn1/cms/RecipientEncryptedKey.cs new file mode 100644
index 000000000..5ba25a742 --- /dev/null +++ b/Crypto/src/asn1/cms/RecipientEncryptedKey.cs
@@ -0,0 +1,88 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class RecipientEncryptedKey + : Asn1Encodable + { + private readonly KeyAgreeRecipientIdentifier identifier; + private readonly Asn1OctetString encryptedKey; + + private RecipientEncryptedKey( + Asn1Sequence seq) + { + identifier = KeyAgreeRecipientIdentifier.GetInstance(seq[0]); + encryptedKey = (Asn1OctetString) seq[1]; + } + + /** + * return an RecipientEncryptedKey object from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param isExplicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception ArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static RecipientEncryptedKey GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit)); + } + + /** + * return a RecipientEncryptedKey object from the given object. + * + * @param obj the object we want converted. + * @exception ArgumentException if the object cannot be converted. + */ + public static RecipientEncryptedKey GetInstance( + object obj) + { + if (obj == null || obj is RecipientEncryptedKey) + { + return (RecipientEncryptedKey) obj; + } + + if (obj is Asn1Sequence) + { + return new RecipientEncryptedKey((Asn1Sequence) obj); + } + + throw new ArgumentException("Invalid RecipientEncryptedKey: " + obj.GetType().FullName, "obj"); + } + + public RecipientEncryptedKey( + KeyAgreeRecipientIdentifier id, + Asn1OctetString encryptedKey) + { + this.identifier = id; + this.encryptedKey = encryptedKey; + } + + public KeyAgreeRecipientIdentifier Identifier + { + get { return identifier; } + } + + public Asn1OctetString EncryptedKey + { + get { return encryptedKey; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * RecipientEncryptedKey ::= SEQUENCE { + * rid KeyAgreeRecipientIdentifier, + * encryptedKey EncryptedKey + * } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + return new DerSequence(identifier, encryptedKey); + } + } +} diff --git a/Crypto/src/asn1/cms/RecipientIdentifier.cs b/Crypto/src/asn1/cms/RecipientIdentifier.cs new file mode 100644
index 000000000..4982bc16a --- /dev/null +++ b/Crypto/src/asn1/cms/RecipientIdentifier.cs
@@ -0,0 +1,89 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class RecipientIdentifier + : Asn1Encodable, IAsn1Choice + { + private Asn1Encodable id; + + public RecipientIdentifier( + IssuerAndSerialNumber id) + { + this.id = id; + } + + public RecipientIdentifier( + Asn1OctetString id) + { + this.id = new DerTaggedObject(false, 0, id); + } + + public RecipientIdentifier( + Asn1Object id) + { + this.id = id; + } + + /** + * return a RecipientIdentifier object from the given object. + * + * @param o the object we want converted. + * @exception ArgumentException if the object cannot be converted. + */ + public static RecipientIdentifier GetInstance( + object o) + { + if (o == null || o is RecipientIdentifier) + return (RecipientIdentifier)o; + + if (o is IssuerAndSerialNumber) + return new RecipientIdentifier((IssuerAndSerialNumber) o); + + if (o is Asn1OctetString) + return new RecipientIdentifier((Asn1OctetString) o); + + if (o is Asn1Object) + return new RecipientIdentifier((Asn1Object) o); + + throw new ArgumentException( + "Illegal object in RecipientIdentifier: " + o.GetType().Name); + } + + public bool IsTagged + { + get { return (id is Asn1TaggedObject); } + } + + public Asn1Encodable ID + { + get + { + if (id is 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 override Asn1Object ToAsn1Object() + { + return id.ToAsn1Object(); + } + } +} diff --git a/Crypto/src/asn1/cms/RecipientInfo.cs b/Crypto/src/asn1/cms/RecipientInfo.cs new file mode 100644
index 000000000..daaf5a5e4 --- /dev/null +++ b/Crypto/src/asn1/cms/RecipientInfo.cs
@@ -0,0 +1,145 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class RecipientInfo + : Asn1Encodable, IAsn1Choice + { + internal 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( + Asn1Object info) + { + this.info = info; + } + + public static RecipientInfo GetInstance( + object o) + { + if (o == null || o is RecipientInfo) + return (RecipientInfo) o; + + if (o is Asn1Sequence) + return new RecipientInfo((Asn1Sequence) o); + + if (o is Asn1TaggedObject) + return new RecipientInfo((Asn1TaggedObject) o); + + throw new ArgumentException("unknown object in factory: " + o.GetType().Name); + } + + public DerInteger Version + { + get + { + if (info is Asn1TaggedObject) + { + Asn1TaggedObject o = (Asn1TaggedObject) info; + + switch (o.TagNo) + { + case 1: + return KeyAgreeRecipientInfo.GetInstance(o, false).Version; + case 2: + return GetKekInfo(o).Version; + case 3: + return PasswordRecipientInfo.GetInstance(o, false).Version; + case 4: + return new DerInteger(0); // no syntax version for OtherRecipientInfo + default: + throw new InvalidOperationException("unknown tag"); + } + } + + return KeyTransRecipientInfo.GetInstance(info).Version; + } + } + + public bool IsTagged + { + get { return info is Asn1TaggedObject; } + } + + public Asn1Encodable Info + { + get + { + if (info is Asn1TaggedObject) + { + Asn1TaggedObject o = (Asn1TaggedObject) info; + + switch (o.TagNo) + { + 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 InvalidOperationException("unknown tag"); + } + } + + return KeyTransRecipientInfo.GetInstance(info); + } + } + + private KekRecipientInfo GetKekInfo( + Asn1TaggedObject o) + { + // For compatibility with erroneous version, we don't always pass 'false' here + return KekRecipientInfo.GetInstance(o, o.IsExplicit()); + } + + /** + * 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 override Asn1Object ToAsn1Object() + { + return info.ToAsn1Object(); + } + } +} diff --git a/Crypto/src/asn1/cms/RecipientKeyIdentifier.cs b/Crypto/src/asn1/cms/RecipientKeyIdentifier.cs new file mode 100644
index 000000000..f3e45644b --- /dev/null +++ b/Crypto/src/asn1/cms/RecipientKeyIdentifier.cs
@@ -0,0 +1,137 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class RecipientKeyIdentifier + : Asn1Encodable + { + 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) + : this(subjectKeyIdentifier, null, null) + { + } + + public RecipientKeyIdentifier( + byte[] subjectKeyIdentifier, + DerGeneralizedTime date, + OtherKeyAttribute other) + { + this.subjectKeyIdentifier = new DerOctetString(subjectKeyIdentifier); + this.date = date; + this.other = other; + } + + public RecipientKeyIdentifier( + Asn1Sequence seq) + { + subjectKeyIdentifier = Asn1OctetString.GetInstance( + seq[0]); + + switch(seq.Count) + { + case 1: + break; + case 2: + if (seq[1] is DerGeneralizedTime) + { + date = (DerGeneralizedTime) seq[1]; + } + else + { + other = OtherKeyAttribute.GetInstance(seq[2]); + } + break; + case 3: + date = (DerGeneralizedTime) seq[1]; + other = OtherKeyAttribute.GetInstance(seq[2]); + break; + default: + throw new ArgumentException("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 ArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static RecipientKeyIdentifier GetInstance( + Asn1TaggedObject ato, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(ato, explicitly)); + } + + /** + * return a RecipientKeyIdentifier object from the given object. + * + * @param _obj the object we want converted. + * @exception ArgumentException if the object cannot be converted. + */ + public static RecipientKeyIdentifier GetInstance( + object obj) + { + if (obj == null || obj is RecipientKeyIdentifier) + return (RecipientKeyIdentifier) obj; + + if (obj is Asn1Sequence) + return new RecipientKeyIdentifier((Asn1Sequence) obj); + + throw new ArgumentException("Invalid RecipientKeyIdentifier: " + obj.GetType().Name); + } + + public Asn1OctetString SubjectKeyIdentifier + { + get { return subjectKeyIdentifier; } + } + + public DerGeneralizedTime Date + { + get { return date; } + } + + public OtherKeyAttribute OtherKeyAttribute + { + get { 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(subjectKeyIdentifier); + v.AddOptional(date, other); + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/cms/SignedData.cs b/Crypto/src/asn1/cms/SignedData.cs new file mode 100644
index 000000000..40ed09f32 --- /dev/null +++ b/Crypto/src/asn1/cms/SignedData.cs
@@ -0,0 +1,282 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Cms +{ + /** + * a signed data object. + */ + public class SignedData + : Asn1Encodable + { + private readonly DerInteger version; + private readonly Asn1Set digestAlgorithms; + private readonly ContentInfo contentInfo; + private readonly Asn1Set certificates; + private readonly Asn1Set crls; + private readonly Asn1Set signerInfos; + private readonly bool certsBer; + private readonly bool crlsBer; + + public static SignedData GetInstance( + object obj) + { + if (obj is SignedData) + return (SignedData) obj; + + if (obj is Asn1Sequence) + return new SignedData((Asn1Sequence) obj); + + throw new ArgumentException("Unknown object in factory: " + obj.GetType().FullName, "obj"); + } + + public SignedData( + Asn1Set digestAlgorithms, + ContentInfo contentInfo, + Asn1Set certificates, + Asn1Set crls, + Asn1Set signerInfos) + { + this.version = CalculateVersion(contentInfo.ContentType, certificates, crls, signerInfos); + this.digestAlgorithms = digestAlgorithms; + this.contentInfo = contentInfo; + this.certificates = certificates; + this.crls = crls; + this.signerInfos = signerInfos; + this.crlsBer = crls is BerSet; + this.certsBer = certificates is 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 DerInteger CalculateVersion( + DerObjectIdentifier contentOid, + Asn1Set certs, + Asn1Set crls, + Asn1Set signerInfs) + { + bool otherCert = false; + bool otherCrl = false; + bool attrCertV1Found = false; + bool attrCertV2Found = false; + + if (certs != null) + { + foreach (object obj in certs) + { + if (obj is Asn1TaggedObject) + { + Asn1TaggedObject tagged = (Asn1TaggedObject)obj; + + if (tagged.TagNo == 1) + { + attrCertV1Found = true; + } + else if (tagged.TagNo == 2) + { + attrCertV2Found = true; + } + else if (tagged.TagNo == 3) + { + otherCert = true; + break; + } + } + } + } + + if (otherCert) + { + return new DerInteger(5); + } + + if (crls != null) + { + foreach (object obj in crls) + { + if (obj is Asn1TaggedObject) + { + otherCrl = true; + break; + } + } + } + + if (otherCrl) + { + return new DerInteger(5); + } + + if (attrCertV2Found) + { + return new DerInteger(4); + } + + if (attrCertV1Found || !CmsObjectIdentifiers.Data.Equals(contentOid) || CheckForVersion3(signerInfs)) + { + return new DerInteger(3); + } + + return new DerInteger(1); + } + + private bool CheckForVersion3( + Asn1Set signerInfs) + { + foreach (object obj in signerInfs) + { + SignerInfo s = SignerInfo.GetInstance(obj); + + if (s.Version.Value.IntValue == 3) + { + return true; + } + } + + return false; + } + + private SignedData( + Asn1Sequence seq) + { + IEnumerator e = seq.GetEnumerator(); + + e.MoveNext(); + version = (DerInteger)e.Current; + + e.MoveNext(); + digestAlgorithms = ((Asn1Set)e.Current); + + e.MoveNext(); + contentInfo = ContentInfo.GetInstance(e.Current); + + while (e.MoveNext()) + { + Asn1Object o = (Asn1Object)e.Current; + + // + // 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 is Asn1TaggedObject) + { + Asn1TaggedObject tagged = (Asn1TaggedObject)o; + + switch (tagged.TagNo) + { + case 0: + certsBer = tagged is BerTaggedObject; + certificates = Asn1Set.GetInstance(tagged, false); + break; + case 1: + crlsBer = tagged is BerTaggedObject; + crls = Asn1Set.GetInstance(tagged, false); + break; + default: + throw new ArgumentException("unknown tag value " + tagged.TagNo); + } + } + else + { + signerInfos = (Asn1Set) o; + } + } + } + + public DerInteger Version + { + get { return version; } + } + + public Asn1Set DigestAlgorithms + { + get { return digestAlgorithms; } + } + + public ContentInfo EncapContentInfo + { + get { return contentInfo; } + } + + public Asn1Set Certificates + { + get { return certificates; } + } + + public Asn1Set CRLs + { + get { return crls; } + } + + public Asn1Set SignerInfos + { + get { 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector( + version, digestAlgorithms, 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/Crypto/src/asn1/cms/SignedDataParser.cs b/Crypto/src/asn1/cms/SignedDataParser.cs new file mode 100644
index 000000000..341309263 --- /dev/null +++ b/Crypto/src/asn1/cms/SignedDataParser.cs
@@ -0,0 +1,112 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Asn1.Cms +{ + /** + * <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 DerInteger _version; + private object _nextObject; + private bool _certsCalled; + private bool _crlsCalled; + + public static SignedDataParser GetInstance( + object o) + { + if (o is Asn1Sequence) + return new SignedDataParser(((Asn1Sequence)o).Parser); + + if (o is Asn1SequenceParser) + return new SignedDataParser((Asn1SequenceParser)o); + + throw new IOException("unknown object encountered: " + o.GetType().Name); + } + + public SignedDataParser( + Asn1SequenceParser seq) + { + this._seq = seq; + this._version = (DerInteger)seq.ReadObject(); + } + + public DerInteger Version + { + get { return _version; } + } + + public Asn1SetParser GetDigestAlgorithms() + { + return (Asn1SetParser)_seq.ReadObject(); + } + + public ContentInfoParser GetEncapContentInfo() + { + return new ContentInfoParser((Asn1SequenceParser)_seq.ReadObject()); + } + + public Asn1SetParser GetCertificates() + { + _certsCalled = true; + _nextObject = _seq.ReadObject(); + + if (_nextObject is Asn1TaggedObjectParser && ((Asn1TaggedObjectParser)_nextObject).TagNo == 0) + { + Asn1SetParser certs = (Asn1SetParser)((Asn1TaggedObjectParser)_nextObject).GetObjectParser(Asn1Tags.Set, false); + _nextObject = null; + + return certs; + } + + return null; + } + + public Asn1SetParser GetCrls() + { + if (!_certsCalled) + throw new IOException("GetCerts() has not been called."); + + _crlsCalled = true; + + if (_nextObject == null) + { + _nextObject = _seq.ReadObject(); + } + + if (_nextObject is Asn1TaggedObjectParser && ((Asn1TaggedObjectParser)_nextObject).TagNo == 1) + { + Asn1SetParser crls = (Asn1SetParser)((Asn1TaggedObjectParser)_nextObject).GetObjectParser(Asn1Tags.Set, false); + _nextObject = null; + + return crls; + } + + return null; + } + + public Asn1SetParser GetSignerInfos() + { + 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/Crypto/src/asn1/cms/SignerIdentifier.cs b/Crypto/src/asn1/cms/SignerIdentifier.cs new file mode 100644
index 000000000..5742cee75 --- /dev/null +++ b/Crypto/src/asn1/cms/SignerIdentifier.cs
@@ -0,0 +1,89 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class SignerIdentifier + : Asn1Encodable, IAsn1Choice + { + private Asn1Encodable id; + + public SignerIdentifier( + IssuerAndSerialNumber id) + { + this.id = id; + } + + public SignerIdentifier( + Asn1OctetString id) + { + this.id = new DerTaggedObject(false, 0, id); + } + + public SignerIdentifier( + Asn1Object id) + { + this.id = id; + } + + /** + * return a SignerIdentifier object from the given object. + * + * @param o the object we want converted. + * @exception ArgumentException if the object cannot be converted. + */ + public static SignerIdentifier GetInstance( + object o) + { + if (o == null || o is SignerIdentifier) + return (SignerIdentifier) o; + + if (o is IssuerAndSerialNumber) + return new SignerIdentifier((IssuerAndSerialNumber) o); + + if (o is Asn1OctetString) + return new SignerIdentifier((Asn1OctetString) o); + + if (o is Asn1Object) + return new SignerIdentifier((Asn1Object) o); + + throw new ArgumentException( + "Illegal object in SignerIdentifier: " + o.GetType().Name); + } + + public bool IsTagged + { + get { return (id is Asn1TaggedObject); } + } + + public Asn1Encodable ID + { + get + { + if (id is 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 override Asn1Object ToAsn1Object() + { + return id.ToAsn1Object(); + } + } +} diff --git a/Crypto/src/asn1/cms/SignerInfo.cs b/Crypto/src/asn1/cms/SignerInfo.cs new file mode 100644
index 000000000..ef71ddfc4 --- /dev/null +++ b/Crypto/src/asn1/cms/SignerInfo.cs
@@ -0,0 +1,175 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class SignerInfo + : Asn1Encodable + { + private DerInteger version; + private SignerIdentifier sid; + private AlgorithmIdentifier digAlgorithm; + private Asn1Set authenticatedAttributes; + private AlgorithmIdentifier digEncryptionAlgorithm; + private Asn1OctetString encryptedDigest; + private Asn1Set unauthenticatedAttributes; + + public static SignerInfo GetInstance( + object obj) + { + if (obj == null || obj is SignerInfo) + return (SignerInfo) obj; + + if (obj is Asn1Sequence) + return new SignerInfo((Asn1Sequence) obj); + + throw new ArgumentException("Unknown object in factory: " + obj.GetType().FullName, "obj"); + } + + public SignerInfo( + SignerIdentifier sid, + AlgorithmIdentifier digAlgorithm, + Asn1Set authenticatedAttributes, + AlgorithmIdentifier digEncryptionAlgorithm, + Asn1OctetString encryptedDigest, + Asn1Set unauthenticatedAttributes) + { + if (sid.IsTagged) + { + this.version = new DerInteger(3); + } + else + { + this.version = new DerInteger(1); + } + + this.sid = sid; + this.digAlgorithm = digAlgorithm; + this.authenticatedAttributes = authenticatedAttributes; + this.digEncryptionAlgorithm = digEncryptionAlgorithm; + this.encryptedDigest = encryptedDigest; + this.unauthenticatedAttributes = unauthenticatedAttributes; + } + + public SignerInfo( + Asn1Sequence seq) + { + IEnumerator e = seq.GetEnumerator(); + + e.MoveNext(); + version = (DerInteger) e.Current; + + e.MoveNext(); + sid = SignerIdentifier.GetInstance(e.Current); + + e.MoveNext(); + digAlgorithm = AlgorithmIdentifier.GetInstance(e.Current); + + e.MoveNext(); + object obj = e.Current; + + if (obj is Asn1TaggedObject) + { + authenticatedAttributes = Asn1Set.GetInstance((Asn1TaggedObject) obj, false); + + e.MoveNext(); + digEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(e.Current); + } + else + { + authenticatedAttributes = null; + digEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(obj); + } + + e.MoveNext(); + encryptedDigest = DerOctetString.GetInstance(e.Current); + + if (e.MoveNext()) + { + unauthenticatedAttributes = Asn1Set.GetInstance((Asn1TaggedObject) e.Current, false); + } + else + { + unauthenticatedAttributes = null; + } + } + + public DerInteger Version + { + get { return version; } + } + + public SignerIdentifier SignerID + { + get { return sid; } + } + + public Asn1Set AuthenticatedAttributes + { + get { return authenticatedAttributes; } + } + + public AlgorithmIdentifier DigestAlgorithm + { + get { return digAlgorithm; } + } + + public Asn1OctetString EncryptedDigest + { + get { return encryptedDigest; } + } + + public AlgorithmIdentifier DigestEncryptionAlgorithm + { + get { return digEncryptionAlgorithm; } + } + + public Asn1Set UnauthenticatedAttributes + { + get { 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector( + version, sid, digAlgorithm); + + if (authenticatedAttributes != null) + { + v.Add(new DerTaggedObject(false, 0, authenticatedAttributes)); + } + + v.Add(digEncryptionAlgorithm, encryptedDigest); + + if (unauthenticatedAttributes != null) + { + v.Add(new DerTaggedObject(false, 1, unauthenticatedAttributes)); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/cms/Time.cs b/Crypto/src/asn1/cms/Time.cs new file mode 100644
index 000000000..d113bfa2e --- /dev/null +++ b/Crypto/src/asn1/cms/Time.cs
@@ -0,0 +1,118 @@ +using System; +using System.Globalization; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class Time + : Asn1Encodable, IAsn1Choice + { + private readonly Asn1Object time; + + public static Time GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(obj.GetObject()); + } + + public Time( + Asn1Object time) + { + if (!(time is DerUtcTime) + && !(time is DerGeneralizedTime)) + { + throw new ArgumentException("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( + DateTime date) + { + string d = date.ToString("yyyyMMddHHmmss") + "Z"; + + int year = int.Parse(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 is Time) + return (Time)obj; + + if (obj is DerUtcTime) + return new Time((DerUtcTime)obj); + + if (obj is DerGeneralizedTime) + return new Time((DerGeneralizedTime)obj); + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public string TimeString + { + get + { + if (time is DerUtcTime) + { + return ((DerUtcTime)time).AdjustedTimeString; + } + else + { + return ((DerGeneralizedTime)time).GetTime(); + } + } + } + + public DateTime Date + { + get + { + try + { + if (time is DerUtcTime) + { + return ((DerUtcTime)time).ToAdjustedDateTime(); + } + + return ((DerGeneralizedTime)time).ToDateTime(); + } + catch (FormatException e) + { + // this should never happen + throw new InvalidOperationException("invalid date string: " + e.Message); + } + } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * Time ::= CHOICE { + * utcTime UTCTime, + * generalTime GeneralizedTime } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + return time; + } + } +} diff --git a/Crypto/src/asn1/cms/TimeStampAndCRL.cs b/Crypto/src/asn1/cms/TimeStampAndCRL.cs new file mode 100644
index 000000000..4cb5f2a52 --- /dev/null +++ b/Crypto/src/asn1/cms/TimeStampAndCRL.cs
@@ -0,0 +1,62 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class TimeStampAndCrl + : Asn1Encodable + { + private ContentInfo timeStamp; + private X509.CertificateList crl; + + public TimeStampAndCrl(ContentInfo timeStamp) + { + this.timeStamp = timeStamp; + } + + private TimeStampAndCrl(Asn1Sequence seq) + { + this.timeStamp = ContentInfo.GetInstance(seq[0]); + if (seq.Count == 2) + { + this.crl = X509.CertificateList.GetInstance(seq[1]); + } + } + + public static TimeStampAndCrl GetInstance(object obj) + { + if (obj is TimeStampAndCrl) + return (TimeStampAndCrl)obj; + + if (obj != null) + return new TimeStampAndCrl(Asn1Sequence.GetInstance(obj)); + + return null; + } + + public virtual ContentInfo TimeStampToken + { + get { return this.timeStamp; } + } + + public virtual X509.CertificateList Crl + { + get { return this.crl; } + } + + /** + * <pre> + * TimeStampAndCRL ::= SEQUENCE { + * timeStamp TimeStampToken, -- according to RFC 3161 + * crl CertificateList OPTIONAL -- according to RFC 5280 + * } + * </pre> + * @return + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(timeStamp); + v.AddOptional(crl); + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/cms/TimeStampTokenEvidence.cs b/Crypto/src/asn1/cms/TimeStampTokenEvidence.cs new file mode 100644
index 000000000..8625d058e --- /dev/null +++ b/Crypto/src/asn1/cms/TimeStampTokenEvidence.cs
@@ -0,0 +1,65 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class TimeStampTokenEvidence + : Asn1Encodable + { + private TimeStampAndCrl[] timeStampAndCrls; + + public TimeStampTokenEvidence(TimeStampAndCrl[] timeStampAndCrls) + { + this.timeStampAndCrls = timeStampAndCrls; + } + + public TimeStampTokenEvidence(TimeStampAndCrl timeStampAndCrl) + { + this.timeStampAndCrls = new TimeStampAndCrl[]{ timeStampAndCrl }; + } + + private TimeStampTokenEvidence(Asn1Sequence seq) + { + this.timeStampAndCrls = new TimeStampAndCrl[seq.Count]; + + int count = 0; + + foreach (Asn1Encodable ae in seq) + { + this.timeStampAndCrls[count++] = TimeStampAndCrl.GetInstance(ae.ToAsn1Object()); + } + } + + public static TimeStampTokenEvidence GetInstance(Asn1TaggedObject tagged, bool isExplicit) + { + return GetInstance(Asn1Sequence.GetInstance(tagged, isExplicit)); + } + + public static TimeStampTokenEvidence GetInstance(object obj) + { + if (obj is TimeStampTokenEvidence) + return (TimeStampTokenEvidence)obj; + + if (obj != null) + return new TimeStampTokenEvidence(Asn1Sequence.GetInstance(obj)); + + return null; + } + + public virtual TimeStampAndCrl[] ToTimeStampAndCrlArray() + { + return (TimeStampAndCrl[])timeStampAndCrls.Clone(); + } + + /** + * <pre> + * TimeStampTokenEvidence ::= + * SEQUENCE SIZE(1..MAX) OF TimeStampAndCrl + * </pre> + * @return + */ + public override Asn1Object ToAsn1Object() + { + return new DerSequence(timeStampAndCrls); + } + } +} diff --git a/Crypto/src/asn1/cms/TimeStampedData.cs b/Crypto/src/asn1/cms/TimeStampedData.cs new file mode 100644
index 000000000..15448a923 --- /dev/null +++ b/Crypto/src/asn1/cms/TimeStampedData.cs
@@ -0,0 +1,95 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class TimeStampedData + : Asn1Encodable + { + private DerInteger 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 DerInteger(1); + this.dataUri = dataUri; + this.metaData = metaData; + this.content = content; + this.temporalEvidence = temporalEvidence; + } + + private TimeStampedData(Asn1Sequence seq) + { + this.version = DerInteger.GetInstance(seq[0]); + + int index = 1; + if (seq[index] is DerIA5String) + { + this.dataUri = DerIA5String.GetInstance(seq[index++]); + } + if (seq[index] is MetaData || seq[index] is Asn1Sequence) + { + this.metaData = MetaData.GetInstance(seq[index++]); + } + if (seq[index] is Asn1OctetString) + { + this.content = Asn1OctetString.GetInstance(seq[index++]); + } + this.temporalEvidence = Evidence.GetInstance(seq[index]); + } + + public static TimeStampedData GetInstance(object obj) + { + if (obj is TimeStampedData) + return (TimeStampedData)obj; + + if (obj != null) + return new TimeStampedData(Asn1Sequence.GetInstance(obj)); + + return null; + } + + public virtual DerIA5String DataUri + { + get { return dataUri; } + } + + public MetaData MetaData + { + get { return metaData; } + } + + public Asn1OctetString Content + { + get { return content; } + } + + public Evidence TemporalEvidence + { + get { return temporalEvidence; } + } + + /** + * <pre> + * TimeStampedData ::= SEQUENCE { + * version INTEGER { v1(1) }, + * dataUri IA5String OPTIONAL, + * metaData MetaData OPTIONAL, + * content OCTET STRING OPTIONAL, + * temporalEvidence Evidence + * } + * </pre> + * @return + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(version); + v.AddOptional(dataUri, metaData, content); + v.Add(temporalEvidence); + return new BerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/cms/TimeStampedDataParser.cs b/Crypto/src/asn1/cms/TimeStampedDataParser.cs new file mode 100644
index 000000000..90307bff9 --- /dev/null +++ b/Crypto/src/asn1/cms/TimeStampedDataParser.cs
@@ -0,0 +1,76 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class TimeStampedDataParser + { + private DerInteger version; + private DerIA5String dataUri; + private MetaData metaData; + private Asn1OctetStringParser content; + private Evidence temporalEvidence; + private Asn1SequenceParser parser; + + private TimeStampedDataParser(Asn1SequenceParser parser) + { + this.parser = parser; + this.version = DerInteger.GetInstance(parser.ReadObject()); + + Asn1Object obj = parser.ReadObject().ToAsn1Object(); + + if (obj is DerIA5String) + { + this.dataUri = DerIA5String.GetInstance(obj); + obj = parser.ReadObject().ToAsn1Object(); + } + + if (//obj is MetaData || + obj is Asn1SequenceParser) + { + this.metaData = MetaData.GetInstance(obj.ToAsn1Object()); + obj = parser.ReadObject().ToAsn1Object(); + } + + if (obj is Asn1OctetStringParser) + { + this.content = (Asn1OctetStringParser)obj; + } + } + + public static TimeStampedDataParser GetInstance(object obj) + { + if (obj is Asn1Sequence) + return new TimeStampedDataParser(((Asn1Sequence)obj).Parser); + + if (obj is Asn1SequenceParser) + return new TimeStampedDataParser((Asn1SequenceParser)obj); + + return null; + } + + public virtual DerIA5String DataUri + { + get { return dataUri; } + } + + public virtual MetaData MetaData + { + get { return metaData; } + } + + public virtual Asn1OctetStringParser Content + { + get { return content; } + } + + public virtual Evidence GetTemporalEvidence() + { + if (temporalEvidence == null) + { + temporalEvidence = Evidence.GetInstance(parser.ReadObject().ToAsn1Object()); + } + + return temporalEvidence; + } + } +} diff --git a/Crypto/src/asn1/cms/ecc/MQVuserKeyingMaterial.cs b/Crypto/src/asn1/cms/ecc/MQVuserKeyingMaterial.cs new file mode 100644
index 000000000..53c5c706b --- /dev/null +++ b/Crypto/src/asn1/cms/ecc/MQVuserKeyingMaterial.cs
@@ -0,0 +1,103 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cms.Ecc +{ + public class MQVuserKeyingMaterial + : Asn1Encodable + { + 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[0]); + + if (seq.Count > 1) + { + this.addedukm = Asn1OctetString.GetInstance( + (Asn1TaggedObject)seq[1], true); + } + } + + /** + * return an AuthEnvelopedData object from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param isExplicit true if the object is meant to be explicitly + * tagged false otherwise. + * @throws ArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static MQVuserKeyingMaterial GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit)); + } + + /** + * return an AuthEnvelopedData object from the given object. + * + * @param obj the object we want converted. + * @throws ArgumentException if the object cannot be converted. + */ + public static MQVuserKeyingMaterial GetInstance( + object obj) + { + if (obj == null || obj is MQVuserKeyingMaterial) + { + return (MQVuserKeyingMaterial)obj; + } + + if (obj is Asn1Sequence) + { + return new MQVuserKeyingMaterial((Asn1Sequence)obj); + } + + throw new ArgumentException("Invalid MQVuserKeyingMaterial: " + obj.GetType().Name); + } + + public OriginatorPublicKey EphemeralPublicKey + { + get { return ephemeralPublicKey; } + } + + public Asn1OctetString AddedUkm + { + get { return addedukm; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * MQVuserKeyingMaterial ::= SEQUENCE { + * ephemeralPublicKey OriginatorPublicKey, + * addedukm [0] EXPLICIT UserKeyingMaterial OPTIONAL } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(ephemeralPublicKey); + + if (addedukm != null) + { + v.Add(new DerTaggedObject(true, 0, addedukm)); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/crmf/AttributeTypeAndValue.cs b/Crypto/src/asn1/crmf/AttributeTypeAndValue.cs new file mode 100644
index 000000000..823668992 --- /dev/null +++ b/Crypto/src/asn1/crmf/AttributeTypeAndValue.cs
@@ -0,0 +1,66 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + public class AttributeTypeAndValue + : Asn1Encodable + { + private readonly DerObjectIdentifier type; + private readonly Asn1Encodable value; + + private AttributeTypeAndValue(Asn1Sequence seq) + { + type = (DerObjectIdentifier)seq[0]; + value = (Asn1Encodable)seq[1]; + } + + public static AttributeTypeAndValue GetInstance(object obj) + { + if (obj is AttributeTypeAndValue) + return (AttributeTypeAndValue)obj; + + if (obj is Asn1Sequence) + return new AttributeTypeAndValue((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public AttributeTypeAndValue( + String oid, + Asn1Encodable value) + : this(new DerObjectIdentifier(oid), value) + { + } + + public AttributeTypeAndValue( + DerObjectIdentifier type, + Asn1Encodable value) + { + this.type = type; + this.value = value; + } + + public virtual DerObjectIdentifier Type + { + get { return type; } + } + + public virtual Asn1Encodable Value + { + get { return value; } + } + + /** + * <pre> + * AttributeTypeAndValue ::= SEQUENCE { + * type OBJECT IDENTIFIER, + * value ANY DEFINED BY type } + * </pre> + * @return a basic ASN.1 object representation. + */ + public override Asn1Object ToAsn1Object() + { + return new DerSequence(type, value); + } + } +} diff --git a/Crypto/src/asn1/crmf/CertId.cs b/Crypto/src/asn1/crmf/CertId.cs new file mode 100644
index 000000000..10c2cc8b4 --- /dev/null +++ b/Crypto/src/asn1/crmf/CertId.cs
@@ -0,0 +1,58 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + public class CertId + : Asn1Encodable + { + private readonly GeneralName issuer; + private readonly DerInteger serialNumber; + + private CertId(Asn1Sequence seq) + { + issuer = GeneralName.GetInstance(seq[0]); + serialNumber = DerInteger.GetInstance(seq[1]); + } + + public static CertId GetInstance(object obj) + { + if (obj is CertId) + return (CertId)obj; + + if (obj is Asn1Sequence) + return new CertId((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public static CertId GetInstance(Asn1TaggedObject obj, bool isExplicit) + { + return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit)); + } + + public virtual GeneralName Issuer + { + get { return issuer; } + } + + public virtual DerInteger SerialNumber + { + get { return serialNumber; } + } + + /** + * <pre> + * CertId ::= SEQUENCE { + * issuer GeneralName, + * serialNumber INTEGER } + * </pre> + * @return a basic ASN.1 object representation. + */ + public override Asn1Object ToAsn1Object() + { + return new DerSequence(issuer, serialNumber); + } + } +} diff --git a/Crypto/src/asn1/crmf/CertReqMessages.cs b/Crypto/src/asn1/crmf/CertReqMessages.cs new file mode 100644
index 000000000..9247281e8 --- /dev/null +++ b/Crypto/src/asn1/crmf/CertReqMessages.cs
@@ -0,0 +1,52 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + public class CertReqMessages + : Asn1Encodable + { + private readonly Asn1Sequence content; + + private CertReqMessages(Asn1Sequence seq) + { + content = seq; + } + + public static CertReqMessages GetInstance(object obj) + { + if (obj is CertReqMessages) + return (CertReqMessages)obj; + + if (obj is Asn1Sequence) + return new CertReqMessages((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public CertReqMessages(params CertReqMsg[] msgs) + { + content = new DerSequence(msgs); + } + + public virtual CertReqMsg[] ToCertReqMsgArray() + { + CertReqMsg[] result = new CertReqMsg[content.Count]; + for (int i = 0; i != result.Length; ++i) + { + result[i] = CertReqMsg.GetInstance(content[i]); + } + return result; + } + + /** + * <pre> + * CertReqMessages ::= SEQUENCE SIZE (1..MAX) OF CertReqMsg + * </pre> + * @return a basic ASN.1 object representation. + */ + public override Asn1Object ToAsn1Object() + { + return content; + } + } +} diff --git a/Crypto/src/asn1/crmf/CertReqMsg.cs b/Crypto/src/asn1/crmf/CertReqMsg.cs new file mode 100644
index 000000000..2ca319a57 --- /dev/null +++ b/Crypto/src/asn1/crmf/CertReqMsg.cs
@@ -0,0 +1,106 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + public class CertReqMsg + : Asn1Encodable + { + private readonly CertRequest certReq; + private readonly ProofOfPossession popo; + private readonly Asn1Sequence regInfo; + + private CertReqMsg(Asn1Sequence seq) + { + certReq = CertRequest.GetInstance(seq[0]); + + for (int pos = 1; pos < seq.Count; ++pos) + { + object o = seq[pos]; + + if (o is Asn1TaggedObject || o is ProofOfPossession) + { + popo = ProofOfPossession.GetInstance(o); + } + else + { + regInfo = Asn1Sequence.GetInstance(o); + } + } + } + + public static CertReqMsg GetInstance(object obj) + { + if (obj is CertReqMsg) + return (CertReqMsg)obj; + + if (obj != null) + return new CertReqMsg(Asn1Sequence.GetInstance(obj)); + + return null; + } + + /** + * Creates a new CertReqMsg. + * @param certReq CertRequest + * @param popo may be null + * @param regInfo may be null + */ + public CertReqMsg( + CertRequest certReq, + ProofOfPossession popo, + AttributeTypeAndValue[] regInfo) + { + if (certReq == null) + throw new ArgumentNullException("certReq"); + + this.certReq = certReq; + this.popo = popo; + + if (regInfo != null) + { + this.regInfo = new DerSequence(regInfo); + } + } + + public virtual CertRequest CertReq + { + get { return certReq; } + } + + public virtual ProofOfPossession Popo + { + get { return popo; } + } + + public virtual AttributeTypeAndValue[] GetRegInfo() + { + if (regInfo == null) + return null; + + AttributeTypeAndValue[] results = new AttributeTypeAndValue[regInfo.Count]; + for (int i = 0; i != results.Length; ++i) + { + results[i] = AttributeTypeAndValue.GetInstance(regInfo[i]); + } + return results; + } + + /** + * <pre> + * CertReqMsg ::= SEQUENCE { + * certReq CertRequest, + * pop ProofOfPossession OPTIONAL, + * -- content depends upon key type + * regInfo SEQUENCE SIZE(1..MAX) OF AttributeTypeAndValue OPTIONAL } + * </pre> + * @return a basic ASN.1 object representation. + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(certReq); + v.AddOptional(popo); + v.AddOptional(regInfo); + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/crmf/CertRequest.cs b/Crypto/src/asn1/crmf/CertRequest.cs new file mode 100644
index 000000000..625a9b519 --- /dev/null +++ b/Crypto/src/asn1/crmf/CertRequest.cs
@@ -0,0 +1,82 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + public class CertRequest + : Asn1Encodable + { + private readonly DerInteger certReqId; + private readonly CertTemplate certTemplate; + private readonly Controls controls; + + private CertRequest(Asn1Sequence seq) + { + certReqId = DerInteger.GetInstance(seq[0]); + certTemplate = CertTemplate.GetInstance(seq[1]); + if (seq.Count > 2) + { + controls = Controls.GetInstance(seq[2]); + } + } + + public static CertRequest GetInstance(object obj) + { + if (obj is CertRequest) + return (CertRequest)obj; + + if (obj != null) + return new CertRequest(Asn1Sequence.GetInstance(obj)); + + return null; + } + + public CertRequest( + int certReqId, + CertTemplate certTemplate, + Controls controls) + : this(new DerInteger(certReqId), certTemplate, controls) + { + } + + public CertRequest( + DerInteger certReqId, + CertTemplate certTemplate, + Controls controls) + { + this.certReqId = certReqId; + this.certTemplate = certTemplate; + this.controls = controls; + } + + public virtual DerInteger CertReqID + { + get { return certReqId; } + } + + public virtual CertTemplate CertTemplate + { + get { return certTemplate; } + } + + public virtual Controls Controls + { + get { 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(certReqId, certTemplate); + v.AddOptional(controls); + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/crmf/CertTemplate.cs b/Crypto/src/asn1/crmf/CertTemplate.cs new file mode 100644
index 000000000..3de9f1d5a --- /dev/null +++ b/Crypto/src/asn1/crmf/CertTemplate.cs
@@ -0,0 +1,149 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + public class CertTemplate + : Asn1Encodable + { + private readonly Asn1Sequence seq; + + private readonly DerInteger version; + private readonly DerInteger serialNumber; + private readonly AlgorithmIdentifier signingAlg; + private readonly X509Name issuer; + private readonly OptionalValidity validity; + private readonly X509Name subject; + private readonly SubjectPublicKeyInfo publicKey; + private readonly DerBitString issuerUID; + private readonly DerBitString subjectUID; + private readonly X509Extensions extensions; + + private CertTemplate(Asn1Sequence seq) + { + this.seq = seq; + + foreach (Asn1TaggedObject tObj in seq) + { + switch (tObj.TagNo) + { + case 0: + version = DerInteger.GetInstance(tObj, false); + break; + case 1: + serialNumber = DerInteger.GetInstance(tObj, false); + break; + case 2: + signingAlg = AlgorithmIdentifier.GetInstance(tObj, false); + break; + case 3: + issuer = X509Name.GetInstance(tObj, true); // CHOICE + break; + case 4: + validity = OptionalValidity.GetInstance(Asn1Sequence.GetInstance(tObj, false)); + break; + case 5: + subject = X509Name.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 = X509Extensions.GetInstance(tObj, false); + break; + default: + throw new ArgumentException("unknown tag: " + tObj.TagNo, "seq"); + } + } + } + + public static CertTemplate GetInstance(object obj) + { + if (obj is CertTemplate) + return (CertTemplate)obj; + + if (obj != null) + return new CertTemplate(Asn1Sequence.GetInstance(obj)); + + return null; + } + + public virtual int Version + { + get { return version.Value.IntValue; } + } + + public virtual DerInteger SerialNumber + { + get { return serialNumber; } + } + + public virtual AlgorithmIdentifier SigningAlg + { + get { return signingAlg; } + } + + public virtual X509Name Issuer + { + get { return issuer; } + } + + public virtual OptionalValidity Validity + { + get { return validity; } + } + + public virtual X509Name Subject + { + get { return subject; } + } + + public virtual SubjectPublicKeyInfo PublicKey + { + get { return publicKey; } + } + + public virtual DerBitString IssuerUID + { + get { return issuerUID; } + } + + public virtual DerBitString SubjectUID + { + get { return subjectUID; } + } + + public virtual X509Extensions Extensions + { + get { 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 override Asn1Object ToAsn1Object() + { + return seq; + } + } +} diff --git a/Crypto/src/asn1/crmf/CertTemplateBuilder.cs b/Crypto/src/asn1/crmf/CertTemplateBuilder.cs new file mode 100644
index 000000000..51c73c4e1 --- /dev/null +++ b/Crypto/src/asn1/crmf/CertTemplateBuilder.cs
@@ -0,0 +1,125 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + public class CertTemplateBuilder + { + private DerInteger version; + private DerInteger serialNumber; + private AlgorithmIdentifier signingAlg; + private X509Name issuer; + private OptionalValidity validity; + private X509Name subject; + private SubjectPublicKeyInfo publicKey; + private DerBitString issuerUID; + private DerBitString subjectUID; + private X509Extensions extensions; + + /** Sets the X.509 version. Note: for X509v3, use 2 here. */ + public virtual CertTemplateBuilder SetVersion(int ver) + { + version = new DerInteger(ver); + return this; + } + + public virtual CertTemplateBuilder SetSerialNumber(DerInteger ser) + { + serialNumber = ser; + return this; + } + + public virtual CertTemplateBuilder SetSigningAlg(AlgorithmIdentifier aid) + { + signingAlg = aid; + return this; + } + + public virtual CertTemplateBuilder SetIssuer(X509Name name) + { + issuer = name; + return this; + } + + public virtual CertTemplateBuilder SetValidity(OptionalValidity v) + { + validity = v; + return this; + } + + public virtual CertTemplateBuilder SetSubject(X509Name name) + { + subject = name; + return this; + } + + public virtual CertTemplateBuilder SetPublicKey(SubjectPublicKeyInfo spki) + { + publicKey = spki; + return this; + } + + /** Sets the issuer unique ID (deprecated in X.509v3) */ + public virtual CertTemplateBuilder SetIssuerUID(DerBitString uid) + { + issuerUID = uid; + return this; + } + + /** Sets the subject unique ID (deprecated in X.509v3) */ + public virtual CertTemplateBuilder SetSubjectUID(DerBitString uid) + { + subjectUID = uid; + return this; + } + + public virtual CertTemplateBuilder SetExtensions(X509Extensions 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 virtual 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, bool isExplicit, Asn1Encodable obj) + { + if (obj != null) + { + v.Add(new DerTaggedObject(isExplicit, tagNo, obj)); + } + } + } +} diff --git a/Crypto/src/asn1/crmf/Controls.cs b/Crypto/src/asn1/crmf/Controls.cs new file mode 100644
index 000000000..cc52ea4bb --- /dev/null +++ b/Crypto/src/asn1/crmf/Controls.cs
@@ -0,0 +1,52 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + public class Controls + : Asn1Encodable + { + private readonly Asn1Sequence content; + + private Controls(Asn1Sequence seq) + { + content = seq; + } + + public static Controls GetInstance(object obj) + { + if (obj is Controls) + return (Controls)obj; + + if (obj is Asn1Sequence) + return new Controls((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public Controls(params AttributeTypeAndValue[] atvs) + { + content = new DerSequence(atvs); + } + + public virtual AttributeTypeAndValue[] ToAttributeTypeAndValueArray() + { + AttributeTypeAndValue[] result = new AttributeTypeAndValue[content.Count]; + for (int i = 0; i != result.Length; ++i) + { + result[i] = AttributeTypeAndValue.GetInstance(content[i]); + } + return result; + } + + /** + * <pre> + * Controls ::= SEQUENCE SIZE(1..MAX) OF AttributeTypeAndValue + * </pre> + * @return a basic ASN.1 object representation. + */ + public override Asn1Object ToAsn1Object() + { + return content; + } + } +} diff --git a/Crypto/src/asn1/crmf/CrmfObjectIdentifiers.cs b/Crypto/src/asn1/crmf/CrmfObjectIdentifiers.cs new file mode 100644
index 000000000..eaa1f7ba4 --- /dev/null +++ b/Crypto/src/asn1/crmf/CrmfObjectIdentifiers.cs
@@ -0,0 +1,23 @@ +using System; + +using Org.BouncyCastle.Asn1.Pkcs; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + public abstract class CrmfObjectIdentifiers + { + public static readonly DerObjectIdentifier id_pkix = new DerObjectIdentifier("1.3.6.1.5.5.7"); + + // arc for Internet X.509 PKI protocols and their components + + public static readonly DerObjectIdentifier id_pkip = id_pkix.Branch("5"); + + public static readonly DerObjectIdentifier id_regCtrl = id_pkip.Branch("1"); + public static readonly DerObjectIdentifier id_regCtrl_regToken = id_regCtrl.Branch("1"); + public static readonly DerObjectIdentifier id_regCtrl_authenticator = id_regCtrl.Branch("2"); + public static readonly DerObjectIdentifier id_regCtrl_pkiPublicationInfo = id_regCtrl.Branch("3"); + public static readonly DerObjectIdentifier id_regCtrl_pkiArchiveOptions = id_regCtrl.Branch("4"); + + public static readonly DerObjectIdentifier id_ct_encKeyWithID = new DerObjectIdentifier(PkcsObjectIdentifiers.IdCT + ".21"); + } +} diff --git a/Crypto/src/asn1/crmf/EncKeyWithID.cs b/Crypto/src/asn1/crmf/EncKeyWithID.cs new file mode 100644
index 000000000..6de56fa0b --- /dev/null +++ b/Crypto/src/asn1/crmf/EncKeyWithID.cs
@@ -0,0 +1,103 @@ +using System; + +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + public class EncKeyWithID + : Asn1Encodable + { + private readonly PrivateKeyInfo privKeyInfo; + private readonly Asn1Encodable identifier; + + public static EncKeyWithID GetInstance(object obj) + { + if (obj is EncKeyWithID) + return (EncKeyWithID)obj; + + if (obj != null) + return new EncKeyWithID(Asn1Sequence.GetInstance(obj)); + + return null; + } + + private EncKeyWithID(Asn1Sequence seq) + { + this.privKeyInfo = PrivateKeyInfo.GetInstance(seq[0]); + + if (seq.Count > 1) + { + if (!(seq[1] is DerUtf8String)) + { + this.identifier = GeneralName.GetInstance(seq[1]); + } + else + { + this.identifier = (Asn1Encodable)seq[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 virtual PrivateKeyInfo PrivateKey + { + get { return privKeyInfo; } + } + + public virtual bool HasIdentifier + { + get { return identifier != null; } + } + + public virtual bool IsIdentifierUtf8String + { + get { return identifier is DerUtf8String; } + } + + public virtual Asn1Encodable Identifier + { + get { return identifier; } + } + + /** + * <pre> + * EncKeyWithID ::= SEQUENCE { + * privateKey PrivateKeyInfo, + * identifier CHOICE { + * string UTF8String, + * generalName GeneralName + * } OPTIONAL + * } + * </pre> + * @return + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(privKeyInfo); + v.AddOptional(identifier); + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/crmf/EncryptedKey.cs b/Crypto/src/asn1/crmf/EncryptedKey.cs new file mode 100644
index 000000000..850fbd219 --- /dev/null +++ b/Crypto/src/asn1/crmf/EncryptedKey.cs
@@ -0,0 +1,78 @@ +using System; + +using Org.BouncyCastle.Asn1.Cms; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + public class EncryptedKey + : Asn1Encodable, IAsn1Choice + { + private readonly EnvelopedData envelopedData; + private readonly EncryptedValue encryptedValue; + + public static EncryptedKey GetInstance(object o) + { + if (o is EncryptedKey) + { + return (EncryptedKey)o; + } + else if (o is Asn1TaggedObject) + { + return new EncryptedKey(EnvelopedData.GetInstance((Asn1TaggedObject)o, false)); + } + else if (o is 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 virtual bool IsEncryptedValue + { + get { return encryptedValue != null; } + } + + public virtual Asn1Encodable Value + { + get + { + 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 override Asn1Object ToAsn1Object() + { + if (encryptedValue != null) + { + return encryptedValue.ToAsn1Object(); + } + + return new DerTaggedObject(false, 0, envelopedData); + } + } +} diff --git a/Crypto/src/asn1/crmf/EncryptedValue.cs b/Crypto/src/asn1/crmf/EncryptedValue.cs new file mode 100644
index 000000000..83122e220 --- /dev/null +++ b/Crypto/src/asn1/crmf/EncryptedValue.cs
@@ -0,0 +1,154 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + public class EncryptedValue + : Asn1Encodable + { + private readonly AlgorithmIdentifier intendedAlg; + private readonly AlgorithmIdentifier symmAlg; + private readonly DerBitString encSymmKey; + private readonly AlgorithmIdentifier keyAlg; + private readonly Asn1OctetString valueHint; + private readonly DerBitString encValue; + + private EncryptedValue(Asn1Sequence seq) + { + int index = 0; + while (seq[index] is Asn1TaggedObject) + { + Asn1TaggedObject tObj = (Asn1TaggedObject)seq[index]; + + switch (tObj.TagNo) + { + 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[index]); + } + + public static EncryptedValue GetInstance(object obj) + { + if (obj is EncryptedValue) + return (EncryptedValue)obj; + + if (obj != null) + return new EncryptedValue(Asn1Sequence.GetInstance(obj)); + + return null; + } + + public EncryptedValue( + AlgorithmIdentifier intendedAlg, + AlgorithmIdentifier symmAlg, + DerBitString encSymmKey, + AlgorithmIdentifier keyAlg, + Asn1OctetString valueHint, + DerBitString encValue) + { + if (encValue == null) + { + throw new ArgumentNullException("encValue"); + } + + this.intendedAlg = intendedAlg; + this.symmAlg = symmAlg; + this.encSymmKey = encSymmKey; + this.keyAlg = keyAlg; + this.valueHint = valueHint; + this.encValue = encValue; + } + + public virtual AlgorithmIdentifier IntendedAlg + { + get { return intendedAlg; } + } + + public virtual AlgorithmIdentifier SymmAlg + { + get { return symmAlg; } + } + + public virtual DerBitString EncSymmKey + { + get { return encSymmKey; } + } + + public virtual AlgorithmIdentifier KeyAlg + { + get { return keyAlg; } + } + + public virtual Asn1OctetString ValueHint + { + get { return valueHint; } + } + + public virtual DerBitString EncValue + { + get { 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 override Asn1Object ToAsn1Object() + { + 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/Crypto/src/asn1/crmf/OptionalValidity.cs b/Crypto/src/asn1/crmf/OptionalValidity.cs new file mode 100644
index 000000000..c0037999a --- /dev/null +++ b/Crypto/src/asn1/crmf/OptionalValidity.cs
@@ -0,0 +1,64 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + public class OptionalValidity + : Asn1Encodable + { + private readonly Time notBefore; + private readonly Time notAfter; + + private OptionalValidity(Asn1Sequence seq) + { + foreach (Asn1TaggedObject tObj in seq) + { + if (tObj.TagNo == 0) + { + notBefore = Time.GetInstance(tObj, true); + } + else + { + notAfter = Time.GetInstance(tObj, true); + } + } + } + + public static OptionalValidity GetInstance(object obj) + { + if (obj is OptionalValidity) + return (OptionalValidity)obj; + + if (obj is Asn1Sequence) + return new OptionalValidity((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + /** + * <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 override Asn1Object ToAsn1Object() + { + 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/Crypto/src/asn1/crmf/PKIArchiveOptions.cs b/Crypto/src/asn1/crmf/PKIArchiveOptions.cs new file mode 100644
index 000000000..910f73b22 --- /dev/null +++ b/Crypto/src/asn1/crmf/PKIArchiveOptions.cs
@@ -0,0 +1,105 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + public class PkiArchiveOptions + : Asn1Encodable, IAsn1Choice + { + public const int encryptedPrivKey = 0; + public const int keyGenParameters = 1; + public const int archiveRemGenPrivKey = 2; + + private readonly Asn1Encodable value; + + public static PkiArchiveOptions GetInstance(object obj) + { + if (obj is PkiArchiveOptions) + return (PkiArchiveOptions)obj; + + if (obj is Asn1TaggedObject) + return new PkiArchiveOptions((Asn1TaggedObject)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + private PkiArchiveOptions(Asn1TaggedObject tagged) + { + switch (tagged.TagNo) + { + case encryptedPrivKey: + value = EncryptedKey.GetInstance(tagged.GetObject()); + break; + case keyGenParameters: + value = Asn1OctetString.GetInstance(tagged, false); + break; + case archiveRemGenPrivKey: + value = DerBoolean.GetInstance(tagged, false); + break; + default: + throw new ArgumentException("unknown tag number: " + tagged.TagNo, "tagged"); + } + } + + public PkiArchiveOptions(EncryptedKey encKey) + { + this.value = encKey; + } + + public PkiArchiveOptions(Asn1OctetString keyGenParameters) + { + this.value = keyGenParameters; + } + + public PkiArchiveOptions(bool archiveRemGenPrivKey) + { + this.value = DerBoolean.GetInstance(archiveRemGenPrivKey); + } + + public virtual int Type + { + get + { + if (value is EncryptedKey) + return encryptedPrivKey; + + if (value is Asn1OctetString) + return keyGenParameters; + + return archiveRemGenPrivKey; + } + } + + public virtual Asn1Encodable Value + { + get { 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 override Asn1Object ToAsn1Object() + { + if (value is EncryptedKey) + { + return new DerTaggedObject(true, encryptedPrivKey, value); // choice + } + + if (value is Asn1OctetString) + { + return new DerTaggedObject(false, keyGenParameters, value); + } + + return new DerTaggedObject(false, archiveRemGenPrivKey, value); + } + } +} diff --git a/Crypto/src/asn1/crmf/PKIPublicationInfo.cs b/Crypto/src/asn1/crmf/PKIPublicationInfo.cs new file mode 100644
index 000000000..c8bc1403e --- /dev/null +++ b/Crypto/src/asn1/crmf/PKIPublicationInfo.cs
@@ -0,0 +1,64 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + public class PkiPublicationInfo + : Asn1Encodable + { + private readonly DerInteger action; + private readonly Asn1Sequence pubInfos; + + private PkiPublicationInfo(Asn1Sequence seq) + { + action = DerInteger.GetInstance(seq[0]); + pubInfos = Asn1Sequence.GetInstance(seq[1]); + } + + public static PkiPublicationInfo GetInstance(object obj) + { + if (obj is PkiPublicationInfo) + return (PkiPublicationInfo)obj; + + if (obj is Asn1Sequence) + return new PkiPublicationInfo((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public virtual DerInteger Action + { + get { return action; } + } + + public virtual SinglePubInfo[] GetPubInfos() + { + if (pubInfos == null) + return null; + + SinglePubInfo[] results = new SinglePubInfo[pubInfos.Count]; + for (int i = 0; i != results.Length; ++i) + { + results[i] = SinglePubInfo.GetInstance(pubInfos[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 override Asn1Object ToAsn1Object() + { + return new DerSequence(action, pubInfos); + } + } +} diff --git a/Crypto/src/asn1/crmf/PKMacValue.cs b/Crypto/src/asn1/crmf/PKMacValue.cs new file mode 100644
index 000000000..20a08fd1d --- /dev/null +++ b/Crypto/src/asn1/crmf/PKMacValue.cs
@@ -0,0 +1,89 @@ +using System; + +using Org.BouncyCastle.Asn1.Cmp; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + /** + * Password-based MAC value for use with POPOSigningKeyInput. + */ + public class PKMacValue + : Asn1Encodable + { + private readonly AlgorithmIdentifier algID; + private readonly DerBitString macValue; + + private PKMacValue(Asn1Sequence seq) + { + this.algID = AlgorithmIdentifier.GetInstance(seq[0]); + this.macValue = DerBitString.GetInstance(seq[1]); + } + + public static PKMacValue GetInstance(object obj) + { + if (obj is PKMacValue) + return (PKMacValue)obj; + + if (obj is Asn1Sequence) + return new PKMacValue((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public static PKMacValue GetInstance(Asn1TaggedObject obj, bool 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 pbmParams, + DerBitString macValue) + : this(new AlgorithmIdentifier(CmpObjectIdentifiers.passwordBasedMac, pbmParams), macValue) + { + } + + /** + * Creates a new PKMACValue. + * @param aid CMPObjectIdentifiers.passwordBasedMAC, with PBMParameter + * @param value MAC of the DER-encoded SubjectPublicKeyInfo + */ + public PKMacValue( + AlgorithmIdentifier algID, + DerBitString macValue) + { + this.algID = algID; + this.macValue = macValue; + } + + public virtual AlgorithmIdentifier AlgID + { + get { return algID; } + } + + public virtual DerBitString MacValue + { + get { return macValue; } + } + + /** + * <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 override Asn1Object ToAsn1Object() + { + return new DerSequence(algID, macValue); + } + } +} diff --git a/Crypto/src/asn1/crmf/PopoPrivKey.cs b/Crypto/src/asn1/crmf/PopoPrivKey.cs new file mode 100644
index 000000000..0cedc5127 --- /dev/null +++ b/Crypto/src/asn1/crmf/PopoPrivKey.cs
@@ -0,0 +1,84 @@ +using System; + +using Org.BouncyCastle.Asn1.Cms; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + public class PopoPrivKey + : Asn1Encodable, IAsn1Choice + { + public const int thisMessage = 0; + public const int subsequentMessage = 1; + public const int dhMAC = 2; + public const int agreeMAC = 3; + public const int encryptedKey = 4; + + private readonly int tagNo; + private readonly Asn1Encodable obj; + + private PopoPrivKey(Asn1TaggedObject obj) + { + this.tagNo = obj.TagNo; + + switch (tagNo) + { + case thisMessage: + this.obj = DerBitString.GetInstance(obj, false); + break; + case subsequentMessage: + this.obj = SubsequentMessage.ValueOf(DerInteger.GetInstance(obj, false).Value.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 ArgumentException("unknown tag in PopoPrivKey", "obj"); + } + } + + public static PopoPrivKey GetInstance(Asn1TaggedObject tagged, bool isExplicit) + { + return new PopoPrivKey(Asn1TaggedObject.GetInstance(tagged.GetObject())); + } + + public PopoPrivKey(SubsequentMessage msg) + { + this.tagNo = subsequentMessage; + this.obj = msg; + } + + public virtual int Type + { + get { return tagNo; } + } + + public virtual Asn1Encodable Value + { + get { 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 override Asn1Object ToAsn1Object() + { + return new DerTaggedObject(false, tagNo, obj); + } + } +} diff --git a/Crypto/src/asn1/crmf/PopoSigningKey.cs b/Crypto/src/asn1/crmf/PopoSigningKey.cs new file mode 100644
index 000000000..614278eda --- /dev/null +++ b/Crypto/src/asn1/crmf/PopoSigningKey.cs
@@ -0,0 +1,115 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + public class PopoSigningKey + : Asn1Encodable + { + private readonly PopoSigningKeyInput poposkInput; + private readonly AlgorithmIdentifier algorithmIdentifier; + private readonly DerBitString signature; + + private PopoSigningKey(Asn1Sequence seq) + { + int index = 0; + + if (seq[index] is Asn1TaggedObject) + { + Asn1TaggedObject tagObj + = (Asn1TaggedObject) seq[index++]; + if (tagObj.TagNo != 0) + { + throw new ArgumentException( "Unknown PopoSigningKeyInput tag: " + tagObj.TagNo, "seq"); + } + poposkInput = PopoSigningKeyInput.GetInstance(tagObj.GetObject()); + } + algorithmIdentifier = AlgorithmIdentifier.GetInstance(seq[index++]); + signature = DerBitString.GetInstance(seq[index]); + } + + public static PopoSigningKey GetInstance(object obj) + { + if (obj is PopoSigningKey) + return (PopoSigningKey)obj; + + if (obj is Asn1Sequence) + return new PopoSigningKey((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public static PopoSigningKey GetInstance(Asn1TaggedObject obj, bool isExplicit) + { + return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit)); + } + + /** + * 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 virtual PopoSigningKeyInput PoposkInput + { + get { return poposkInput; } + } + + public virtual AlgorithmIdentifier AlgorithmIdentifier + { + get { return algorithmIdentifier; } + } + + public virtual DerBitString Signature + { + get { 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 override Asn1Object ToAsn1Object() + { + 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/Crypto/src/asn1/crmf/PopoSigningKeyInput.cs b/Crypto/src/asn1/crmf/PopoSigningKeyInput.cs new file mode 100644
index 000000000..63695262f --- /dev/null +++ b/Crypto/src/asn1/crmf/PopoSigningKeyInput.cs
@@ -0,0 +1,115 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + public class PopoSigningKeyInput + : Asn1Encodable + { + private readonly GeneralName sender; + private readonly PKMacValue publicKeyMac; + private readonly SubjectPublicKeyInfo publicKey; + + private PopoSigningKeyInput(Asn1Sequence seq) + { + Asn1Encodable authInfo = (Asn1Encodable)seq[0]; + + if (authInfo is Asn1TaggedObject) + { + Asn1TaggedObject tagObj = (Asn1TaggedObject)authInfo; + if (tagObj.TagNo != 0) + { + throw new ArgumentException("Unknown authInfo tag: " + tagObj.TagNo, "seq"); + } + sender = GeneralName.GetInstance(tagObj.GetObject()); + } + else + { + publicKeyMac = PKMacValue.GetInstance(authInfo); + } + + publicKey = SubjectPublicKeyInfo.GetInstance(seq[1]); + } + + public static PopoSigningKeyInput GetInstance(object obj) + { + if (obj is PopoSigningKeyInput) + return (PopoSigningKeyInput)obj; + + if (obj is Asn1Sequence) + return new PopoSigningKeyInput((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + /** 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 virtual GeneralName Sender + { + get { return sender; } + } + + /** Returns the publicKeyMac field, or null if authInfo is sender */ + public virtual PKMacValue PublicKeyMac + { + get { return publicKeyMac; } + } + + public virtual SubjectPublicKeyInfo PublicKey + { + get { 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 override Asn1Object ToAsn1Object() + { + 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/Crypto/src/asn1/crmf/ProofOfPossession.cs b/Crypto/src/asn1/crmf/ProofOfPossession.cs new file mode 100644
index 000000000..fc00edb32 --- /dev/null +++ b/Crypto/src/asn1/crmf/ProofOfPossession.cs
@@ -0,0 +1,98 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + public class ProofOfPossession + : Asn1Encodable, IAsn1Choice + { + public const int TYPE_RA_VERIFIED = 0; + public const int TYPE_SIGNING_KEY = 1; + public const int TYPE_KEY_ENCIPHERMENT = 2; + public const int TYPE_KEY_AGREEMENT = 3; + + private readonly int tagNo; + private readonly Asn1Encodable obj; + + private ProofOfPossession(Asn1TaggedObject tagged) + { + tagNo = tagged.TagNo; + switch (tagNo) + { + case 0: + obj = DerNull.Instance; + break; + case 1: + obj = PopoSigningKey.GetInstance(tagged, false); + break; + case 2: + case 3: + obj = PopoPrivKey.GetInstance(tagged, false); + break; + default: + throw new ArgumentException("unknown tag: " + tagNo, "tagged"); + } + } + + public static ProofOfPossession GetInstance(object obj) + { + if (obj is ProofOfPossession) + return (ProofOfPossession)obj; + + if (obj is Asn1TaggedObject) + return new ProofOfPossession((Asn1TaggedObject)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + /** 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 virtual int Type + { + get { return tagNo; } + } + + public virtual Asn1Encodable Object + { + get { 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 override Asn1Object ToAsn1Object() + { + return new DerTaggedObject(false, tagNo, obj); + } + } +} diff --git a/Crypto/src/asn1/crmf/SinglePubInfo.cs b/Crypto/src/asn1/crmf/SinglePubInfo.cs new file mode 100644
index 000000000..eaf8a3efd --- /dev/null +++ b/Crypto/src/asn1/crmf/SinglePubInfo.cs
@@ -0,0 +1,58 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + public class SinglePubInfo + : Asn1Encodable + { + private readonly DerInteger pubMethod; + private readonly GeneralName pubLocation; + + private SinglePubInfo(Asn1Sequence seq) + { + pubMethod = DerInteger.GetInstance(seq[0]); + + if (seq.Count == 2) + { + pubLocation = GeneralName.GetInstance(seq[1]); + } + } + + public static SinglePubInfo GetInstance(object obj) + { + if (obj is SinglePubInfo) + return (SinglePubInfo)obj; + + if (obj is Asn1Sequence) + return new SinglePubInfo((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + } + + public virtual GeneralName PubLocation + { + get { 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(pubMethod); + v.AddOptional(pubLocation); + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/crmf/SubsequentMessage.cs b/Crypto/src/asn1/crmf/SubsequentMessage.cs new file mode 100644
index 000000000..cc1c16492 --- /dev/null +++ b/Crypto/src/asn1/crmf/SubsequentMessage.cs
@@ -0,0 +1,27 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + public class SubsequentMessage + : DerInteger + { + public static readonly SubsequentMessage encrCert = new SubsequentMessage(0); + public static readonly SubsequentMessage challengeResp = new SubsequentMessage(1); + + private SubsequentMessage(int value) + : base(value) + { + } + + public static SubsequentMessage ValueOf(int value) + { + if (value == 0) + return encrCert; + + if (value == 1) + return challengeResp; + + throw new ArgumentException("unknown value: " + value, "value"); + } + } +} diff --git a/Crypto/src/asn1/cryptopro/CryptoProObjectIdentifiers.cs b/Crypto/src/asn1/cryptopro/CryptoProObjectIdentifiers.cs new file mode 100644
index 000000000..6e0e023b9 --- /dev/null +++ b/Crypto/src/asn1/cryptopro/CryptoProObjectIdentifiers.cs
@@ -0,0 +1,48 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.CryptoPro +{ + public abstract class CryptoProObjectIdentifiers + { + // GOST Algorithms OBJECT IDENTIFIERS : + // { iso(1) member-body(2) ru(643) rans(2) cryptopro(2)} + public const string GostID = "1.2.643.2.2"; + + public static readonly DerObjectIdentifier GostR3411 = new DerObjectIdentifier(GostID + ".9"); + + public static readonly DerObjectIdentifier GostR28147Cbc = new DerObjectIdentifier(GostID + ".21"); + + public static readonly DerObjectIdentifier GostR3410x94 = new DerObjectIdentifier(GostID + ".20"); + public static readonly DerObjectIdentifier GostR3410x2001 = new DerObjectIdentifier(GostID + ".19"); + public static readonly DerObjectIdentifier GostR3411x94WithGostR3410x94 = new DerObjectIdentifier(GostID + ".4"); + public static readonly DerObjectIdentifier GostR3411x94WithGostR3410x2001 = new DerObjectIdentifier(GostID + ".3"); + + // { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) hashes(30) } + public static readonly DerObjectIdentifier GostR3411x94CryptoProParamSet = new DerObjectIdentifier(GostID + ".30.1"); + + // { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) signs(32) } + public static readonly DerObjectIdentifier GostR3410x94CryptoProA = new DerObjectIdentifier(GostID + ".32.2"); + public static readonly DerObjectIdentifier GostR3410x94CryptoProB = new DerObjectIdentifier(GostID + ".32.3"); + public static readonly DerObjectIdentifier GostR3410x94CryptoProC = new DerObjectIdentifier(GostID + ".32.4"); + public static readonly DerObjectIdentifier GostR3410x94CryptoProD = new DerObjectIdentifier(GostID + ".32.5"); + + // { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) exchanges(33) } + public static readonly DerObjectIdentifier GostR3410x94CryptoProXchA = new DerObjectIdentifier(GostID + ".33.1"); + public static readonly DerObjectIdentifier GostR3410x94CryptoProXchB = new DerObjectIdentifier(GostID + ".33.2"); + public static readonly DerObjectIdentifier GostR3410x94CryptoProXchC = new DerObjectIdentifier(GostID + ".33.3"); + + //{ iso(1) member-body(2)ru(643) rans(2) cryptopro(2) ecc-signs(35) } + public static readonly DerObjectIdentifier GostR3410x2001CryptoProA = new DerObjectIdentifier(GostID + ".35.1"); + public static readonly DerObjectIdentifier GostR3410x2001CryptoProB = new DerObjectIdentifier(GostID + ".35.2"); + public static readonly DerObjectIdentifier GostR3410x2001CryptoProC = new DerObjectIdentifier(GostID + ".35.3"); + + // { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) ecc-exchanges(36) } + public static readonly DerObjectIdentifier GostR3410x2001CryptoProXchA = new DerObjectIdentifier(GostID + ".36.0"); + public static readonly DerObjectIdentifier GostR3410x2001CryptoProXchB = new DerObjectIdentifier(GostID + ".36.1"); + + public static readonly DerObjectIdentifier GostElSgDH3410Default = new DerObjectIdentifier(GostID + ".36.0"); + public static readonly DerObjectIdentifier GostElSgDH3410x1 = new DerObjectIdentifier(GostID + ".36.1"); + } +} diff --git a/Crypto/src/asn1/cryptopro/ECGOST3410NamedCurves.cs b/Crypto/src/asn1/cryptopro/ECGOST3410NamedCurves.cs new file mode 100644
index 000000000..998e0e06f --- /dev/null +++ b/Crypto/src/asn1/cryptopro/ECGOST3410NamedCurves.cs
@@ -0,0 +1,179 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Asn1.CryptoPro +{ + /** + * table of the available named parameters for GOST 3410-2001. + */ + public sealed class ECGost3410NamedCurves + { + private ECGost3410NamedCurves() + { + } + + internal static readonly IDictionary objIds = Platform.CreateHashtable(); + internal static readonly IDictionary parameters = Platform.CreateHashtable(); + internal static readonly IDictionary names = Platform.CreateHashtable(); + + static ECGost3410NamedCurves() + { + BigInteger mod_p = new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639319"); + BigInteger mod_q = new BigInteger("115792089237316195423570985008687907853073762908499243225378155805079068850323"); + + FpCurve curve = new FpCurve( + mod_p, // p + new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639316"), // a + new BigInteger("166")); // b + + ECDomainParameters ecParams = new ECDomainParameters( + curve, + curve.CreatePoint( + BigInteger.One, // x + new BigInteger("64033881142927202683649881450433473985931760268884941288852745803908878638612"), // y + false), + mod_q); + + parameters[CryptoProObjectIdentifiers.GostR3410x2001CryptoProA] = ecParams; + + mod_p = new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639319"); + mod_q = new BigInteger("115792089237316195423570985008687907853073762908499243225378155805079068850323"); + + curve = new FpCurve( + mod_p, // p + new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639316"), + new BigInteger("166")); + + ecParams = new ECDomainParameters( + curve, + curve.CreatePoint( + BigInteger.One, // x + new BigInteger("64033881142927202683649881450433473985931760268884941288852745803908878638612"), // y + false), + mod_q); + + parameters[CryptoProObjectIdentifiers.GostR3410x2001CryptoProXchA] = ecParams; + + mod_p = new BigInteger("57896044618658097711785492504343953926634992332820282019728792003956564823193"); //p + mod_q = new BigInteger("57896044618658097711785492504343953927102133160255826820068844496087732066703"); //q + + curve = new FpCurve( + mod_p, // p + new BigInteger("57896044618658097711785492504343953926634992332820282019728792003956564823190"), // a + new BigInteger("28091019353058090096996979000309560759124368558014865957655842872397301267595")); // b + + ecParams = new ECDomainParameters( + curve, + curve.CreatePoint( + BigInteger.One, // x + new BigInteger("28792665814854611296992347458380284135028636778229113005756334730996303888124"), // y + false), + mod_q); // q + + parameters[CryptoProObjectIdentifiers.GostR3410x2001CryptoProB] = ecParams; + + mod_p = new BigInteger("70390085352083305199547718019018437841079516630045180471284346843705633502619"); + mod_q = new BigInteger("70390085352083305199547718019018437840920882647164081035322601458352298396601"); + + curve = new FpCurve( + mod_p, // p + new BigInteger("70390085352083305199547718019018437841079516630045180471284346843705633502616"), + new BigInteger("32858")); + + ecParams = new ECDomainParameters( + curve, + curve.CreatePoint( + BigInteger.Zero, // x + new BigInteger("29818893917731240733471273240314769927240550812383695689146495261604565990247"), // y + false), + mod_q); + + parameters[CryptoProObjectIdentifiers.GostR3410x2001CryptoProXchB] = ecParams; + + mod_p = new BigInteger("70390085352083305199547718019018437841079516630045180471284346843705633502619"); //p + mod_q = new BigInteger("70390085352083305199547718019018437840920882647164081035322601458352298396601"); //q + curve = new FpCurve( + mod_p, // p + new BigInteger("70390085352083305199547718019018437841079516630045180471284346843705633502616"), // a + new BigInteger("32858")); // b + + ecParams = new ECDomainParameters( + curve, + curve.CreatePoint( + BigInteger.Zero, // x + new BigInteger("29818893917731240733471273240314769927240550812383695689146495261604565990247"), // y + false), + mod_q); // q + + parameters[CryptoProObjectIdentifiers.GostR3410x2001CryptoProC] = ecParams; + + objIds["GostR3410-2001-CryptoPro-A"] = CryptoProObjectIdentifiers.GostR3410x2001CryptoProA; + objIds["GostR3410-2001-CryptoPro-B"] = CryptoProObjectIdentifiers.GostR3410x2001CryptoProB; + objIds["GostR3410-2001-CryptoPro-C"] = CryptoProObjectIdentifiers.GostR3410x2001CryptoProC; + objIds["GostR3410-2001-CryptoPro-XchA"] = CryptoProObjectIdentifiers.GostR3410x2001CryptoProXchA; + objIds["GostR3410-2001-CryptoPro-XchB"] = CryptoProObjectIdentifiers.GostR3410x2001CryptoProXchB; + + names[CryptoProObjectIdentifiers.GostR3410x2001CryptoProA] = "GostR3410-2001-CryptoPro-A"; + names[CryptoProObjectIdentifiers.GostR3410x2001CryptoProB] = "GostR3410-2001-CryptoPro-B"; + names[CryptoProObjectIdentifiers.GostR3410x2001CryptoProC] = "GostR3410-2001-CryptoPro-C"; + names[CryptoProObjectIdentifiers.GostR3410x2001CryptoProXchA] = "GostR3410-2001-CryptoPro-XchA"; + names[CryptoProObjectIdentifiers.GostR3410x2001CryptoProXchB] = "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( + DerObjectIdentifier oid) + { + return (ECDomainParameters) parameters[oid]; + } + + /** + * returns an enumeration containing the name strings for curves + * contained in this structure. + */ + public static IEnumerable Names + { + get { return new EnumerableProxy(objIds.Keys); } + } + + public static ECDomainParameters GetByName( + string name) + { + DerObjectIdentifier oid = (DerObjectIdentifier) objIds[name]; + + if (oid != null) + { + return (ECDomainParameters) parameters[oid]; + } + + return null; + } + + /** + * return the named curve name represented by the given object identifier. + */ + public static string GetName( + DerObjectIdentifier oid) + { + return (string) names[oid]; + } + + public static DerObjectIdentifier GetOid( + string name) + { + return (DerObjectIdentifier) objIds[name]; + } + } +} diff --git a/Crypto/src/asn1/cryptopro/ECGOST3410ParamSetParameters.cs b/Crypto/src/asn1/cryptopro/ECGOST3410ParamSetParameters.cs new file mode 100644
index 000000000..6f4435d7b --- /dev/null +++ b/Crypto/src/asn1/cryptopro/ECGOST3410ParamSetParameters.cs
@@ -0,0 +1,86 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Asn1.CryptoPro +{ + public class ECGost3410ParamSetParameters + : Asn1Encodable + { + internal readonly DerInteger p, q, a, b, x, y; + + public static ECGost3410ParamSetParameters GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static ECGost3410ParamSetParameters GetInstance( + object obj) + { + if (obj == null || obj is ECGost3410ParamSetParameters) + { + return (ECGost3410ParamSetParameters) obj; + } + + if (obj is Asn1Sequence) + { + return new ECGost3410ParamSetParameters((Asn1Sequence) obj); + } + + throw new ArgumentException("Invalid GOST3410Parameter: " + obj.GetType().Name); + } + + public ECGost3410ParamSetParameters( + BigInteger a, + BigInteger b, + BigInteger p, + BigInteger q, + int x, + BigInteger y) + { + this.a = new DerInteger(a); + this.b = new DerInteger(b); + this.p = new DerInteger(p); + this.q = new DerInteger(q); + this.x = new DerInteger(x); + this.y = new DerInteger(y); + } + + public ECGost3410ParamSetParameters( + Asn1Sequence seq) + { + if (seq.Count != 6) + throw new ArgumentException("Wrong number of elements in sequence", "seq"); + + this.a = DerInteger.GetInstance(seq[0]); + this.b = DerInteger.GetInstance(seq[1]); + this.p = DerInteger.GetInstance(seq[2]); + this.q = DerInteger.GetInstance(seq[3]); + this.x = DerInteger.GetInstance(seq[4]); + this.y = DerInteger.GetInstance(seq[5]); + } + + public BigInteger P + { + get { return p.PositiveValue; } + } + + public BigInteger Q + { + get { return q.PositiveValue; } + } + + public BigInteger A + { + get { return a.PositiveValue; } + } + + public override Asn1Object ToAsn1Object() + { + return new DerSequence(a, b, p, q, x, y); + } + } +} diff --git a/Crypto/src/asn1/cryptopro/GOST28147Parameters.cs b/Crypto/src/asn1/cryptopro/GOST28147Parameters.cs new file mode 100644
index 000000000..eb7e0e3f6 --- /dev/null +++ b/Crypto/src/asn1/cryptopro/GOST28147Parameters.cs
@@ -0,0 +1,63 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.CryptoPro +{ + public class Gost28147Parameters + : Asn1Encodable + { + private readonly Asn1OctetString iv; + private readonly DerObjectIdentifier paramSet; + + public static Gost28147Parameters GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static Gost28147Parameters GetInstance( + object obj) + { + if (obj == null || obj is Gost28147Parameters) + { + return (Gost28147Parameters) obj; + } + + if (obj is Asn1Sequence) + { + return new Gost28147Parameters((Asn1Sequence) obj); + } + + throw new ArgumentException("Invalid GOST3410Parameter: " + obj.GetType().Name); + } + + private Gost28147Parameters( + Asn1Sequence seq) + { + if (seq.Count != 2) + throw new ArgumentException("Wrong number of elements in sequence", "seq"); + + this.iv = Asn1OctetString.GetInstance(seq[0]); + this.paramSet = DerObjectIdentifier.GetInstance(seq[1]); + } + + /** + * <pre> + * Gost28147-89-Parameters ::= + * SEQUENCE { + * iv Gost28147-89-IV, + * encryptionParamSet OBJECT IDENTIFIER + * } + * + * Gost28147-89-IV ::= OCTET STRING (SIZE (8)) + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + return new DerSequence(iv, paramSet); + } + } +} diff --git a/Crypto/src/asn1/cryptopro/GOST3410NamedParameters.cs b/Crypto/src/asn1/cryptopro/GOST3410NamedParameters.cs new file mode 100644
index 000000000..66dba51d7 --- /dev/null +++ b/Crypto/src/asn1/cryptopro/GOST3410NamedParameters.cs
@@ -0,0 +1,123 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Asn1.CryptoPro +{ + /** + * table of the available named parameters for GOST 3410-94. + */ + public sealed class Gost3410NamedParameters + { + private Gost3410NamedParameters() + { + } + + private static readonly IDictionary objIds = Platform.CreateHashtable(); + private static readonly IDictionary parameters = Platform.CreateHashtable(); + + private static readonly 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 + // } + // } + + ); + + private static readonly 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 + // } + // } + //} + ); + + private static readonly Gost3410ParamSetParameters cryptoProXchA = new Gost3410ParamSetParameters( + 1024, + new BigInteger("142011741597563481196368286022318089743276138395243738762872573441927459393512718973631166078467600360848946623567625795282774719212241929071046134208380636394084512691828894000571524625445295769349356752728956831541775441763139384457191755096847107846595662547942312293338483924514339614727760681880609734239"), + new BigInteger("91771529896554605945588149018382750217296858393520724172743325725474374979801"), + new BigInteger("133531813272720673433859519948319001217942375967847486899482359599369642528734712461590403327731821410328012529253871914788598993103310567744136196364803064721377826656898686468463277710150809401182608770201615324990468332931294920912776241137878030224355746606283971659376426832674269780880061631528163475887") + ); + + static Gost3410NamedParameters() + { + parameters[CryptoProObjectIdentifiers.GostR3410x94CryptoProA] = cryptoProA; + parameters[CryptoProObjectIdentifiers.GostR3410x94CryptoProB] = cryptoProB; + //parameters[CryptoProObjectIdentifiers.GostR3410x94CryptoProC] = cryptoProC; + //parameters[CryptoProObjectIdentifiers.GostR3410x94CryptoProD] = cryptoProD; + parameters[CryptoProObjectIdentifiers.GostR3410x94CryptoProXchA] = cryptoProXchA; + //parameters[CryptoProObjectIdentifiers.GostR3410x94CryptoProXchB] = cryptoProXchA; + //parameters[CryptoProObjectIdentifiers.GostR3410x94CryptoProXchC] = cryptoProXchA; + + objIds["GostR3410-94-CryptoPro-A"] = CryptoProObjectIdentifiers.GostR3410x94CryptoProA; + objIds["GostR3410-94-CryptoPro-B"] = CryptoProObjectIdentifiers.GostR3410x94CryptoProB; + objIds["GostR3410-94-CryptoPro-XchA"] = CryptoProObjectIdentifiers.GostR3410x94CryptoProXchA; + } + + /** + * 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( + DerObjectIdentifier oid) + { + return (Gost3410ParamSetParameters) parameters[oid]; + } + + /** + * returns an enumeration containing the name strings for parameters + * contained in this structure. + */ + public static IEnumerable Names + { + get { return new EnumerableProxy(objIds.Keys); } + } + + public static Gost3410ParamSetParameters GetByName( + string name) + { + DerObjectIdentifier oid = (DerObjectIdentifier) objIds[name]; + + if (oid != null) + { + return (Gost3410ParamSetParameters) parameters[oid]; + } + + return null; + } + + public static DerObjectIdentifier GetOid( + string name) + { + return (DerObjectIdentifier) objIds[name]; + } + } +} diff --git a/Crypto/src/asn1/cryptopro/GOST3410ParamSetParameters.cs b/Crypto/src/asn1/cryptopro/GOST3410ParamSetParameters.cs new file mode 100644
index 000000000..f133cdf1b --- /dev/null +++ b/Crypto/src/asn1/cryptopro/GOST3410ParamSetParameters.cs
@@ -0,0 +1,87 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Asn1.CryptoPro +{ + public class Gost3410ParamSetParameters + : Asn1Encodable + { + private readonly int keySize; + private readonly DerInteger p, q, a; + + public static Gost3410ParamSetParameters GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static Gost3410ParamSetParameters GetInstance( + object obj) + { + if (obj == null || obj is Gost3410ParamSetParameters) + { + return (Gost3410ParamSetParameters) obj; + } + + if (obj is Asn1Sequence) + { + return new Gost3410ParamSetParameters((Asn1Sequence) obj); + } + + throw new ArgumentException("Invalid GOST3410Parameter: " + obj.GetType().Name); + } + + public Gost3410ParamSetParameters( + int keySize, + BigInteger p, + BigInteger q, + BigInteger a) + { + this.keySize = keySize; + this.p = new DerInteger(p); + this.q = new DerInteger(q); + this.a = new DerInteger(a); + } + + private Gost3410ParamSetParameters( + Asn1Sequence seq) + { + if (seq.Count != 4) + throw new ArgumentException("Wrong number of elements in sequence", "seq"); + + this.keySize = DerInteger.GetInstance(seq[0]).Value.IntValue; + this.p = DerInteger.GetInstance(seq[1]); + this.q = DerInteger.GetInstance(seq[2]); + this.a = DerInteger.GetInstance(seq[3]); + } + + public int KeySize + { + get { return keySize; } + } + + public BigInteger P + { + get { return p.PositiveValue; } + } + + public BigInteger Q + { + get { return q.PositiveValue; } + } + + public BigInteger A + { + get { return a.PositiveValue; } + } + + public override Asn1Object ToAsn1Object() + { + return new DerSequence(new DerInteger(keySize), p, q, a); + } + } +} diff --git a/Crypto/src/asn1/cryptopro/GOST3410PublicKeyAlgParameters.cs b/Crypto/src/asn1/cryptopro/GOST3410PublicKeyAlgParameters.cs new file mode 100644
index 000000000..8bc1460af --- /dev/null +++ b/Crypto/src/asn1/cryptopro/GOST3410PublicKeyAlgParameters.cs
@@ -0,0 +1,99 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.CryptoPro +{ + public class Gost3410PublicKeyAlgParameters + : Asn1Encodable + { + private DerObjectIdentifier publicKeyParamSet; + private DerObjectIdentifier digestParamSet; + private DerObjectIdentifier encryptionParamSet; + + public static Gost3410PublicKeyAlgParameters GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static Gost3410PublicKeyAlgParameters GetInstance( + object obj) + { + if (obj == null || obj is Gost3410PublicKeyAlgParameters) + { + return (Gost3410PublicKeyAlgParameters) obj; + } + + if (obj is Asn1Sequence) + { + return new Gost3410PublicKeyAlgParameters((Asn1Sequence) obj); + } + + throw new ArgumentException("Invalid GOST3410Parameter: " + obj.GetType().Name); + } + + public Gost3410PublicKeyAlgParameters( + DerObjectIdentifier publicKeyParamSet, + DerObjectIdentifier digestParamSet) + : this (publicKeyParamSet, digestParamSet, null) + { + } + + public Gost3410PublicKeyAlgParameters( + DerObjectIdentifier publicKeyParamSet, + DerObjectIdentifier digestParamSet, + DerObjectIdentifier encryptionParamSet) + { + if (publicKeyParamSet == null) + throw new ArgumentNullException("publicKeyParamSet"); + if (digestParamSet == null) + throw new ArgumentNullException("digestParamSet"); + + this.publicKeyParamSet = publicKeyParamSet; + this.digestParamSet = digestParamSet; + this.encryptionParamSet = encryptionParamSet; + } + + public Gost3410PublicKeyAlgParameters( + Asn1Sequence seq) + { + this.publicKeyParamSet = (DerObjectIdentifier) seq[0]; + this.digestParamSet = (DerObjectIdentifier) seq[1]; + + if (seq.Count > 2) + { + this.encryptionParamSet = (DerObjectIdentifier) seq[2]; + } + } + + public DerObjectIdentifier PublicKeyParamSet + { + get { return publicKeyParamSet; } + } + + public DerObjectIdentifier DigestParamSet + { + get { return digestParamSet; } + } + + public DerObjectIdentifier EncryptionParamSet + { + get { return encryptionParamSet; } + } + + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector( + publicKeyParamSet, digestParamSet); + + if (encryptionParamSet != null) + { + v.Add(encryptionParamSet); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/eac/EACObjectIdentifiers.cs b/Crypto/src/asn1/eac/EACObjectIdentifiers.cs new file mode 100644
index 000000000..d54ef0eba --- /dev/null +++ b/Crypto/src/asn1/eac/EACObjectIdentifiers.cs
@@ -0,0 +1,50 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Eac +{ + public abstract class EacObjectIdentifiers + { + // bsi-de OBJECT IDENTIFIER ::= { + // itu-t(0) identified-organization(4) etsi(0) + // reserved(127) etsi-identified-organization(0) 7 + // } + public static readonly DerObjectIdentifier bsi_de = new DerObjectIdentifier("0.4.0.127.0.7"); + + // id-PK OBJECT IDENTIFIER ::= { + // bsi-de protocols(2) smartcard(2) 1 + // } + public static readonly DerObjectIdentifier id_PK = new DerObjectIdentifier(bsi_de + ".2.2.1"); + + public static readonly DerObjectIdentifier id_PK_DH = new DerObjectIdentifier(id_PK + ".1"); + public static readonly DerObjectIdentifier id_PK_ECDH = new DerObjectIdentifier(id_PK + ".2"); + + // id-CA OBJECT IDENTIFIER ::= { + // bsi-de protocols(2) smartcard(2) 3 + // } + public static readonly DerObjectIdentifier id_CA = new DerObjectIdentifier(bsi_de + ".2.2.3"); + public static readonly DerObjectIdentifier id_CA_DH = new DerObjectIdentifier(id_CA + ".1"); + public static readonly DerObjectIdentifier id_CA_DH_3DES_CBC_CBC = new DerObjectIdentifier(id_CA_DH + ".1"); + public static readonly DerObjectIdentifier id_CA_ECDH = new DerObjectIdentifier(id_CA + ".2"); + public static readonly DerObjectIdentifier id_CA_ECDH_3DES_CBC_CBC = new DerObjectIdentifier(id_CA_ECDH + ".1"); + + // + // id-TA OBJECT IDENTIFIER ::= { + // bsi-de protocols(2) smartcard(2) 2 + // } + public static readonly DerObjectIdentifier id_TA = new DerObjectIdentifier(bsi_de + ".2.2.2"); + + public static readonly DerObjectIdentifier id_TA_RSA = new DerObjectIdentifier(id_TA + ".1"); + public static readonly DerObjectIdentifier id_TA_RSA_v1_5_SHA_1 = new DerObjectIdentifier(id_TA_RSA + ".1"); + public static readonly DerObjectIdentifier id_TA_RSA_v1_5_SHA_256 = new DerObjectIdentifier(id_TA_RSA + ".2"); + public static readonly DerObjectIdentifier id_TA_RSA_PSS_SHA_1 = new DerObjectIdentifier(id_TA_RSA + ".3"); + public static readonly DerObjectIdentifier id_TA_RSA_PSS_SHA_256 = new DerObjectIdentifier(id_TA_RSA + ".4"); + public static readonly DerObjectIdentifier id_TA_ECDSA = new DerObjectIdentifier(id_TA + ".2"); + public static readonly DerObjectIdentifier id_TA_ECDSA_SHA_1 = new DerObjectIdentifier(id_TA_ECDSA + ".1"); + public static readonly DerObjectIdentifier id_TA_ECDSA_SHA_224 = new DerObjectIdentifier(id_TA_ECDSA + ".2"); + public static readonly DerObjectIdentifier id_TA_ECDSA_SHA_256 = new DerObjectIdentifier(id_TA_ECDSA + ".3"); + public static readonly DerObjectIdentifier id_TA_ECDSA_SHA_384 = new DerObjectIdentifier(id_TA_ECDSA + ".4"); + public static readonly DerObjectIdentifier id_TA_ECDSA_SHA_512 = new DerObjectIdentifier(id_TA_ECDSA + ".5"); + } +} diff --git a/Crypto/src/asn1/esf/CertificateValues.cs b/Crypto/src/asn1/esf/CertificateValues.cs new file mode 100644
index 000000000..e0fb39b83 --- /dev/null +++ b/Crypto/src/asn1/esf/CertificateValues.cs
@@ -0,0 +1,85 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Asn1.Esf +{ + /// <remarks> + /// RFC 3126: 4.3.1 Certificate Values Attribute Definition + /// <code> + /// CertificateValues ::= SEQUENCE OF Certificate + /// </code> + /// </remarks> + public class CertificateValues + : Asn1Encodable + { + private readonly Asn1Sequence certificates; + + public static CertificateValues GetInstance( + object obj) + { + if (obj == null || obj is CertificateValues) + return (CertificateValues) obj; + + if (obj is Asn1Sequence) + return new CertificateValues((Asn1Sequence) obj); + + throw new ArgumentException( + "Unknown object in 'CertificateValues' factory: " + + obj.GetType().Name, + "obj"); + } + + private CertificateValues( + Asn1Sequence seq) + { + if (seq == null) + throw new ArgumentNullException("seq"); + + foreach (Asn1Encodable ae in seq) + { + X509CertificateStructure.GetInstance(ae.ToAsn1Object()); + } + + this.certificates = seq; + } + + public CertificateValues( + params X509CertificateStructure[] certificates) + { + if (certificates == null) + throw new ArgumentNullException("certificates"); + + this.certificates = new DerSequence(certificates); + } + + public CertificateValues( + IEnumerable certificates) + { + if (certificates == null) + throw new ArgumentNullException("certificates"); + if (!CollectionUtilities.CheckElementsAreOfType(certificates, typeof(X509CertificateStructure))) + throw new ArgumentException("Must contain only 'X509CertificateStructure' objects", "certificates"); + + this.certificates = new DerSequence( + Asn1EncodableVector.FromEnumerable(certificates)); + } + + public X509CertificateStructure[] GetCertificates() + { + X509CertificateStructure[] result = new X509CertificateStructure[certificates.Count]; + for (int i = 0; i < certificates.Count; ++i) + { + result[i] = X509CertificateStructure.GetInstance(certificates[i]); + } + return result; + } + + public override Asn1Object ToAsn1Object() + { + return certificates; + } + } +} diff --git a/Crypto/src/asn1/esf/CommitmentTypeIdentifier.cs b/Crypto/src/asn1/esf/CommitmentTypeIdentifier.cs new file mode 100644
index 000000000..65cd45b4a --- /dev/null +++ b/Crypto/src/asn1/esf/CommitmentTypeIdentifier.cs
@@ -0,0 +1,17 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Pkcs; + +namespace Org.BouncyCastle.Asn1.Esf +{ + public abstract class CommitmentTypeIdentifier + { + public static readonly DerObjectIdentifier ProofOfOrigin = PkcsObjectIdentifiers.IdCtiEtsProofOfOrigin; + public static readonly DerObjectIdentifier ProofOfReceipt = PkcsObjectIdentifiers.IdCtiEtsProofOfReceipt; + public static readonly DerObjectIdentifier ProofOfDelivery = PkcsObjectIdentifiers.IdCtiEtsProofOfDelivery; + public static readonly DerObjectIdentifier ProofOfSender = PkcsObjectIdentifiers.IdCtiEtsProofOfSender; + public static readonly DerObjectIdentifier ProofOfApproval = PkcsObjectIdentifiers.IdCtiEtsProofOfApproval; + public static readonly DerObjectIdentifier ProofOfCreation = PkcsObjectIdentifiers.IdCtiEtsProofOfCreation; + } +} diff --git a/Crypto/src/asn1/esf/CommitmentTypeIndication.cs b/Crypto/src/asn1/esf/CommitmentTypeIndication.cs new file mode 100644
index 000000000..8342cbf8d --- /dev/null +++ b/Crypto/src/asn1/esf/CommitmentTypeIndication.cs
@@ -0,0 +1,95 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Esf +{ + public class CommitmentTypeIndication + : Asn1Encodable + { + private readonly DerObjectIdentifier commitmentTypeId; + private readonly Asn1Sequence commitmentTypeQualifier; + + public static CommitmentTypeIndication GetInstance( + object obj) + { + if (obj == null || obj is CommitmentTypeIndication) + return (CommitmentTypeIndication) obj; + + if (obj is Asn1Sequence) + return new CommitmentTypeIndication((Asn1Sequence) obj); + + throw new ArgumentException( + "Unknown object in 'CommitmentTypeIndication' factory: " + + obj.GetType().Name, + "obj"); + } + + public CommitmentTypeIndication( + Asn1Sequence seq) + { + if (seq == null) + throw new ArgumentNullException("seq"); + if (seq.Count < 1 || seq.Count > 2) + throw new ArgumentException("Bad sequence size: " + seq.Count, "seq"); + + this.commitmentTypeId = (DerObjectIdentifier) seq[0].ToAsn1Object(); + + if (seq.Count > 1) + { + this.commitmentTypeQualifier = (Asn1Sequence) seq[1].ToAsn1Object(); + } + } + + public CommitmentTypeIndication( + DerObjectIdentifier commitmentTypeId) + : this(commitmentTypeId, null) + { + } + + public CommitmentTypeIndication( + DerObjectIdentifier commitmentTypeId, + Asn1Sequence commitmentTypeQualifier) + { + if (commitmentTypeId == null) + throw new ArgumentNullException("commitmentTypeId"); + + this.commitmentTypeId = commitmentTypeId; + + if (commitmentTypeQualifier != null) + { + this.commitmentTypeQualifier = commitmentTypeQualifier; + } + } + + public DerObjectIdentifier CommitmentTypeID + { + get { return commitmentTypeId; } + } + + public Asn1Sequence CommitmentTypeQualifier + { + get { return commitmentTypeQualifier; } + } + + /** + * <pre> + * CommitmentTypeIndication ::= SEQUENCE { + * commitmentTypeId CommitmentTypeIdentifier, + * commitmentTypeQualifier SEQUENCE SIZE (1..MAX) OF + * CommitmentTypeQualifier OPTIONAL } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(commitmentTypeId); + + if (commitmentTypeQualifier != null) + { + v.Add(commitmentTypeQualifier); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/esf/CommitmentTypeQualifier.cs b/Crypto/src/asn1/esf/CommitmentTypeQualifier.cs new file mode 100644
index 000000000..09ff70714 --- /dev/null +++ b/Crypto/src/asn1/esf/CommitmentTypeQualifier.cs
@@ -0,0 +1,119 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Esf +{ + /** + * 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 + : Asn1Encodable + { + private readonly DerObjectIdentifier commitmentTypeIdentifier; + private readonly Asn1Object qualifier; + + /** + * Creates a new <code>CommitmentTypeQualifier</code> instance. + * + * @param commitmentTypeIdentifier a <code>CommitmentTypeIdentifier</code> value + */ + public CommitmentTypeQualifier( + DerObjectIdentifier 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( + DerObjectIdentifier commitmentTypeIdentifier, + Asn1Encodable qualifier) + { + if (commitmentTypeIdentifier == null) + throw new ArgumentNullException("commitmentTypeIdentifier"); + + this.commitmentTypeIdentifier = commitmentTypeIdentifier; + + if (qualifier != null) + { + this.qualifier = qualifier.ToAsn1Object(); + } + } + + /** + * Creates a new <code>CommitmentTypeQualifier</code> instance. + * + * @param as <code>CommitmentTypeQualifier</code> structure + * encoded as an Asn1Sequence. + */ + public CommitmentTypeQualifier( + Asn1Sequence seq) + { + if (seq == null) + throw new ArgumentNullException("seq"); + if (seq.Count < 1 || seq.Count > 2) + throw new ArgumentException("Bad sequence size: " + seq.Count, "seq"); + + commitmentTypeIdentifier = (DerObjectIdentifier) seq[0].ToAsn1Object(); + + if (seq.Count > 1) + { + qualifier = seq[1].ToAsn1Object(); + } + } + + public static CommitmentTypeQualifier GetInstance( + object obj) + { + if (obj == null || obj is CommitmentTypeQualifier) + return (CommitmentTypeQualifier) obj; + + if (obj is Asn1Sequence) + return new CommitmentTypeQualifier((Asn1Sequence) obj); + + throw new ArgumentException( + "Unknown object in 'CommitmentTypeQualifier' factory: " + + obj.GetType().Name, + "obj"); + } + + public DerObjectIdentifier CommitmentTypeIdentifier + { + get { return commitmentTypeIdentifier; } + } + + public Asn1Object Qualifier + { + get { return qualifier; } + } + + /** + * Returns a DER-encodable representation of this instance. + * + * @return a <code>Asn1Object</code> value + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector( + commitmentTypeIdentifier); + + if (qualifier != null) + { + v.Add(qualifier); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/esf/CompleteCertificateRefs.cs b/Crypto/src/asn1/esf/CompleteCertificateRefs.cs new file mode 100644
index 000000000..7f1c835c9 --- /dev/null +++ b/Crypto/src/asn1/esf/CompleteCertificateRefs.cs
@@ -0,0 +1,84 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Asn1.Esf +{ + /// <remarks> + /// RFC 3126: 4.2.1 Complete Certificate Refs Attribute Definition + /// <code> + /// CompleteCertificateRefs ::= SEQUENCE OF OtherCertID + /// </code> + /// </remarks> + public class CompleteCertificateRefs + : Asn1Encodable + { + private readonly Asn1Sequence otherCertIDs; + + public static CompleteCertificateRefs GetInstance( + object obj) + { + if (obj == null || obj is CompleteCertificateRefs) + return (CompleteCertificateRefs) obj; + + if (obj is Asn1Sequence) + return new CompleteCertificateRefs((Asn1Sequence) obj); + + throw new ArgumentException( + "Unknown object in 'CompleteCertificateRefs' factory: " + + obj.GetType().Name, + "obj"); + } + + private CompleteCertificateRefs( + Asn1Sequence seq) + { + if (seq == null) + throw new ArgumentNullException("seq"); + + foreach (Asn1Encodable ae in seq) + { + OtherCertID.GetInstance(ae.ToAsn1Object()); + } + + this.otherCertIDs = seq; + } + + public CompleteCertificateRefs( + params OtherCertID[] otherCertIDs) + { + if (otherCertIDs == null) + throw new ArgumentNullException("otherCertIDs"); + + this.otherCertIDs = new DerSequence(otherCertIDs); + } + + public CompleteCertificateRefs( + IEnumerable otherCertIDs) + { + if (otherCertIDs == null) + throw new ArgumentNullException("otherCertIDs"); + if (!CollectionUtilities.CheckElementsAreOfType(otherCertIDs, typeof(OtherCertID))) + throw new ArgumentException("Must contain only 'OtherCertID' objects", "otherCertIDs"); + + this.otherCertIDs = new DerSequence( + Asn1EncodableVector.FromEnumerable(otherCertIDs)); + } + + public OtherCertID[] GetOtherCertIDs() + { + OtherCertID[] result = new OtherCertID[otherCertIDs.Count]; + for (int i = 0; i < otherCertIDs.Count; ++i) + { + result[i] = OtherCertID.GetInstance(otherCertIDs[i].ToAsn1Object()); + } + return result; + } + + public override Asn1Object ToAsn1Object() + { + return otherCertIDs; + } + } +} diff --git a/Crypto/src/asn1/esf/CompleteRevocationRefs.cs b/Crypto/src/asn1/esf/CompleteRevocationRefs.cs new file mode 100644
index 000000000..4e1fb403d --- /dev/null +++ b/Crypto/src/asn1/esf/CompleteRevocationRefs.cs
@@ -0,0 +1,84 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Asn1.Esf +{ + /// <remarks> + /// RFC 3126: 4.2.2 Complete Revocation Refs Attribute Definition + /// <code> + /// CompleteRevocationRefs ::= SEQUENCE OF CrlOcspRef + /// </code> + /// </remarks> + public class CompleteRevocationRefs + : Asn1Encodable + { + private readonly Asn1Sequence crlOcspRefs; + + public static CompleteRevocationRefs GetInstance( + object obj) + { + if (obj == null || obj is CompleteRevocationRefs) + return (CompleteRevocationRefs) obj; + + if (obj is Asn1Sequence) + return new CompleteRevocationRefs((Asn1Sequence) obj); + + throw new ArgumentException( + "Unknown object in 'CompleteRevocationRefs' factory: " + + obj.GetType().Name, + "obj"); + } + + private CompleteRevocationRefs( + Asn1Sequence seq) + { + if (seq == null) + throw new ArgumentNullException("seq"); + + foreach (Asn1Encodable ae in seq) + { + CrlOcspRef.GetInstance(ae.ToAsn1Object()); + } + + this.crlOcspRefs = seq; + } + + public CompleteRevocationRefs( + params CrlOcspRef[] crlOcspRefs) + { + if (crlOcspRefs == null) + throw new ArgumentNullException("crlOcspRefs"); + + this.crlOcspRefs = new DerSequence(crlOcspRefs); + } + + public CompleteRevocationRefs( + IEnumerable crlOcspRefs) + { + if (crlOcspRefs == null) + throw new ArgumentNullException("crlOcspRefs"); + if (!CollectionUtilities.CheckElementsAreOfType(crlOcspRefs, typeof(CrlOcspRef))) + throw new ArgumentException("Must contain only 'CrlOcspRef' objects", "crlOcspRefs"); + + this.crlOcspRefs = new DerSequence( + Asn1EncodableVector.FromEnumerable(crlOcspRefs)); + } + + public CrlOcspRef[] GetCrlOcspRefs() + { + CrlOcspRef[] result = new CrlOcspRef[crlOcspRefs.Count]; + for (int i = 0; i < crlOcspRefs.Count; ++i) + { + result[i] = CrlOcspRef.GetInstance(crlOcspRefs[i].ToAsn1Object()); + } + return result; + } + + public override Asn1Object ToAsn1Object() + { + return crlOcspRefs; + } + } +} diff --git a/Crypto/src/asn1/esf/CrlIdentifier.cs b/Crypto/src/asn1/esf/CrlIdentifier.cs new file mode 100644
index 000000000..dfff7d838 --- /dev/null +++ b/Crypto/src/asn1/esf/CrlIdentifier.cs
@@ -0,0 +1,110 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Asn1.Esf +{ + /// <remarks> + /// RFC 3126: 4.2.2 Complete Revocation Refs Attribute Definition + /// <code> + /// CrlIdentifier ::= SEQUENCE + /// { + /// crlissuer Name, + /// crlIssuedTime UTCTime, + /// crlNumber INTEGER OPTIONAL + /// } + /// </code> + /// </remarks> + public class CrlIdentifier + : Asn1Encodable + { + private readonly X509Name crlIssuer; + private readonly DerUtcTime crlIssuedTime; + private readonly DerInteger crlNumber; + + public static CrlIdentifier GetInstance( + object obj) + { + if (obj == null || obj is CrlIdentifier) + return (CrlIdentifier) obj; + + if (obj is Asn1Sequence) + return new CrlIdentifier((Asn1Sequence) obj); + + throw new ArgumentException( + "Unknown object in 'CrlIdentifier' factory: " + + obj.GetType().Name, + "obj"); + } + + private CrlIdentifier( + Asn1Sequence seq) + { + if (seq == null) + throw new ArgumentNullException("seq"); + if (seq.Count < 2 || seq.Count > 3) + throw new ArgumentException("Bad sequence size: " + seq.Count, "seq"); + + this.crlIssuer = X509Name.GetInstance(seq[0]); + this.crlIssuedTime = DerUtcTime.GetInstance(seq[1]); + + if (seq.Count > 2) + { + this.crlNumber = DerInteger.GetInstance(seq[2]); + } + } + + public CrlIdentifier( + X509Name crlIssuer, + DateTime crlIssuedTime) + : this(crlIssuer, crlIssuedTime, null) + { + } + + public CrlIdentifier( + X509Name crlIssuer, + DateTime crlIssuedTime, + BigInteger crlNumber) + { + if (crlIssuer == null) + throw new ArgumentNullException("crlIssuer"); + + this.crlIssuer = crlIssuer; + this.crlIssuedTime = new DerUtcTime(crlIssuedTime); + + if (crlNumber != null) + { + this.crlNumber = new DerInteger(crlNumber); + } + } + + public X509Name CrlIssuer + { + get { return crlIssuer; } + } + + public DateTime CrlIssuedTime + { + get { return crlIssuedTime.ToAdjustedDateTime(); } + } + + public BigInteger CrlNumber + { + get { return crlNumber == null ? null : crlNumber.Value; } + } + + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector( + crlIssuer.ToAsn1Object(), crlIssuedTime); + + if (crlNumber != null) + { + v.Add(crlNumber); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/esf/CrlListID.cs b/Crypto/src/asn1/esf/CrlListID.cs new file mode 100644
index 000000000..2aae9b965 --- /dev/null +++ b/Crypto/src/asn1/esf/CrlListID.cs
@@ -0,0 +1,89 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Asn1.Esf +{ + /// <remarks> + /// RFC 3126: 4.2.2 Complete Revocation Refs Attribute Definition + /// <code> + /// CRLListID ::= SEQUENCE + /// { + /// crls SEQUENCE OF CrlValidatedID + /// } + /// </code> + /// </remarks> + public class CrlListID + : Asn1Encodable + { + private readonly Asn1Sequence crls; + + public static CrlListID GetInstance( + object obj) + { + if (obj == null || obj is CrlListID) + return (CrlListID) obj; + + if (obj is Asn1Sequence) + return new CrlListID((Asn1Sequence) obj); + + throw new ArgumentException( + "Unknown object in 'CrlListID' factory: " + + obj.GetType().Name, + "obj"); + } + + private CrlListID( + Asn1Sequence seq) + { + if (seq == null) + throw new ArgumentNullException("seq"); + if (seq.Count != 1) + throw new ArgumentException("Bad sequence size: " + seq.Count, "seq"); + + this.crls = (Asn1Sequence) seq[0].ToAsn1Object(); + + foreach (Asn1Encodable ae in this.crls) + { + CrlValidatedID.GetInstance(ae.ToAsn1Object()); + } + } + + public CrlListID( + params CrlValidatedID[] crls) + { + if (crls == null) + throw new ArgumentNullException("crls"); + + this.crls = new DerSequence(crls); + } + + public CrlListID( + IEnumerable crls) + { + if (crls == null) + throw new ArgumentNullException("crls"); + if (!CollectionUtilities.CheckElementsAreOfType(crls, typeof(CrlValidatedID))) + throw new ArgumentException("Must contain only 'CrlValidatedID' objects", "crls"); + + this.crls = new DerSequence( + Asn1EncodableVector.FromEnumerable(crls)); + } + + public CrlValidatedID[] GetCrls() + { + CrlValidatedID[] result = new CrlValidatedID[crls.Count]; + for (int i = 0; i < crls.Count; ++i) + { + result[i] = CrlValidatedID.GetInstance(crls[i].ToAsn1Object()); + } + return result; + } + + public override Asn1Object ToAsn1Object() + { + return new DerSequence(crls); + } + } +} diff --git a/Crypto/src/asn1/esf/CrlOcspRef.cs b/Crypto/src/asn1/esf/CrlOcspRef.cs new file mode 100644
index 000000000..c8e10d504 --- /dev/null +++ b/Crypto/src/asn1/esf/CrlOcspRef.cs
@@ -0,0 +1,111 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Esf +{ + /// <remarks> + /// RFC 3126: 4.2.2 Complete Revocation Refs Attribute Definition + /// <code> + /// CrlOcspRef ::= SEQUENCE { + /// crlids [0] CRLListID OPTIONAL, + /// ocspids [1] OcspListID OPTIONAL, + /// otherRev [2] OtherRevRefs OPTIONAL + /// } + /// </code> + /// </remarks> + public class CrlOcspRef + : Asn1Encodable + { + private readonly CrlListID crlids; + private readonly OcspListID ocspids; + private readonly OtherRevRefs otherRev; + + public static CrlOcspRef GetInstance( + object obj) + { + if (obj == null || obj is CrlOcspRef) + return (CrlOcspRef) obj; + + if (obj is Asn1Sequence) + return new CrlOcspRef((Asn1Sequence) obj); + + throw new ArgumentException( + "Unknown object in 'CrlOcspRef' factory: " + + obj.GetType().Name, + "obj"); + } + + private CrlOcspRef( + Asn1Sequence seq) + { + if (seq == null) + throw new ArgumentNullException("seq"); + + foreach (Asn1TaggedObject taggedObj in seq) + { + Asn1Object asn1Obj = taggedObj.GetObject(); + + switch (taggedObj.TagNo) + { + case 0: + this.crlids = CrlListID.GetInstance(asn1Obj); + break; + case 1: + this.ocspids = OcspListID.GetInstance(asn1Obj); + break; + case 2: + this.otherRev = OtherRevRefs.GetInstance(asn1Obj); + break; + default: + throw new ArgumentException("Illegal tag in CrlOcspRef", "seq"); + } + } + } + + public CrlOcspRef( + CrlListID crlids, + OcspListID ocspids, + OtherRevRefs otherRev) + { + this.crlids = crlids; + this.ocspids = ocspids; + this.otherRev = otherRev; + } + + public CrlListID CrlIDs + { + get { return crlids; } + } + + public OcspListID OcspIDs + { + get { return ocspids; } + } + + public OtherRevRefs OtherRev + { + get { return otherRev; } + } + + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(); + + if (crlids != null) + { + v.Add(new DerTaggedObject(true, 0, crlids.ToAsn1Object())); + } + + if (ocspids != null) + { + v.Add(new DerTaggedObject(true, 1, ocspids.ToAsn1Object())); + } + + if (otherRev != null) + { + v.Add(new DerTaggedObject(true, 2, otherRev.ToAsn1Object())); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/esf/CrlValidatedID.cs b/Crypto/src/asn1/esf/CrlValidatedID.cs new file mode 100644
index 000000000..165f547a8 --- /dev/null +++ b/Crypto/src/asn1/esf/CrlValidatedID.cs
@@ -0,0 +1,89 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Esf +{ + /// <remarks> + /// RFC 3126: 4.2.2 Complete Revocation Refs Attribute Definition + /// <code> + /// CrlValidatedID ::= SEQUENCE { + /// crlHash OtherHash, + /// crlIdentifier CrlIdentifier OPTIONAL} + /// </code> + /// </remarks> + public class CrlValidatedID + : Asn1Encodable + { + private readonly OtherHash crlHash; + private readonly CrlIdentifier crlIdentifier; + + public static CrlValidatedID GetInstance( + object obj) + { + if (obj == null || obj is CrlValidatedID) + return (CrlValidatedID) obj; + + if (obj is Asn1Sequence) + return new CrlValidatedID((Asn1Sequence) obj); + + throw new ArgumentException( + "Unknown object in 'CrlValidatedID' factory: " + + obj.GetType().Name, + "obj"); + } + + private CrlValidatedID( + Asn1Sequence seq) + { + if (seq == null) + throw new ArgumentNullException("seq"); + if (seq.Count < 1 || seq.Count > 2) + throw new ArgumentException("Bad sequence size: " + seq.Count, "seq"); + + this.crlHash = OtherHash.GetInstance(seq[0].ToAsn1Object()); + + if (seq.Count > 1) + { + this.crlIdentifier = CrlIdentifier.GetInstance(seq[1].ToAsn1Object()); + } + } + + public CrlValidatedID( + OtherHash crlHash) + : this(crlHash, null) + { + } + + public CrlValidatedID( + OtherHash crlHash, + CrlIdentifier crlIdentifier) + { + if (crlHash == null) + throw new ArgumentNullException("crlHash"); + + this.crlHash = crlHash; + this.crlIdentifier = crlIdentifier; + } + + public OtherHash CrlHash + { + get { return crlHash; } + } + + public CrlIdentifier CrlIdentifier + { + get { return crlIdentifier; } + } + + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(crlHash.ToAsn1Object()); + + if (crlIdentifier != null) + { + v.Add(crlIdentifier.ToAsn1Object()); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/esf/ESFAttributes.cs b/Crypto/src/asn1/esf/ESFAttributes.cs new file mode 100644
index 000000000..9401ffb8e --- /dev/null +++ b/Crypto/src/asn1/esf/ESFAttributes.cs
@@ -0,0 +1,25 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Pkcs; + +namespace Org.BouncyCastle.Asn1.Esf +{ + public abstract class EsfAttributes + { + public static readonly DerObjectIdentifier SigPolicyId = PkcsObjectIdentifiers.IdAAEtsSigPolicyID; + public static readonly DerObjectIdentifier CommitmentType = PkcsObjectIdentifiers.IdAAEtsCommitmentType; + public static readonly DerObjectIdentifier SignerLocation = PkcsObjectIdentifiers.IdAAEtsSignerLocation; + public static readonly DerObjectIdentifier SignerAttr = PkcsObjectIdentifiers.IdAAEtsSignerAttr; + public static readonly DerObjectIdentifier OtherSigCert = PkcsObjectIdentifiers.IdAAEtsOtherSigCert; + public static readonly DerObjectIdentifier ContentTimestamp = PkcsObjectIdentifiers.IdAAEtsContentTimestamp; + public static readonly DerObjectIdentifier CertificateRefs = PkcsObjectIdentifiers.IdAAEtsCertificateRefs; + public static readonly DerObjectIdentifier RevocationRefs = PkcsObjectIdentifiers.IdAAEtsRevocationRefs; + public static readonly DerObjectIdentifier CertValues = PkcsObjectIdentifiers.IdAAEtsCertValues; + public static readonly DerObjectIdentifier RevocationValues = PkcsObjectIdentifiers.IdAAEtsRevocationValues; + public static readonly DerObjectIdentifier EscTimeStamp = PkcsObjectIdentifiers.IdAAEtsEscTimeStamp; + public static readonly DerObjectIdentifier CertCrlTimestamp = PkcsObjectIdentifiers.IdAAEtsCertCrlTimestamp; + public static readonly DerObjectIdentifier ArchiveTimestamp = PkcsObjectIdentifiers.IdAAEtsArchiveTimestamp; + public static readonly DerObjectIdentifier ArchiveTimestampV2 = new DerObjectIdentifier(PkcsObjectIdentifiers.IdAA + ".48"); + } +} diff --git a/Crypto/src/asn1/esf/OcspIdentifier.cs b/Crypto/src/asn1/esf/OcspIdentifier.cs new file mode 100644
index 000000000..949b68243 --- /dev/null +++ b/Crypto/src/asn1/esf/OcspIdentifier.cs
@@ -0,0 +1,77 @@ +using System; + +using Org.BouncyCastle.Asn1.Ocsp; + +namespace Org.BouncyCastle.Asn1.Esf +{ + /// <remarks> + /// RFC 3126: 4.2.2 Complete Revocation Refs Attribute Definition + /// <code> + /// OcspIdentifier ::= SEQUENCE { + /// ocspResponderID ResponderID, + /// -- As in OCSP response data + /// producedAt GeneralizedTime + /// -- As in OCSP response data + /// } + /// </code> + /// </remarks> + public class OcspIdentifier + : Asn1Encodable + { + private readonly ResponderID ocspResponderID; + private readonly DerGeneralizedTime producedAt; + + public static OcspIdentifier GetInstance( + object obj) + { + if (obj == null || obj is OcspIdentifier) + return (OcspIdentifier) obj; + + if (obj is Asn1Sequence) + return new OcspIdentifier((Asn1Sequence) obj); + + throw new ArgumentException( + "Unknown object in 'OcspIdentifier' factory: " + + obj.GetType().Name, + "obj"); + } + + private OcspIdentifier( + Asn1Sequence seq) + { + if (seq == null) + throw new ArgumentNullException("seq"); + if (seq.Count != 2) + throw new ArgumentException("Bad sequence size: " + seq.Count, "seq"); + + this.ocspResponderID = ResponderID.GetInstance(seq[0].ToAsn1Object()); + this.producedAt = (DerGeneralizedTime) seq[1].ToAsn1Object(); + } + + public OcspIdentifier( + ResponderID ocspResponderID, + DateTime producedAt) + { + if (ocspResponderID == null) + throw new ArgumentNullException(); + + this.ocspResponderID = ocspResponderID; + this.producedAt = new DerGeneralizedTime(producedAt); + } + + public ResponderID OcspResponderID + { + get { return ocspResponderID; } + } + + public DateTime ProducedAt + { + get { return producedAt.ToDateTime(); } + } + + public override Asn1Object ToAsn1Object() + { + return new DerSequence(ocspResponderID, producedAt); + } + } +} diff --git a/Crypto/src/asn1/esf/OcspListID.cs b/Crypto/src/asn1/esf/OcspListID.cs new file mode 100644
index 000000000..1f3f3a337 --- /dev/null +++ b/Crypto/src/asn1/esf/OcspListID.cs
@@ -0,0 +1,88 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Asn1.Esf +{ + /// <remarks> + /// RFC 3126: 4.2.2 Complete Revocation Refs Attribute Definition + /// <code> + /// OcspListID ::= SEQUENCE { + /// ocspResponses SEQUENCE OF OcspResponsesID + /// } + /// </code> + /// </remarks> + public class OcspListID + : Asn1Encodable + { + private readonly Asn1Sequence ocspResponses; + + public static OcspListID GetInstance( + object obj) + { + if (obj == null || obj is OcspListID) + return (OcspListID) obj; + + if (obj is Asn1Sequence) + return new OcspListID((Asn1Sequence) obj); + + throw new ArgumentException( + "Unknown object in 'OcspListID' factory: " + + obj.GetType().Name, + "obj"); + } + + private OcspListID( + Asn1Sequence seq) + { + if (seq == null) + throw new ArgumentNullException("seq"); + if (seq.Count != 1) + throw new ArgumentException("Bad sequence size: " + seq.Count, "seq"); + + this.ocspResponses = (Asn1Sequence) seq[0].ToAsn1Object(); + + foreach (Asn1Encodable ae in this.ocspResponses) + { + OcspResponsesID.GetInstance(ae.ToAsn1Object()); + } + } + + public OcspListID( + params OcspResponsesID[] ocspResponses) + { + if (ocspResponses == null) + throw new ArgumentNullException("ocspResponses"); + + this.ocspResponses = new DerSequence(ocspResponses); + } + + public OcspListID( + IEnumerable ocspResponses) + { + if (ocspResponses == null) + throw new ArgumentNullException("ocspResponses"); + if (!CollectionUtilities.CheckElementsAreOfType(ocspResponses, typeof(OcspResponsesID))) + throw new ArgumentException("Must contain only 'OcspResponsesID' objects", "ocspResponses"); + + this.ocspResponses = new DerSequence( + Asn1EncodableVector.FromEnumerable(ocspResponses)); + } + + public OcspResponsesID[] GetOcspResponses() + { + OcspResponsesID[] result = new OcspResponsesID[ocspResponses.Count]; + for (int i = 0; i < ocspResponses.Count; ++i) + { + result[i] = OcspResponsesID.GetInstance(ocspResponses[i].ToAsn1Object()); + } + return result; + } + + public override Asn1Object ToAsn1Object() + { + return new DerSequence(ocspResponses); + } + } +} diff --git a/Crypto/src/asn1/esf/OcspResponsesID.cs b/Crypto/src/asn1/esf/OcspResponsesID.cs new file mode 100644
index 000000000..e09508a01 --- /dev/null +++ b/Crypto/src/asn1/esf/OcspResponsesID.cs
@@ -0,0 +1,92 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Esf +{ + /// <remarks> + /// RFC 3126: 4.2.2 Complete Revocation Refs Attribute Definition + /// <code> + /// OcspResponsesID ::= SEQUENCE { + /// ocspIdentifier OcspIdentifier, + /// ocspRepHash OtherHash OPTIONAL + /// } + /// </code> + /// </remarks> + public class OcspResponsesID + : Asn1Encodable + { + private readonly OcspIdentifier ocspIdentifier; + private readonly OtherHash ocspRepHash; + + public static OcspResponsesID GetInstance( + object obj) + { + if (obj == null || obj is OcspResponsesID) + return (OcspResponsesID) obj; + + if (obj is Asn1Sequence) + return new OcspResponsesID((Asn1Sequence) obj); + + throw new ArgumentException( + "Unknown object in 'OcspResponsesID' factory: " + + obj.GetType().Name, + "obj"); + } + + private OcspResponsesID( + Asn1Sequence seq) + { + if (seq == null) + throw new ArgumentNullException("seq"); + if (seq.Count < 1 || seq.Count > 2) + throw new ArgumentException("Bad sequence size: " + seq.Count, "seq"); + + this.ocspIdentifier = OcspIdentifier.GetInstance(seq[0].ToAsn1Object()); + + if (seq.Count > 1) + { + this.ocspRepHash = OtherHash.GetInstance(seq[1].ToAsn1Object()); + } + } + + public OcspResponsesID( + OcspIdentifier ocspIdentifier) + : this(ocspIdentifier, null) + { + } + + public OcspResponsesID( + OcspIdentifier ocspIdentifier, + OtherHash ocspRepHash) + { + if (ocspIdentifier == null) + throw new ArgumentNullException("ocspIdentifier"); + + this.ocspIdentifier = ocspIdentifier; + this.ocspRepHash = ocspRepHash; + } + + public OcspIdentifier OcspIdentifier + { + get { return ocspIdentifier; } + } + + public OtherHash OcspRepHash + { + get { return ocspRepHash; } + } + + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector( + ocspIdentifier.ToAsn1Object()); + + if (ocspRepHash != null) + { + v.Add(ocspRepHash.ToAsn1Object()); + } + + return new DerSequence(v); + } + + } +} diff --git a/Crypto/src/asn1/esf/OtherCertID.cs b/Crypto/src/asn1/esf/OtherCertID.cs new file mode 100644
index 000000000..6d1255535 --- /dev/null +++ b/Crypto/src/asn1/esf/OtherCertID.cs
@@ -0,0 +1,93 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Esf +{ + /// <remarks> + /// <code> + /// OtherCertID ::= SEQUENCE { + /// otherCertHash OtherHash, + /// issuerSerial IssuerSerial OPTIONAL + /// } + /// </code> + /// </remarks> + public class OtherCertID + : Asn1Encodable + { + private readonly OtherHash otherCertHash; + private readonly IssuerSerial issuerSerial; + + public static OtherCertID GetInstance( + object obj) + { + if (obj == null || obj is OtherCertID) + return (OtherCertID) obj; + + if (obj is Asn1Sequence) + return new OtherCertID((Asn1Sequence) obj); + + throw new ArgumentException( + "Unknown object in 'OtherCertID' factory: " + + obj.GetType().Name, + "obj"); + } + + private OtherCertID( + Asn1Sequence seq) + { + if (seq == null) + throw new ArgumentNullException("seq"); + if (seq.Count < 1 || seq.Count > 2) + throw new ArgumentException("Bad sequence size: " + seq.Count, "seq"); + + this.otherCertHash = OtherHash.GetInstance(seq[0].ToAsn1Object()); + + if (seq.Count > 1) + { + this.issuerSerial = IssuerSerial.GetInstance(seq[1].ToAsn1Object()); + } + } + + public OtherCertID( + OtherHash otherCertHash) + : this(otherCertHash, null) + { + } + + public OtherCertID( + OtherHash otherCertHash, + IssuerSerial issuerSerial) + { + if (otherCertHash == null) + throw new ArgumentNullException("otherCertHash"); + + this.otherCertHash = otherCertHash; + this.issuerSerial = issuerSerial; + } + + public OtherHash OtherCertHash + { + get { return otherCertHash; } + } + + public IssuerSerial IssuerSerial + { + get { return issuerSerial; } + } + + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector( + otherCertHash.ToAsn1Object()); + + if (issuerSerial != null) + { + v.Add(issuerSerial.ToAsn1Object()); + } + + return new DerSequence(v); + } + + } +} diff --git a/Crypto/src/asn1/esf/OtherHash.cs b/Crypto/src/asn1/esf/OtherHash.cs new file mode 100644
index 000000000..2ee162478 --- /dev/null +++ b/Crypto/src/asn1/esf/OtherHash.cs
@@ -0,0 +1,88 @@ +using System; + +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Esf +{ + /// <remarks> + /// <code> + /// OtherHash ::= CHOICE { + /// sha1Hash OtherHashValue, -- This contains a SHA-1 hash + /// otherHash OtherHashAlgAndValue + /// } + /// + /// OtherHashValue ::= OCTET STRING + /// </code> + /// </remarks> + public class OtherHash + : Asn1Encodable, IAsn1Choice + { + private readonly Asn1OctetString sha1Hash; + private readonly OtherHashAlgAndValue otherHash; + + public static OtherHash GetInstance( + object obj) + { + if (obj == null || obj is OtherHash) + return (OtherHash) obj; + + if (obj is Asn1OctetString) + return new OtherHash((Asn1OctetString) obj); + + return new OtherHash( + OtherHashAlgAndValue.GetInstance(obj)); + } + + public OtherHash( + byte[] sha1Hash) + { + if (sha1Hash == null) + throw new ArgumentNullException("sha1Hash"); + + this.sha1Hash = new DerOctetString(sha1Hash); + } + + public OtherHash( + Asn1OctetString sha1Hash) + { + if (sha1Hash == null) + throw new ArgumentNullException("sha1Hash"); + + this.sha1Hash = sha1Hash; + } + + public OtherHash( + OtherHashAlgAndValue otherHash) + { + if (otherHash == null) + throw new ArgumentNullException("otherHash"); + + this.otherHash = otherHash; + } + + public AlgorithmIdentifier HashAlgorithm + { + get + { + return otherHash == null + ? new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1) + : otherHash.HashAlgorithm; + } + } + + public byte[] GetHashValue() + { + return otherHash == null + ? sha1Hash.GetOctets() + : otherHash.GetHashValue(); + } + + public override Asn1Object ToAsn1Object() + { + return otherHash == null + ? sha1Hash + : otherHash.ToAsn1Object(); + } + } +} diff --git a/Crypto/src/asn1/esf/OtherHashAlgAndValue.cs b/Crypto/src/asn1/esf/OtherHashAlgAndValue.cs new file mode 100644
index 000000000..b6bd4f498 --- /dev/null +++ b/Crypto/src/asn1/esf/OtherHashAlgAndValue.cs
@@ -0,0 +1,94 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Esf +{ + /// <summary> + /// Summary description for OtherHashAlgAndValue. + /// </summary> + /// <remarks> + /// <code> + /// OtherHashAlgAndValue ::= SEQUENCE { + /// hashAlgorithm AlgorithmIdentifier, + /// hashValue OtherHashValue + /// } + /// + /// OtherHashValue ::= OCTET STRING + /// </code> + /// </remarks> + public class OtherHashAlgAndValue + : Asn1Encodable + { + private readonly AlgorithmIdentifier hashAlgorithm; + private readonly Asn1OctetString hashValue; + + public static OtherHashAlgAndValue GetInstance( + object obj) + { + if (obj == null || obj is OtherHashAlgAndValue) + return (OtherHashAlgAndValue) obj; + + if (obj is Asn1Sequence) + return new OtherHashAlgAndValue((Asn1Sequence) obj); + + throw new ArgumentException( + "Unknown object in 'OtherHashAlgAndValue' factory: " + + obj.GetType().Name, + "obj"); + } + + private OtherHashAlgAndValue( + Asn1Sequence seq) + { + if (seq == null) + throw new ArgumentNullException("seq"); + if (seq.Count != 2) + throw new ArgumentException("Bad sequence size: " + seq.Count, "seq"); + + this.hashAlgorithm = AlgorithmIdentifier.GetInstance(seq[0].ToAsn1Object()); + this.hashValue = (Asn1OctetString) seq[1].ToAsn1Object(); + } + + public OtherHashAlgAndValue( + AlgorithmIdentifier hashAlgorithm, + byte[] hashValue) + { + if (hashAlgorithm == null) + throw new ArgumentNullException("hashAlgorithm"); + if (hashValue == null) + throw new ArgumentNullException("hashValue"); + + this.hashAlgorithm = hashAlgorithm; + this.hashValue = new DerOctetString(hashValue); + } + + public OtherHashAlgAndValue( + AlgorithmIdentifier hashAlgorithm, + Asn1OctetString hashValue) + { + if (hashAlgorithm == null) + throw new ArgumentNullException("hashAlgorithm"); + if (hashValue == null) + throw new ArgumentNullException("hashValue"); + + this.hashAlgorithm = hashAlgorithm; + this.hashValue = hashValue; + } + + public AlgorithmIdentifier HashAlgorithm + { + get { return hashAlgorithm; } + } + + public byte[] GetHashValue() + { + return hashValue.GetOctets(); + } + + public override Asn1Object ToAsn1Object() + { + return new DerSequence(hashAlgorithm, hashValue); + } + } +} diff --git a/Crypto/src/asn1/esf/OtherRevRefs.cs b/Crypto/src/asn1/esf/OtherRevRefs.cs new file mode 100644
index 000000000..56713e3f2 --- /dev/null +++ b/Crypto/src/asn1/esf/OtherRevRefs.cs
@@ -0,0 +1,78 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Esf +{ + /// <remarks> + /// RFC 3126: 4.2.2 Complete Revocation Refs Attribute Definition + /// <code> + /// OtherRevRefs ::= SEQUENCE + /// { + /// otherRevRefType OtherRevRefType, + /// otherRevRefs ANY DEFINED BY otherRevRefType + /// } + /// + /// OtherRevRefType ::= OBJECT IDENTIFIER + /// </code> + /// </remarks> + public class OtherRevRefs + : Asn1Encodable + { + private readonly DerObjectIdentifier otherRevRefType; + private readonly Asn1Object otherRevRefs; + + public static OtherRevRefs GetInstance( + object obj) + { + if (obj == null || obj is OtherRevRefs) + return (OtherRevRefs) obj; + + if (obj is Asn1Sequence) + return new OtherRevRefs((Asn1Sequence) obj); + + throw new ArgumentException( + "Unknown object in 'OtherRevRefs' factory: " + + obj.GetType().Name, + "obj"); + } + + private OtherRevRefs( + Asn1Sequence seq) + { + if (seq == null) + throw new ArgumentNullException("seq"); + if (seq.Count != 2) + throw new ArgumentException("Bad sequence size: " + seq.Count, "seq"); + + this.otherRevRefType = (DerObjectIdentifier) seq[0].ToAsn1Object(); + this.otherRevRefs = seq[1].ToAsn1Object(); + } + + public OtherRevRefs( + DerObjectIdentifier otherRevRefType, + Asn1Encodable otherRevRefs) + { + if (otherRevRefType == null) + throw new ArgumentNullException("otherRevRefType"); + if (otherRevRefs == null) + throw new ArgumentNullException("otherRevRefs"); + + this.otherRevRefType = otherRevRefType; + this.otherRevRefs = otherRevRefs.ToAsn1Object(); + } + + public DerObjectIdentifier OtherRevRefType + { + get { return otherRevRefType; } + } + + public Asn1Object OtherRevRefsObject + { + get { return otherRevRefs; } + } + + public override Asn1Object ToAsn1Object() + { + return new DerSequence(otherRevRefType, otherRevRefs); + } + } +} diff --git a/Crypto/src/asn1/esf/OtherRevVals.cs b/Crypto/src/asn1/esf/OtherRevVals.cs new file mode 100644
index 000000000..b88a1a72a --- /dev/null +++ b/Crypto/src/asn1/esf/OtherRevVals.cs
@@ -0,0 +1,78 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Esf +{ + /// <remarks> + /// RFC 3126: 4.3.2 Revocation Values Attribute Definition + /// <code> + /// OtherRevVals ::= SEQUENCE + /// { + /// otherRevValType OtherRevValType, + /// otherRevVals ANY DEFINED BY otherRevValType + /// } + /// + /// OtherRevValType ::= OBJECT IDENTIFIER + /// </code> + /// </remarks> + public class OtherRevVals + : Asn1Encodable + { + private readonly DerObjectIdentifier otherRevValType; + private readonly Asn1Object otherRevVals; + + public static OtherRevVals GetInstance( + object obj) + { + if (obj == null || obj is OtherRevVals) + return (OtherRevVals) obj; + + if (obj is Asn1Sequence) + return new OtherRevVals((Asn1Sequence) obj); + + throw new ArgumentException( + "Unknown object in 'OtherRevVals' factory: " + + obj.GetType().Name, + "obj"); + } + + private OtherRevVals( + Asn1Sequence seq) + { + if (seq == null) + throw new ArgumentNullException("seq"); + if (seq.Count != 2) + throw new ArgumentException("Bad sequence size: " + seq.Count, "seq"); + + this.otherRevValType = (DerObjectIdentifier) seq[0].ToAsn1Object(); + this.otherRevVals = seq[1].ToAsn1Object(); + } + + public OtherRevVals( + DerObjectIdentifier otherRevValType, + Asn1Encodable otherRevVals) + { + if (otherRevValType == null) + throw new ArgumentNullException("otherRevValType"); + if (otherRevVals == null) + throw new ArgumentNullException("otherRevVals"); + + this.otherRevValType = otherRevValType; + this.otherRevVals = otherRevVals.ToAsn1Object(); + } + + public DerObjectIdentifier OtherRevValType + { + get { return otherRevValType; } + } + + public Asn1Object OtherRevValsObject + { + get { return otherRevVals; } + } + + public override Asn1Object ToAsn1Object() + { + return new DerSequence(otherRevValType, otherRevVals); + } + } +} diff --git a/Crypto/src/asn1/esf/OtherSigningCertificate.cs b/Crypto/src/asn1/esf/OtherSigningCertificate.cs new file mode 100644
index 000000000..90e385a33 --- /dev/null +++ b/Crypto/src/asn1/esf/OtherSigningCertificate.cs
@@ -0,0 +1,138 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Asn1.Esf +{ + /// <remarks> + /// <code> + /// OtherSigningCertificate ::= SEQUENCE { + /// certs SEQUENCE OF OtherCertID, + /// policies SEQUENCE OF PolicyInformation OPTIONAL + /// } + /// </code> + /// </remarks> + public class OtherSigningCertificate + : Asn1Encodable + { + private readonly Asn1Sequence certs; + private readonly Asn1Sequence policies; + + public static OtherSigningCertificate GetInstance( + object obj) + { + if (obj == null || obj is OtherSigningCertificate) + return (OtherSigningCertificate) obj; + + if (obj is Asn1Sequence) + return new OtherSigningCertificate((Asn1Sequence) obj); + + throw new ArgumentException( + "Unknown object in 'OtherSigningCertificate' factory: " + + obj.GetType().Name, + "obj"); + } + + private OtherSigningCertificate( + Asn1Sequence seq) + { + if (seq == null) + throw new ArgumentNullException("seq"); + if (seq.Count < 1 || seq.Count > 2) + throw new ArgumentException("Bad sequence size: " + seq.Count, "seq"); + + this.certs = Asn1Sequence.GetInstance(seq[0].ToAsn1Object()); + + if (seq.Count > 1) + { + this.policies = Asn1Sequence.GetInstance(seq[1].ToAsn1Object()); + } + } + + public OtherSigningCertificate( + params OtherCertID[] certs) + : this(certs, null) + { + } + + public OtherSigningCertificate( + OtherCertID[] certs, + params PolicyInformation[] policies) + { + if (certs == null) + throw new ArgumentNullException("certs"); + + this.certs = new DerSequence(certs); + + if (policies != null) + { + this.policies = new DerSequence(policies); + } + } + + public OtherSigningCertificate( + IEnumerable certs) + : this(certs, null) + { + } + + public OtherSigningCertificate( + IEnumerable certs, + IEnumerable policies) + { + if (certs == null) + throw new ArgumentNullException("certs"); + if (!CollectionUtilities.CheckElementsAreOfType(certs, typeof(OtherCertID))) + throw new ArgumentException("Must contain only 'OtherCertID' objects", "certs"); + + this.certs = new DerSequence( + Asn1EncodableVector.FromEnumerable(certs)); + + if (policies != null) + { + if (!CollectionUtilities.CheckElementsAreOfType(policies, typeof(PolicyInformation))) + throw new ArgumentException("Must contain only 'PolicyInformation' objects", "policies"); + + this.policies = new DerSequence( + Asn1EncodableVector.FromEnumerable(policies)); + } + } + + public OtherCertID[] GetCerts() + { + OtherCertID[] cs = new OtherCertID[certs.Count]; + for (int i = 0; i < certs.Count; ++i) + { + cs[i] = OtherCertID.GetInstance(certs[i].ToAsn1Object()); + } + return cs; + } + + public PolicyInformation[] GetPolicies() + { + if (policies == null) + return null; + + PolicyInformation[] ps = new PolicyInformation[policies.Count]; + for (int i = 0; i < policies.Count; ++i) + { + ps[i] = PolicyInformation.GetInstance(policies[i].ToAsn1Object()); + } + return ps; + } + + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(certs); + + if (policies != null) + { + v.Add(policies); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/esf/RevocationValues.cs b/Crypto/src/asn1/esf/RevocationValues.cs new file mode 100644
index 000000000..a7b47b47a --- /dev/null +++ b/Crypto/src/asn1/esf/RevocationValues.cs
@@ -0,0 +1,165 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1.Ocsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Asn1.Esf +{ + /// <remarks> + /// RFC 5126: 6.3.4. revocation-values Attribute Definition + /// <code> + /// RevocationValues ::= SEQUENCE { + /// crlVals [0] SEQUENCE OF CertificateList OPTIONAL, + /// ocspVals [1] SEQUENCE OF BasicOCSPResponse OPTIONAL, + /// otherRevVals [2] OtherRevVals OPTIONAL + /// } + /// </code> + /// </remarks> + public class RevocationValues + : Asn1Encodable + { + private readonly Asn1Sequence crlVals; + private readonly Asn1Sequence ocspVals; + private readonly OtherRevVals otherRevVals; + + public static RevocationValues GetInstance( + object obj) + { + if (obj == null || obj is RevocationValues) + return (RevocationValues) obj; + + return new RevocationValues(Asn1Sequence.GetInstance(obj)); + } + + private RevocationValues( + Asn1Sequence seq) + { + if (seq == null) + throw new ArgumentNullException("seq"); + if (seq.Count > 3) + throw new ArgumentException("Bad sequence size: " + seq.Count, "seq"); + + foreach (Asn1TaggedObject taggedObj in seq) + { + Asn1Object asn1Obj = taggedObj.GetObject(); + switch (taggedObj.TagNo) + { + case 0: + Asn1Sequence crlValsSeq = (Asn1Sequence) asn1Obj; + foreach (Asn1Encodable ae in crlValsSeq) + { + CertificateList.GetInstance(ae.ToAsn1Object()); + } + this.crlVals = crlValsSeq; + break; + case 1: + Asn1Sequence ocspValsSeq = (Asn1Sequence) asn1Obj; + foreach (Asn1Encodable ae in ocspValsSeq) + { + BasicOcspResponse.GetInstance(ae.ToAsn1Object()); + } + this.ocspVals = ocspValsSeq; + break; + case 2: + this.otherRevVals = OtherRevVals.GetInstance(asn1Obj); + break; + default: + throw new ArgumentException("Illegal tag in RevocationValues", "seq"); + } + } + } + + public RevocationValues( + CertificateList[] crlVals, + BasicOcspResponse[] ocspVals, + OtherRevVals otherRevVals) + { + if (crlVals != null) + { + this.crlVals = new DerSequence(crlVals); + } + + if (ocspVals != null) + { + this.ocspVals = new DerSequence(ocspVals); + } + + this.otherRevVals = otherRevVals; + } + + public RevocationValues( + IEnumerable crlVals, + IEnumerable ocspVals, + OtherRevVals otherRevVals) + { + if (crlVals != null) + { + if (!CollectionUtilities.CheckElementsAreOfType(crlVals, typeof(CertificateList))) + throw new ArgumentException("Must contain only 'CertificateList' objects", "crlVals"); + + this.crlVals = new DerSequence( + Asn1EncodableVector.FromEnumerable(crlVals)); + } + + if (ocspVals != null) + { + if (!CollectionUtilities.CheckElementsAreOfType(ocspVals, typeof(BasicOcspResponse))) + throw new ArgumentException("Must contain only 'BasicOcspResponse' objects", "ocspVals"); + + this.ocspVals = new DerSequence( + Asn1EncodableVector.FromEnumerable(ocspVals)); + } + + this.otherRevVals = otherRevVals; + } + + public CertificateList[] GetCrlVals() + { + CertificateList[] result = new CertificateList[crlVals.Count]; + for (int i = 0; i < crlVals.Count; ++i) + { + result[i] = CertificateList.GetInstance(crlVals[i].ToAsn1Object()); + } + return result; + } + + public BasicOcspResponse[] GetOcspVals() + { + BasicOcspResponse[] result = new BasicOcspResponse[ocspVals.Count]; + for (int i = 0; i < ocspVals.Count; ++i) + { + result[i] = BasicOcspResponse.GetInstance(ocspVals[i].ToAsn1Object()); + } + return result; + } + + public OtherRevVals OtherRevVals + { + get { return otherRevVals; } + } + + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(); + + if (crlVals != null) + { + v.Add(new DerTaggedObject(true, 0, crlVals)); + } + + if (ocspVals != null) + { + v.Add(new DerTaggedObject(true, 1, ocspVals)); + } + + if (otherRevVals != null) + { + v.Add(new DerTaggedObject(true, 2, otherRevVals.ToAsn1Object())); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/esf/SigPolicyQualifierInfo.cs b/Crypto/src/asn1/esf/SigPolicyQualifierInfo.cs new file mode 100644
index 000000000..2d36bc751 --- /dev/null +++ b/Crypto/src/asn1/esf/SigPolicyQualifierInfo.cs
@@ -0,0 +1,71 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Esf +{ + /// <remarks> + /// <code> + /// SigPolicyQualifierInfo ::= SEQUENCE { + /// sigPolicyQualifierId SigPolicyQualifierId, + /// sigQualifier ANY DEFINED BY sigPolicyQualifierId + /// } + /// + /// SigPolicyQualifierId ::= OBJECT IDENTIFIER + /// </code> + /// </remarks> + public class SigPolicyQualifierInfo + : Asn1Encodable + { + private readonly DerObjectIdentifier sigPolicyQualifierId; + private readonly Asn1Object sigQualifier; + + public static SigPolicyQualifierInfo GetInstance( + object obj) + { + if (obj == null || obj is SigPolicyQualifierInfo) + return (SigPolicyQualifierInfo) obj; + + if (obj is Asn1Sequence) + return new SigPolicyQualifierInfo((Asn1Sequence) obj); + + throw new ArgumentException( + "Unknown object in 'SigPolicyQualifierInfo' factory: " + + obj.GetType().Name, + "obj"); + } + + private SigPolicyQualifierInfo( + Asn1Sequence seq) + { + if (seq == null) + throw new ArgumentNullException("seq"); + if (seq.Count != 2) + throw new ArgumentException("Bad sequence size: " + seq.Count, "seq"); + + this.sigPolicyQualifierId = (DerObjectIdentifier) seq[0].ToAsn1Object(); + this.sigQualifier = seq[1].ToAsn1Object(); + } + + public SigPolicyQualifierInfo( + DerObjectIdentifier sigPolicyQualifierId, + Asn1Encodable sigQualifier) + { + this.sigPolicyQualifierId = sigPolicyQualifierId; + this.sigQualifier = sigQualifier.ToAsn1Object(); + } + + public DerObjectIdentifier SigPolicyQualifierId + { + get { return sigPolicyQualifierId; } + } + + public Asn1Object SigQualifier + { + get { return sigQualifier; } + } + + public override Asn1Object ToAsn1Object() + { + return new DerSequence(sigPolicyQualifierId, sigQualifier); + } + } +} diff --git a/Crypto/src/asn1/esf/SignaturePolicyId.cs b/Crypto/src/asn1/esf/SignaturePolicyId.cs new file mode 100644
index 000000000..545be2cf4 --- /dev/null +++ b/Crypto/src/asn1/esf/SignaturePolicyId.cs
@@ -0,0 +1,145 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Asn1.Esf +{ + /// <remarks> + /// <code> + /// SignaturePolicyId ::= SEQUENCE { + /// sigPolicyIdentifier SigPolicyId, + /// sigPolicyHash SigPolicyHash, + /// sigPolicyQualifiers SEQUENCE SIZE (1..MAX) OF SigPolicyQualifierInfo OPTIONAL + /// } + /// + /// SigPolicyId ::= OBJECT IDENTIFIER + /// + /// SigPolicyHash ::= OtherHashAlgAndValue + /// </code> + /// </remarks> + public class SignaturePolicyId + : Asn1Encodable + { + private readonly DerObjectIdentifier sigPolicyIdentifier; + private readonly OtherHashAlgAndValue sigPolicyHash; + private readonly Asn1Sequence sigPolicyQualifiers; + + public static SignaturePolicyId GetInstance( + object obj) + { + if (obj == null || obj is SignaturePolicyId) + return (SignaturePolicyId) obj; + + if (obj is Asn1Sequence) + return new SignaturePolicyId((Asn1Sequence) obj); + + throw new ArgumentException( + "Unknown object in 'SignaturePolicyId' factory: " + + obj.GetType().Name, + "obj"); + } + + private SignaturePolicyId( + Asn1Sequence seq) + { + if (seq == null) + throw new ArgumentNullException("seq"); + if (seq.Count < 2 || seq.Count > 3) + throw new ArgumentException("Bad sequence size: " + seq.Count, "seq"); + + this.sigPolicyIdentifier = (DerObjectIdentifier) seq[0].ToAsn1Object(); + this.sigPolicyHash = OtherHashAlgAndValue.GetInstance(seq[1].ToAsn1Object()); + + if (seq.Count > 2) + { + this.sigPolicyQualifiers = (Asn1Sequence) seq[2].ToAsn1Object(); + } + } + + public SignaturePolicyId( + DerObjectIdentifier sigPolicyIdentifier, + OtherHashAlgAndValue sigPolicyHash) + : this(sigPolicyIdentifier, sigPolicyHash, null) + { + } + + public SignaturePolicyId( + DerObjectIdentifier sigPolicyIdentifier, + OtherHashAlgAndValue sigPolicyHash, + params SigPolicyQualifierInfo[] sigPolicyQualifiers) + { + if (sigPolicyIdentifier == null) + throw new ArgumentNullException("sigPolicyIdentifier"); + if (sigPolicyHash == null) + throw new ArgumentNullException("sigPolicyHash"); + + this.sigPolicyIdentifier = sigPolicyIdentifier; + this.sigPolicyHash = sigPolicyHash; + + if (sigPolicyQualifiers != null) + { + this.sigPolicyQualifiers = new DerSequence(sigPolicyQualifiers); + } + } + + public SignaturePolicyId( + DerObjectIdentifier sigPolicyIdentifier, + OtherHashAlgAndValue sigPolicyHash, + IEnumerable sigPolicyQualifiers) + { + if (sigPolicyIdentifier == null) + throw new ArgumentNullException("sigPolicyIdentifier"); + if (sigPolicyHash == null) + throw new ArgumentNullException("sigPolicyHash"); + + this.sigPolicyIdentifier = sigPolicyIdentifier; + this.sigPolicyHash = sigPolicyHash; + + if (sigPolicyQualifiers != null) + { + if (!CollectionUtilities.CheckElementsAreOfType(sigPolicyQualifiers, typeof(SigPolicyQualifierInfo))) + throw new ArgumentException("Must contain only 'SigPolicyQualifierInfo' objects", "sigPolicyQualifiers"); + + this.sigPolicyQualifiers = new DerSequence( + Asn1EncodableVector.FromEnumerable(sigPolicyQualifiers)); + } + } + + public DerObjectIdentifier SigPolicyIdentifier + { + get { return sigPolicyIdentifier; } + } + + public OtherHashAlgAndValue SigPolicyHash + { + get { return sigPolicyHash; } + } + + public SigPolicyQualifierInfo[] GetSigPolicyQualifiers() + { + if (sigPolicyQualifiers == null) + return null; + + SigPolicyQualifierInfo[] infos = new SigPolicyQualifierInfo[sigPolicyQualifiers.Count]; + for (int i = 0; i < sigPolicyQualifiers.Count; ++i) + { + infos[i] = SigPolicyQualifierInfo.GetInstance(sigPolicyQualifiers[i]); + } + return infos; + } + + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector( + sigPolicyIdentifier, sigPolicyHash.ToAsn1Object()); + + if (sigPolicyQualifiers != null) + { + v.Add(sigPolicyQualifiers.ToAsn1Object()); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/esf/SignaturePolicyIdentifier.cs b/Crypto/src/asn1/esf/SignaturePolicyIdentifier.cs new file mode 100644
index 000000000..3a639f444 --- /dev/null +++ b/Crypto/src/asn1/esf/SignaturePolicyIdentifier.cs
@@ -0,0 +1,64 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Esf +{ + /// <remarks> + /// <code> + /// SignaturePolicyIdentifier ::= CHOICE { + /// SignaturePolicyId SignaturePolicyId, + /// SignaturePolicyImplied SignaturePolicyImplied + /// } + /// + /// SignaturePolicyImplied ::= NULL + /// </code> + /// </remarks> + public class SignaturePolicyIdentifier + : Asn1Encodable, IAsn1Choice + { + private readonly SignaturePolicyId sigPolicy; + + public static SignaturePolicyIdentifier GetInstance( + object obj) + { + if (obj == null || obj is SignaturePolicyIdentifier) + return (SignaturePolicyIdentifier) obj; + + if (obj is SignaturePolicyId) + return new SignaturePolicyIdentifier((SignaturePolicyId) obj); + + if (obj is Asn1Null) + return new SignaturePolicyIdentifier(); + + throw new ArgumentException( + "Unknown object in 'SignaturePolicyIdentifier' factory: " + + obj.GetType().Name, + "obj"); + } + + public SignaturePolicyIdentifier() + { + this.sigPolicy = null; + } + + public SignaturePolicyIdentifier( + SignaturePolicyId signaturePolicyId) + { + if (signaturePolicyId == null) + throw new ArgumentNullException("signaturePolicyId"); + + this.sigPolicy = signaturePolicyId; + } + + public SignaturePolicyId SignaturePolicyId + { + get { return sigPolicy; } + } + + public override Asn1Object ToAsn1Object() + { + return sigPolicy == null + ? DerNull.Instance + : sigPolicy.ToAsn1Object(); + } + } +} diff --git a/Crypto/src/asn1/esf/SignerAttribute.cs b/Crypto/src/asn1/esf/SignerAttribute.cs new file mode 100644
index 000000000..ddee53c69 --- /dev/null +++ b/Crypto/src/asn1/esf/SignerAttribute.cs
@@ -0,0 +1,96 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Esf +{ + public class SignerAttribute + : Asn1Encodable + { + private Asn1Sequence claimedAttributes; + private AttributeCertificate certifiedAttributes; + + public static SignerAttribute GetInstance( + object obj) + { + if (obj == null || obj is SignerAttribute) + return (SignerAttribute) obj; + + if (obj is Asn1Sequence) + return new SignerAttribute(obj); + + throw new ArgumentException( + "Unknown object in 'SignerAttribute' factory: " + + obj.GetType().Name, + "obj"); + } + + private SignerAttribute( + object obj) + { + Asn1Sequence seq = (Asn1Sequence) obj; + DerTaggedObject taggedObject = (DerTaggedObject) seq[0]; + if (taggedObject.TagNo == 0) + { + claimedAttributes = Asn1Sequence.GetInstance(taggedObject, true); + } + else if (taggedObject.TagNo == 1) + { + certifiedAttributes = AttributeCertificate.GetInstance(taggedObject); + } + else + { + throw new ArgumentException("illegal tag.", "obj"); + } + } + + public SignerAttribute( + Asn1Sequence claimedAttributes) + { + this.claimedAttributes = claimedAttributes; + } + + public SignerAttribute( + AttributeCertificate certifiedAttributes) + { + this.certifiedAttributes = certifiedAttributes; + } + + public virtual Asn1Sequence ClaimedAttributes + { + get { return claimedAttributes; } + } + + public virtual AttributeCertificate CertifiedAttributes + { + get { return certifiedAttributes; } + } + + /** + * + * <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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(); + + if (claimedAttributes != null) + { + v.Add(new DerTaggedObject(0, claimedAttributes)); + } + else + { + v.Add(new DerTaggedObject(1, certifiedAttributes)); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/esf/SignerLocation.cs b/Crypto/src/asn1/esf/SignerLocation.cs new file mode 100644
index 000000000..d2cef51bb --- /dev/null +++ b/Crypto/src/asn1/esf/SignerLocation.cs
@@ -0,0 +1,144 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Esf +{ + /** + * 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 + : Asn1Encodable + { + // TODO Should these be using DirectoryString? + private DerUtf8String countryName; + private DerUtf8String localityName; + private Asn1Sequence postalAddress; + + public SignerLocation( + Asn1Sequence seq) + { + foreach (Asn1TaggedObject obj in seq) + { + switch (obj.TagNo) + { + case 0: + this.countryName = DerUtf8String.GetInstance(obj, true); + break; + case 1: + this.localityName = DerUtf8String.GetInstance(obj, true); + break; + case 2: + bool isExplicit = obj.IsExplicit(); // handle erroneous implicitly tagged sequences + this.postalAddress = Asn1Sequence.GetInstance(obj, isExplicit); + if (postalAddress != null && postalAddress.Count > 6) + throw new ArgumentException("postal address must contain less than 6 strings"); + break; + default: + throw new ArgumentException("illegal tag"); + } + } + } + + public SignerLocation( + DerUtf8String countryName, + DerUtf8String localityName, + Asn1Sequence postalAddress) + { + if (postalAddress != null && postalAddress.Count > 6) + { + throw new ArgumentException("postal address must contain less than 6 strings"); + } + + if (countryName != null) + { + this.countryName = DerUtf8String.GetInstance(countryName.ToAsn1Object()); + } + + if (localityName != null) + { + this.localityName = DerUtf8String.GetInstance(localityName.ToAsn1Object()); + } + + if (postalAddress != null) + { + this.postalAddress = (Asn1Sequence) postalAddress.ToAsn1Object(); + } + } + + public static SignerLocation GetInstance( + object obj) + { + if (obj == null || obj is SignerLocation) + { + return (SignerLocation) obj; + } + + return new SignerLocation(Asn1Sequence.GetInstance(obj)); + } + + public DerUtf8String CountryName + { + get { return countryName; } + } + + public DerUtf8String LocalityName + { + get { return localityName; } + } + + public Asn1Sequence PostalAddress + { + get { 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 override Asn1Object ToAsn1Object() + { + 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/Crypto/src/asn1/ess/ContentHints.cs b/Crypto/src/asn1/ess/ContentHints.cs new file mode 100644
index 000000000..a430fea8d --- /dev/null +++ b/Crypto/src/asn1/ess/ContentHints.cs
@@ -0,0 +1,92 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Ess +{ + public class ContentHints + : Asn1Encodable + { + private readonly DerUtf8String contentDescription; + private readonly DerObjectIdentifier contentType; + + public static ContentHints GetInstance( + object o) + { + if (o == null || o is ContentHints) + { + return (ContentHints)o; + } + + if (o is Asn1Sequence) + { + return new ContentHints((Asn1Sequence)o); + } + + throw new ArgumentException("unknown object in 'ContentHints' factory : " + + o.GetType().Name + "."); + } + + /** + * constructor + */ + private ContentHints( + Asn1Sequence seq) + { + IAsn1Convertible field = seq[0]; + if (field.ToAsn1Object() is DerUtf8String) + { + contentDescription = DerUtf8String.GetInstance(field); + contentType = DerObjectIdentifier.GetInstance(seq[1]); + } + else + { + contentType = DerObjectIdentifier.GetInstance(seq[0]); + } + } + + public ContentHints( + DerObjectIdentifier contentType) + { + this.contentType = contentType; + this.contentDescription = null; + } + + public ContentHints( + DerObjectIdentifier contentType, + DerUtf8String contentDescription) + { + this.contentType = contentType; + this.contentDescription = contentDescription; + } + + public DerObjectIdentifier ContentType + { + get { return contentType; } + } + + public DerUtf8String ContentDescription + { + get { return contentDescription; } + } + + /** + * <pre> + * ContentHints ::= SEQUENCE { + * contentDescription UTF8String (SIZE (1..MAX)) OPTIONAL, + * contentType ContentType } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(); + + if (contentDescription != null) + { + v.Add(contentDescription); + } + + v.Add(contentType); + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/ess/ContentIdentifier.cs b/Crypto/src/asn1/ess/ContentIdentifier.cs new file mode 100644
index 000000000..8058dcc53 --- /dev/null +++ b/Crypto/src/asn1/ess/ContentIdentifier.cs
@@ -0,0 +1,65 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Ess +{ + public class ContentIdentifier + : Asn1Encodable + { + private Asn1OctetString value; + + public static ContentIdentifier GetInstance( + object o) + { + if (o == null || o is ContentIdentifier) + { + return (ContentIdentifier) o; + } + + if (o is Asn1OctetString) + { + return new ContentIdentifier((Asn1OctetString) o); + } + + throw new ArgumentException( + "unknown object in 'ContentIdentifier' factory : " + + o.GetType().Name + "."); + } + + /** + * Create from OCTET STRING whose octets represent the identifier. + */ + public ContentIdentifier( + Asn1OctetString value) + { + this.value = value; + } + + /** + * Create from byte array representing the identifier. + */ + public ContentIdentifier( + byte[] value) + : this(new DerOctetString(value)) + { + } + + public Asn1OctetString Value + { + get { 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 override Asn1Object ToAsn1Object() + { + return value; + } + } +} diff --git a/Crypto/src/asn1/ess/ESSCertID.cs b/Crypto/src/asn1/ess/ESSCertID.cs new file mode 100644
index 000000000..4d449a746 --- /dev/null +++ b/Crypto/src/asn1/ess/ESSCertID.cs
@@ -0,0 +1,93 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Ess +{ + public class EssCertID + : Asn1Encodable + { + private Asn1OctetString certHash; + private IssuerSerial issuerSerial; + + public static EssCertID GetInstance( + object o) + { + if (o == null || o is EssCertID) + { + return (EssCertID) o; + } + + if (o is Asn1Sequence) + { + return new EssCertID((Asn1Sequence) o); + } + + throw new ArgumentException( + "unknown object in 'EssCertID' factory : " + + o.GetType().Name + "."); + } + + /** + * constructor + */ + public EssCertID( + Asn1Sequence seq) + { + if (seq.Count < 1 || seq.Count > 2) + { + throw new ArgumentException("Bad sequence size: " + seq.Count); + } + + this.certHash = Asn1OctetString.GetInstance(seq[0]); + + if (seq.Count > 1) + { + issuerSerial = IssuerSerial.GetInstance(seq[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 IssuerSerial + { + get { return issuerSerial; } + } + + /** + * <pre> + * EssCertID ::= SEQUENCE { + * certHash Hash, + * issuerSerial IssuerSerial OPTIONAL } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(certHash); + + if (issuerSerial != null) + { + v.Add(issuerSerial); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/ess/ESSCertIDv2.cs b/Crypto/src/asn1/ess/ESSCertIDv2.cs new file mode 100644
index 000000000..1d154cd27 --- /dev/null +++ b/Crypto/src/asn1/ess/ESSCertIDv2.cs
@@ -0,0 +1,138 @@ +using System; + +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1.Ess +{ + public class EssCertIDv2 + : Asn1Encodable + { + private readonly AlgorithmIdentifier hashAlgorithm; + private readonly byte[] certHash; + private readonly IssuerSerial issuerSerial; + + private static readonly AlgorithmIdentifier DefaultAlgID = new AlgorithmIdentifier( + NistObjectIdentifiers.IdSha256); + + public static EssCertIDv2 GetInstance( + object o) + { + if (o == null || o is EssCertIDv2) + return (EssCertIDv2) o; + + if (o is Asn1Sequence) + return new EssCertIDv2((Asn1Sequence) o); + + throw new ArgumentException( + "unknown object in 'EssCertIDv2' factory : " + + o.GetType().Name + "."); + } + + private EssCertIDv2( + Asn1Sequence seq) + { + if (seq.Count > 3) + throw new ArgumentException("Bad sequence size: " + seq.Count, "seq"); + + int count = 0; + + if (seq[0] is Asn1OctetString) + { + // Default value + this.hashAlgorithm = DefaultAlgID; + } + else + { + this.hashAlgorithm = AlgorithmIdentifier.GetInstance(seq[count++].ToAsn1Object()); + } + + this.certHash = Asn1OctetString.GetInstance(seq[count++].ToAsn1Object()).GetOctets(); + + if (seq.Count > count) + { + this.issuerSerial = IssuerSerial.GetInstance( + Asn1Sequence.GetInstance(seq[count].ToAsn1Object())); + } + } + + public EssCertIDv2( + AlgorithmIdentifier algId, + byte[] certHash) + : this(algId, certHash, null) + { + } + + public EssCertIDv2( + AlgorithmIdentifier algId, + byte[] certHash, + IssuerSerial issuerSerial) + { + if (algId == null) + { + // Default value + this.hashAlgorithm = DefaultAlgID; + } + else + { + this.hashAlgorithm = algId; + } + + this.certHash = certHash; + this.issuerSerial = issuerSerial; + } + + public AlgorithmIdentifier HashAlgorithm + { + get { return this.hashAlgorithm; } + } + + public byte[] GetCertHash() + { + return Arrays.Clone(certHash); + } + + public IssuerSerial IssuerSerial + { + get { 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(); + + if (!hashAlgorithm.Equals(DefaultAlgID)) + { + v.Add(hashAlgorithm); + } + + v.Add(new DerOctetString(certHash).ToAsn1Object()); + + if (issuerSerial != null) + { + v.Add(issuerSerial); + } + + return new DerSequence(v); + } + + } +} diff --git a/Crypto/src/asn1/ess/OtherCertID.cs b/Crypto/src/asn1/ess/OtherCertID.cs new file mode 100644
index 000000000..972ef8c6b --- /dev/null +++ b/Crypto/src/asn1/ess/OtherCertID.cs
@@ -0,0 +1,132 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Ess +{ + [Obsolete("Use version in Asn1.Esf instead")] + public class OtherCertID + : Asn1Encodable + { + private Asn1Encodable otherCertHash; + private IssuerSerial issuerSerial; + + public static OtherCertID GetInstance( + object o) + { + if (o == null || o is OtherCertID) + { + return (OtherCertID) o; + } + + if (o is Asn1Sequence) + { + return new OtherCertID((Asn1Sequence) o); + } + + throw new ArgumentException( + "unknown object in 'OtherCertID' factory : " + + o.GetType().Name + "."); + } + + /** + * constructor + */ + public OtherCertID( + Asn1Sequence seq) + { + if (seq.Count < 1 || seq.Count > 2) + { + throw new ArgumentException("Bad sequence size: " + seq.Count); + } + + if (seq[0].ToAsn1Object() is Asn1OctetString) + { + otherCertHash = Asn1OctetString.GetInstance(seq[0]); + } + else + { + otherCertHash = DigestInfo.GetInstance(seq[0]); + } + + if (seq.Count > 1) + { + issuerSerial = IssuerSerial.GetInstance(Asn1Sequence.GetInstance(seq[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 AlgorithmHash + { + get + { + if (otherCertHash.ToAsn1Object() is Asn1OctetString) + { + // SHA-1 + return new AlgorithmIdentifier("1.3.14.3.2.26"); + } + + return DigestInfo.GetInstance(otherCertHash).AlgorithmID; + } + } + + public byte[] GetCertHash() + { + if (otherCertHash.ToAsn1Object() is Asn1OctetString) + { + // SHA-1 + return ((Asn1OctetString) otherCertHash.ToAsn1Object()).GetOctets(); + } + + return DigestInfo.GetInstance(otherCertHash).GetDigest(); + } + + public IssuerSerial IssuerSerial + { + get { 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(otherCertHash); + + if (issuerSerial != null) + { + v.Add(issuerSerial); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/ess/OtherSigningCertificate.cs b/Crypto/src/asn1/ess/OtherSigningCertificate.cs new file mode 100644
index 000000000..c165fecea --- /dev/null +++ b/Crypto/src/asn1/ess/OtherSigningCertificate.cs
@@ -0,0 +1,109 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Ess +{ + [Obsolete("Use version in Asn1.Esf instead")] + public class OtherSigningCertificate + : Asn1Encodable + { + private Asn1Sequence certs, policies; + + public static OtherSigningCertificate GetInstance( + object o) + { + if (o == null || o is OtherSigningCertificate) + { + return (OtherSigningCertificate) o; + } + + if (o is Asn1Sequence) + { + return new OtherSigningCertificate((Asn1Sequence) o); + } + + throw new ArgumentException( + "unknown object in 'OtherSigningCertificate' factory : " + + o.GetType().Name + "."); + } + + /** + * constructors + */ + public OtherSigningCertificate( + Asn1Sequence seq) + { + if (seq.Count < 1 || seq.Count > 2) + { + throw new ArgumentException("Bad sequence size: " + seq.Count); + } + + this.certs = Asn1Sequence.GetInstance(seq[0]); + + if (seq.Count > 1) + { + this.policies = Asn1Sequence.GetInstance(seq[1]); + } + } + + public OtherSigningCertificate( + OtherCertID otherCertID) + { + certs = new DerSequence(otherCertID); + } + + public OtherCertID[] GetCerts() + { + OtherCertID[] cs = new OtherCertID[certs.Count]; + + for (int i = 0; i != certs.Count; ++i) + { + cs[i] = OtherCertID.GetInstance(certs[i]); + } + + return cs; + } + + public PolicyInformation[] GetPolicies() + { + if (policies == null) + { + return null; + } + + PolicyInformation[] ps = new PolicyInformation[policies.Count]; + + for (int i = 0; i != policies.Count; i++) + { + ps[i] = PolicyInformation.GetInstance(policies[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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(certs); + + if (policies != null) + { + v.Add(policies); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/ess/SigningCertificate.cs b/Crypto/src/asn1/ess/SigningCertificate.cs new file mode 100644
index 000000000..366749bc3 --- /dev/null +++ b/Crypto/src/asn1/ess/SigningCertificate.cs
@@ -0,0 +1,108 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Ess +{ + public class SigningCertificate + : Asn1Encodable + { + private Asn1Sequence certs, policies; + + public static SigningCertificate GetInstance( + object o) + { + if (o == null || o is SigningCertificate) + { + return (SigningCertificate) o; + } + + if (o is Asn1Sequence) + { + return new SigningCertificate((Asn1Sequence) o); + } + + throw new ArgumentException( + "unknown object in 'SigningCertificate' factory : " + + o.GetType().Name + "."); + } + + /** + * constructors + */ + public SigningCertificate( + Asn1Sequence seq) + { + if (seq.Count < 1 || seq.Count > 2) + { + throw new ArgumentException("Bad sequence size: " + seq.Count); + } + + this.certs = Asn1Sequence.GetInstance(seq[0]); + + if (seq.Count > 1) + { + this.policies = Asn1Sequence.GetInstance(seq[1]); + } + } + + public SigningCertificate( + EssCertID essCertID) + { + certs = new DerSequence(essCertID); + } + + public EssCertID[] GetCerts() + { + EssCertID[] cs = new EssCertID[certs.Count]; + + for (int i = 0; i != certs.Count; i++) + { + cs[i] = EssCertID.GetInstance(certs[i]); + } + + return cs; + } + + public PolicyInformation[] GetPolicies() + { + if (policies == null) + { + return null; + } + + PolicyInformation[] ps = new PolicyInformation[policies.Count]; + + for (int i = 0; i != policies.Count; i++) + { + ps[i] = PolicyInformation.GetInstance(policies[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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(certs); + + if (policies != null) + { + v.Add(policies); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/ess/SigningCertificateV2.cs b/Crypto/src/asn1/ess/SigningCertificateV2.cs new file mode 100644
index 000000000..a2aff4866 --- /dev/null +++ b/Crypto/src/asn1/ess/SigningCertificateV2.cs
@@ -0,0 +1,106 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Ess +{ + public class SigningCertificateV2 + : Asn1Encodable + { + private readonly Asn1Sequence certs; + private readonly Asn1Sequence policies; + + public static SigningCertificateV2 GetInstance( + object o) + { + if (o == null || o is SigningCertificateV2) + return (SigningCertificateV2) o; + + if (o is Asn1Sequence) + return new SigningCertificateV2((Asn1Sequence) o); + + throw new ArgumentException( + "unknown object in 'SigningCertificateV2' factory : " + + o.GetType().Name + "."); + } + + private SigningCertificateV2( + Asn1Sequence seq) + { + if (seq.Count < 1 || seq.Count > 2) + throw new ArgumentException("Bad sequence size: " + seq.Count, "seq"); + + this.certs = Asn1Sequence.GetInstance(seq[0].ToAsn1Object()); + + if (seq.Count > 1) + { + this.policies = Asn1Sequence.GetInstance(seq[1].ToAsn1Object()); + } + } + + public SigningCertificateV2( + EssCertIDv2[] certs) + { + this.certs = new DerSequence(certs); + } + + public SigningCertificateV2( + EssCertIDv2[] certs, + PolicyInformation[] policies) + { + this.certs = new DerSequence(certs); + + if (policies != null) + { + this.policies = new DerSequence(policies); + } + } + + public EssCertIDv2[] GetCerts() + { + EssCertIDv2[] certIds = new EssCertIDv2[certs.Count]; + for (int i = 0; i != certs.Count; i++) + { + certIds[i] = EssCertIDv2.GetInstance(certs[i]); + } + return certIds; + } + + public PolicyInformation[] GetPolicies() + { + if (policies == null) + return null; + + PolicyInformation[] policyInformations = new PolicyInformation[policies.Count]; + for (int i = 0; i != policies.Count; i++) + { + policyInformations[i] = PolicyInformation.GetInstance(policies[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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(certs); + + if (policies != null) + { + v.Add(policies); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/gnu/GNUObjectIdentifiers.cs b/Crypto/src/asn1/gnu/GNUObjectIdentifiers.cs new file mode 100644
index 000000000..9311a3ac1 --- /dev/null +++ b/Crypto/src/asn1/gnu/GNUObjectIdentifiers.cs
@@ -0,0 +1,31 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Gnu +{ + public abstract class GnuObjectIdentifiers + { + public static readonly DerObjectIdentifier Gnu = new DerObjectIdentifier("1.3.6.1.4.1.11591.1"); // GNU Radius + public static readonly DerObjectIdentifier GnuPG = new DerObjectIdentifier("1.3.6.1.4.1.11591.2"); // GnuPG (Ägypten) + public static readonly DerObjectIdentifier Notation = new DerObjectIdentifier("1.3.6.1.4.1.11591.2.1"); // notation + public static readonly DerObjectIdentifier PkaAddress = new DerObjectIdentifier("1.3.6.1.4.1.11591.2.1.1"); // pkaAddress + public static readonly DerObjectIdentifier GnuRadar = new DerObjectIdentifier("1.3.6.1.4.1.11591.3"); // GNU Radar + public static readonly DerObjectIdentifier DigestAlgorithm = new DerObjectIdentifier("1.3.6.1.4.1.11591.12"); // digestAlgorithm + public static readonly DerObjectIdentifier Tiger192 = new DerObjectIdentifier("1.3.6.1.4.1.11591.12.2"); // TIGER/192 + public static readonly DerObjectIdentifier EncryptionAlgorithm = new DerObjectIdentifier("1.3.6.1.4.1.11591.13"); // encryptionAlgorithm + public static readonly DerObjectIdentifier Serpent = new DerObjectIdentifier("1.3.6.1.4.1.11591.13.2"); // Serpent + public static readonly DerObjectIdentifier Serpent128Ecb = new DerObjectIdentifier("1.3.6.1.4.1.11591.13.2.1"); // Serpent-128-ECB + public static readonly DerObjectIdentifier Serpent128Cbc = new DerObjectIdentifier("1.3.6.1.4.1.11591.13.2.2"); // Serpent-128-CBC + public static readonly DerObjectIdentifier Serpent128Ofb = new DerObjectIdentifier("1.3.6.1.4.1.11591.13.2.3"); // Serpent-128-OFB + public static readonly DerObjectIdentifier Serpent128Cfb = new DerObjectIdentifier("1.3.6.1.4.1.11591.13.2.4"); // Serpent-128-CFB + public static readonly DerObjectIdentifier Serpent192Ecb = new DerObjectIdentifier("1.3.6.1.4.1.11591.13.2.21"); // Serpent-192-ECB + public static readonly DerObjectIdentifier Serpent192Cbc = new DerObjectIdentifier("1.3.6.1.4.1.11591.13.2.22"); // Serpent-192-CBC + public static readonly DerObjectIdentifier Serpent192Ofb = new DerObjectIdentifier("1.3.6.1.4.1.11591.13.2.23"); // Serpent-192-OFB + public static readonly DerObjectIdentifier Serpent192Cfb = new DerObjectIdentifier("1.3.6.1.4.1.11591.13.2.24"); // Serpent-192-CFB + public static readonly DerObjectIdentifier Serpent256Ecb = new DerObjectIdentifier("1.3.6.1.4.1.11591.13.2.41"); // Serpent-256-ECB + public static readonly DerObjectIdentifier Serpent256Cbc = new DerObjectIdentifier("1.3.6.1.4.1.11591.13.2.42"); // Serpent-256-CBC + public static readonly DerObjectIdentifier Serpent256Ofb = new DerObjectIdentifier("1.3.6.1.4.1.11591.13.2.43"); // Serpent-256-OFB + public static readonly DerObjectIdentifier Serpent256Cfb = new DerObjectIdentifier("1.3.6.1.4.1.11591.13.2.44"); // Serpent-256-CFB + public static readonly DerObjectIdentifier Crc = new DerObjectIdentifier("1.3.6.1.4.1.11591.14"); // CRC algorithms + public static readonly DerObjectIdentifier Crc32 = new DerObjectIdentifier("1.3.6.1.4.1.11591.14.1"); // CRC 32 + } +} diff --git a/Crypto/src/asn1/iana/IANAObjectIdentifiers.cs b/Crypto/src/asn1/iana/IANAObjectIdentifiers.cs new file mode 100644
index 000000000..63343f5ce --- /dev/null +++ b/Crypto/src/asn1/iana/IANAObjectIdentifiers.cs
@@ -0,0 +1,18 @@ +namespace Org.BouncyCastle.Asn1.Iana +{ + public abstract class IanaObjectIdentifiers + { + // id-SHA1 OBJECT IDENTIFIER ::= + // {iso(1) identified-organization(3) dod(6) internet(1) security(5) mechanisms(5) ipsec(8) isakmpOakley(1)} + // + + public static readonly DerObjectIdentifier IsakmpOakley = new DerObjectIdentifier("1.3.6.1.5.5.8.1"); + + public static readonly DerObjectIdentifier HmacMD5 = new DerObjectIdentifier(IsakmpOakley + ".1"); + public static readonly DerObjectIdentifier HmacSha1 = new DerObjectIdentifier(IsakmpOakley + ".2"); + + public static readonly DerObjectIdentifier HmacTiger = new DerObjectIdentifier(IsakmpOakley + ".3"); + + public static readonly DerObjectIdentifier HmacRipeMD160 = new DerObjectIdentifier(IsakmpOakley + ".4"); + } +} diff --git a/Crypto/src/asn1/icao/CscaMasterList.cs b/Crypto/src/asn1/icao/CscaMasterList.cs new file mode 100644
index 000000000..6890d8a2e --- /dev/null +++ b/Crypto/src/asn1/icao/CscaMasterList.cs
@@ -0,0 +1,83 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Icao +{ + /** + * The CscaMasterList object. This object can be wrapped in a + * CMSSignedData to be published in LDAP. + * + * <pre> + * CscaMasterList ::= SEQUENCE { + * version CscaMasterListVersion, + * certList SET OF Certificate } + * + * CscaMasterListVersion :: INTEGER {v0(0)} + * </pre> + */ + public class CscaMasterList + : Asn1Encodable + { + private DerInteger version = new DerInteger(0); + private X509CertificateStructure[] certList; + + public static CscaMasterList GetInstance( + object obj) + { + if (obj is CscaMasterList) + return (CscaMasterList)obj; + + if (obj != null) + return new CscaMasterList(Asn1Sequence.GetInstance(obj)); + + return null; + } + + private CscaMasterList( + Asn1Sequence seq) + { + if (seq == null || seq.Count == 0) + throw new ArgumentException("null or empty sequence passed."); + + if (seq.Count != 2) + throw new ArgumentException("Incorrect sequence size: " + seq.Count); + + this.version = DerInteger.GetInstance(seq[0]); + + Asn1Set certSet = Asn1Set.GetInstance(seq[1]); + + this.certList = new X509CertificateStructure[certSet.Count]; + for (int i = 0; i < certList.Length; i++) + { + certList[i] = X509CertificateStructure.GetInstance(certSet[i]); + } + } + + public CscaMasterList( + X509CertificateStructure[] certStructs) + { + certList = CopyCertList(certStructs); + } + + public virtual int Version + { + get { return version.Value.IntValue; } + } + + public X509CertificateStructure[] GetCertStructs() + { + return CopyCertList(certList); + } + + private static X509CertificateStructure[] CopyCertList(X509CertificateStructure[] orig) + { + return (X509CertificateStructure[])orig.Clone(); + } + + public override Asn1Object ToAsn1Object() + { + return new DerSequence(version, new DerSet(certList)); + } + } +} diff --git a/Crypto/src/asn1/icao/DataGroupHash.cs b/Crypto/src/asn1/icao/DataGroupHash.cs new file mode 100644
index 000000000..e0d7eee7b --- /dev/null +++ b/Crypto/src/asn1/icao/DataGroupHash.cs
@@ -0,0 +1,86 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Icao +{ + /** + * 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 + : Asn1Encodable + { + private readonly DerInteger dataGroupNumber; + private readonly Asn1OctetString dataGroupHashValue; + + public static DataGroupHash GetInstance( + object obj) + { + if (obj is DataGroupHash) + return (DataGroupHash)obj; + + if (obj != null) + return new DataGroupHash(Asn1Sequence.GetInstance(obj)); + + return null; + } + + private DataGroupHash( + Asn1Sequence seq) + { + if (seq.Count != 2) + throw new ArgumentException("Wrong number of elements in sequence", "seq"); + + this.dataGroupNumber = DerInteger.GetInstance(seq[0]); + this.dataGroupHashValue = Asn1OctetString.GetInstance(seq[1]); + } + + public DataGroupHash( + int dataGroupNumber, + Asn1OctetString dataGroupHashValue) + { + this.dataGroupNumber = new DerInteger(dataGroupNumber); + this.dataGroupHashValue = dataGroupHashValue; + } + + public int DataGroupNumber + { + get { return dataGroupNumber.Value.IntValue; } + } + + public Asn1OctetString DataGroupHashValue + { + get { return dataGroupHashValue; } + } + + public override Asn1Object ToAsn1Object() + { + return new DerSequence(dataGroupNumber, dataGroupHashValue); + } + } +} diff --git a/Crypto/src/asn1/icao/ICAOObjectIdentifiers.cs b/Crypto/src/asn1/icao/ICAOObjectIdentifiers.cs new file mode 100644
index 000000000..389d4dacd --- /dev/null +++ b/Crypto/src/asn1/icao/ICAOObjectIdentifiers.cs
@@ -0,0 +1,34 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Icao +{ + public abstract class IcaoObjectIdentifiers + { + // + // base id + // + public static readonly DerObjectIdentifier IdIcao = new DerObjectIdentifier("2.23.136"); + + public static readonly DerObjectIdentifier IdIcaoMrtd = IdIcao.Branch("1"); + public static readonly DerObjectIdentifier IdIcaoMrtdSecurity = IdIcaoMrtd.Branch("1"); + + // LDS security object, see ICAO Doc 9303-Volume 2-Section IV-A3.2 + public static readonly DerObjectIdentifier IdIcaoLdsSecurityObject = IdIcaoMrtdSecurity.Branch("1"); + + // CSCA master list, see TR CSCA Countersigning and Master List issuance + public static readonly DerObjectIdentifier IdIcaoCscaMasterList = IdIcaoMrtdSecurity.Branch("2"); + public static readonly DerObjectIdentifier IdIcaoCscaMasterListSigningKey = IdIcaoMrtdSecurity.Branch("3"); + + // document type list, see draft TR LDS and PKI Maintenance, par. 3.2.1 + public static readonly DerObjectIdentifier IdIcaoDocumentTypeList = IdIcaoMrtdSecurity.Branch("4"); + + // Active Authentication protocol, see draft TR LDS and PKI Maintenance, + // par. 5.2.2 + public static readonly DerObjectIdentifier IdIcaoAAProtocolObject = IdIcaoMrtdSecurity.Branch("5"); + + // CSCA name change and key reoll-over, see draft TR LDS and PKI + // Maintenance, par. 3.2.1 + public static readonly DerObjectIdentifier IdIcaoExtensions = IdIcaoMrtdSecurity.Branch("6"); + public static readonly DerObjectIdentifier IdIcaoExtensionsNamechangekeyrollover = IdIcaoExtensions.Branch("1"); + } +} diff --git a/Crypto/src/asn1/icao/LDSSecurityObject.cs b/Crypto/src/asn1/icao/LDSSecurityObject.cs new file mode 100644
index 000000000..c33ca6877 --- /dev/null +++ b/Crypto/src/asn1/icao/LDSSecurityObject.cs
@@ -0,0 +1,145 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Asn1.Icao +{ + /** + * 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 + : Asn1Encodable + { + public const int UBDataGroups = 16; + + private DerInteger version = new DerInteger(0); + private AlgorithmIdentifier digestAlgorithmIdentifier; + private DataGroupHash[] datagroupHash; + private LdsVersionInfo versionInfo; + + public static LdsSecurityObject GetInstance( + object obj) + { + if (obj is LdsSecurityObject) + return (LdsSecurityObject)obj; + + if (obj != null) + return new LdsSecurityObject(Asn1Sequence.GetInstance(obj)); + + return null; + } + + private LdsSecurityObject( + Asn1Sequence seq) + { + if (seq == null || seq.Count == 0) + throw new ArgumentException("null or empty sequence passed."); + + IEnumerator e = seq.GetEnumerator(); + + // version + e.MoveNext(); + version = DerInteger.GetInstance(e.Current); + // digestAlgorithmIdentifier + e.MoveNext(); + digestAlgorithmIdentifier = AlgorithmIdentifier.GetInstance(e.Current); + + e.MoveNext(); + Asn1Sequence datagroupHashSeq = Asn1Sequence.GetInstance(e.Current); + + if (version.Value.Equals(BigInteger.One)) + { + e.MoveNext(); + versionInfo = LdsVersionInfo.GetInstance(e.Current); + } + + CheckDatagroupHashSeqSize(datagroupHashSeq.Count); + + datagroupHash = new DataGroupHash[datagroupHashSeq.Count]; + for (int i= 0; i< datagroupHashSeq.Count; i++) + { + datagroupHash[i] = DataGroupHash.GetInstance(datagroupHashSeq[i]); + } + } + + public LdsSecurityObject( + AlgorithmIdentifier digestAlgorithmIdentifier, + DataGroupHash[] datagroupHash) + { + this.version = new DerInteger(0); + this.digestAlgorithmIdentifier = digestAlgorithmIdentifier; + this.datagroupHash = datagroupHash; + + CheckDatagroupHashSeqSize(datagroupHash.Length); + } + + + public LdsSecurityObject( + AlgorithmIdentifier digestAlgorithmIdentifier, + DataGroupHash[] datagroupHash, + LdsVersionInfo versionInfo) + { + this.version = new DerInteger(1); + this.digestAlgorithmIdentifier = digestAlgorithmIdentifier; + this.datagroupHash = datagroupHash; + this.versionInfo = versionInfo; + + CheckDatagroupHashSeqSize(datagroupHash.Length); + } + + private void CheckDatagroupHashSeqSize(int size) + { + if (size < 2 || size > UBDataGroups) + throw new ArgumentException("wrong size in DataGroupHashValues : not in (2.."+ UBDataGroups +")"); + } + + public BigInteger Version + { + get { return version.Value; } + } + + public AlgorithmIdentifier DigestAlgorithmIdentifier + { + get { return digestAlgorithmIdentifier; } + } + + public DataGroupHash[] GetDatagroupHash() + { + return datagroupHash; + } + + public LdsVersionInfo VersionInfo + { + get { return versionInfo; } + } + + public override Asn1Object ToAsn1Object() + { + DerSequence hashSeq = new DerSequence(datagroupHash); + + Asn1EncodableVector v = new Asn1EncodableVector(version, digestAlgorithmIdentifier, hashSeq); + + if (versionInfo != null) + { + v.Add(versionInfo); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/icao/LDSVersionInfo.cs b/Crypto/src/asn1/icao/LDSVersionInfo.cs new file mode 100644
index 000000000..2cdcad2db --- /dev/null +++ b/Crypto/src/asn1/icao/LDSVersionInfo.cs
@@ -0,0 +1,61 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Icao +{ + public class LdsVersionInfo + : Asn1Encodable + { + 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.Count != 2) + throw new ArgumentException("sequence wrong size for LDSVersionInfo", "seq"); + + this.ldsVersion = DerPrintableString.GetInstance(seq[0]); + this.unicodeVersion = DerPrintableString.GetInstance(seq[1]); + } + + public static LdsVersionInfo GetInstance(object obj) + { + if (obj is LdsVersionInfo) + return (LdsVersionInfo)obj; + + if (obj != null) + return new LdsVersionInfo(Asn1Sequence.GetInstance(obj)); + + return null; + } + + public virtual string GetLdsVersion() + { + return ldsVersion.GetString(); + } + + public virtual string GetUnicodeVersion() + { + return unicodeVersion.GetString(); + } + + /** + * <pre> + * LDSVersionInfo ::= SEQUENCE { + * ldsVersion PRINTABLE STRING + * unicodeVersion PRINTABLE STRING + * } + * </pre> + * @return + */ + public override Asn1Object ToAsn1Object() + { + return new DerSequence(ldsVersion, unicodeVersion); + } + } +} diff --git a/Crypto/src/asn1/isismtt/ISISMTTObjectIdentifiers.cs b/Crypto/src/asn1/isismtt/ISISMTTObjectIdentifiers.cs new file mode 100644
index 000000000..af60b030a --- /dev/null +++ b/Crypto/src/asn1/isismtt/ISISMTTObjectIdentifiers.cs
@@ -0,0 +1,177 @@ +namespace Org.BouncyCastle.Asn1.IsisMtt +{ + public abstract class IsisMttObjectIdentifiers + { + public static readonly DerObjectIdentifier IdIsisMtt = new DerObjectIdentifier("1.3.36.8"); + + public static readonly DerObjectIdentifier IdIsisMttCP = new DerObjectIdentifier(IdIsisMtt + ".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. + */ + public static readonly DerObjectIdentifier IdIsisMttCPAccredited = new DerObjectIdentifier(IdIsisMttCP + ".1"); + + public static readonly DerObjectIdentifier IdIsisMttAT = new DerObjectIdentifier(IdIsisMtt + ".3"); + + /** + * Certificate extensionDate of certificate generation + * + * <pre> + * DateOfCertGenSyntax ::= GeneralizedTime + * </pre> + */ + public static readonly DerObjectIdentifier IdIsisMttATDateOfCertGen = new DerObjectIdentifier(IdIsisMttAT + ".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. + */ + public static readonly DerObjectIdentifier IdIsisMttATProcuration = new DerObjectIdentifier(IdIsisMttAT + ".2"); + + /** + * Attribute to indicate admissions to certain professions. May be used as + * attribute in attribute certificate or as extension in a certificate + */ + public static readonly DerObjectIdentifier IdIsisMttATAdmission = new DerObjectIdentifier(IdIsisMttAT + ".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). + */ + public static readonly DerObjectIdentifier IdIsisMttATMonetaryLimit = new DerObjectIdentifier(IdIsisMttAT + ".4"); + + /** + * A declaration of majority. May be used as attribute in attribute + * certificate or as extension in a certificate + */ + public static readonly DerObjectIdentifier IdIsisMttATDeclarationOfMajority = new DerObjectIdentifier(IdIsisMttAT + ".5"); + + /** + * + * Serial number of the smart card containing the corresponding private key + * + * <pre> + * ICCSNSyntax ::= OCTET STRING (SIZE(8..20)) + * </pre> + */ + public static readonly DerObjectIdentifier IdIsisMttATIccsn = new DerObjectIdentifier(IdIsisMttAT + ".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> + */ + public static readonly DerObjectIdentifier IdIsisMttATPKReference = new DerObjectIdentifier(IdIsisMttAT + ".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 + */ + public static readonly DerObjectIdentifier IdIsisMttATRestriction = new DerObjectIdentifier(IdIsisMttAT + ".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> + */ + public static readonly DerObjectIdentifier IdIsisMttATRetrieveIfAllowed = new DerObjectIdentifier(IdIsisMttAT + ".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 + */ + public static readonly DerObjectIdentifier IdIsisMttATRequestedCertificate = new DerObjectIdentifier(IdIsisMttAT + ".10"); + + /** + * Base ObjectIdentifier for naming authorities + */ + public static readonly DerObjectIdentifier IdIsisMttATNamingAuthorities = new DerObjectIdentifier(IdIsisMttAT + ".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> + */ + public static readonly DerObjectIdentifier IdIsisMttATCertInDirSince = new DerObjectIdentifier(IdIsisMttAT + ".12"); + + /** + * Hash of a certificate in OCSP. + * + * @see Org.BouncyCastle.Asn1.IsisMtt.Ocsp.CertHash + */ + public static readonly DerObjectIdentifier IdIsisMttATCertHash = new DerObjectIdentifier(IdIsisMttAT + ".13"); + + /** + * <pre> + * NameAtBirth ::= DirectoryString(SIZE(1..64) + * </pre> + * + * Used in + * {@link Org.BouncyCastle.Asn1.X509.SubjectDirectoryAttributes SubjectDirectoryAttributes} + */ + public static readonly DerObjectIdentifier IdIsisMttATNameAtBirth = new DerObjectIdentifier(IdIsisMttAT + ".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 + */ + public static readonly DerObjectIdentifier IdIsisMttATAdditionalInformation = new DerObjectIdentifier(IdIsisMttAT + ".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> + */ + public static readonly DerObjectIdentifier IdIsisMttATLiabilityLimitationFlag = new DerObjectIdentifier("0.2.262.1.10.12.0"); + } +} diff --git a/Crypto/src/asn1/isismtt/ocsp/CertHash.cs b/Crypto/src/asn1/isismtt/ocsp/CertHash.cs new file mode 100644
index 000000000..da5b530e4 --- /dev/null +++ b/Crypto/src/asn1/isismtt/ocsp/CertHash.cs
@@ -0,0 +1,121 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.IsisMtt.Ocsp +{ + /** + * 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 + : Asn1Encodable + { + private readonly AlgorithmIdentifier hashAlgorithm; + private readonly byte[] certificateHash; + + public static CertHash GetInstance( + object obj) + { + if (obj == null || obj is CertHash) + { + return (CertHash) obj; + } + + if (obj is Asn1Sequence) + { + return new CertHash((Asn1Sequence) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + /** + * 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.Count != 2) + throw new ArgumentException("Bad sequence size: " + seq.Count); + + this.hashAlgorithm = AlgorithmIdentifier.GetInstance(seq[0]); + this.certificateHash = DerOctetString.GetInstance(seq[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) + { + if (hashAlgorithm == null) + throw new ArgumentNullException("hashAlgorithm"); + if (certificateHash == null) + throw new ArgumentNullException("certificateHash"); + + this.hashAlgorithm = hashAlgorithm; + this.certificateHash = (byte[]) certificateHash.Clone(); + } + + public AlgorithmIdentifier HashAlgorithm + { + get { return hashAlgorithm; } + } + + public byte[] CertificateHash + { + get { return (byte[]) certificateHash.Clone(); } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <p/> + * Returns: + * <p/> + * <pre> + * CertHash ::= SEQUENCE { + * hashAlgorithm AlgorithmIdentifier, + * certificateHash OCTET STRING + * } + * </pre> + * + * @return an Asn1Object + */ + public override Asn1Object ToAsn1Object() + { + return new DerSequence(hashAlgorithm, new DerOctetString(certificateHash)); + } + } +} diff --git a/Crypto/src/asn1/isismtt/ocsp/RequestedCertificate.cs b/Crypto/src/asn1/isismtt/ocsp/RequestedCertificate.cs new file mode 100644
index 000000000..7724bfed6 --- /dev/null +++ b/Crypto/src/asn1/isismtt/ocsp/RequestedCertificate.cs
@@ -0,0 +1,186 @@ +using System; +using System.IO; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.IsisMtt.Ocsp +{ + /** + * 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 isExplicit 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> + * <li>b) the responder supports the extension but is NOT allowed to publish + * the certificate: RequestedCertificate returned including an empty OCTET + * STRING</li> + * <li>c) the responder does not support the extension: RequestedCertificate is + * not included in the response</li> + * </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 + : Asn1Encodable, IAsn1Choice + { + public enum Choice + { + Certificate = -1, + PublicKeyCertificate = 0, + AttributeCertificate = 1 + } + + private readonly X509CertificateStructure cert; + private readonly byte[] publicKeyCert; + private readonly byte[] attributeCert; + + public static RequestedCertificate GetInstance( + object obj) + { + if (obj == null || obj is RequestedCertificate) + { + return (RequestedCertificate) obj; + } + + if (obj is Asn1Sequence) + { + return new RequestedCertificate(X509CertificateStructure.GetInstance(obj)); + } + + if (obj is Asn1TaggedObject) + { + return new RequestedCertificate((Asn1TaggedObject) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public static RequestedCertificate GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + if (!isExplicit) + throw new ArgumentException("choice item must be explicitly tagged"); + + return GetInstance(obj.GetObject()); + } + + private RequestedCertificate( + Asn1TaggedObject tagged) + { + switch ((Choice) tagged.TagNo) + { + case Choice.AttributeCertificate: + this.attributeCert = Asn1OctetString.GetInstance(tagged, true).GetOctets(); + break; + case Choice.PublicKeyCertificate: + this.publicKeyCert = Asn1OctetString.GetInstance(tagged, true).GetOctets(); + break; + default: + throw new ArgumentException("unknown tag number: " + tagged.TagNo); + } + } + + /** + * 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( + X509CertificateStructure certificate) + { + this.cert = certificate; + } + + public RequestedCertificate( + Choice type, + byte[] certificateOctets) + : this(new DerTaggedObject((int) type, new DerOctetString(certificateOctets))) + { + } + + public Choice Type + { + get + { + if (cert != null) + return Choice.Certificate; + + if (publicKeyCert != null) + return Choice.PublicKeyCertificate; + + return Choice.AttributeCertificate; + } + } + + public byte[] GetCertificateBytes() + { + if (cert != null) + { + try + { + return cert.GetEncoded(); + } + catch (IOException e) + { + throw new InvalidOperationException("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 an Asn1Object + */ + public override Asn1Object ToAsn1Object() + { + if (publicKeyCert != null) + { + return new DerTaggedObject(0, new DerOctetString(publicKeyCert)); + } + + if (attributeCert != null) + { + return new DerTaggedObject(1, new DerOctetString(attributeCert)); + } + + return cert.ToAsn1Object(); + } + } +} diff --git a/Crypto/src/asn1/isismtt/x509/AdditionalInformationSyntax.cs b/Crypto/src/asn1/isismtt/x509/AdditionalInformationSyntax.cs new file mode 100644
index 000000000..f81d459c6 --- /dev/null +++ b/Crypto/src/asn1/isismtt/x509/AdditionalInformationSyntax.cs
@@ -0,0 +1,70 @@ +using System; + +using Org.BouncyCastle.Asn1.X500; + +namespace Org.BouncyCastle.Asn1.IsisMtt.X509 +{ + /** + * Some other information of non-restrictive nature regarding the usage of this + * certificate. + * + * <pre> + * AdditionalInformationSyntax ::= DirectoryString (SIZE(1..2048)) + * </pre> + */ + public class AdditionalInformationSyntax + : Asn1Encodable + { + private readonly DirectoryString information; + + public static AdditionalInformationSyntax GetInstance( + object obj) + { + if (obj is AdditionalInformationSyntax) + return (AdditionalInformationSyntax) obj; + + if (obj is IAsn1String) + return new AdditionalInformationSyntax(DirectoryString.GetInstance(obj)); + + throw new ArgumentException("Unknown object in GetInstance: " + obj.GetType().Name, "obj"); + } + + private AdditionalInformationSyntax( + DirectoryString information) + { + this.information = information; + } + + /** + * Constructor from a given details. + * + * @param information The describtion of the information. + */ + public AdditionalInformationSyntax( + string information) + { + this.information = new DirectoryString(information); + } + + public virtual DirectoryString Information + { + get { return information; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <p/> + * Returns: + * <p/> + * <pre> + * AdditionalInformationSyntax ::= DirectoryString (SIZE(1..2048)) + * </pre> + * + * @return an Asn1Object + */ + public override Asn1Object ToAsn1Object() + { + return information.ToAsn1Object(); + } + } +} diff --git a/Crypto/src/asn1/isismtt/x509/AdmissionSyntax.cs b/Crypto/src/asn1/isismtt/x509/AdmissionSyntax.cs new file mode 100644
index 000000000..f322ef88f --- /dev/null +++ b/Crypto/src/asn1/isismtt/x509/AdmissionSyntax.cs
Binary files differdiff --git a/Crypto/src/asn1/isismtt/x509/Admissions.cs b/Crypto/src/asn1/isismtt/x509/Admissions.cs new file mode 100644
index 000000000..40290c608 --- /dev/null +++ b/Crypto/src/asn1/isismtt/x509/Admissions.cs
@@ -0,0 +1,186 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.IsisMtt.X509 +{ + /** + * 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 + : Asn1Encodable + { + private readonly GeneralName admissionAuthority; + private readonly NamingAuthority namingAuthority; + private readonly Asn1Sequence professionInfos; + + public static Admissions GetInstance( + object obj) + { + if (obj == null || obj is Admissions) + { + return (Admissions) obj; + } + + if (obj is Asn1Sequence) + { + return new Admissions((Asn1Sequence) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + /** + * 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.Count > 3) + throw new ArgumentException("Bad sequence size: " + seq.Count); + + IEnumerator e = seq.GetEnumerator(); + + e.MoveNext(); + Asn1Encodable o = (Asn1Encodable) e.Current; + if (o is Asn1TaggedObject) + { + switch (((Asn1TaggedObject)o).TagNo) + { + case 0: + admissionAuthority = GeneralName.GetInstance((Asn1TaggedObject)o, true); + break; + case 1: + namingAuthority = NamingAuthority.GetInstance((Asn1TaggedObject)o, true); + break; + default: + throw new ArgumentException("Bad tag number: " + ((Asn1TaggedObject)o).TagNo); + } + e.MoveNext(); + o = (Asn1Encodable) e.Current; + } + if (o is Asn1TaggedObject) + { + switch (((Asn1TaggedObject)o).TagNo) + { + case 1: + namingAuthority = NamingAuthority.GetInstance((Asn1TaggedObject)o, true); + break; + default: + throw new ArgumentException("Bad tag number: " + ((Asn1TaggedObject)o).TagNo); + } + e.MoveNext(); + o = (Asn1Encodable) e.Current; + } + professionInfos = Asn1Sequence.GetInstance(o); + if (e.MoveNext()) + { + throw new ArgumentException("Bad object encountered: " + e.Current.GetType().Name); + } + } + + /** + * 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 virtual GeneralName AdmissionAuthority + { + get { return admissionAuthority; } + } + + public virtual NamingAuthority NamingAuthority + { + get { return namingAuthority; } + } + + public ProfessionInfo[] GetProfessionInfos() + { + ProfessionInfo[] infos = new ProfessionInfo[professionInfos.Count]; + int count = 0; + foreach (Asn1Encodable ae in professionInfos) + { + infos[count++] = ProfessionInfo.GetInstance(ae); + } + 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 Asn1Object + */ + public override Asn1Object ToAsn1Object() + { + 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/Crypto/src/asn1/isismtt/x509/DeclarationOfMajority.cs b/Crypto/src/asn1/isismtt/x509/DeclarationOfMajority.cs new file mode 100644
index 000000000..dfac65040 --- /dev/null +++ b/Crypto/src/asn1/isismtt/x509/DeclarationOfMajority.cs
@@ -0,0 +1,170 @@ +using System; + +namespace Org.BouncyCastle.Asn1.IsisMtt.X509 +{ + /** + * 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 + : Asn1Encodable, IAsn1Choice + { + public enum Choice + { + NotYoungerThan = 0, + FullAgeAtCountry = 1, + DateOfBirth = 2 + }; + + private readonly Asn1TaggedObject declaration; + + public DeclarationOfMajority( + int notYoungerThan) + { + declaration = new DerTaggedObject(false, 0, new DerInteger(notYoungerThan)); + } + + public DeclarationOfMajority( + bool fullAge, + string country) + { + if (country.Length > 2) + throw new ArgumentException("country can only be 2 characters"); + + DerPrintableString countryString = new DerPrintableString(country, true); + + DerSequence seq; + if (fullAge) + { + seq = new DerSequence(countryString); + } + else + { + seq = new DerSequence(DerBoolean.False, countryString); + } + + this.declaration = new DerTaggedObject(false, 1, seq); + } + + public DeclarationOfMajority( + DerGeneralizedTime dateOfBirth) + { + this.declaration = new DerTaggedObject(false, 2, dateOfBirth); + } + + public static DeclarationOfMajority GetInstance( + object obj) + { + if (obj == null || obj is DeclarationOfMajority) + { + return (DeclarationOfMajority) obj; + } + + if (obj is Asn1TaggedObject) + { + return new DeclarationOfMajority((Asn1TaggedObject) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + private DeclarationOfMajority( + Asn1TaggedObject o) + { + if (o.TagNo > 2) + throw new ArgumentException("Bad tag number: " + o.TagNo); + + this.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 an Asn1Object + */ + public override Asn1Object ToAsn1Object() + { + return declaration; + } + + public Choice Type + { + get { return (Choice) declaration.TagNo; } + } + + /** + * @return notYoungerThan if that's what we are, -1 otherwise + */ + public virtual int NotYoungerThan + { + get + { + switch ((Choice) declaration.TagNo) + { + case Choice.NotYoungerThan: + return DerInteger.GetInstance(declaration, false).Value.IntValue; + default: + return -1; + } + } + } + + public virtual Asn1Sequence FullAgeAtCountry + { + get + { + switch ((Choice) declaration.TagNo) + { + case Choice.FullAgeAtCountry: + return Asn1Sequence.GetInstance(declaration, false); + default: + return null; + } + } + } + + public virtual DerGeneralizedTime DateOfBirth + { + get + { + switch ((Choice) declaration.TagNo) + { + case Choice.DateOfBirth: + return DerGeneralizedTime.GetInstance(declaration, false); + default: + return null; + } + } + } + } +} diff --git a/Crypto/src/asn1/isismtt/x509/MonetaryLimit.cs b/Crypto/src/asn1/isismtt/x509/MonetaryLimit.cs new file mode 100644
index 000000000..80b6b684b --- /dev/null +++ b/Crypto/src/asn1/isismtt/x509/MonetaryLimit.cs
@@ -0,0 +1,121 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Asn1.IsisMtt.X509 +{ + /** + * 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 + : Asn1Encodable + { + private readonly DerPrintableString currency; + private readonly DerInteger amount; + private readonly DerInteger exponent; + + public static MonetaryLimit GetInstance( + object obj) + { + if (obj == null || obj is MonetaryLimit) + { + return (MonetaryLimit) obj; + } + + if (obj is Asn1Sequence) + { + return new MonetaryLimit(Asn1Sequence.GetInstance(obj)); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + private MonetaryLimit( + Asn1Sequence seq) + { + if (seq.Count != 3) + throw new ArgumentException("Bad sequence size: " + seq.Count); + + currency = DerPrintableString.GetInstance(seq[0]); + amount = DerInteger.GetInstance(seq[1]); + exponent = DerInteger.GetInstance(seq[2]); + } + + /** + * 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 DerInteger(amount); + this.exponent = new DerInteger(exponent); + } + + public virtual string Currency + { + get { return currency.GetString(); } + } + + public virtual BigInteger Amount + { + get { return amount.Value; } + } + + public virtual BigInteger Exponent + { + get { return exponent.Value; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <p/> + * Returns: + * <p/> + * <pre> + * MonetaryLimitSyntax ::= SEQUENCE + * { + * currency PrintableString (SIZE(3)), + * amount INTEGER, + * exponent INTEGER + * } + * </pre> + * + * @return an Asn1Object + */ + public override Asn1Object ToAsn1Object() + { + return new DerSequence(currency, amount, exponent); + } + } +} diff --git a/Crypto/src/asn1/isismtt/x509/NamingAuthority.cs b/Crypto/src/asn1/isismtt/x509/NamingAuthority.cs new file mode 100644
index 000000000..4262fd0f4 --- /dev/null +++ b/Crypto/src/asn1/isismtt/x509/NamingAuthority.cs
@@ -0,0 +1,214 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1.X500; + +namespace Org.BouncyCastle.Asn1.IsisMtt.X509 +{ + /** + * 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 + : Asn1Encodable + { + /** + * 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 readonly DerObjectIdentifier IdIsisMttATNamingAuthoritiesRechtWirtschaftSteuern + = new DerObjectIdentifier(IsisMttObjectIdentifiers.IdIsisMttATNamingAuthorities + ".1"); + + private readonly DerObjectIdentifier namingAuthorityID; + private readonly string namingAuthorityUrl; + private readonly DirectoryString namingAuthorityText; + + public static NamingAuthority GetInstance( + object obj) + { + if (obj == null || obj is NamingAuthority) + { + return (NamingAuthority) obj; + } + + if (obj is Asn1Sequence) + { + return new NamingAuthority((Asn1Sequence) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public static NamingAuthority GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit)); + } + + /** + * 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.Count > 3) + throw new ArgumentException("Bad sequence size: " + seq.Count); + + IEnumerator e = seq.GetEnumerator(); + + if (e.MoveNext()) + { + Asn1Encodable o = (Asn1Encodable) e.Current; + if (o is DerObjectIdentifier) + { + namingAuthorityID = (DerObjectIdentifier) o; + } + else if (o is DerIA5String) + { + namingAuthorityUrl = DerIA5String.GetInstance(o).GetString(); + } + else if (o is IAsn1String) + { + namingAuthorityText = DirectoryString.GetInstance(o); + } + else + { + throw new ArgumentException("Bad object encountered: " + o.GetType().Name); + } + } + + if (e.MoveNext()) + { + Asn1Encodable o = (Asn1Encodable) e.Current; + if (o is DerIA5String) + { + namingAuthorityUrl = DerIA5String.GetInstance(o).GetString(); + } + else if (o is IAsn1String) + { + namingAuthorityText = DirectoryString.GetInstance(o); + } + else + { + throw new ArgumentException("Bad object encountered: " + o.GetType().Name); + } + } + + if (e.MoveNext()) + { + Asn1Encodable o = (Asn1Encodable) e.Current; + if (o is IAsn1String) + { + namingAuthorityText = DirectoryString.GetInstance(o); + } + else + { + throw new ArgumentException("Bad object encountered: " + o.GetType().Name); + } + } + } + + /** + * @return Returns the namingAuthorityID. + */ + public virtual DerObjectIdentifier NamingAuthorityID + { + get { return namingAuthorityID; } + } + + /** + * @return Returns the namingAuthorityText. + */ + public virtual DirectoryString NamingAuthorityText + { + get { return namingAuthorityText; } + } + + /** + * @return Returns the namingAuthorityUrl. + */ + public virtual string NamingAuthorityUrl + { + get { 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. + */ + public NamingAuthority( + DerObjectIdentifier 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 an Asn1Object + */ + public override Asn1Object ToAsn1Object() + { + 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/Crypto/src/asn1/isismtt/x509/ProcurationSyntax.cs b/Crypto/src/asn1/isismtt/x509/ProcurationSyntax.cs new file mode 100644
index 000000000..a25df225e --- /dev/null +++ b/Crypto/src/asn1/isismtt/x509/ProcurationSyntax.cs
@@ -0,0 +1,232 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1.X500; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.IsisMtt.X509 +{ + /** + * 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> + * <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). + * </p> + * <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 + : Asn1Encodable + { + private readonly string country; + private readonly DirectoryString typeOfSubstitution; + private readonly GeneralName thirdPerson; + private readonly IssuerSerial certRef; + + public static ProcurationSyntax GetInstance( + object obj) + { + if (obj == null || obj is ProcurationSyntax) + { + return (ProcurationSyntax) obj; + } + + if (obj is Asn1Sequence) + { + return new ProcurationSyntax((Asn1Sequence) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + /** + * 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.Count < 1 || seq.Count > 3) + throw new ArgumentException("Bad sequence size: " + seq.Count); + + IEnumerator e = seq.GetEnumerator(); + + while (e.MoveNext()) + { + Asn1TaggedObject o = Asn1TaggedObject.GetInstance(e.Current); + switch (o.TagNo) + { + case 1: + country = DerPrintableString.GetInstance(o, true).GetString(); + break; + case 2: + typeOfSubstitution = DirectoryString.GetInstance(o, true); + break; + case 3: + Asn1Object signingFor = o.GetObject(); + if (signingFor is Asn1TaggedObject) + { + thirdPerson = GeneralName.GetInstance(signingFor); + } + else + { + certRef = IssuerSerial.GetInstance(signingFor); + } + break; + default: + throw new ArgumentException("Bad tag number: " + o.TagNo); + } + } + } + + /** + * 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 virtual string Country + { + get { return country; } + } + + public virtual DirectoryString TypeOfSubstitution + { + get { return typeOfSubstitution; } + } + + public virtual GeneralName ThirdPerson + { + get { return thirdPerson; } + } + + public virtual IssuerSerial CertRef + { + get { 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 an Asn1Object + */ + public override Asn1Object ToAsn1Object() + { + 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/Crypto/src/asn1/isismtt/x509/ProfessionInfo.cs b/Crypto/src/asn1/isismtt/x509/ProfessionInfo.cs new file mode 100644
index 000000000..3bad2cbc4 --- /dev/null +++ b/Crypto/src/asn1/isismtt/x509/ProfessionInfo.cs
@@ -0,0 +1,386 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1.X500; + +namespace Org.BouncyCastle.Asn1.IsisMtt.X509 +{ + /** + * 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 + : Asn1Encodable + { + /** + * Rechtsanw�ltin + */ + public static readonly DerObjectIdentifier Rechtsanwltin = new DerObjectIdentifier( + NamingAuthority.IdIsisMttATNamingAuthoritiesRechtWirtschaftSteuern + ".1"); + + /** + * Rechtsanwalt + */ + public static readonly DerObjectIdentifier Rechtsanwalt = new DerObjectIdentifier( + NamingAuthority.IdIsisMttATNamingAuthoritiesRechtWirtschaftSteuern + ".2"); + + /** + * Rechtsbeistand + */ + public static readonly DerObjectIdentifier Rechtsbeistand = new DerObjectIdentifier( + NamingAuthority.IdIsisMttATNamingAuthoritiesRechtWirtschaftSteuern + ".3"); + + /** + * Steuerberaterin + */ + public static readonly DerObjectIdentifier Steuerberaterin = new DerObjectIdentifier( + NamingAuthority.IdIsisMttATNamingAuthoritiesRechtWirtschaftSteuern + ".4"); + + /** + * Steuerberater + */ + public static readonly DerObjectIdentifier Steuerberater = new DerObjectIdentifier( + NamingAuthority.IdIsisMttATNamingAuthoritiesRechtWirtschaftSteuern + ".5"); + + /** + * Steuerbevollm�chtigte + */ + public static readonly DerObjectIdentifier Steuerbevollmchtigte = new DerObjectIdentifier( + NamingAuthority.IdIsisMttATNamingAuthoritiesRechtWirtschaftSteuern + ".6"); + + /** + * Steuerbevollm�chtigter + */ + public static readonly DerObjectIdentifier Steuerbevollmchtigter = new DerObjectIdentifier( + NamingAuthority.IdIsisMttATNamingAuthoritiesRechtWirtschaftSteuern + ".7"); + + /** + * Notarin + */ + public static readonly DerObjectIdentifier Notarin = new DerObjectIdentifier( + NamingAuthority.IdIsisMttATNamingAuthoritiesRechtWirtschaftSteuern + ".8"); + + /** + * Notar + */ + public static readonly DerObjectIdentifier Notar = new DerObjectIdentifier( + NamingAuthority.IdIsisMttATNamingAuthoritiesRechtWirtschaftSteuern + ".9"); + + /** + * Notarvertreterin + */ + public static readonly DerObjectIdentifier Notarvertreterin = new DerObjectIdentifier( + NamingAuthority.IdIsisMttATNamingAuthoritiesRechtWirtschaftSteuern + ".10"); + + /** + * Notarvertreter + */ + public static readonly DerObjectIdentifier Notarvertreter = new DerObjectIdentifier( + NamingAuthority.IdIsisMttATNamingAuthoritiesRechtWirtschaftSteuern + ".11"); + + /** + * Notariatsverwalterin + */ + public static readonly DerObjectIdentifier Notariatsverwalterin = new DerObjectIdentifier( + NamingAuthority.IdIsisMttATNamingAuthoritiesRechtWirtschaftSteuern + ".12"); + + /** + * Notariatsverwalter + */ + public static readonly DerObjectIdentifier Notariatsverwalter = new DerObjectIdentifier( + NamingAuthority.IdIsisMttATNamingAuthoritiesRechtWirtschaftSteuern + ".13"); + + /** + * Wirtschaftspr�ferin + */ + public static readonly DerObjectIdentifier Wirtschaftsprferin = new DerObjectIdentifier( + NamingAuthority.IdIsisMttATNamingAuthoritiesRechtWirtschaftSteuern + ".14"); + + /** + * Wirtschaftspr�fer + */ + public static readonly DerObjectIdentifier Wirtschaftsprfer = new DerObjectIdentifier( + NamingAuthority.IdIsisMttATNamingAuthoritiesRechtWirtschaftSteuern + ".15"); + + /** + * Vereidigte Buchpr�ferin + */ + public static readonly DerObjectIdentifier VereidigteBuchprferin = new DerObjectIdentifier( + NamingAuthority.IdIsisMttATNamingAuthoritiesRechtWirtschaftSteuern + ".16"); + + /** + * Vereidigter Buchpr�fer + */ + public static readonly DerObjectIdentifier VereidigterBuchprfer = new DerObjectIdentifier( + NamingAuthority.IdIsisMttATNamingAuthoritiesRechtWirtschaftSteuern + ".17"); + + /** + * Patentanw�ltin + */ + public static readonly DerObjectIdentifier Patentanwltin = new DerObjectIdentifier( + NamingAuthority.IdIsisMttATNamingAuthoritiesRechtWirtschaftSteuern + ".18"); + + /** + * Patentanwalt + */ + public static readonly DerObjectIdentifier Patentanwalt = new DerObjectIdentifier( + NamingAuthority.IdIsisMttATNamingAuthoritiesRechtWirtschaftSteuern + ".19"); + + private readonly NamingAuthority namingAuthority; + private readonly Asn1Sequence professionItems; + private readonly Asn1Sequence professionOids; + private readonly string registrationNumber; + private readonly Asn1OctetString addProfessionInfo; + + public static ProfessionInfo GetInstance( + object obj) + { + if (obj == null || obj is ProfessionInfo) + { + return (ProfessionInfo) obj; + } + + if (obj is Asn1Sequence) + { + return new ProfessionInfo((Asn1Sequence) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + /** + * 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.Count > 5) + throw new ArgumentException("Bad sequence size: " + seq.Count); + + IEnumerator e = seq.GetEnumerator(); + + e.MoveNext(); + Asn1Encodable o = (Asn1Encodable) e.Current; + + if (o is Asn1TaggedObject) + { + Asn1TaggedObject ato = (Asn1TaggedObject) o; + if (ato.TagNo != 0) + throw new ArgumentException("Bad tag number: " + ato.TagNo); + + namingAuthority = NamingAuthority.GetInstance(ato, true); + e.MoveNext(); + o = (Asn1Encodable) e.Current; + } + + professionItems = Asn1Sequence.GetInstance(o); + + if (e.MoveNext()) + { + o = (Asn1Encodable) e.Current; + if (o is Asn1Sequence) + { + professionOids = Asn1Sequence.GetInstance(o); + } + else if (o is DerPrintableString) + { + registrationNumber = DerPrintableString.GetInstance(o).GetString(); + } + else if (o is Asn1OctetString) + { + addProfessionInfo = Asn1OctetString.GetInstance(o); + } + else + { + throw new ArgumentException("Bad object encountered: " + o.GetType().Name); + } + } + + if (e.MoveNext()) + { + o = (Asn1Encodable) e.Current; + if (o is DerPrintableString) + { + registrationNumber = DerPrintableString.GetInstance(o).GetString(); + } + else if (o is DerOctetString) + { + addProfessionInfo = (DerOctetString) o; + } + else + { + throw new ArgumentException("Bad object encountered: " + o.GetType().Name); + } + } + + if (e.MoveNext()) + { + o = (Asn1Encodable) e.Current; + if (o is DerOctetString) + { + addProfessionInfo = (DerOctetString) o; + } + else + { + throw new ArgumentException("Bad object encountered: " + o.GetType().Name); + } + } + } + + /** + * 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, + DerObjectIdentifier[] professionOids, + string registrationNumber, + Asn1OctetString addProfessionInfo) + { + this.namingAuthority = namingAuthority; + this.professionItems = new DerSequence(professionItems); + if (professionOids != null) + { + this.professionOids = new DerSequence(professionOids); + } + 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 an Asn1Object + */ + public override Asn1Object ToAsn1Object() + { + 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 virtual Asn1OctetString AddProfessionInfo + { + get { return addProfessionInfo; } + } + + /** + * @return Returns the namingAuthority. + */ + public virtual NamingAuthority NamingAuthority + { + get { return namingAuthority; } + } + + /** + * @return Returns the professionItems. + */ + public virtual DirectoryString[] GetProfessionItems() + { + DirectoryString[] result = new DirectoryString[professionItems.Count]; + + for (int i = 0; i < professionItems.Count; ++i) + { + result[i] = DirectoryString.GetInstance(professionItems[i]); + } + + return result; + } + + /** + * @return Returns the professionOids. + */ + public virtual DerObjectIdentifier[] GetProfessionOids() + { + if (professionOids == null) + { + return new DerObjectIdentifier[0]; + } + + DerObjectIdentifier[] result = new DerObjectIdentifier[professionOids.Count]; + + for (int i = 0; i < professionOids.Count; ++i) + { + result[i] = DerObjectIdentifier.GetInstance(professionOids[i]); + } + + return result; + } + + /** + * @return Returns the registrationNumber. + */ + public virtual string RegistrationNumber + { + get { return registrationNumber; } + } + } +} diff --git a/Crypto/src/asn1/isismtt/x509/Restriction.cs b/Crypto/src/asn1/isismtt/x509/Restriction.cs new file mode 100644
index 000000000..c97766999 --- /dev/null +++ b/Crypto/src/asn1/isismtt/x509/Restriction.cs
@@ -0,0 +1,81 @@ +using System; + +using Org.BouncyCastle.Asn1.X500; + +namespace Org.BouncyCastle.Asn1.IsisMtt.X509 +{ + /** + * Some other restriction regarding the usage of this certificate. + * <p/> + * <pre> + * RestrictionSyntax ::= DirectoryString (SIZE(1..1024)) + * </pre> + */ + public class Restriction + : Asn1Encodable + { + private readonly DirectoryString restriction; + + public static Restriction GetInstance( + object obj) + { + if (obj is Restriction) + return (Restriction) obj; + + if (obj is IAsn1String) + return new Restriction(DirectoryString.GetInstance(obj)); + + throw new ArgumentException("Unknown object in GetInstance: " + obj.GetType().Name, "obj"); + } + + /** + * Constructor from DirectoryString. + * <p/> + * The DirectoryString is of type RestrictionSyntax: + * <p/> + * <pre> + * RestrictionSyntax ::= DirectoryString (SIZE(1..1024)) + * </pre> + * + * @param restriction A IAsn1String. + */ + private Restriction( + DirectoryString restriction) + { + this.restriction = restriction; + } + + /** + * Constructor from a given details. + * + * @param restriction The description of the restriction. + */ + public Restriction( + string restriction) + { + this.restriction = new DirectoryString(restriction); + } + + public virtual DirectoryString RestrictionString + { + get { return restriction; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <p/> + * Returns: + * <p/> + * <pre> + * RestrictionSyntax ::= DirectoryString (SIZE(1..1024)) + * <p/> + * </pre> + * + * @return an Asn1Object + */ + public override Asn1Object ToAsn1Object() + { + return restriction.ToAsn1Object(); + } + } +} diff --git a/Crypto/src/asn1/kisa/KISAObjectIdentifiers.cs b/Crypto/src/asn1/kisa/KISAObjectIdentifiers.cs new file mode 100644
index 000000000..05351ec75 --- /dev/null +++ b/Crypto/src/asn1/kisa/KISAObjectIdentifiers.cs
@@ -0,0 +1,8 @@ +namespace Org.BouncyCastle.Asn1.Kisa +{ + public abstract class KisaObjectIdentifiers + { + public static readonly DerObjectIdentifier IdSeedCbc = new DerObjectIdentifier("1.2.410.200004.1.4"); + public static readonly DerObjectIdentifier IdNpkiAppCmsSeedWrap = new DerObjectIdentifier("1.2.410.200004.7.1.1.1"); + } +} diff --git a/Crypto/src/asn1/microsoft/MicrosoftObjectIdentifiers.cs b/Crypto/src/asn1/microsoft/MicrosoftObjectIdentifiers.cs new file mode 100644
index 000000000..b8aba7ee9 --- /dev/null +++ b/Crypto/src/asn1/microsoft/MicrosoftObjectIdentifiers.cs
@@ -0,0 +1,18 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Microsoft +{ + public abstract class MicrosoftObjectIdentifiers + { + // + // Microsoft + // iso(1) identified-organization(3) dod(6) internet(1) private(4) enterprise(1) Microsoft(311) + // + public static readonly DerObjectIdentifier Microsoft = new DerObjectIdentifier("1.3.6.1.4.1.311"); + public static readonly DerObjectIdentifier MicrosoftCertTemplateV1 = new DerObjectIdentifier(Microsoft + ".20.2"); + public static readonly DerObjectIdentifier MicrosoftCAVersion = new DerObjectIdentifier(Microsoft + ".21.1"); + public static readonly DerObjectIdentifier MicrosoftPrevCACertHash = new DerObjectIdentifier(Microsoft + ".21.2"); + public static readonly DerObjectIdentifier MicrosoftCertTemplateV2 = new DerObjectIdentifier(Microsoft + ".21.7"); + public static readonly DerObjectIdentifier MicrosoftAppPolicies = new DerObjectIdentifier(Microsoft + ".21.10"); + } +} diff --git a/Crypto/src/asn1/misc/CAST5CBCParameters.cs b/Crypto/src/asn1/misc/CAST5CBCParameters.cs new file mode 100644
index 000000000..51fd6607a --- /dev/null +++ b/Crypto/src/asn1/misc/CAST5CBCParameters.cs
@@ -0,0 +1,74 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1.Misc +{ + public class Cast5CbcParameters + : Asn1Encodable + { + private readonly DerInteger keyLength; + private readonly Asn1OctetString iv; + + public static Cast5CbcParameters GetInstance( + object o) + { + if (o is Cast5CbcParameters) + { + return (Cast5CbcParameters) o; + } + + if (o is Asn1Sequence) + { + return new Cast5CbcParameters((Asn1Sequence) o); + } + + throw new ArgumentException("unknown object in Cast5CbcParameters factory"); + } + + public Cast5CbcParameters( + byte[] iv, + int keyLength) + { + this.iv = new DerOctetString(iv); + this.keyLength = new DerInteger(keyLength); + } + + private Cast5CbcParameters( + Asn1Sequence seq) + { + if (seq.Count != 2) + throw new ArgumentException("Wrong number of elements in sequence", "seq"); + + iv = (Asn1OctetString) seq[0]; + keyLength = (DerInteger) seq[1]; + } + + public byte[] GetIV() + { + return Arrays.Clone(iv.GetOctets()); + } + + public int KeyLength + { + get { return keyLength.Value.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 override Asn1Object ToAsn1Object() + { + return new DerSequence(iv, keyLength); + } + } +} diff --git a/Crypto/src/asn1/misc/IDEACBCPar.cs b/Crypto/src/asn1/misc/IDEACBCPar.cs new file mode 100644
index 000000000..72a60b9dc --- /dev/null +++ b/Crypto/src/asn1/misc/IDEACBCPar.cs
@@ -0,0 +1,68 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Misc +{ + public class IdeaCbcPar + : Asn1Encodable + { + internal Asn1OctetString iv; + + public static IdeaCbcPar GetInstance( + object o) + { + if (o is IdeaCbcPar) + { + return (IdeaCbcPar) o; + } + + if (o is Asn1Sequence) + { + return new IdeaCbcPar((Asn1Sequence) o); + } + + throw new ArgumentException("unknown object in IDEACBCPar factory"); + } + + public IdeaCbcPar( + byte[] iv) + { + this.iv = new DerOctetString(iv); + } + + private IdeaCbcPar( + Asn1Sequence seq) + { + if (seq.Count == 1) + { + iv = (Asn1OctetString) seq[0]; + } + } + + public byte[] GetIV() + { + return iv == null ? null : iv.GetOctets(); + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * IDEA-CBCPar ::= Sequence { + * iv OCTET STRING OPTIONAL -- exactly 8 octets + * } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(); + + if (iv != null) + { + v.Add(iv); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/misc/MiscObjectIdentifiers.cs b/Crypto/src/asn1/misc/MiscObjectIdentifiers.cs new file mode 100644
index 000000000..01004d889 --- /dev/null +++ b/Crypto/src/asn1/misc/MiscObjectIdentifiers.cs
@@ -0,0 +1,48 @@ +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Misc +{ + public abstract class MiscObjectIdentifiers + { + // + // Netscape + // iso/itu(2) joint-assign(16) us(840) uscompany(1) Netscape(113730) cert-extensions(1) } + // + public static readonly DerObjectIdentifier Netscape = new DerObjectIdentifier("2.16.840.1.113730.1"); + public static readonly DerObjectIdentifier NetscapeCertType = new DerObjectIdentifier(Netscape + ".1"); + public static readonly DerObjectIdentifier NetscapeBaseUrl = new DerObjectIdentifier(Netscape + ".2"); + public static readonly DerObjectIdentifier NetscapeRevocationUrl = new DerObjectIdentifier(Netscape + ".3"); + public static readonly DerObjectIdentifier NetscapeCARevocationUrl = new DerObjectIdentifier(Netscape + ".4"); + public static readonly DerObjectIdentifier NetscapeRenewalUrl = new DerObjectIdentifier(Netscape + ".7"); + public static readonly DerObjectIdentifier NetscapeCAPolicyUrl = new DerObjectIdentifier(Netscape + ".8"); + public static readonly DerObjectIdentifier NetscapeSslServerName = new DerObjectIdentifier(Netscape + ".12"); + public static readonly DerObjectIdentifier NetscapeCertComment = new DerObjectIdentifier(Netscape + ".13"); + // + // Verisign + // iso/itu(2) joint-assign(16) us(840) uscompany(1) verisign(113733) cert-extensions(1) } + // + internal const string Verisign = "2.16.840.1.113733.1"; + + // + // CZAG - country, zip, age, and gender + // + public static readonly DerObjectIdentifier VerisignCzagExtension = new DerObjectIdentifier(Verisign + ".6.3"); + + // D&B D-U-N-S number + public static readonly DerObjectIdentifier VerisignDnbDunsNumber = new DerObjectIdentifier(Verisign + ".6.15"); + + // + // Novell + // iso/itu(2) country(16) us(840) organization(1) novell(113719) + // + public static readonly string Novell = "2.16.840.1.113719"; + public static readonly DerObjectIdentifier NovellSecurityAttribs = new DerObjectIdentifier(Novell + ".1.9.4.1"); + + // + // Entrust + // iso(1) member-body(16) us(840) nortelnetworks(113533) entrust(7) + // + public static readonly string Entrust = "1.2.840.113533.7"; + public static readonly DerObjectIdentifier EntrustVersionExtension = new DerObjectIdentifier(Entrust + ".65.0"); + } +} diff --git a/Crypto/src/asn1/misc/NetscapeCertType.cs b/Crypto/src/asn1/misc/NetscapeCertType.cs new file mode 100644
index 000000000..d5db6523d --- /dev/null +++ b/Crypto/src/asn1/misc/NetscapeCertType.cs
@@ -0,0 +1,54 @@ +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Misc +{ + /** + * 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 + : DerBitString + { + public const int SslClient = (1 << 7); + public const int SslServer = (1 << 6); + public const int Smime = (1 << 5); + public const int ObjectSigning = (1 << 4); + public const int Reserved = (1 << 3); + public const int SslCA = (1 << 2); + public const int SmimeCA = (1 << 1); + public const 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) + : base(GetBytes(usage), GetPadBits(usage)) + { + } + + public NetscapeCertType(DerBitString usage) + : base(usage.GetBytes(), usage.PadBits) + { + } + + public override string ToString() + { + byte[] data = GetBytes(); + return "NetscapeCertType: 0x" + (data[0] & 0xff).ToString("X"); + } + } +} diff --git a/Crypto/src/asn1/misc/NetscapeRevocationURL.cs b/Crypto/src/asn1/misc/NetscapeRevocationURL.cs new file mode 100644
index 000000000..6cac031f2 --- /dev/null +++ b/Crypto/src/asn1/misc/NetscapeRevocationURL.cs
@@ -0,0 +1,18 @@ +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Misc +{ + public class NetscapeRevocationUrl + : DerIA5String + { + public NetscapeRevocationUrl(DerIA5String str) + : base(str.GetString()) + { + } + + public override string ToString() + { + return "NetscapeRevocationUrl: " + this.GetString(); + } + } +} diff --git a/Crypto/src/asn1/misc/VerisignCzagExtension.cs b/Crypto/src/asn1/misc/VerisignCzagExtension.cs new file mode 100644
index 000000000..1c3054b32 --- /dev/null +++ b/Crypto/src/asn1/misc/VerisignCzagExtension.cs
@@ -0,0 +1,18 @@ +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Misc +{ + public class VerisignCzagExtension + : DerIA5String + { + public VerisignCzagExtension(DerIA5String str) + : base(str.GetString()) + { + } + + public override string ToString() + { + return "VerisignCzagExtension: " + this.GetString(); + } + } +} diff --git a/Crypto/src/asn1/mozilla/PublicKeyAndChallenge.cs b/Crypto/src/asn1/mozilla/PublicKeyAndChallenge.cs new file mode 100644
index 000000000..1e08b809d --- /dev/null +++ b/Crypto/src/asn1/mozilla/PublicKeyAndChallenge.cs
@@ -0,0 +1,67 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Mozilla +{ + /** + * 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 + : Asn1Encodable + { + private Asn1Sequence pkacSeq; + private SubjectPublicKeyInfo spki; + private DerIA5String challenge; + + public static PublicKeyAndChallenge GetInstance( + object obj) + { + if (obj is PublicKeyAndChallenge) + { + return (PublicKeyAndChallenge) obj; + } + + if (obj is Asn1Sequence) + { + return new PublicKeyAndChallenge((Asn1Sequence) obj); + } + + throw new ArgumentException( + "unknown object in 'PublicKeyAndChallenge' factory : " + + obj.GetType().Name + "."); + } + + public PublicKeyAndChallenge( + Asn1Sequence seq) + { + pkacSeq = seq; + spki = SubjectPublicKeyInfo.GetInstance(seq[0]); + challenge = DerIA5String.GetInstance(seq[1]); + } + + public override Asn1Object ToAsn1Object() + { + return pkacSeq; + } + + public SubjectPublicKeyInfo SubjectPublicKeyInfo + { + get { return spki; } + } + + public DerIA5String Challenge + { + get { return challenge; } + } + } +} diff --git a/Crypto/src/asn1/nist/NISTNamedCurves.cs b/Crypto/src/asn1/nist/NISTNamedCurves.cs new file mode 100644
index 000000000..3b4c59df2 --- /dev/null +++ b/Crypto/src/asn1/nist/NISTNamedCurves.cs
@@ -0,0 +1,102 @@ +using System; +using System.Collections; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Sec; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Asn1.Nist +{ + /** + * Utility class for fetching curves using their NIST names as published in FIPS-PUB 186-2 + */ + public sealed class NistNamedCurves + { + private NistNamedCurves() + { + } + + private static readonly IDictionary objIds = Platform.CreateHashtable(); + private static readonly IDictionary names = Platform.CreateHashtable(); + + private static void DefineCurve( + string name, + DerObjectIdentifier oid) + { + objIds.Add(name, oid); + names.Add(oid, name); + } + + static NistNamedCurves() + { + 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("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) + { + DerObjectIdentifier oid = (DerObjectIdentifier) objIds[name.ToUpperInvariant()]; + + 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( + DerObjectIdentifier 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 DerObjectIdentifier GetOid( + string name) + { + return (DerObjectIdentifier) objIds[name.ToUpperInvariant()]; + } + + /** + * return the named curve name represented by the given object identifier. + */ + public static string GetName( + DerObjectIdentifier oid) + { + return (string) names[oid]; + } + + /** + * returns an enumeration containing the name strings for curves + * contained in this structure. + */ + public static IEnumerable Names + { + get { return new EnumerableProxy(objIds.Keys); } + } + } +} diff --git a/Crypto/src/asn1/nist/NISTObjectIdentifiers.cs b/Crypto/src/asn1/nist/NISTObjectIdentifiers.cs new file mode 100644
index 000000000..f3957062f --- /dev/null +++ b/Crypto/src/asn1/nist/NISTObjectIdentifiers.cs
@@ -0,0 +1,61 @@ +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Nist +{ + public sealed class NistObjectIdentifiers + { + private NistObjectIdentifiers() + { + } + + // + // NIST + // iso/itu(2) joint-assign(16) us(840) organization(1) gov(101) csor(3) + + // + // nistalgorithms(4) + // + public static readonly DerObjectIdentifier NistAlgorithm = new DerObjectIdentifier("2.16.840.1.101.3.4"); + + public static readonly DerObjectIdentifier IdSha256 = new DerObjectIdentifier(NistAlgorithm + ".2.1"); + public static readonly DerObjectIdentifier IdSha384 = new DerObjectIdentifier(NistAlgorithm + ".2.2"); + public static readonly DerObjectIdentifier IdSha512 = new DerObjectIdentifier(NistAlgorithm + ".2.3"); + public static readonly DerObjectIdentifier IdSha224 = new DerObjectIdentifier(NistAlgorithm + ".2.4"); + + public static readonly DerObjectIdentifier Aes = new DerObjectIdentifier(NistAlgorithm + ".1"); + + public static readonly DerObjectIdentifier IdAes128Ecb = new DerObjectIdentifier(Aes + ".1"); + public static readonly DerObjectIdentifier IdAes128Cbc = new DerObjectIdentifier(Aes + ".2"); + public static readonly DerObjectIdentifier IdAes128Ofb = new DerObjectIdentifier(Aes + ".3"); + public static readonly DerObjectIdentifier IdAes128Cfb = new DerObjectIdentifier(Aes + ".4"); + public static readonly DerObjectIdentifier IdAes128Wrap = new DerObjectIdentifier(Aes + ".5"); + public static readonly DerObjectIdentifier IdAes128Gcm = new DerObjectIdentifier(Aes + ".6"); + public static readonly DerObjectIdentifier IdAes128Ccm = new DerObjectIdentifier(Aes + ".7"); + + public static readonly DerObjectIdentifier IdAes192Ecb = new DerObjectIdentifier(Aes + ".21"); + public static readonly DerObjectIdentifier IdAes192Cbc = new DerObjectIdentifier(Aes + ".22"); + public static readonly DerObjectIdentifier IdAes192Ofb = new DerObjectIdentifier(Aes + ".23"); + public static readonly DerObjectIdentifier IdAes192Cfb = new DerObjectIdentifier(Aes + ".24"); + public static readonly DerObjectIdentifier IdAes192Wrap = new DerObjectIdentifier(Aes + ".25"); + public static readonly DerObjectIdentifier IdAes192Gcm = new DerObjectIdentifier(Aes + ".26"); + public static readonly DerObjectIdentifier IdAes192Ccm = new DerObjectIdentifier(Aes + ".27"); + + public static readonly DerObjectIdentifier IdAes256Ecb = new DerObjectIdentifier(Aes + ".41"); + public static readonly DerObjectIdentifier IdAes256Cbc = new DerObjectIdentifier(Aes + ".42"); + public static readonly DerObjectIdentifier IdAes256Ofb = new DerObjectIdentifier(Aes + ".43"); + public static readonly DerObjectIdentifier IdAes256Cfb = new DerObjectIdentifier(Aes + ".44"); + public static readonly DerObjectIdentifier IdAes256Wrap = new DerObjectIdentifier(Aes + ".45"); + public static readonly DerObjectIdentifier IdAes256Gcm = new DerObjectIdentifier(Aes + ".46"); + public static readonly DerObjectIdentifier IdAes256Ccm = new DerObjectIdentifier(Aes + ".47"); + + // + // signatures + // + public static readonly DerObjectIdentifier IdDsaWithSha2 = new DerObjectIdentifier(NistAlgorithm + ".3"); + + public static readonly DerObjectIdentifier DsaWithSha224 = new DerObjectIdentifier(IdDsaWithSha2 + ".1"); + public static readonly DerObjectIdentifier DsaWithSha256 = new DerObjectIdentifier(IdDsaWithSha2 + ".2"); + public static readonly DerObjectIdentifier DsaWithSha384 = new DerObjectIdentifier(IdDsaWithSha2 + ".3"); + public static readonly DerObjectIdentifier DsaWithSha512 = new DerObjectIdentifier(IdDsaWithSha2 + ".4"); + } +} diff --git a/Crypto/src/asn1/ntt/NTTObjectIdentifiers.cs b/Crypto/src/asn1/ntt/NTTObjectIdentifiers.cs new file mode 100644
index 000000000..cd2595600 --- /dev/null +++ b/Crypto/src/asn1/ntt/NTTObjectIdentifiers.cs
@@ -0,0 +1,14 @@ +namespace Org.BouncyCastle.Asn1.Ntt +{ + /// <summary>From RFC 3657</summary> + public abstract class NttObjectIdentifiers + { + public static readonly DerObjectIdentifier IdCamellia128Cbc = new DerObjectIdentifier("1.2.392.200011.61.1.1.1.2"); + public static readonly DerObjectIdentifier IdCamellia192Cbc = new DerObjectIdentifier("1.2.392.200011.61.1.1.1.3"); + public static readonly DerObjectIdentifier IdCamellia256Cbc = new DerObjectIdentifier("1.2.392.200011.61.1.1.1.4"); + + public static readonly DerObjectIdentifier IdCamellia128Wrap = new DerObjectIdentifier("1.2.392.200011.61.1.1.3.2"); + public static readonly DerObjectIdentifier IdCamellia192Wrap = new DerObjectIdentifier("1.2.392.200011.61.1.1.3.3"); + public static readonly DerObjectIdentifier IdCamellia256Wrap = new DerObjectIdentifier("1.2.392.200011.61.1.1.3.4"); + } +} diff --git a/Crypto/src/asn1/ocsp/BasicOCSPResponse.cs b/Crypto/src/asn1/ocsp/BasicOCSPResponse.cs new file mode 100644
index 000000000..dd666addf --- /dev/null +++ b/Crypto/src/asn1/ocsp/BasicOCSPResponse.cs
@@ -0,0 +1,131 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Ocsp +{ + public class BasicOcspResponse + : Asn1Encodable + { + private readonly ResponseData tbsResponseData; + private readonly AlgorithmIdentifier signatureAlgorithm; + private readonly DerBitString signature; + private readonly Asn1Sequence certs; + + public static BasicOcspResponse GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static BasicOcspResponse GetInstance( + object obj) + { + if (obj == null || obj is BasicOcspResponse) + { + return (BasicOcspResponse)obj; + } + + if (obj is Asn1Sequence) + { + return new BasicOcspResponse((Asn1Sequence)obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + 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[0]); + this.signatureAlgorithm = AlgorithmIdentifier.GetInstance(seq[1]); + this.signature = (DerBitString)seq[2]; + + if (seq.Count > 3) + { + this.certs = Asn1Sequence.GetInstance((Asn1TaggedObject)seq[3], true); + } + } + + [Obsolete("Use TbsResponseData property instead")] + public ResponseData GetTbsResponseData() + { + return tbsResponseData; + } + + public ResponseData TbsResponseData + { + get { return tbsResponseData; } + } + + [Obsolete("Use SignatureAlgorithm property instead")] + public AlgorithmIdentifier GetSignatureAlgorithm() + { + return signatureAlgorithm; + } + + public AlgorithmIdentifier SignatureAlgorithm + { + get { return signatureAlgorithm; } + } + + [Obsolete("Use Signature property instead")] + public DerBitString GetSignature() + { + return signature; + } + + public DerBitString Signature + { + get { return signature; } + } + + [Obsolete("Use Certs property instead")] + public Asn1Sequence GetCerts() + { + return certs; + } + + public Asn1Sequence Certs + { + get { 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector( + tbsResponseData, signatureAlgorithm, signature); + + if (certs != null) + { + v.Add(new DerTaggedObject(true, 0, certs)); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/ocsp/CertID.cs b/Crypto/src/asn1/ocsp/CertID.cs new file mode 100644
index 000000000..4b251095b --- /dev/null +++ b/Crypto/src/asn1/ocsp/CertID.cs
@@ -0,0 +1,98 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Ocsp +{ + public class CertID + : Asn1Encodable + { + private readonly AlgorithmIdentifier hashAlgorithm; + private readonly Asn1OctetString issuerNameHash; + private readonly Asn1OctetString issuerKeyHash; + private readonly DerInteger serialNumber; + + public static CertID GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static CertID GetInstance( + object obj) + { + if (obj == null || obj is CertID) + { + return (CertID)obj; + } + + if (obj is Asn1Sequence) + { + return new CertID((Asn1Sequence)obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public CertID( + AlgorithmIdentifier hashAlgorithm, + Asn1OctetString issuerNameHash, + Asn1OctetString issuerKeyHash, + DerInteger serialNumber) + { + this.hashAlgorithm = hashAlgorithm; + this.issuerNameHash = issuerNameHash; + this.issuerKeyHash = issuerKeyHash; + this.serialNumber = serialNumber; + } + + private CertID( + Asn1Sequence seq) + { + if (seq.Count != 4) + throw new ArgumentException("Wrong number of elements in sequence", "seq"); + + this.hashAlgorithm = AlgorithmIdentifier.GetInstance(seq[0]); + this.issuerNameHash = Asn1OctetString.GetInstance(seq[1]); + this.issuerKeyHash = Asn1OctetString.GetInstance(seq[2]); + this.serialNumber = DerInteger.GetInstance(seq[3]); + } + + public AlgorithmIdentifier HashAlgorithm + { + get { return hashAlgorithm; } + } + + public Asn1OctetString IssuerNameHash + { + get { return issuerNameHash; } + } + + public Asn1OctetString IssuerKeyHash + { + get { return issuerKeyHash; } + } + + public DerInteger SerialNumber + { + get { 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 override Asn1Object ToAsn1Object() + { + return new DerSequence(hashAlgorithm, issuerNameHash, issuerKeyHash, serialNumber); + } + } +} diff --git a/Crypto/src/asn1/ocsp/CertStatus.cs b/Crypto/src/asn1/ocsp/CertStatus.cs new file mode 100644
index 000000000..d5b1a94a2 --- /dev/null +++ b/Crypto/src/asn1/ocsp/CertStatus.cs
@@ -0,0 +1,94 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Ocsp +{ + public class CertStatus + : Asn1Encodable, IAsn1Choice + { + private readonly int tagNo; + private readonly 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.TagNo; + + switch (choice.TagNo) + { + case 1: + value = RevokedInfo.GetInstance(choice, false); + break; + case 0: + case 2: + value = DerNull.Instance; + break; + } + } + + public static CertStatus GetInstance( + object obj) + { + if (obj == null || obj is CertStatus) + { + return (CertStatus)obj; + } + + if (obj is Asn1TaggedObject) + { + return new CertStatus((Asn1TaggedObject)obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public int TagNo + { + get { return tagNo; } + } + + public Asn1Encodable Status + { + get { 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 override Asn1Object ToAsn1Object() + { + return new DerTaggedObject(false, tagNo, value); + } + } +} diff --git a/Crypto/src/asn1/ocsp/CrlID.cs b/Crypto/src/asn1/ocsp/CrlID.cs new file mode 100644
index 000000000..cfb3d6fcb --- /dev/null +++ b/Crypto/src/asn1/ocsp/CrlID.cs
@@ -0,0 +1,82 @@ +using System; +using System.Collections; + +namespace Org.BouncyCastle.Asn1.Ocsp +{ + public class CrlID + : Asn1Encodable + { + private readonly DerIA5String crlUrl; + private readonly DerInteger crlNum; + private readonly DerGeneralizedTime crlTime; + + // TODO Add GetInstance method(s) and amke this private? + public CrlID( + Asn1Sequence seq) + { + foreach (Asn1TaggedObject o in seq) + { + switch (o.TagNo) + { + case 0: + crlUrl = DerIA5String.GetInstance(o, true); + break; + case 1: + crlNum = DerInteger.GetInstance(o, true); + break; + case 2: + crlTime = DerGeneralizedTime.GetInstance(o, true); + break; + default: + throw new ArgumentException("unknown tag number: " + o.TagNo); + } + } + } + + public DerIA5String CrlUrl + { + get { return crlUrl; } + } + + public DerInteger CrlNum + { + get { return crlNum; } + } + + public DerGeneralizedTime CrlTime + { + get { 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 override Asn1Object ToAsn1Object() + { + 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/Crypto/src/asn1/ocsp/OCSPObjectIdentifiers.cs b/Crypto/src/asn1/ocsp/OCSPObjectIdentifiers.cs new file mode 100644
index 000000000..a37c8552d --- /dev/null +++ b/Crypto/src/asn1/ocsp/OCSPObjectIdentifiers.cs
@@ -0,0 +1,23 @@ +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Ocsp +{ + public abstract class OcspObjectIdentifiers + { + internal const string PkixOcspId = "1.3.6.1.5.5.7.48.1"; + + public static readonly DerObjectIdentifier PkixOcsp = new DerObjectIdentifier(PkixOcspId); + public static readonly DerObjectIdentifier PkixOcspBasic = new DerObjectIdentifier(PkixOcspId + ".1"); + + // + // extensions + // + public static readonly DerObjectIdentifier PkixOcspNonce = new DerObjectIdentifier(PkixOcsp + ".2"); + public static readonly DerObjectIdentifier PkixOcspCrl = new DerObjectIdentifier(PkixOcsp + ".3"); + + public static readonly DerObjectIdentifier PkixOcspResponse = new DerObjectIdentifier(PkixOcsp + ".4"); + public static readonly DerObjectIdentifier PkixOcspNocheck = new DerObjectIdentifier(PkixOcsp + ".5"); + public static readonly DerObjectIdentifier PkixOcspArchiveCutoff = new DerObjectIdentifier(PkixOcsp + ".6"); + public static readonly DerObjectIdentifier PkixOcspServiceLocator = new DerObjectIdentifier(PkixOcsp + ".7"); + } +} diff --git a/Crypto/src/asn1/ocsp/OCSPRequest.cs b/Crypto/src/asn1/ocsp/OCSPRequest.cs new file mode 100644
index 000000000..1e804d78e --- /dev/null +++ b/Crypto/src/asn1/ocsp/OCSPRequest.cs
@@ -0,0 +1,89 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Ocsp +{ + public class OcspRequest + : Asn1Encodable + { + private readonly TbsRequest tbsRequest; + private readonly Signature optionalSignature; + + public static OcspRequest GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static OcspRequest GetInstance( + object obj) + { + if (obj == null || obj is OcspRequest) + { + return (OcspRequest)obj; + } + + if (obj is Asn1Sequence) + { + return new OcspRequest((Asn1Sequence)obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public OcspRequest( + TbsRequest tbsRequest, + Signature optionalSignature) + { + if (tbsRequest == null) + throw new ArgumentNullException("tbsRequest"); + + this.tbsRequest = tbsRequest; + this.optionalSignature = optionalSignature; + } + + private OcspRequest( + Asn1Sequence seq) + { + tbsRequest = TbsRequest.GetInstance(seq[0]); + + if (seq.Count == 2) + { + optionalSignature = Signature.GetInstance( + (Asn1TaggedObject)seq[1], true); + } + } + + public TbsRequest TbsRequest + { + get { return tbsRequest; } + } + + public Signature OptionalSignature + { + get { return optionalSignature; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * OcspRequest ::= Sequence { + * tbsRequest TBSRequest, + * optionalSignature [0] EXPLICIT Signature OPTIONAL } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(tbsRequest); + + if (optionalSignature != null) + { + v.Add(new DerTaggedObject(true, 0, optionalSignature)); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/ocsp/OCSPResponse.cs b/Crypto/src/asn1/ocsp/OCSPResponse.cs new file mode 100644
index 000000000..e9aad8100 --- /dev/null +++ b/Crypto/src/asn1/ocsp/OCSPResponse.cs
@@ -0,0 +1,90 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Ocsp +{ + public class OcspResponse + : Asn1Encodable + { + private readonly OcspResponseStatus responseStatus; + private readonly ResponseBytes responseBytes; + + public static OcspResponse GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static OcspResponse GetInstance( + object obj) + { + if (obj == null || obj is OcspResponse) + { + return (OcspResponse)obj; + } + + if (obj is Asn1Sequence) + { + return new OcspResponse((Asn1Sequence)obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public OcspResponse( + OcspResponseStatus responseStatus, + ResponseBytes responseBytes) + { + if (responseStatus == null) + throw new ArgumentNullException("responseStatus"); + + this.responseStatus = responseStatus; + this.responseBytes = responseBytes; + } + + private OcspResponse( + Asn1Sequence seq) + { + responseStatus = new OcspResponseStatus( + DerEnumerated.GetInstance(seq[0])); + + if (seq.Count == 2) + { + responseBytes = ResponseBytes.GetInstance( + (Asn1TaggedObject)seq[1], true); + } + } + + public OcspResponseStatus ResponseStatus + { + get { return responseStatus; } + } + + public ResponseBytes ResponseBytes + { + get { return responseBytes; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * OcspResponse ::= Sequence { + * responseStatus OcspResponseStatus, + * responseBytes [0] EXPLICIT ResponseBytes OPTIONAL } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(responseStatus); + + if (responseBytes != null) + { + v.Add(new DerTaggedObject(true, 0, responseBytes)); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/ocsp/OCSPResponseStatus.cs b/Crypto/src/asn1/ocsp/OCSPResponseStatus.cs new file mode 100644
index 000000000..653317e33 --- /dev/null +++ b/Crypto/src/asn1/ocsp/OCSPResponseStatus.cs
@@ -0,0 +1,41 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Ocsp +{ + public class OcspResponseStatus + : DerEnumerated + { + public const int Successful = 0; + public const int MalformedRequest = 1; + public const int InternalError = 2; + public const int TryLater = 3; + public const int SignatureRequired = 5; + public const int Unauthorized = 6; + + /** + * 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) + : base(value) + { + } + + public OcspResponseStatus(DerEnumerated value) + : base(value.Value.IntValue) + { + } + } +} diff --git a/Crypto/src/asn1/ocsp/Request.cs b/Crypto/src/asn1/ocsp/Request.cs new file mode 100644
index 000000000..116c15e73 --- /dev/null +++ b/Crypto/src/asn1/ocsp/Request.cs
@@ -0,0 +1,90 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Ocsp +{ + public class Request + : Asn1Encodable + { + private readonly CertID reqCert; + private readonly X509Extensions singleRequestExtensions; + + public static Request GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static Request GetInstance( + object obj) + { + if (obj == null || obj is Request) + { + return (Request)obj; + } + + if (obj is Asn1Sequence) + { + return new Request((Asn1Sequence)obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public Request( + CertID reqCert, + X509Extensions singleRequestExtensions) + { + if (reqCert == null) + throw new ArgumentNullException("reqCert"); + + this.reqCert = reqCert; + this.singleRequestExtensions = singleRequestExtensions; + } + + private Request( + Asn1Sequence seq) + { + reqCert = CertID.GetInstance(seq[0]); + + if (seq.Count == 2) + { + singleRequestExtensions = X509Extensions.GetInstance( + (Asn1TaggedObject)seq[1], true); + } + } + + public CertID ReqCert + { + get { return reqCert; } + } + + public X509Extensions SingleRequestExtensions + { + get { return singleRequestExtensions; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * Request ::= Sequence { + * reqCert CertID, + * singleRequestExtensions [0] EXPLICIT Extensions OPTIONAL } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(reqCert); + + if (singleRequestExtensions != null) + { + v.Add(new DerTaggedObject(true, 0, singleRequestExtensions)); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/ocsp/ResponderID.cs b/Crypto/src/asn1/ocsp/ResponderID.cs new file mode 100644
index 000000000..143b17339 --- /dev/null +++ b/Crypto/src/asn1/ocsp/ResponderID.cs
@@ -0,0 +1,107 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Ocsp +{ + public class ResponderID + : Asn1Encodable, IAsn1Choice + { + private readonly Asn1Encodable id; + + public static ResponderID GetInstance( + object obj) + { + if (obj == null || obj is ResponderID) + { + return (ResponderID)obj; + } + + if (obj is DerOctetString) + { + return new ResponderID((DerOctetString)obj); + } + + if (obj is Asn1TaggedObject) + { + Asn1TaggedObject o = (Asn1TaggedObject)obj; + + if (o.TagNo == 1) + { + return new ResponderID(X509Name.GetInstance(o, true)); + } + + return new ResponderID(Asn1OctetString.GetInstance(o, true)); + } + + return new ResponderID(X509Name.GetInstance(obj)); + } + + public ResponderID( + Asn1OctetString id) + { + if (id == null) + throw new ArgumentNullException("id"); + + this.id = id; + } + + public ResponderID( + X509Name id) + { + if (id == null) + throw new ArgumentNullException("id"); + + this.id = id; + } + + public static ResponderID GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + return GetInstance(obj.GetObject()); // must be explicitly tagged + } + + public virtual byte[] GetKeyHash() + { + if (id is Asn1OctetString) + { + return ((Asn1OctetString)id).GetOctets(); + } + + return null; + } + + public virtual X509Name Name + { + get + { + if (id is Asn1OctetString) + { + return null; + } + + return X509Name.GetInstance(id); + } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * ResponderID ::= CHOICE { + * byName [1] Name, + * byKey [2] KeyHash } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + if (id is Asn1OctetString) + { + return new DerTaggedObject(true, 2, id); + } + + return new DerTaggedObject(true, 1, id); + } + } +} diff --git a/Crypto/src/asn1/ocsp/ResponseBytes.cs b/Crypto/src/asn1/ocsp/ResponseBytes.cs new file mode 100644
index 000000000..2ce59faea --- /dev/null +++ b/Crypto/src/asn1/ocsp/ResponseBytes.cs
@@ -0,0 +1,82 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Ocsp +{ + public class ResponseBytes + : Asn1Encodable + { + private readonly DerObjectIdentifier responseType; + private readonly Asn1OctetString response; + + public static ResponseBytes GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static ResponseBytes GetInstance( + object obj) + { + if (obj == null || obj is ResponseBytes) + { + return (ResponseBytes)obj; + } + + if (obj is Asn1Sequence) + { + return new ResponseBytes((Asn1Sequence)obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public ResponseBytes( + DerObjectIdentifier responseType, + Asn1OctetString response) + { + if (responseType == null) + throw new ArgumentNullException("responseType"); + if (response == null) + throw new ArgumentNullException("response"); + + this.responseType = responseType; + this.response = response; + } + + private ResponseBytes( + Asn1Sequence seq) + { + if (seq.Count != 2) + throw new ArgumentException("Wrong number of elements in sequence", "seq"); + + this.responseType = DerObjectIdentifier.GetInstance(seq[0]); + this.response = Asn1OctetString.GetInstance(seq[1]); + } + + public DerObjectIdentifier ResponseType + { + get { return responseType; } + } + + public Asn1OctetString Response + { + get { return response; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * ResponseBytes ::= Sequence { + * responseType OBJECT IDENTIFIER, + * response OCTET STRING } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + return new DerSequence(responseType, response); + } + } +} diff --git a/Crypto/src/asn1/ocsp/ResponseData.cs b/Crypto/src/asn1/ocsp/ResponseData.cs new file mode 100644
index 000000000..173829db8 --- /dev/null +++ b/Crypto/src/asn1/ocsp/ResponseData.cs
@@ -0,0 +1,158 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Ocsp +{ + public class ResponseData + : Asn1Encodable + { + private static readonly DerInteger V1 = new DerInteger(0); + + private readonly bool versionPresent; + private readonly DerInteger version; + private readonly ResponderID responderID; + private readonly DerGeneralizedTime producedAt; + private readonly Asn1Sequence responses; + private readonly X509Extensions responseExtensions; + + public static ResponseData GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static ResponseData GetInstance( + object obj) + { + if (obj == null || obj is ResponseData) + { + return (ResponseData)obj; + } + + if (obj is Asn1Sequence) + { + return new ResponseData((Asn1Sequence)obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public ResponseData( + DerInteger version, + ResponderID responderID, + DerGeneralizedTime producedAt, + Asn1Sequence responses, + X509Extensions responseExtensions) + { + this.version = version; + this.responderID = responderID; + this.producedAt = producedAt; + this.responses = responses; + this.responseExtensions = responseExtensions; + } + + public ResponseData( + ResponderID responderID, + DerGeneralizedTime producedAt, + Asn1Sequence responses, + X509Extensions responseExtensions) + : this(V1, responderID, producedAt, responses, responseExtensions) + { + } + + private ResponseData( + Asn1Sequence seq) + { + int index = 0; + + Asn1Encodable enc = seq[0]; + if (enc is Asn1TaggedObject) + { + Asn1TaggedObject o = (Asn1TaggedObject)enc; + + if (o.TagNo == 0) + { + this.versionPresent = true; + this.version = DerInteger.GetInstance(o, true); + index++; + } + else + { + this.version = V1; + } + } + else + { + this.version = V1; + } + + this.responderID = ResponderID.GetInstance(seq[index++]); + this.producedAt = (DerGeneralizedTime)seq[index++]; + this.responses = (Asn1Sequence)seq[index++]; + + if (seq.Count > index) + { + this.responseExtensions = X509Extensions.GetInstance( + (Asn1TaggedObject)seq[index], true); + } + } + + public DerInteger Version + { + get { return version; } + } + + public ResponderID ResponderID + { + get { return responderID; } + } + + public DerGeneralizedTime ProducedAt + { + get { return producedAt; } + } + + public Asn1Sequence Responses + { + get { return responses; } + } + + public X509Extensions ResponseExtensions + { + get { 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(); + + if (versionPresent || !version.Equals(V1)) + { + v.Add(new DerTaggedObject(true, 0, version)); + } + + v.Add(responderID, producedAt, responses); + + if (responseExtensions != null) + { + v.Add(new DerTaggedObject(true, 1, responseExtensions)); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/ocsp/RevokedInfo.cs b/Crypto/src/asn1/ocsp/RevokedInfo.cs new file mode 100644
index 000000000..7d9d590e3 --- /dev/null +++ b/Crypto/src/asn1/ocsp/RevokedInfo.cs
@@ -0,0 +1,96 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Ocsp +{ + public class RevokedInfo + : Asn1Encodable + { + private readonly DerGeneralizedTime revocationTime; + private readonly CrlReason revocationReason; + + public static RevokedInfo GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static RevokedInfo GetInstance( + object obj) + { + if (obj == null || obj is RevokedInfo) + { + return (RevokedInfo) obj; + } + + if (obj is Asn1Sequence) + { + return new RevokedInfo((Asn1Sequence) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public RevokedInfo( + DerGeneralizedTime revocationTime) + : this(revocationTime, null) + { + } + + public RevokedInfo( + DerGeneralizedTime revocationTime, + CrlReason revocationReason) + { + if (revocationTime == null) + throw new ArgumentNullException("revocationTime"); + + this.revocationTime = revocationTime; + this.revocationReason = revocationReason; + } + + private RevokedInfo( + Asn1Sequence seq) + { + this.revocationTime = (DerGeneralizedTime) seq[0]; + + if (seq.Count > 1) + { + this.revocationReason = new CrlReason( + DerEnumerated.GetInstance((Asn1TaggedObject) seq[1], true)); + } + } + + public DerGeneralizedTime RevocationTime + { + get { return revocationTime; } + } + + public CrlReason RevocationReason + { + get { return revocationReason; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * RevokedInfo ::= Sequence { + * revocationTime GeneralizedTime, + * revocationReason [0] EXPLICIT CRLReason OPTIONAL } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(revocationTime); + + if (revocationReason != null) + { + v.Add(new DerTaggedObject(true, 0, revocationReason)); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/ocsp/ServiceLocator.cs b/Crypto/src/asn1/ocsp/ServiceLocator.cs new file mode 100644
index 000000000..56bc49ded --- /dev/null +++ b/Crypto/src/asn1/ocsp/ServiceLocator.cs
@@ -0,0 +1,95 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Ocsp +{ + public class ServiceLocator + : Asn1Encodable + { + private readonly X509Name issuer; + private readonly Asn1Object locator; + + public static ServiceLocator GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static ServiceLocator GetInstance( + object obj) + { + if (obj == null || obj is ServiceLocator) + { + return (ServiceLocator) obj; + } + + if (obj is Asn1Sequence) + { + return new ServiceLocator((Asn1Sequence) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public ServiceLocator( + X509Name issuer) + : this(issuer, null) + { + } + + public ServiceLocator( + X509Name issuer, + Asn1Object locator) + { + if (issuer == null) + throw new ArgumentNullException("issuer"); + + this.issuer = issuer; + this.locator = locator; + } + + private ServiceLocator( + Asn1Sequence seq) + { + this.issuer = X509Name.GetInstance(seq[0]); + + if (seq.Count > 1) + { + this.locator = seq[1].ToAsn1Object(); + } + } + + public X509Name Issuer + { + get { return issuer; } + } + + public Asn1Object Locator + { + get { return locator; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * ServiceLocator ::= Sequence { + * issuer Name, + * locator AuthorityInfoAccessSyntax OPTIONAL } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(issuer); + + if (locator != null) + { + v.Add(locator); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/ocsp/Signature.cs b/Crypto/src/asn1/ocsp/Signature.cs new file mode 100644
index 000000000..a07e7a709 --- /dev/null +++ b/Crypto/src/asn1/ocsp/Signature.cs
@@ -0,0 +1,110 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Ocsp +{ + public class Signature + : Asn1Encodable + { + internal AlgorithmIdentifier signatureAlgorithm; + internal DerBitString signatureValue; + internal Asn1Sequence certs; + + public static Signature GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static Signature GetInstance( + object obj) + { + if (obj == null || obj is Signature) + { + return (Signature)obj; + } + + if (obj is Asn1Sequence) + { + return new Signature((Asn1Sequence)obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public Signature( + AlgorithmIdentifier signatureAlgorithm, + DerBitString signatureValue) + : this(signatureAlgorithm, signatureValue, null) + { + } + + public Signature( + AlgorithmIdentifier signatureAlgorithm, + DerBitString signatureValue, + Asn1Sequence certs) + { + if (signatureAlgorithm == null) + throw new ArgumentException("signatureAlgorithm"); + if (signatureValue == null) + throw new ArgumentException("signatureValue"); + + this.signatureAlgorithm = signatureAlgorithm; + this.signatureValue = signatureValue; + this.certs = certs; + } + + private Signature( + Asn1Sequence seq) + { + signatureAlgorithm = AlgorithmIdentifier.GetInstance(seq[0]); + signatureValue = (DerBitString)seq[1]; + + if (seq.Count == 3) + { + certs = Asn1Sequence.GetInstance( + (Asn1TaggedObject)seq[2], true); + } + } + + public AlgorithmIdentifier SignatureAlgorithm + { + get { return signatureAlgorithm; } + } + + public DerBitString SignatureValue + { + get { return signatureValue; } + } + + public Asn1Sequence Certs + { + get { 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector( + signatureAlgorithm, signatureValue); + + if (certs != null) + { + v.Add(new DerTaggedObject(true, 0, certs)); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/ocsp/SingleResponse.cs b/Crypto/src/asn1/ocsp/SingleResponse.cs new file mode 100644
index 000000000..93d4c21d6 --- /dev/null +++ b/Crypto/src/asn1/ocsp/SingleResponse.cs
@@ -0,0 +1,137 @@ +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +using System; + +namespace Org.BouncyCastle.Asn1.Ocsp +{ + public class SingleResponse + : Asn1Encodable + { + private readonly CertID certID; + private readonly CertStatus certStatus; + private readonly DerGeneralizedTime thisUpdate; + private readonly DerGeneralizedTime nextUpdate; + private readonly X509Extensions singleExtensions; + + public SingleResponse( + CertID certID, + CertStatus certStatus, + DerGeneralizedTime thisUpdate, + DerGeneralizedTime nextUpdate, + X509Extensions singleExtensions) + { + this.certID = certID; + this.certStatus = certStatus; + this.thisUpdate = thisUpdate; + this.nextUpdate = nextUpdate; + this.singleExtensions = singleExtensions; + } + + public SingleResponse( + Asn1Sequence seq) + { + this.certID = CertID.GetInstance(seq[0]); + this.certStatus = CertStatus.GetInstance(seq[1]); + this.thisUpdate = (DerGeneralizedTime)seq[2]; + + if (seq.Count > 4) + { + this.nextUpdate = DerGeneralizedTime.GetInstance( + (Asn1TaggedObject) seq[3], true); + this.singleExtensions = X509Extensions.GetInstance( + (Asn1TaggedObject) seq[4], true); + } + else if (seq.Count > 3) + { + Asn1TaggedObject o = (Asn1TaggedObject) seq[3]; + + if (o.TagNo == 0) + { + this.nextUpdate = DerGeneralizedTime.GetInstance(o, true); + } + else + { + this.singleExtensions = X509Extensions.GetInstance(o, true); + } + } + } + + public static SingleResponse GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static SingleResponse GetInstance( + object obj) + { + if (obj == null || obj is SingleResponse) + { + return (SingleResponse)obj; + } + + if (obj is Asn1Sequence) + { + return new SingleResponse((Asn1Sequence)obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public CertID CertId + { + get { return certID; } + } + + public CertStatus CertStatus + { + get { return certStatus; } + } + + public DerGeneralizedTime ThisUpdate + { + get { return thisUpdate; } + } + + public DerGeneralizedTime NextUpdate + { + get { return nextUpdate; } + } + + public X509Extensions SingleExtensions + { + get { 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector( + certID, certStatus, 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/Crypto/src/asn1/ocsp/TBSRequest.cs b/Crypto/src/asn1/ocsp/TBSRequest.cs new file mode 100644
index 000000000..6bf75eb96 --- /dev/null +++ b/Crypto/src/asn1/ocsp/TBSRequest.cs
@@ -0,0 +1,151 @@ +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +using System; + +namespace Org.BouncyCastle.Asn1.Ocsp +{ + public class TbsRequest + : Asn1Encodable + { + private static readonly DerInteger V1 = new DerInteger(0); + + private readonly DerInteger version; + private readonly GeneralName requestorName; + private readonly Asn1Sequence requestList; + private readonly X509Extensions requestExtensions; + + private bool versionSet; + + public static TbsRequest GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static TbsRequest GetInstance( + object obj) + { + if (obj == null || obj is TbsRequest) + { + return (TbsRequest)obj; + } + + if (obj is Asn1Sequence) + { + return new TbsRequest((Asn1Sequence)obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public TbsRequest( + GeneralName requestorName, + Asn1Sequence requestList, + X509Extensions requestExtensions) + { + this.version = V1; + this.requestorName = requestorName; + this.requestList = requestList; + this.requestExtensions = requestExtensions; + } + + private TbsRequest( + Asn1Sequence seq) + { + int index = 0; + + Asn1Encodable enc = seq[0]; + if (enc is Asn1TaggedObject) + { + Asn1TaggedObject o = (Asn1TaggedObject) enc; + + if (o.TagNo == 0) + { + versionSet = true; + version = DerInteger.GetInstance(o, true); + index++; + } + else + { + version = V1; + } + } + else + { + version = V1; + } + + if (seq[index] is Asn1TaggedObject) + { + requestorName = GeneralName.GetInstance((Asn1TaggedObject) seq[index++], true); + } + + requestList = (Asn1Sequence) seq[index++]; + + if (seq.Count == (index + 1)) + { + requestExtensions = X509Extensions.GetInstance((Asn1TaggedObject) seq[index], true); + } + } + + public DerInteger Version + { + get { return version; } + } + + public GeneralName RequestorName + { + get { return requestorName; } + } + + public Asn1Sequence RequestList + { + get { return requestList; } + } + + public X509Extensions RequestExtensions + { + get { 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 override Asn1Object ToAsn1Object() + { + 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/Crypto/src/asn1/oiw/ElGamalParameter.cs b/Crypto/src/asn1/oiw/ElGamalParameter.cs new file mode 100644
index 000000000..3e020f059 --- /dev/null +++ b/Crypto/src/asn1/oiw/ElGamalParameter.cs
@@ -0,0 +1,47 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Asn1.Oiw +{ + public class ElGamalParameter + : Asn1Encodable + { + internal DerInteger p, g; + + public ElGamalParameter( + BigInteger p, + BigInteger g) + { + this.p = new DerInteger(p); + this.g = new DerInteger(g); + } + + public ElGamalParameter( + Asn1Sequence seq) + { + if (seq.Count != 2) + throw new ArgumentException("Wrong number of elements in sequence", "seq"); + + p = DerInteger.GetInstance(seq[0]); + g = DerInteger.GetInstance(seq[1]); + } + + public BigInteger P + { + get { return p.PositiveValue; } + } + + public BigInteger G + { + get { return g.PositiveValue; } + } + + public override Asn1Object ToAsn1Object() + { + return new DerSequence(p, g); + } + } +} diff --git a/Crypto/src/asn1/oiw/OIWObjectIdentifiers.cs b/Crypto/src/asn1/oiw/OIWObjectIdentifiers.cs new file mode 100644
index 000000000..3da226301 --- /dev/null +++ b/Crypto/src/asn1/oiw/OIWObjectIdentifiers.cs
@@ -0,0 +1,29 @@ +namespace Org.BouncyCastle.Asn1.Oiw +{ + public abstract class OiwObjectIdentifiers + { + public static readonly DerObjectIdentifier MD4WithRsa = new DerObjectIdentifier("1.3.14.3.2.2"); + public static readonly DerObjectIdentifier MD5WithRsa = new DerObjectIdentifier("1.3.14.3.2.3"); + public static readonly DerObjectIdentifier MD4WithRsaEncryption = new DerObjectIdentifier("1.3.14.3.2.4"); + + public static readonly DerObjectIdentifier DesEcb = new DerObjectIdentifier("1.3.14.3.2.6"); + public static readonly DerObjectIdentifier DesCbc = new DerObjectIdentifier("1.3.14.3.2.7"); + public static readonly DerObjectIdentifier DesOfb = new DerObjectIdentifier("1.3.14.3.2.8"); + public static readonly DerObjectIdentifier DesCfb = new DerObjectIdentifier("1.3.14.3.2.9"); + + public static readonly DerObjectIdentifier DesEde = new DerObjectIdentifier("1.3.14.3.2.17"); + + // id-SHA1 OBJECT IDENTIFIER ::= + // {iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 26 } // + public static readonly DerObjectIdentifier IdSha1 = new DerObjectIdentifier("1.3.14.3.2.26"); + + public static readonly DerObjectIdentifier DsaWithSha1 = new DerObjectIdentifier("1.3.14.3.2.27"); + + public static readonly DerObjectIdentifier Sha1WithRsa = new DerObjectIdentifier("1.3.14.3.2.29"); + + // ElGamal Algorithm OBJECT IDENTIFIER ::= + // {iso(1) identified-organization(3) oiw(14) dirservsig(7) algorithm(2) encryption(1) 1 } + // + public static readonly DerObjectIdentifier ElGamalAlgorithm = new DerObjectIdentifier("1.3.14.7.2.1.1"); + } +} diff --git a/Crypto/src/asn1/pkcs/Attribute.cs b/Crypto/src/asn1/pkcs/Attribute.cs new file mode 100644
index 000000000..ceec115bd --- /dev/null +++ b/Crypto/src/asn1/pkcs/Attribute.cs
@@ -0,0 +1,79 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Pkcs +{ + public class AttributePkcs + : Asn1Encodable + { + private readonly DerObjectIdentifier attrType; + private readonly Asn1Set attrValues; + + /** + * return an Attribute object from the given object. + * + * @param o the object we want converted. + * @exception ArgumentException if the object cannot be converted. + */ + public static AttributePkcs GetInstance( + object obj) + { + AttributePkcs attr = obj as AttributePkcs; + if (obj == null || attr != null) + { + return attr; + } + + Asn1Sequence seq = obj as Asn1Sequence; + if (seq != null) + { + return new AttributePkcs(seq); + } + + throw new ArgumentException("Unknown object in factory: " + obj.GetType().FullName, "obj"); + } + + private AttributePkcs( + Asn1Sequence seq) + { + if (seq.Count != 2) + throw new ArgumentException("Wrong number of elements in sequence", "seq"); + + attrType = DerObjectIdentifier.GetInstance(seq[0]); + attrValues = Asn1Set.GetInstance(seq[1]); + } + + public AttributePkcs( + DerObjectIdentifier attrType, + Asn1Set attrValues) + { + this.attrType = attrType; + this.attrValues = attrValues; + } + + public DerObjectIdentifier AttrType + { + get { return attrType; } + } + + public Asn1Set AttrValues + { + get { return attrValues; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * Attr ::= Sequence { + * attrType OBJECT IDENTIFIER, + * attrValues Set OF AttributeValue + * } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + return new DerSequence(attrType, attrValues); + } + } +} diff --git a/Crypto/src/asn1/pkcs/AuthenticatedSafe.cs b/Crypto/src/asn1/pkcs/AuthenticatedSafe.cs new file mode 100644
index 000000000..f3dabb89c --- /dev/null +++ b/Crypto/src/asn1/pkcs/AuthenticatedSafe.cs
@@ -0,0 +1,37 @@ +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Pkcs +{ + public class AuthenticatedSafe + : Asn1Encodable + { + private readonly ContentInfo[] info; + + public AuthenticatedSafe( + Asn1Sequence seq) + { + info = new ContentInfo[seq.Count]; + + for (int i = 0; i != info.Length; i++) + { + info[i] = ContentInfo.GetInstance(seq[i]); + } + } + + public AuthenticatedSafe( + ContentInfo[] info) + { + this.info = (ContentInfo[]) info.Clone(); + } + + public ContentInfo[] GetContentInfo() + { + return (ContentInfo[]) info.Clone(); + } + + public override Asn1Object ToAsn1Object() + { + return new BerSequence(info); + } + } +} diff --git a/Crypto/src/asn1/pkcs/CertBag.cs b/Crypto/src/asn1/pkcs/CertBag.cs new file mode 100644
index 000000000..b6f4c8a30 --- /dev/null +++ b/Crypto/src/asn1/pkcs/CertBag.cs
@@ -0,0 +1,46 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Pkcs +{ + public class CertBag + : Asn1Encodable + { +// private readonly Asn1Sequence seq; + private readonly DerObjectIdentifier certID; + private readonly Asn1Object certValue; + + public CertBag( + Asn1Sequence seq) + { + if (seq.Count != 2) + throw new ArgumentException("Wrong number of elements in sequence", "seq"); + +// this.seq = seq; + this.certID = DerObjectIdentifier.GetInstance(seq[0]); + this.certValue = DerTaggedObject.GetInstance(seq[1]).GetObject(); + } + + public CertBag( + DerObjectIdentifier certID, + Asn1Object certValue) + { + this.certID = certID; + this.certValue = certValue; + } + + public DerObjectIdentifier CertID + { + get { return certID; } + } + + public Asn1Object CertValue + { + get { return certValue; } + } + + public override Asn1Object ToAsn1Object() + { + return new DerSequence(certID, new DerTaggedObject(0, certValue)); + } + } +} diff --git a/Crypto/src/asn1/pkcs/CertificationRequest.cs b/Crypto/src/asn1/pkcs/CertificationRequest.cs new file mode 100644
index 000000000..32b1612d2 --- /dev/null +++ b/Crypto/src/asn1/pkcs/CertificationRequest.cs
@@ -0,0 +1,81 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Pkcs +{ + /** + * Pkcs10 Certfication request object. + * <pre> + * CertificationRequest ::= Sequence { + * certificationRequestInfo CertificationRequestInfo, + * signatureAlgorithm AlgorithmIdentifier{{ SignatureAlgorithms }}, + * signature BIT STRING + * } + * </pre> + */ + public class CertificationRequest + : Asn1Encodable + { + protected CertificationRequestInfo reqInfo; + protected AlgorithmIdentifier sigAlgId; + protected DerBitString sigBits; + + public static CertificationRequest GetInstance( + object obj) + { + if (obj is CertificationRequest) + return (CertificationRequest)obj; + + if (obj != null) + return new CertificationRequest((Asn1Sequence)obj); + + 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) + { + if (seq.Count != 3) + throw new ArgumentException("Wrong number of elements in sequence", "seq"); + + reqInfo = CertificationRequestInfo.GetInstance(seq[0]); + sigAlgId = AlgorithmIdentifier.GetInstance(seq[1]); + sigBits = DerBitString.GetInstance(seq[2]); + } + + public CertificationRequestInfo GetCertificationRequestInfo() + { + return reqInfo; + } + + public AlgorithmIdentifier SignatureAlgorithm + { + get { return sigAlgId; } + } + + public DerBitString Signature + { + get { return sigBits; } + } + + public override Asn1Object ToAsn1Object() + { + return new DerSequence(reqInfo, sigAlgId, sigBits); + } + } +} diff --git a/Crypto/src/asn1/pkcs/CertificationRequestInfo.cs b/Crypto/src/asn1/pkcs/CertificationRequestInfo.cs new file mode 100644
index 000000000..690d06878 --- /dev/null +++ b/Crypto/src/asn1/pkcs/CertificationRequestInfo.cs
@@ -0,0 +1,123 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Pkcs +{ + /** + * 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 Attr{{ IOSet }} + * + * Attr { ATTRIBUTE:IOSet } ::= Sequence { + * type ATTRIBUTE.&amp;id({IOSet}), + * values Set SIZE(1..MAX) OF ATTRIBUTE.&amp;Type({IOSet}{\@type}) + * } + * </pre> + */ + public class CertificationRequestInfo + : Asn1Encodable + { + internal DerInteger version = new DerInteger(0); + internal X509Name subject; + internal SubjectPublicKeyInfo subjectPKInfo; + internal Asn1Set attributes; + + public static CertificationRequestInfo GetInstance( + object obj) + { + if (obj is CertificationRequestInfo) + { + return (CertificationRequestInfo) obj; + } + + if (obj is Asn1Sequence) + { + return new CertificationRequestInfo((Asn1Sequence) obj); + } + + throw new ArgumentException("Unknown object in factory: " + obj.GetType().FullName, "obj"); + } + + public CertificationRequestInfo( + X509Name subject, + SubjectPublicKeyInfo pkInfo, + Asn1Set attributes) + { + this.subject = subject; + this.subjectPKInfo = pkInfo; + this.attributes = attributes; + + if (subject == null || version == null || subjectPKInfo == null) + { + throw new ArgumentException( + "Not all mandatory fields set in CertificationRequestInfo generator."); + } + } + + private CertificationRequestInfo( + Asn1Sequence seq) + { + version = (DerInteger) seq[0]; + + subject = X509Name.GetInstance(seq[1]); + subjectPKInfo = SubjectPublicKeyInfo.GetInstance(seq[2]); + + // + // some CertificationRequestInfo objects seem to treat this field + // as optional. + // + if (seq.Count > 3) + { + DerTaggedObject tagobj = (DerTaggedObject) seq[3]; + attributes = Asn1Set.GetInstance(tagobj, false); + } + + if (subject == null || version == null || subjectPKInfo == null) + { + throw new ArgumentException( + "Not all mandatory fields set in CertificationRequestInfo generator."); + } + } + + public DerInteger Version + { + get { return version; } + } + + public X509Name Subject + { + get { return subject; } + } + + public SubjectPublicKeyInfo SubjectPublicKeyInfo + { + get { return subjectPKInfo; } + } + + public Asn1Set Attributes + { + get { return attributes; } + } + + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector( + version, subject, subjectPKInfo); + + if (attributes != null) + { + v.Add(new DerTaggedObject(false, 0, attributes)); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/pkcs/ContentInfo.cs b/Crypto/src/asn1/pkcs/ContentInfo.cs new file mode 100644
index 000000000..78213c138 --- /dev/null +++ b/Crypto/src/asn1/pkcs/ContentInfo.cs
@@ -0,0 +1,80 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Pkcs +{ + public class ContentInfo + : Asn1Encodable + { + private readonly DerObjectIdentifier contentType; + private readonly Asn1Encodable content; + + public static ContentInfo GetInstance( + object obj) + { + if (obj is ContentInfo) + { + return (ContentInfo) obj; + } + + if (obj is Asn1Sequence) + { + return new ContentInfo((Asn1Sequence) obj); + } + + throw new ArgumentException("Unknown object in factory: " + obj.GetType().FullName, "obj"); + } + + private ContentInfo( + Asn1Sequence seq) + { + contentType = (DerObjectIdentifier) seq[0]; + + if (seq.Count > 1) + { + content = ((Asn1TaggedObject) seq[1]).GetObject(); + } + } + + public ContentInfo( + DerObjectIdentifier contentType, + Asn1Encodable content) + { + this.contentType = contentType; + this.content = content; + } + + public DerObjectIdentifier ContentType + { + get { return contentType; } + } + + public Asn1Encodable Content + { + get { return content; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * ContentInfo ::= Sequence { + * contentType ContentType, + * content + * [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(contentType); + + if (content != null) + { + v.Add(new BerTaggedObject(0, content)); + } + + return new BerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/pkcs/DHParameter.cs b/Crypto/src/asn1/pkcs/DHParameter.cs new file mode 100644
index 000000000..25a091a97 --- /dev/null +++ b/Crypto/src/asn1/pkcs/DHParameter.cs
@@ -0,0 +1,72 @@ +using Org.BouncyCastle.Asn1; +using System; +using System.Collections; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Asn1.Pkcs +{ + public class DHParameter + : Asn1Encodable + { + internal DerInteger p, g, l; + + public DHParameter( + BigInteger p, + BigInteger g, + int l) + { + this.p = new DerInteger(p); + this.g = new DerInteger(g); + + if (l != 0) + { + this.l = new DerInteger(l); + } + } + + public DHParameter( + Asn1Sequence seq) + { + IEnumerator e = seq.GetEnumerator(); + + e.MoveNext(); + p = (DerInteger)e.Current; + + e.MoveNext(); + g = (DerInteger)e.Current; + + if (e.MoveNext()) + { + l = (DerInteger) e.Current; + } + } + + public BigInteger P + { + get { return p.PositiveValue; } + } + + public BigInteger G + { + get { return g.PositiveValue; } + } + + public BigInteger L + { + get { return l == null ? null : l.PositiveValue; } + } + + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(p, g); + + if (this.l != null) + { + v.Add(l); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/pkcs/EncryptedData.cs b/Crypto/src/asn1/pkcs/EncryptedData.cs new file mode 100644
index 000000000..912064ace --- /dev/null +++ b/Crypto/src/asn1/pkcs/EncryptedData.cs
@@ -0,0 +1,104 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Pkcs +{ + /** + * 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 + : Asn1Encodable + { + private readonly Asn1Sequence data; +// private readonly DerObjectIdentifier bagId; +// private readonly Asn1Object bagValue; + + public static EncryptedData GetInstance( + object obj) + { + if (obj is EncryptedData) + { + return (EncryptedData) obj; + } + + if (obj is Asn1Sequence) + { + return new EncryptedData((Asn1Sequence) obj); + } + + throw new ArgumentException("Unknown object in factory: " + obj.GetType().FullName, "obj"); + } + + private EncryptedData( + Asn1Sequence seq) + { + if (seq.Count != 2) + throw new ArgumentException("Wrong number of elements in sequence", "seq"); + + int version = ((DerInteger) seq[0]).Value.IntValue; + if (version != 0) + { + throw new ArgumentException("sequence not version 0"); + } + + this.data = (Asn1Sequence) seq[1]; + } + + public EncryptedData( + DerObjectIdentifier contentType, + AlgorithmIdentifier encryptionAlgorithm, + Asn1Encodable content) + { + data = new BerSequence( + contentType, + encryptionAlgorithm.ToAsn1Object(), + new BerTaggedObject(false, 0, content)); + } + + public DerObjectIdentifier ContentType + { + get { return (DerObjectIdentifier) data[0]; } + } + + public AlgorithmIdentifier EncryptionAlgorithm + { + get { return AlgorithmIdentifier.GetInstance(data[1]); } + } + + public Asn1OctetString Content + { + get + { + if (data.Count == 3) + { + DerTaggedObject o = (DerTaggedObject) data[2]; + + return Asn1OctetString.GetInstance(o, false); + } + + return null; + } + } + + public override Asn1Object ToAsn1Object() + { + return new BerSequence(new DerInteger(0), data); + } + } +} diff --git a/Crypto/src/asn1/pkcs/EncryptedPrivateKeyInfo.cs b/Crypto/src/asn1/pkcs/EncryptedPrivateKeyInfo.cs new file mode 100644
index 000000000..b97b8f5ea --- /dev/null +++ b/Crypto/src/asn1/pkcs/EncryptedPrivateKeyInfo.cs
@@ -0,0 +1,78 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Pkcs +{ + public class EncryptedPrivateKeyInfo + : Asn1Encodable + { + private readonly AlgorithmIdentifier algId; + private readonly Asn1OctetString data; + + private EncryptedPrivateKeyInfo( + Asn1Sequence seq) + { + if (seq.Count != 2) + throw new ArgumentException("Wrong number of elements in sequence", "seq"); + + algId = AlgorithmIdentifier.GetInstance(seq[0]); + data = Asn1OctetString.GetInstance(seq[1]); + } + + public EncryptedPrivateKeyInfo( + AlgorithmIdentifier algId, + byte[] encoding) + { + this.algId = algId; + this.data = new DerOctetString(encoding); + } + + public static EncryptedPrivateKeyInfo GetInstance( + object obj) + { + if (obj is EncryptedPrivateKeyInfo) + { + return (EncryptedPrivateKeyInfo) obj; + } + + if (obj is Asn1Sequence) + { + return new EncryptedPrivateKeyInfo((Asn1Sequence) obj); + } + + throw new ArgumentException("Unknown object in factory: " + obj.GetType().FullName, "obj"); + } + + public AlgorithmIdentifier EncryptionAlgorithm + { + get { 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 override Asn1Object ToAsn1Object() + { + return new DerSequence(algId, data); + } + } +} diff --git a/Crypto/src/asn1/pkcs/EncryptionScheme.cs b/Crypto/src/asn1/pkcs/EncryptionScheme.cs new file mode 100644
index 000000000..5b64d6f67 --- /dev/null +++ b/Crypto/src/asn1/pkcs/EncryptionScheme.cs
@@ -0,0 +1,49 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Pkcs +{ + public class EncryptionScheme + : AlgorithmIdentifier + { + public EncryptionScheme( + DerObjectIdentifier objectID, + Asn1Encodable parameters) + : base(objectID, parameters) + { + } + + internal EncryptionScheme( + Asn1Sequence seq) + : this((DerObjectIdentifier)seq[0], seq[1]) + { + } + + public new static EncryptionScheme GetInstance(object obj) + { + if (obj is EncryptionScheme) + { + return (EncryptionScheme)obj; + } + + if (obj is Asn1Sequence) + { + return new EncryptionScheme((Asn1Sequence)obj); + } + + throw new ArgumentException("Unknown object in factory: " + obj.GetType().FullName, "obj"); + } + + public Asn1Object Asn1Object + { + get { return Parameters.ToAsn1Object(); } + } + + public override Asn1Object ToAsn1Object() + { + return new DerSequence(ObjectID, Parameters); + } + } +} diff --git a/Crypto/src/asn1/pkcs/IssuerAndSerialNumber.cs b/Crypto/src/asn1/pkcs/IssuerAndSerialNumber.cs new file mode 100644
index 000000000..ff608f15b --- /dev/null +++ b/Crypto/src/asn1/pkcs/IssuerAndSerialNumber.cs
@@ -0,0 +1,71 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Asn1.Pkcs +{ + public class IssuerAndSerialNumber + : Asn1Encodable + { + private readonly X509Name name; + private readonly DerInteger certSerialNumber; + + public static IssuerAndSerialNumber GetInstance( + object obj) + { + if (obj is IssuerAndSerialNumber) + { + return (IssuerAndSerialNumber) obj; + } + + if (obj is Asn1Sequence) + { + return new IssuerAndSerialNumber((Asn1Sequence) obj); + } + + throw new ArgumentException("Unknown object in factory: " + obj.GetType().FullName, "obj"); + } + + private IssuerAndSerialNumber( + Asn1Sequence seq) + { + if (seq.Count != 2) + throw new ArgumentException("Wrong number of elements in sequence", "seq"); + + this.name = X509Name.GetInstance(seq[0]); + this.certSerialNumber = DerInteger.GetInstance(seq[1]); + } + + public IssuerAndSerialNumber( + X509Name name, + BigInteger certSerialNumber) + { + this.name = name; + this.certSerialNumber = new DerInteger(certSerialNumber); + } + + public IssuerAndSerialNumber( + X509Name name, + DerInteger certSerialNumber) + { + this.name = name; + this.certSerialNumber = certSerialNumber; + } + + public X509Name Name + { + get { return name; } + } + + public DerInteger CertificateSerialNumber + { + get { return certSerialNumber; } + } + + public override Asn1Object ToAsn1Object() + { + return new DerSequence(name, certSerialNumber); + } + } +} diff --git a/Crypto/src/asn1/pkcs/KeyDerivationFunc.cs b/Crypto/src/asn1/pkcs/KeyDerivationFunc.cs new file mode 100644
index 000000000..9fc89853b --- /dev/null +++ b/Crypto/src/asn1/pkcs/KeyDerivationFunc.cs
@@ -0,0 +1,21 @@ +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Pkcs +{ + public class KeyDerivationFunc + : AlgorithmIdentifier + { + internal KeyDerivationFunc(Asn1Sequence seq) + : base(seq) + { + } + + public KeyDerivationFunc( + DerObjectIdentifier id, + Asn1Encodable parameters) + : base(id, parameters) + { + } + } +} \ No newline at end of file diff --git a/Crypto/src/asn1/pkcs/MacData.cs b/Crypto/src/asn1/pkcs/MacData.cs new file mode 100644
index 000000000..780b24153 --- /dev/null +++ b/Crypto/src/asn1/pkcs/MacData.cs
@@ -0,0 +1,96 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Asn1.Pkcs +{ + public class MacData + : Asn1Encodable + { + internal DigestInfo digInfo; + internal byte[] salt; + internal BigInteger iterationCount; + + public static MacData GetInstance( + object obj) + { + if (obj is MacData) + { + return (MacData) obj; + } + + if (obj is Asn1Sequence) + { + return new MacData((Asn1Sequence) obj); + } + + throw new ArgumentException("Unknown object in factory: " + obj.GetType().FullName, "obj"); + } + + private MacData( + Asn1Sequence seq) + { + this.digInfo = DigestInfo.GetInstance(seq[0]); + this.salt = ((Asn1OctetString) seq[1]).GetOctets(); + + if (seq.Count == 3) + { + this.iterationCount = ((DerInteger) seq[2]).Value; + } + else + { + this.iterationCount = BigInteger.One; + } + } + + public MacData( + DigestInfo digInfo, + byte[] salt, + int iterationCount) + { + this.digInfo = digInfo; + this.salt = (byte[]) salt.Clone(); + this.iterationCount = BigInteger.ValueOf(iterationCount); + } + + public DigestInfo Mac + { + get { return digInfo; } + } + + public byte[] GetSalt() + { + return (byte[]) salt.Clone(); + } + + public BigInteger IterationCount + { + get { 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 DERObject construction. + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(digInfo, new DerOctetString(salt)); + + if (!iterationCount.Equals(BigInteger.One)) + { + v.Add(new DerInteger(iterationCount)); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/pkcs/PBEParameter.cs b/Crypto/src/asn1/pkcs/PBEParameter.cs new file mode 100644
index 000000000..80d5ec3e1 --- /dev/null +++ b/Crypto/src/asn1/pkcs/PBEParameter.cs
@@ -0,0 +1,60 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Asn1.Pkcs +{ + public class PbeParameter + : Asn1Encodable + { + private readonly Asn1OctetString salt; + private readonly DerInteger iterationCount; + + public static PbeParameter GetInstance(object obj) + { + if (obj is PbeParameter || obj == null) + { + return (PbeParameter) obj; + } + + if (obj is Asn1Sequence) + { + return new PbeParameter((Asn1Sequence) obj); + } + + throw new ArgumentException("Unknown object in factory: " + obj.GetType().FullName, "obj"); + } + + private PbeParameter(Asn1Sequence seq) + { + if (seq.Count != 2) + throw new ArgumentException("Wrong number of elements in sequence", "seq"); + + salt = Asn1OctetString.GetInstance(seq[0]); + iterationCount = DerInteger.GetInstance(seq[1]); + } + + public PbeParameter(byte[] salt, int iterationCount) + { + this.salt = new DerOctetString(salt); + this.iterationCount = new DerInteger(iterationCount); + } + + public byte[] GetSalt() + { + return salt.GetOctets(); + } + + public BigInteger IterationCount + { + get { return iterationCount.Value; } + } + + public override Asn1Object ToAsn1Object() + { + return new DerSequence(salt, iterationCount); + } + } +} diff --git a/Crypto/src/asn1/pkcs/PBES2Parameters.cs b/Crypto/src/asn1/pkcs/PBES2Parameters.cs new file mode 100644
index 000000000..645bb867c --- /dev/null +++ b/Crypto/src/asn1/pkcs/PBES2Parameters.cs
@@ -0,0 +1,61 @@ +using System; +using System.Collections; + +namespace Org.BouncyCastle.Asn1.Pkcs +{ + public class PbeS2Parameters + : Asn1Encodable + { + private readonly KeyDerivationFunc func; + private readonly EncryptionScheme scheme; + + public static PbeS2Parameters GetInstance( + object obj) + { + if (obj == null || obj is PbeS2Parameters) + return (PbeS2Parameters) obj; + + if (obj is Asn1Sequence) + return new PbeS2Parameters((Asn1Sequence) obj); + + throw new ArgumentException("Unknown object in factory: " + obj.GetType().FullName, "obj"); + } + + public PbeS2Parameters( + Asn1Sequence seq) + { + if (seq.Count != 2) + throw new ArgumentException("Wrong number of elements in sequence", "seq"); + + Asn1Sequence funcSeq = (Asn1Sequence)seq[0].ToAsn1Object(); + + // TODO Not sure if this special case is really necessary/appropriate + if (funcSeq[0].Equals(PkcsObjectIdentifiers.IdPbkdf2)) + { + func = new KeyDerivationFunc(PkcsObjectIdentifiers.IdPbkdf2, + Pbkdf2Params.GetInstance(funcSeq[1])); + } + else + { + func = new KeyDerivationFunc(funcSeq); + } + + scheme = EncryptionScheme.GetInstance(seq[1].ToAsn1Object()); + } + + public KeyDerivationFunc KeyDerivationFunc + { + get { return func; } + } + + public EncryptionScheme EncryptionScheme + { + get { return scheme; } + } + + public override Asn1Object ToAsn1Object() + { + return new DerSequence(func, scheme); + } + } +} diff --git a/Crypto/src/asn1/pkcs/PBKDF2Params.cs b/Crypto/src/asn1/pkcs/PBKDF2Params.cs new file mode 100644
index 000000000..1351b94cf --- /dev/null +++ b/Crypto/src/asn1/pkcs/PBKDF2Params.cs
@@ -0,0 +1,86 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Asn1.Pkcs +{ + public class Pbkdf2Params + : Asn1Encodable + { + private readonly Asn1OctetString octStr; + private readonly DerInteger iterationCount; + private readonly DerInteger keyLength; + + public static Pbkdf2Params GetInstance( + object obj) + { + if (obj == null || obj is Pbkdf2Params) + return (Pbkdf2Params)obj; + + if (obj is Asn1Sequence) + return new Pbkdf2Params((Asn1Sequence)obj); + + throw new ArgumentException("Unknown object in factory: " + obj.GetType().FullName, "obj"); + } + + public Pbkdf2Params( + Asn1Sequence seq) + { + if (seq.Count < 2 || seq.Count > 3) + throw new ArgumentException("Wrong number of elements in sequence", "seq"); + + octStr = (Asn1OctetString)seq[0]; + iterationCount = (DerInteger)seq[1]; + + if (seq.Count > 2) + { + keyLength = (DerInteger)seq[2]; + } + } + + public Pbkdf2Params( + byte[] salt, + int iterationCount) + { + this.octStr = new DerOctetString(salt); + this.iterationCount = new DerInteger(iterationCount); + } + + public Pbkdf2Params( + byte[] salt, + int iterationCount, + int keyLength) + : this(salt, iterationCount) + { + this.keyLength = new DerInteger(keyLength); + } + + public byte[] GetSalt() + { + return octStr.GetOctets(); + } + + public BigInteger IterationCount + { + get { return iterationCount.Value; } + } + + public BigInteger KeyLength + { + get { return keyLength == null ? null : keyLength.Value; } + } + + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector( + octStr, iterationCount); + + if (keyLength != null) + { + v.Add(keyLength); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/pkcs/PKCS12PBEParams.cs b/Crypto/src/asn1/pkcs/PKCS12PBEParams.cs new file mode 100644
index 000000000..7521f93ea --- /dev/null +++ b/Crypto/src/asn1/pkcs/PKCS12PBEParams.cs
@@ -0,0 +1,63 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Asn1.Pkcs +{ + public class Pkcs12PbeParams + : Asn1Encodable + { + private readonly DerInteger iterations; + private readonly Asn1OctetString iv; + + public Pkcs12PbeParams( + byte[] salt, + int iterations) + { + this.iv = new DerOctetString(salt); + this.iterations = new DerInteger(iterations); + } + + private Pkcs12PbeParams( + Asn1Sequence seq) + { + if (seq.Count != 2) + throw new ArgumentException("Wrong number of elements in sequence", "seq"); + + iv = Asn1OctetString.GetInstance(seq[0]); + iterations = DerInteger.GetInstance(seq[1]); + } + + public static Pkcs12PbeParams GetInstance( + object obj) + { + if (obj is Pkcs12PbeParams) + { + return (Pkcs12PbeParams) obj; + } + + if (obj is Asn1Sequence) + { + return new Pkcs12PbeParams((Asn1Sequence) obj); + } + + throw new ArgumentException("Unknown object in factory: " + obj.GetType().FullName, "obj"); + } + + public BigInteger Iterations + { + get { return iterations.Value; } + } + + public byte[] GetIV() + { + return iv.GetOctets(); + } + + public override Asn1Object ToAsn1Object() + { + return new DerSequence(iv, iterations); + } + } +} diff --git a/Crypto/src/asn1/pkcs/PKCSObjectIdentifiers.cs b/Crypto/src/asn1/pkcs/PKCSObjectIdentifiers.cs new file mode 100644
index 000000000..0b2ffa0d1 --- /dev/null +++ b/Crypto/src/asn1/pkcs/PKCSObjectIdentifiers.cs
@@ -0,0 +1,256 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Pkcs +{ + public abstract class PkcsObjectIdentifiers + { + // + // pkcs-1 OBJECT IDENTIFIER ::= { + // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 } + // + public const string Pkcs1 = "1.2.840.113549.1.1"; + + public static readonly DerObjectIdentifier RsaEncryption = new DerObjectIdentifier(Pkcs1 + ".1"); + public static readonly DerObjectIdentifier MD2WithRsaEncryption = new DerObjectIdentifier(Pkcs1 + ".2"); + public static readonly DerObjectIdentifier MD4WithRsaEncryption = new DerObjectIdentifier(Pkcs1 + ".3"); + public static readonly DerObjectIdentifier MD5WithRsaEncryption = new DerObjectIdentifier(Pkcs1 + ".4"); + public static readonly DerObjectIdentifier Sha1WithRsaEncryption = new DerObjectIdentifier(Pkcs1 + ".5"); + public static readonly DerObjectIdentifier SrsaOaepEncryptionSet = new DerObjectIdentifier(Pkcs1 + ".6"); + public static readonly DerObjectIdentifier IdRsaesOaep = new DerObjectIdentifier(Pkcs1 + ".7"); + public static readonly DerObjectIdentifier IdMgf1 = new DerObjectIdentifier(Pkcs1 + ".8"); + public static readonly DerObjectIdentifier IdPSpecified = new DerObjectIdentifier(Pkcs1 + ".9"); + public static readonly DerObjectIdentifier IdRsassaPss = new DerObjectIdentifier(Pkcs1 + ".10"); + public static readonly DerObjectIdentifier Sha256WithRsaEncryption = new DerObjectIdentifier(Pkcs1 + ".11"); + public static readonly DerObjectIdentifier Sha384WithRsaEncryption = new DerObjectIdentifier(Pkcs1 + ".12"); + public static readonly DerObjectIdentifier Sha512WithRsaEncryption = new DerObjectIdentifier(Pkcs1 + ".13"); + public static readonly DerObjectIdentifier Sha224WithRsaEncryption = new DerObjectIdentifier(Pkcs1 + ".14"); + + // + // pkcs-3 OBJECT IDENTIFIER ::= { + // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 3 } + // + public const string Pkcs3 = "1.2.840.113549.1.3"; + + public static readonly DerObjectIdentifier DhKeyAgreement = new DerObjectIdentifier(Pkcs3 + ".1"); + + // + // pkcs-5 OBJECT IDENTIFIER ::= { + // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 5 } + // + public const string Pkcs5 = "1.2.840.113549.1.5"; + + public static readonly DerObjectIdentifier PbeWithMD2AndDesCbc = new DerObjectIdentifier(Pkcs5 + ".1"); + public static readonly DerObjectIdentifier PbeWithMD2AndRC2Cbc = new DerObjectIdentifier(Pkcs5 + ".4"); + public static readonly DerObjectIdentifier PbeWithMD5AndDesCbc = new DerObjectIdentifier(Pkcs5 + ".3"); + public static readonly DerObjectIdentifier PbeWithMD5AndRC2Cbc = new DerObjectIdentifier(Pkcs5 + ".6"); + public static readonly DerObjectIdentifier PbeWithSha1AndDesCbc = new DerObjectIdentifier(Pkcs5 + ".10"); + public static readonly DerObjectIdentifier PbeWithSha1AndRC2Cbc = new DerObjectIdentifier(Pkcs5 + ".11"); + + public static readonly DerObjectIdentifier IdPbeS2 = new DerObjectIdentifier(Pkcs5 + ".13"); + public static readonly DerObjectIdentifier IdPbkdf2 = new DerObjectIdentifier(Pkcs5 + ".12"); + + // + // encryptionAlgorithm OBJECT IDENTIFIER ::= { + // iso(1) member-body(2) us(840) rsadsi(113549) 3 } + // + public const string EncryptionAlgorithm = "1.2.840.113549.3"; + + public static readonly DerObjectIdentifier DesEde3Cbc = new DerObjectIdentifier(EncryptionAlgorithm + ".7"); + public static readonly DerObjectIdentifier RC2Cbc = new DerObjectIdentifier(EncryptionAlgorithm + ".2"); + + // + // object identifiers for digests + // + public const string DigestAlgorithm = "1.2.840.113549.2"; + + // + // md2 OBJECT IDENTIFIER ::= + // {iso(1) member-body(2) US(840) rsadsi(113549) DigestAlgorithm(2) 2} + // + public static readonly DerObjectIdentifier MD2 = new DerObjectIdentifier(DigestAlgorithm + ".2"); + + // + // md4 OBJECT IDENTIFIER ::= + // {iso(1) member-body(2) US(840) rsadsi(113549) DigestAlgorithm(2) 4} + // + public static readonly DerObjectIdentifier MD4 = new DerObjectIdentifier(DigestAlgorithm + ".4"); + + // + // md5 OBJECT IDENTIFIER ::= + // {iso(1) member-body(2) US(840) rsadsi(113549) DigestAlgorithm(2) 5} + // + public static readonly DerObjectIdentifier MD5 = new DerObjectIdentifier(DigestAlgorithm + ".5"); + + public static readonly DerObjectIdentifier IdHmacWithSha1 = new DerObjectIdentifier(DigestAlgorithm + ".7"); + public static readonly DerObjectIdentifier IdHmacWithSha224 = new DerObjectIdentifier(DigestAlgorithm + ".8"); + public static readonly DerObjectIdentifier IdHmacWithSha256 = new DerObjectIdentifier(DigestAlgorithm + ".9"); + public static readonly DerObjectIdentifier IdHmacWithSha384 = new DerObjectIdentifier(DigestAlgorithm + ".10"); + public static readonly DerObjectIdentifier IdHmacWithSha512 = new DerObjectIdentifier(DigestAlgorithm + ".11"); + + // + // pkcs-7 OBJECT IDENTIFIER ::= { + // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 7 } + // + public const string Pkcs7 = "1.2.840.113549.1.7"; + + public static readonly DerObjectIdentifier Data = new DerObjectIdentifier(Pkcs7 + ".1"); + public static readonly DerObjectIdentifier SignedData = new DerObjectIdentifier(Pkcs7 + ".2"); + public static readonly DerObjectIdentifier EnvelopedData = new DerObjectIdentifier(Pkcs7 + ".3"); + public static readonly DerObjectIdentifier SignedAndEnvelopedData = new DerObjectIdentifier(Pkcs7 + ".4"); + public static readonly DerObjectIdentifier DigestedData = new DerObjectIdentifier(Pkcs7 + ".5"); + public static readonly DerObjectIdentifier EncryptedData = new DerObjectIdentifier(Pkcs7 + ".6"); + + // + // pkcs-9 OBJECT IDENTIFIER ::= { + // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 9 } + // + public const string Pkcs9 = "1.2.840.113549.1.9"; + + public static readonly DerObjectIdentifier Pkcs9AtEmailAddress = new DerObjectIdentifier(Pkcs9 + ".1"); + public static readonly DerObjectIdentifier Pkcs9AtUnstructuredName = new DerObjectIdentifier(Pkcs9 + ".2"); + public static readonly DerObjectIdentifier Pkcs9AtContentType = new DerObjectIdentifier(Pkcs9 + ".3"); + public static readonly DerObjectIdentifier Pkcs9AtMessageDigest = new DerObjectIdentifier(Pkcs9 + ".4"); + public static readonly DerObjectIdentifier Pkcs9AtSigningTime = new DerObjectIdentifier(Pkcs9 + ".5"); + public static readonly DerObjectIdentifier Pkcs9AtCounterSignature = new DerObjectIdentifier(Pkcs9 + ".6"); + public static readonly DerObjectIdentifier Pkcs9AtChallengePassword = new DerObjectIdentifier(Pkcs9 + ".7"); + public static readonly DerObjectIdentifier Pkcs9AtUnstructuredAddress = new DerObjectIdentifier(Pkcs9 + ".8"); + public static readonly DerObjectIdentifier Pkcs9AtExtendedCertificateAttributes = new DerObjectIdentifier(Pkcs9 + ".9"); + public static readonly DerObjectIdentifier Pkcs9AtSigningDescription = new DerObjectIdentifier(Pkcs9 + ".13"); + public static readonly DerObjectIdentifier Pkcs9AtExtensionRequest = new DerObjectIdentifier(Pkcs9 + ".14"); + public static readonly DerObjectIdentifier Pkcs9AtSmimeCapabilities = new DerObjectIdentifier(Pkcs9 + ".15"); + public static readonly DerObjectIdentifier Pkcs9AtFriendlyName = new DerObjectIdentifier(Pkcs9 + ".20"); + public static readonly DerObjectIdentifier Pkcs9AtLocalKeyID = new DerObjectIdentifier(Pkcs9 + ".21"); + + [Obsolete("Use X509Certificate instead")] + public static readonly DerObjectIdentifier X509CertType = new DerObjectIdentifier(Pkcs9 + ".22.1"); + + public const string CertTypes = Pkcs9 + ".22"; + public static readonly DerObjectIdentifier X509Certificate = new DerObjectIdentifier(CertTypes + ".1"); + public static readonly DerObjectIdentifier SdsiCertificate = new DerObjectIdentifier(CertTypes + ".2"); + + public const string CrlTypes = Pkcs9 + ".23"; + public static readonly DerObjectIdentifier X509Crl = new DerObjectIdentifier(CrlTypes + ".1"); + + public static readonly DerObjectIdentifier IdAlgPwriKek = new DerObjectIdentifier(Pkcs9 + ".16.3.9"); + + // + // SMIME capability sub oids. + // + public static readonly DerObjectIdentifier PreferSignedData = new DerObjectIdentifier(Pkcs9 + ".15.1"); + public static readonly DerObjectIdentifier CannotDecryptAny = new DerObjectIdentifier(Pkcs9 + ".15.2"); + public static readonly DerObjectIdentifier SmimeCapabilitiesVersions = new DerObjectIdentifier(Pkcs9 + ".15.3"); + + // + // other SMIME attributes + // + public static readonly DerObjectIdentifier IdAAReceiptRequest = new DerObjectIdentifier(Pkcs9 + ".16.2.1"); + + // + // id-ct OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840) + // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) ct(1)} + // + public const string IdCT = "1.2.840.113549.1.9.16.1"; + + public static readonly DerObjectIdentifier IdCTAuthData = new DerObjectIdentifier(IdCT + ".2"); + public static readonly DerObjectIdentifier IdCTTstInfo = new DerObjectIdentifier(IdCT + ".4"); + public static readonly DerObjectIdentifier IdCTCompressedData = new DerObjectIdentifier(IdCT + ".9"); + public static readonly DerObjectIdentifier IdCTAuthEnvelopedData = new DerObjectIdentifier(IdCT + ".23"); + public static readonly DerObjectIdentifier IdCTTimestampedData = new DerObjectIdentifier(IdCT + ".31"); + + // + // id-cti OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840) + // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) cti(6)} + // + public const string IdCti = "1.2.840.113549.1.9.16.6"; + + public static readonly DerObjectIdentifier IdCtiEtsProofOfOrigin = new DerObjectIdentifier(IdCti + ".1"); + public static readonly DerObjectIdentifier IdCtiEtsProofOfReceipt = new DerObjectIdentifier(IdCti + ".2"); + public static readonly DerObjectIdentifier IdCtiEtsProofOfDelivery = new DerObjectIdentifier(IdCti + ".3"); + public static readonly DerObjectIdentifier IdCtiEtsProofOfSender = new DerObjectIdentifier(IdCti + ".4"); + public static readonly DerObjectIdentifier IdCtiEtsProofOfApproval = new DerObjectIdentifier(IdCti + ".5"); + public static readonly DerObjectIdentifier IdCtiEtsProofOfCreation = new DerObjectIdentifier(IdCti + ".6"); + + // + // id-aa OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840) + // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) attributes(2)} + // + public const string IdAA = "1.2.840.113549.1.9.16.2"; + + public static readonly DerObjectIdentifier IdAAContentHint = new DerObjectIdentifier(IdAA + ".4"); // See RFC 2634 + public static readonly DerObjectIdentifier IdAAMsgSigDigest = new DerObjectIdentifier(IdAA + ".5"); + public static readonly DerObjectIdentifier IdAAContentReference = new DerObjectIdentifier(IdAA + ".10"); + + /* + * id-aa-encrypKeyPref OBJECT IDENTIFIER ::= {id-aa 11} + * + */ + public static readonly DerObjectIdentifier IdAAEncrypKeyPref = new DerObjectIdentifier(IdAA + ".11"); + public static readonly DerObjectIdentifier IdAASigningCertificate = new DerObjectIdentifier(IdAA + ".12"); + public static readonly DerObjectIdentifier IdAASigningCertificateV2 = new DerObjectIdentifier(IdAA + ".47"); + + public static readonly DerObjectIdentifier IdAAContentIdentifier = new DerObjectIdentifier(IdAA + ".7"); // See RFC 2634 + + /* + * RFC 3126 + */ + public static readonly DerObjectIdentifier IdAASignatureTimeStampToken = new DerObjectIdentifier(IdAA + ".14"); + + public static readonly DerObjectIdentifier IdAAEtsSigPolicyID = new DerObjectIdentifier(IdAA + ".15"); + public static readonly DerObjectIdentifier IdAAEtsCommitmentType = new DerObjectIdentifier(IdAA + ".16"); + public static readonly DerObjectIdentifier IdAAEtsSignerLocation = new DerObjectIdentifier(IdAA + ".17"); + public static readonly DerObjectIdentifier IdAAEtsSignerAttr = new DerObjectIdentifier(IdAA + ".18"); + public static readonly DerObjectIdentifier IdAAEtsOtherSigCert = new DerObjectIdentifier(IdAA + ".19"); + public static readonly DerObjectIdentifier IdAAEtsContentTimestamp = new DerObjectIdentifier(IdAA + ".20"); + public static readonly DerObjectIdentifier IdAAEtsCertificateRefs = new DerObjectIdentifier(IdAA + ".21"); + public static readonly DerObjectIdentifier IdAAEtsRevocationRefs = new DerObjectIdentifier(IdAA + ".22"); + public static readonly DerObjectIdentifier IdAAEtsCertValues = new DerObjectIdentifier(IdAA + ".23"); + public static readonly DerObjectIdentifier IdAAEtsRevocationValues = new DerObjectIdentifier(IdAA + ".24"); + public static readonly DerObjectIdentifier IdAAEtsEscTimeStamp = new DerObjectIdentifier(IdAA + ".25"); + public static readonly DerObjectIdentifier IdAAEtsCertCrlTimestamp = new DerObjectIdentifier(IdAA + ".26"); + public static readonly DerObjectIdentifier IdAAEtsArchiveTimestamp = new DerObjectIdentifier(IdAA + ".27"); + + [Obsolete("Use 'IdAAEtsSigPolicyID' instead")] + public static readonly DerObjectIdentifier IdAASigPolicyID = IdAAEtsSigPolicyID; + [Obsolete("Use 'IdAAEtsCommitmentType' instead")] + public static readonly DerObjectIdentifier IdAACommitmentType = IdAAEtsCommitmentType; + [Obsolete("Use 'IdAAEtsSignerLocation' instead")] + public static readonly DerObjectIdentifier IdAASignerLocation = IdAAEtsSignerLocation; + [Obsolete("Use 'IdAAEtsOtherSigCert' instead")] + public static readonly DerObjectIdentifier IdAAOtherSigCert = IdAAEtsOtherSigCert; + + // + // id-spq OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840) + // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) id-spq(5)} + // + public const string IdSpq = "1.2.840.113549.1.9.16.5"; + + public static readonly DerObjectIdentifier IdSpqEtsUri = new DerObjectIdentifier(IdSpq + ".1"); + public static readonly DerObjectIdentifier IdSpqEtsUNotice = new DerObjectIdentifier(IdSpq + ".2"); + + // + // pkcs-12 OBJECT IDENTIFIER ::= { + // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 12 } + // + public const string Pkcs12 = "1.2.840.113549.1.12"; + public const string BagTypes = Pkcs12 + ".10.1"; + + public static readonly DerObjectIdentifier KeyBag = new DerObjectIdentifier(BagTypes + ".1"); + public static readonly DerObjectIdentifier Pkcs8ShroudedKeyBag = new DerObjectIdentifier(BagTypes + ".2"); + public static readonly DerObjectIdentifier CertBag = new DerObjectIdentifier(BagTypes + ".3"); + public static readonly DerObjectIdentifier CrlBag = new DerObjectIdentifier(BagTypes + ".4"); + public static readonly DerObjectIdentifier SecretBag = new DerObjectIdentifier(BagTypes + ".5"); + public static readonly DerObjectIdentifier SafeContentsBag = new DerObjectIdentifier(BagTypes + ".6"); + + public const string Pkcs12PbeIds = Pkcs12 + ".1"; + + public static readonly DerObjectIdentifier PbeWithShaAnd128BitRC4 = new DerObjectIdentifier(Pkcs12PbeIds + ".1"); + public static readonly DerObjectIdentifier PbeWithShaAnd40BitRC4 = new DerObjectIdentifier(Pkcs12PbeIds + ".2"); + public static readonly DerObjectIdentifier PbeWithShaAnd3KeyTripleDesCbc = new DerObjectIdentifier(Pkcs12PbeIds + ".3"); + public static readonly DerObjectIdentifier PbeWithShaAnd2KeyTripleDesCbc = new DerObjectIdentifier(Pkcs12PbeIds + ".4"); + public static readonly DerObjectIdentifier PbeWithShaAnd128BitRC2Cbc = new DerObjectIdentifier(Pkcs12PbeIds + ".5"); + public static readonly DerObjectIdentifier PbewithShaAnd40BitRC2Cbc = new DerObjectIdentifier(Pkcs12PbeIds + ".6"); + + public static readonly DerObjectIdentifier IdAlgCms3DesWrap = new DerObjectIdentifier("1.2.840.113549.1.9.16.3.6"); + public static readonly DerObjectIdentifier IdAlgCmsRC2Wrap = new DerObjectIdentifier("1.2.840.113549.1.9.16.3.7"); + } +} diff --git a/Crypto/src/asn1/pkcs/Pfx.cs b/Crypto/src/asn1/pkcs/Pfx.cs new file mode 100644
index 000000000..9676f64fc --- /dev/null +++ b/Crypto/src/asn1/pkcs/Pfx.cs
@@ -0,0 +1,65 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Asn1.Pkcs +{ + /** + * the infamous Pfx from Pkcs12 + */ + public class Pfx + : Asn1Encodable + { + private ContentInfo contentInfo; + private MacData macData; + + public Pfx( + Asn1Sequence seq) + { + BigInteger version = ((DerInteger) seq[0]).Value; + if (version.IntValue != 3) + { + throw new ArgumentException("wrong version for PFX PDU"); + } + + contentInfo = ContentInfo.GetInstance(seq[1]); + + if (seq.Count == 3) + { + macData = MacData.GetInstance(seq[2]); + } + } + + public Pfx( + ContentInfo contentInfo, + MacData macData) + { + this.contentInfo = contentInfo; + this.macData = macData; + } + + public ContentInfo AuthSafe + { + get { return contentInfo; } + } + + public MacData MacData + { + get { return macData; } + } + + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector( + new DerInteger(3), contentInfo); + + if (macData != null) + { + v.Add(macData); + } + + return new BerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/pkcs/PrivateKeyInfo.cs b/Crypto/src/asn1/pkcs/PrivateKeyInfo.cs new file mode 100644
index 000000000..bd0ef7d1e --- /dev/null +++ b/Crypto/src/asn1/pkcs/PrivateKeyInfo.cs
@@ -0,0 +1,126 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Asn1.Pkcs +{ + public class PrivateKeyInfo + : Asn1Encodable + { + private readonly Asn1Object privKey; + private readonly AlgorithmIdentifier algID; + private readonly Asn1Set attributes; + + public static PrivateKeyInfo GetInstance( + object obj) + { + if (obj is PrivateKeyInfo) + return (PrivateKeyInfo) obj; + + if (obj != null) + return new PrivateKeyInfo(Asn1Sequence.GetInstance(obj)); + + return null; + } + + public PrivateKeyInfo( + AlgorithmIdentifier algID, + Asn1Object privateKey) + : this(algID, privateKey, null) + { + } + + public PrivateKeyInfo( + AlgorithmIdentifier algID, + Asn1Object privateKey, + Asn1Set attributes) + { + this.privKey = privateKey; + this.algID = algID; + this.attributes = attributes; + } + + private PrivateKeyInfo( + Asn1Sequence seq) + { + IEnumerator e = seq.GetEnumerator(); + + e.MoveNext(); + BigInteger version = ((DerInteger) e.Current).Value; + if (version.IntValue != 0) + { + throw new ArgumentException("wrong version for private key info"); + } + + e.MoveNext(); + algID = AlgorithmIdentifier.GetInstance(e.Current); + + try + { + e.MoveNext(); + Asn1OctetString data = (Asn1OctetString) e.Current; + + privKey = Asn1Object.FromByteArray(data.GetOctets()); + } + catch (IOException) + { + throw new ArgumentException("Error recoverying private key from sequence"); + } + + if (e.MoveNext()) + { + attributes = Asn1Set.GetInstance((Asn1TaggedObject) e.Current, false); + } + } + + public AlgorithmIdentifier AlgorithmID + { + get { return algID; } + } + + public Asn1Object PrivateKey + { + get { return privKey; } + } + + public Asn1Set Attributes + { + get { 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 Attr + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector( + new DerInteger(0), + algID, + new DerOctetString(privKey)); + + if (attributes != null) + { + v.Add(new DerTaggedObject(false, 0, attributes)); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/pkcs/RC2CBCParameter.cs b/Crypto/src/asn1/pkcs/RC2CBCParameter.cs new file mode 100644
index 000000000..f5355d012 --- /dev/null +++ b/Crypto/src/asn1/pkcs/RC2CBCParameter.cs
@@ -0,0 +1,81 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1.Pkcs +{ + public class RC2CbcParameter + : Asn1Encodable + { + internal DerInteger version; + internal Asn1OctetString iv; + + public static RC2CbcParameter GetInstance( + object obj) + { + if (obj is Asn1Sequence) + { + return new RC2CbcParameter((Asn1Sequence) obj); + } + + throw new ArgumentException("Unknown object in factory: " + obj.GetType().FullName, "obj"); + } + + public RC2CbcParameter( + byte[] iv) + { + this.iv = new DerOctetString(iv); + } + + public RC2CbcParameter( + int parameterVersion, + byte[] iv) + { + this.version = new DerInteger(parameterVersion); + this.iv = new DerOctetString(iv); + } + + private RC2CbcParameter( + Asn1Sequence seq) + { + if (seq.Count == 1) + { + iv = (Asn1OctetString)seq[0]; + } + else + { + version = (DerInteger)seq[0]; + iv = (Asn1OctetString)seq[1]; + } + } + + public BigInteger RC2ParameterVersion + { + get + { + return version == null ? null : version.Value; + } + } + + public byte[] GetIV() + { + return Arrays.Clone(iv.GetOctets()); + } + + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(); + + if (version != null) + { + v.Add(version); + } + + v.Add(iv); + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/pkcs/RSAESOAEPparams.cs b/Crypto/src/asn1/pkcs/RSAESOAEPparams.cs new file mode 100644
index 000000000..5ecb394fd --- /dev/null +++ b/Crypto/src/asn1/pkcs/RSAESOAEPparams.cs
@@ -0,0 +1,145 @@ +using System; + +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Pkcs +{ + public class RsaesOaepParameters + : Asn1Encodable + { + private AlgorithmIdentifier hashAlgorithm; + private AlgorithmIdentifier maskGenAlgorithm; + private AlgorithmIdentifier pSourceAlgorithm; + + public readonly static AlgorithmIdentifier DefaultHashAlgorithm = new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1, DerNull.Instance); + public readonly static AlgorithmIdentifier DefaultMaskGenFunction = new AlgorithmIdentifier(PkcsObjectIdentifiers.IdMgf1, DefaultHashAlgorithm); + public readonly static AlgorithmIdentifier DefaultPSourceAlgorithm = new AlgorithmIdentifier(PkcsObjectIdentifiers.IdPSpecified, new DerOctetString(new byte[0])); + + public static RsaesOaepParameters GetInstance( + object obj) + { + if (obj is RsaesOaepParameters) + { + return (RsaesOaepParameters)obj; + } + else if (obj is Asn1Sequence) + { + return new RsaesOaepParameters((Asn1Sequence)obj); + } + + throw new ArgumentException("Unknown object in factory: " + obj.GetType().FullName, "obj"); + } + + /** + * The default version + */ + public RsaesOaepParameters() + { + hashAlgorithm = DefaultHashAlgorithm; + maskGenAlgorithm = DefaultMaskGenFunction; + pSourceAlgorithm = DefaultPSourceAlgorithm; + } + + public RsaesOaepParameters( + AlgorithmIdentifier hashAlgorithm, + AlgorithmIdentifier maskGenAlgorithm, + AlgorithmIdentifier pSourceAlgorithm) + { + this.hashAlgorithm = hashAlgorithm; + this.maskGenAlgorithm = maskGenAlgorithm; + this.pSourceAlgorithm = pSourceAlgorithm; + } + + public RsaesOaepParameters( + Asn1Sequence seq) + { + hashAlgorithm = DefaultHashAlgorithm; + maskGenAlgorithm = DefaultMaskGenFunction; + pSourceAlgorithm = DefaultPSourceAlgorithm; + + for (int i = 0; i != seq.Count; i++) + { + Asn1TaggedObject o = (Asn1TaggedObject)seq[i]; + + switch (o.TagNo) + { + 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 ArgumentException("unknown tag"); + } + } + } + + public AlgorithmIdentifier HashAlgorithm + { + get { return hashAlgorithm; } + } + + public AlgorithmIdentifier MaskGenAlgorithm + { + get { return maskGenAlgorithm; } + } + + public AlgorithmIdentifier PSourceAlgorithm + { + get { 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(); + + if (!hashAlgorithm.Equals(DefaultHashAlgorithm)) + { + v.Add(new DerTaggedObject(true, 0, hashAlgorithm)); + } + + if (!maskGenAlgorithm.Equals(DefaultMaskGenFunction)) + { + v.Add(new DerTaggedObject(true, 1, maskGenAlgorithm)); + } + + if (!pSourceAlgorithm.Equals(DefaultPSourceAlgorithm)) + { + v.Add(new DerTaggedObject(true, 2, pSourceAlgorithm)); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/pkcs/RSAPrivateKeyStructure.cs b/Crypto/src/asn1/pkcs/RSAPrivateKeyStructure.cs new file mode 100644
index 000000000..dbb07c744 --- /dev/null +++ b/Crypto/src/asn1/pkcs/RSAPrivateKeyStructure.cs
@@ -0,0 +1,131 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Asn1.Pkcs +{ + public class RsaPrivateKeyStructure + : Asn1Encodable + { + private readonly BigInteger modulus; + private readonly BigInteger publicExponent; + private readonly BigInteger privateExponent; + private readonly BigInteger prime1; + private readonly BigInteger prime2; + private readonly BigInteger exponent1; + private readonly BigInteger exponent2; + private readonly BigInteger coefficient; + + public RsaPrivateKeyStructure( + BigInteger modulus, + BigInteger publicExponent, + BigInteger privateExponent, + BigInteger prime1, + BigInteger prime2, + BigInteger exponent1, + BigInteger exponent2, + BigInteger coefficient) + { + 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) + { + BigInteger version = ((DerInteger) seq[0]).Value; + if (version.IntValue != 0) + throw new ArgumentException("wrong version for RSA private key"); + + modulus = ((DerInteger) seq[1]).Value; + publicExponent = ((DerInteger) seq[2]).Value; + privateExponent = ((DerInteger) seq[3]).Value; + prime1 = ((DerInteger) seq[4]).Value; + prime2 = ((DerInteger) seq[5]).Value; + exponent1 = ((DerInteger) seq[6]).Value; + exponent2 = ((DerInteger) seq[7]).Value; + coefficient = ((DerInteger) seq[8]).Value; + } + + public BigInteger Modulus + { + get { return modulus; } + } + + public BigInteger PublicExponent + { + get { return publicExponent; } + } + + public BigInteger PrivateExponent + { + get { return privateExponent; } + } + + public BigInteger Prime1 + { + get { return prime1; } + } + + public BigInteger Prime2 + { + get { return prime2; } + } + + public BigInteger Exponent1 + { + get { return exponent1; } + } + + public BigInteger Exponent2 + { + get { return exponent2; } + } + + public BigInteger Coefficient + { + get { 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 + * } + * + * Version ::= Integer + * </pre> + * <p>This routine is written to output Pkcs1 version 0, private keys.</p> + */ + public override Asn1Object ToAsn1Object() + { + return new DerSequence( + new DerInteger(0), // version + new DerInteger(Modulus), + new DerInteger(PublicExponent), + new DerInteger(PrivateExponent), + new DerInteger(Prime1), + new DerInteger(Prime2), + new DerInteger(Exponent1), + new DerInteger(Exponent2), + new DerInteger(Coefficient)); + } + } +} diff --git a/Crypto/src/asn1/pkcs/RSASSAPSSparams.cs b/Crypto/src/asn1/pkcs/RSASSAPSSparams.cs new file mode 100644
index 000000000..941620761 --- /dev/null +++ b/Crypto/src/asn1/pkcs/RSASSAPSSparams.cs
@@ -0,0 +1,165 @@ +using System; + +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Pkcs +{ + public class RsassaPssParameters + : Asn1Encodable + { + private AlgorithmIdentifier hashAlgorithm; + private AlgorithmIdentifier maskGenAlgorithm; + private DerInteger saltLength; + private DerInteger trailerField; + + public readonly static AlgorithmIdentifier DefaultHashAlgorithm = new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1, DerNull.Instance); + public readonly static AlgorithmIdentifier DefaultMaskGenFunction = new AlgorithmIdentifier(PkcsObjectIdentifiers.IdMgf1, DefaultHashAlgorithm); + public readonly static DerInteger DefaultSaltLength = new DerInteger(20); + public readonly static DerInteger DefaultTrailerField = new DerInteger(1); + + public static RsassaPssParameters GetInstance( + object obj) + { + if (obj == null || obj is RsassaPssParameters) + { + return (RsassaPssParameters)obj; + } + + if (obj is Asn1Sequence) + { + return new RsassaPssParameters((Asn1Sequence)obj); + } + + throw new ArgumentException("Unknown object in factory: " + obj.GetType().FullName, "obj"); + } + + /** + * The default version + */ + public RsassaPssParameters() + { + hashAlgorithm = DefaultHashAlgorithm; + maskGenAlgorithm = DefaultMaskGenFunction; + saltLength = DefaultSaltLength; + trailerField = DefaultTrailerField; + } + + public RsassaPssParameters( + AlgorithmIdentifier hashAlgorithm, + AlgorithmIdentifier maskGenAlgorithm, + DerInteger saltLength, + DerInteger trailerField) + { + this.hashAlgorithm = hashAlgorithm; + this.maskGenAlgorithm = maskGenAlgorithm; + this.saltLength = saltLength; + this.trailerField = trailerField; + } + + public RsassaPssParameters( + Asn1Sequence seq) + { + hashAlgorithm = DefaultHashAlgorithm; + maskGenAlgorithm = DefaultMaskGenFunction; + saltLength = DefaultSaltLength; + trailerField = DefaultTrailerField; + + for (int i = 0; i != seq.Count; i++) + { + Asn1TaggedObject o = (Asn1TaggedObject)seq[i]; + + switch (o.TagNo) + { + case 0: + hashAlgorithm = AlgorithmIdentifier.GetInstance(o, true); + break; + case 1: + maskGenAlgorithm = AlgorithmIdentifier.GetInstance(o, true); + break; + case 2: + saltLength = DerInteger.GetInstance(o, true); + break; + case 3: + trailerField = DerInteger.GetInstance(o, true); + break; + default: + throw new ArgumentException("unknown tag"); + } + } + } + + public AlgorithmIdentifier HashAlgorithm + { + get { return hashAlgorithm; } + } + + public AlgorithmIdentifier MaskGenAlgorithm + { + get { return maskGenAlgorithm; } + } + + public DerInteger SaltLength + { + get { return saltLength; } + } + + public DerInteger TrailerField + { + get { return trailerField; } + } + + /** + * <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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(); + + if (!hashAlgorithm.Equals(DefaultHashAlgorithm)) + { + v.Add(new DerTaggedObject(true, 0, hashAlgorithm)); + } + + if (!maskGenAlgorithm.Equals(DefaultMaskGenFunction)) + { + v.Add(new DerTaggedObject(true, 1, maskGenAlgorithm)); + } + + if (!saltLength.Equals(DefaultSaltLength)) + { + v.Add(new DerTaggedObject(true, 2, saltLength)); + } + + if (!trailerField.Equals(DefaultTrailerField)) + { + v.Add(new DerTaggedObject(true, 3, trailerField)); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/pkcs/SafeBag.cs b/Crypto/src/asn1/pkcs/SafeBag.cs new file mode 100644
index 000000000..4b9350bac --- /dev/null +++ b/Crypto/src/asn1/pkcs/SafeBag.cs
@@ -0,0 +1,70 @@ +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Pkcs +{ + public class SafeBag + : Asn1Encodable + { + private readonly DerObjectIdentifier bagID; + private readonly Asn1Object bagValue; + private readonly Asn1Set bagAttributes; + + public SafeBag( + DerObjectIdentifier oid, + Asn1Object obj) + { + this.bagID = oid; + this.bagValue = obj; + this.bagAttributes = null; + } + + public SafeBag( + DerObjectIdentifier oid, + Asn1Object obj, + Asn1Set bagAttributes) + { + this.bagID = oid; + this.bagValue = obj; + this.bagAttributes = bagAttributes; + } + + public SafeBag( + Asn1Sequence seq) + { + this.bagID = (DerObjectIdentifier) seq[0]; + this.bagValue = ((DerTaggedObject) seq[1]).GetObject(); + if (seq.Count == 3) + { + this.bagAttributes = (Asn1Set) seq[2]; + } + } + + public DerObjectIdentifier BagID + { + get { return bagID; } + } + + public Asn1Object BagValue + { + get { return bagValue; } + } + + public Asn1Set BagAttributes + { + get { return bagAttributes; } + } + + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector( + bagID, new DerTaggedObject(0, bagValue)); + + if (bagAttributes != null) + { + v.Add(bagAttributes); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/pkcs/SignedData.cs b/Crypto/src/asn1/pkcs/SignedData.cs new file mode 100644
index 000000000..10951d37a --- /dev/null +++ b/Crypto/src/asn1/pkcs/SignedData.cs
@@ -0,0 +1,163 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Pkcs +{ + /** + * a Pkcs#7 signed data object. + */ + public class SignedData + : Asn1Encodable + { + private readonly DerInteger version; + private readonly Asn1Set digestAlgorithms; + private readonly ContentInfo contentInfo; + private readonly Asn1Set certificates; + private readonly Asn1Set crls; + private readonly Asn1Set signerInfos; + + public static SignedData GetInstance( + object obj) + { + if (obj is SignedData) + { + return (SignedData) obj; + } + + if (obj is Asn1Sequence) + { + return new SignedData((Asn1Sequence) obj); + } + + throw new ArgumentException("Unknown object in factory: " + obj.GetType().FullName, "obj"); + } + + public SignedData( + DerInteger _version, + Asn1Set _digestAlgorithms, + ContentInfo _contentInfo, + Asn1Set _certificates, + Asn1Set _crls, + Asn1Set _signerInfos) + { + version = _version; + digestAlgorithms = _digestAlgorithms; + contentInfo = _contentInfo; + certificates = _certificates; + crls = _crls; + signerInfos = _signerInfos; + } + + private SignedData( + Asn1Sequence seq) + { + IEnumerator e = seq.GetEnumerator(); + + e.MoveNext(); + version = (DerInteger) e.Current; + + e.MoveNext(); + digestAlgorithms = (Asn1Set) e.Current; + + e.MoveNext(); + contentInfo = ContentInfo.GetInstance(e.Current); + + while (e.MoveNext()) + { + Asn1Object o = (Asn1Object) e.Current; + + // + // 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 is DerTaggedObject) + { + DerTaggedObject tagged = (DerTaggedObject) o; + + switch (tagged.TagNo) + { + case 0: + certificates = Asn1Set.GetInstance(tagged, false); + break; + case 1: + crls = Asn1Set.GetInstance(tagged, false); + break; + default: + throw new ArgumentException("unknown tag value " + tagged.TagNo); + } + } + else + { + signerInfos = (Asn1Set) o; + } + } + } + + public DerInteger Version + { + get { return version; } + } + + public Asn1Set DigestAlgorithms + { + get { return digestAlgorithms; } + } + + public ContentInfo ContentInfo + { + get { return contentInfo; } + } + + public Asn1Set Certificates + { + get { return certificates; } + } + + public Asn1Set Crls + { + get { return crls; } + } + + public Asn1Set SignerInfos + { + get { 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector( + version, digestAlgorithms, 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/Crypto/src/asn1/pkcs/SignerInfo.cs b/Crypto/src/asn1/pkcs/SignerInfo.cs new file mode 100644
index 000000000..1e4694547 --- /dev/null +++ b/Crypto/src/asn1/pkcs/SignerInfo.cs
@@ -0,0 +1,154 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Pkcs +{ + /** + * a Pkcs#7 signer info object. + */ + public class SignerInfo + : Asn1Encodable + { + private DerInteger version; + private IssuerAndSerialNumber issuerAndSerialNumber; + private AlgorithmIdentifier digAlgorithm; + private Asn1Set authenticatedAttributes; + private AlgorithmIdentifier digEncryptionAlgorithm; + private Asn1OctetString encryptedDigest; + private Asn1Set unauthenticatedAttributes; + + public static SignerInfo GetInstance( + object obj) + { + if (obj is SignerInfo) + { + return (SignerInfo) obj; + } + + if (obj is Asn1Sequence) + { + return new SignerInfo((Asn1Sequence) obj); + } + + throw new ArgumentException("Unknown object in factory: " + obj.GetType().FullName, "obj"); + } + + public SignerInfo( + DerInteger 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) + { + IEnumerator e = seq.GetEnumerator(); + + e.MoveNext(); + version = (DerInteger) e.Current; + + e.MoveNext(); + issuerAndSerialNumber = IssuerAndSerialNumber.GetInstance(e.Current); + + e.MoveNext(); + digAlgorithm = AlgorithmIdentifier.GetInstance(e.Current); + + e.MoveNext(); + object obj = e.Current; + + if (obj is Asn1TaggedObject) + { + authenticatedAttributes = Asn1Set.GetInstance((Asn1TaggedObject) obj, false); + + e.MoveNext(); + digEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(e.Current); + } + else + { + authenticatedAttributes = null; + digEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(obj); + } + + e.MoveNext(); + encryptedDigest = DerOctetString.GetInstance(e.Current); + + if (e.MoveNext()) + { + unauthenticatedAttributes = Asn1Set.GetInstance((Asn1TaggedObject)e.Current, false); + } + else + { + unauthenticatedAttributes = null; + } + } + + public DerInteger Version { get { return version; } } + + public IssuerAndSerialNumber IssuerAndSerialNumber { get { return issuerAndSerialNumber; } } + + public Asn1Set AuthenticatedAttributes { get { return authenticatedAttributes; } } + + public AlgorithmIdentifier DigestAlgorithm { get { return digAlgorithm; } } + + public Asn1OctetString EncryptedDigest { get { return encryptedDigest; } } + + public AlgorithmIdentifier DigestEncryptionAlgorithm { get { return digEncryptionAlgorithm; } } + + public Asn1Set UnauthenticatedAttributes { get { 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector( + version, issuerAndSerialNumber, digAlgorithm); + + if (authenticatedAttributes != null) + { + v.Add(new DerTaggedObject(false, 0, authenticatedAttributes)); + } + + v.Add(digEncryptionAlgorithm, encryptedDigest); + + if (unauthenticatedAttributes != null) + { + v.Add(new DerTaggedObject(false, 1, unauthenticatedAttributes)); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/sec/ECPrivateKeyStructure.cs b/Crypto/src/asn1/sec/ECPrivateKeyStructure.cs new file mode 100644
index 000000000..2e9c27fd2 --- /dev/null +++ b/Crypto/src/asn1/sec/ECPrivateKeyStructure.cs
@@ -0,0 +1,118 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1.Sec +{ + /** + * the elliptic curve private key object from SEC 1 + */ + public class ECPrivateKeyStructure + : Asn1Encodable + { + private readonly Asn1Sequence seq; + + public ECPrivateKeyStructure( + Asn1Sequence seq) + { + if (seq == null) + throw new ArgumentNullException("seq"); + + this.seq = seq; + } + + public ECPrivateKeyStructure( + BigInteger key) + { + if (key == null) + throw new ArgumentNullException("key"); + + this.seq = new DerSequence( + new DerInteger(1), + new DerOctetString(key.ToByteArrayUnsigned())); + } + + public ECPrivateKeyStructure( + BigInteger key, + Asn1Encodable parameters) + : this(key, null, parameters) + { + } + + public ECPrivateKeyStructure( + BigInteger key, + DerBitString publicKey, + Asn1Encodable parameters) + { + if (key == null) + throw new ArgumentNullException("key"); + + Asn1EncodableVector v = new Asn1EncodableVector( + new DerInteger(1), + new DerOctetString(key.ToByteArrayUnsigned())); + + if (parameters != null) + { + v.Add(new DerTaggedObject(true, 0, parameters)); + } + + if (publicKey != null) + { + v.Add(new DerTaggedObject(true, 1, publicKey)); + } + + this.seq = new DerSequence(v); + } + + public BigInteger GetKey() + { + Asn1OctetString octs = (Asn1OctetString) seq[1]; + + return new BigInteger(1, octs.GetOctets()); + } + + public DerBitString GetPublicKey() + { + return (DerBitString) GetObjectInTag(1); + } + + public Asn1Object GetParameters() + { + return GetObjectInTag(0); + } + + private Asn1Object GetObjectInTag( + int tagNo) + { + foreach (Asn1Encodable ae in seq) + { + Asn1Object obj = ae.ToAsn1Object(); + + if (obj is Asn1TaggedObject) + { + Asn1TaggedObject tag = (Asn1TaggedObject) obj; + if (tag.TagNo == tagNo) + { + return tag.GetObject(); + } + } + } + + return null; + } + + /** + * ECPrivateKey ::= SEQUENCE { + * version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), + * privateKey OCTET STRING, + * parameters [0] Parameters OPTIONAL, + * publicKey [1] BIT STRING OPTIONAL } + */ + public override Asn1Object ToAsn1Object() + { + return seq; + } + } +} diff --git a/Crypto/src/asn1/sec/SECNamedCurves.cs b/Crypto/src/asn1/sec/SECNamedCurves.cs new file mode 100644
index 000000000..c7503144f --- /dev/null +++ b/Crypto/src/asn1/sec/SECNamedCurves.cs
@@ -0,0 +1,1192 @@ +using System; +using System.Collections; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Asn1.Sec +{ + public sealed class SecNamedCurves + { + private SecNamedCurves() + { + } + + private static BigInteger FromHex( + string hex) + { + return new BigInteger(1, Hex.Decode(hex)); + } + + /* + * secp112r1 + */ + internal class Secp112r1Holder + : X9ECParametersHolder + { + private Secp112r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Secp112r1Holder(); + + protected override 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 FpCurve(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 + */ + internal class Secp112r2Holder + : X9ECParametersHolder + { + private Secp112r2Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Secp112r2Holder(); + + protected override 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 FpCurve(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 + */ + internal class Secp128r1Holder + : X9ECParametersHolder + { + private Secp128r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Secp128r1Holder(); + + protected override 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 FpCurve(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 + */ + internal class Secp128r2Holder + : X9ECParametersHolder + { + private Secp128r2Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Secp128r2Holder(); + + protected override 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 FpCurve(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 + */ + internal class Secp160k1Holder + : X9ECParametersHolder + { + private Secp160k1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Secp160k1Holder(); + + protected override 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 = BigInteger.Zero; + BigInteger b = BigInteger.ValueOf(7); + byte[] S = null; + BigInteger n = FromHex("0100000000000000000001B8FA16DFAB9ACA16B6B3"); + BigInteger h = BigInteger.ValueOf(1); + + ECCurve curve = new FpCurve(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 + */ + internal class Secp160r1Holder + : X9ECParametersHolder + { + private Secp160r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Secp160r1Holder(); + + protected override 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 FpCurve(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 + */ + internal class Secp160r2Holder + : X9ECParametersHolder + { + private Secp160r2Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Secp160r2Holder(); + + protected override 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 FpCurve(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 + */ + internal class Secp192k1Holder + : X9ECParametersHolder + { + private Secp192k1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Secp192k1Holder(); + + protected override X9ECParameters CreateParameters() + { + // p = 2^192 - 2^32 - 2^12 - 2^8 - 2^7 - 2^6 - 2^3 - 1 + BigInteger p = FromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37"); + BigInteger a = BigInteger.Zero; + BigInteger b = BigInteger.ValueOf(3); + byte[] S = null; + BigInteger n = FromHex("FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D"); + BigInteger h = BigInteger.ValueOf(1); + + ECCurve curve = new FpCurve(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 + */ + internal class Secp192r1Holder + : X9ECParametersHolder + { + private Secp192r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Secp192r1Holder(); + + protected override 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 FpCurve(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 + */ + internal class Secp224k1Holder + : X9ECParametersHolder + { + private Secp224k1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Secp224k1Holder(); + + protected override 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 = BigInteger.Zero; + BigInteger b = BigInteger.ValueOf(5); + byte[] S = null; + BigInteger n = FromHex("010000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7"); + BigInteger h = BigInteger.ValueOf(1); + + ECCurve curve = new FpCurve(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 + */ + internal class Secp224r1Holder + : X9ECParametersHolder + { + private Secp224r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Secp224r1Holder(); + + protected override 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 FpCurve(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 + */ + internal class Secp256k1Holder + : X9ECParametersHolder + { + private Secp256k1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Secp256k1Holder(); + + protected override X9ECParameters CreateParameters() + { + // p = 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1 + BigInteger p = FromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F"); + BigInteger a = BigInteger.Zero; + BigInteger b = BigInteger.ValueOf(7); + byte[] S = null; + BigInteger n = FromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"); + BigInteger h = BigInteger.ValueOf(1); + + ECCurve curve = new FpCurve(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 + */ + internal class Secp256r1Holder + : X9ECParametersHolder + { + private Secp256r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Secp256r1Holder(); + + protected override 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 FpCurve(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 + */ + internal class Secp384r1Holder + : X9ECParametersHolder + { + private Secp384r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Secp384r1Holder(); + + protected override 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 FpCurve(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 + */ + internal class Secp521r1Holder + : X9ECParametersHolder + { + private Secp521r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Secp521r1Holder(); + + protected override 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 FpCurve(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 + */ + internal class Sect113r1Holder + : X9ECParametersHolder + { + private Sect113r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Sect113r1Holder(); + + private const int m = 113; + private const int k = 9; + + protected override X9ECParameters CreateParameters() + { + 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 F2mCurve(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 + */ + internal class Sect113r2Holder + : X9ECParametersHolder + { + private Sect113r2Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Sect113r2Holder(); + + private const int m = 113; + private const int k = 9; + + protected override X9ECParameters CreateParameters() + { + 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 F2mCurve(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 + */ + internal class Sect131r1Holder + : X9ECParametersHolder + { + private Sect131r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Sect131r1Holder(); + + private const int m = 131; + private const int k1 = 2; + private const int k2 = 3; + private const int k3 = 8; + + protected override X9ECParameters CreateParameters() + { + 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 F2mCurve(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 + */ + internal class Sect131r2Holder + : X9ECParametersHolder + { + private Sect131r2Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Sect131r2Holder(); + + private const int m = 131; + private const int k1 = 2; + private const int k2 = 3; + private const int k3 = 8; + + protected override X9ECParameters CreateParameters() + { + 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 F2mCurve(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 + */ + internal class Sect163k1Holder + : X9ECParametersHolder + { + private Sect163k1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Sect163k1Holder(); + + private const int m = 163; + private const int k1 = 3; + private const int k2 = 6; + private const int k3 = 7; + + protected override X9ECParameters CreateParameters() + { + 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 F2mCurve(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 + */ + internal class Sect163r1Holder + : X9ECParametersHolder + { + private Sect163r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Sect163r1Holder(); + + private const int m = 163; + private const int k1 = 3; + private const int k2 = 6; + private const int k3 = 7; + + protected override X9ECParameters CreateParameters() + { + 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 F2mCurve(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 + */ + internal class Sect163r2Holder + : X9ECParametersHolder + { + private Sect163r2Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Sect163r2Holder(); + + private const int m = 163; + private const int k1 = 3; + private const int k2 = 6; + private const int k3 = 7; + + protected override X9ECParameters CreateParameters() + { + 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 F2mCurve(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 + */ + internal class Sect193r1Holder + : X9ECParametersHolder + { + private Sect193r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Sect193r1Holder(); + + private const int m = 193; + private const int k = 15; + + protected override X9ECParameters CreateParameters() + { + 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 F2mCurve(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 + */ + internal class Sect193r2Holder + : X9ECParametersHolder + { + private Sect193r2Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Sect193r2Holder(); + + private const int m = 193; + private const int k = 15; + + protected override X9ECParameters CreateParameters() + { + 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 F2mCurve(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 + */ + internal class Sect233k1Holder + : X9ECParametersHolder + { + private Sect233k1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Sect233k1Holder(); + + private const int m = 233; + private const int k = 74; + + protected override X9ECParameters CreateParameters() + { + BigInteger a = BigInteger.Zero; + BigInteger b = BigInteger.ValueOf(1); + byte[] S = null; + BigInteger n = FromHex("8000000000000000000000000000069D5BB915BCD46EFB1AD5F173ABDF"); + BigInteger h = BigInteger.ValueOf(4); + + ECCurve curve = new F2mCurve(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 + */ + internal class Sect233r1Holder + : X9ECParametersHolder + { + private Sect233r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Sect233r1Holder(); + + private const int m = 233; + private const int k = 74; + + protected override X9ECParameters CreateParameters() + { + 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 F2mCurve(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 + */ + internal class Sect239k1Holder + : X9ECParametersHolder + { + private Sect239k1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Sect239k1Holder(); + + private const int m = 239; + private const int k = 158; + + protected override X9ECParameters CreateParameters() + { + BigInteger a = BigInteger.Zero; + BigInteger b = BigInteger.ValueOf(1); + byte[] S = null; + BigInteger n = FromHex("2000000000000000000000000000005A79FEC67CB6E91F1C1DA800E478A5"); + BigInteger h = BigInteger.ValueOf(4); + + ECCurve curve = new F2mCurve(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 + */ + internal class Sect283k1Holder + : X9ECParametersHolder + { + private Sect283k1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Sect283k1Holder(); + + private const int m = 283; + private const int k1 = 5; + private const int k2 = 7; + private const int k3 = 12; + + protected override X9ECParameters CreateParameters() + { + BigInteger a = BigInteger.Zero; + BigInteger b = BigInteger.ValueOf(1); + byte[] S = null; + BigInteger n = FromHex("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9AE2ED07577265DFF7F94451E061E163C61"); + BigInteger h = BigInteger.ValueOf(4); + + ECCurve curve = new F2mCurve(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 + */ + internal class Sect283r1Holder + : X9ECParametersHolder + { + private Sect283r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Sect283r1Holder(); + + private const int m = 283; + private const int k1 = 5; + private const int k2 = 7; + private const int k3 = 12; + + protected override X9ECParameters CreateParameters() + { + 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 F2mCurve(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 + */ + internal class Sect409k1Holder + : X9ECParametersHolder + { + private Sect409k1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Sect409k1Holder(); + + private const int m = 409; + private const int k = 87; + + protected override X9ECParameters CreateParameters() + { + BigInteger a = BigInteger.Zero; + BigInteger b = BigInteger.ValueOf(1); + byte[] S = null; + BigInteger n = FromHex("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5F83B2D4EA20400EC4557D5ED3E3E7CA5B4B5C83B8E01E5FCF"); + BigInteger h = BigInteger.ValueOf(4); + + ECCurve curve = new F2mCurve(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 + */ + internal class Sect409r1Holder + : X9ECParametersHolder + { + private Sect409r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Sect409r1Holder(); + + private const int m = 409; + private const int k = 87; + + protected override X9ECParameters CreateParameters() + { + 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 F2mCurve(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 + */ + internal class Sect571k1Holder + : X9ECParametersHolder + { + private Sect571k1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Sect571k1Holder(); + + private const int m = 571; + private const int k1 = 2; + private const int k2 = 5; + private const int k3 = 10; + + protected override X9ECParameters CreateParameters() + { + BigInteger a = BigInteger.Zero; + BigInteger b = BigInteger.ValueOf(1); + byte[] S = null; + BigInteger n = FromHex("020000000000000000000000000000000000000000000000000000000000000000000000131850E1F19A63E4B391A8DB917F4138B630D84BE5D639381E91DEB45CFE778F637C1001"); + BigInteger h = BigInteger.ValueOf(4); + + ECCurve curve = new F2mCurve(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 + */ + internal class Sect571r1Holder + : X9ECParametersHolder + { + private Sect571r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Sect571r1Holder(); + + private const int m = 571; + private const int k1 = 2; + private const int k2 = 5; + private const int k3 = 10; + + protected override X9ECParameters CreateParameters() + { + 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 F2mCurve(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); + } + } + + + private static readonly IDictionary objIds = Platform.CreateHashtable(); + private static readonly IDictionary curves = Platform.CreateHashtable(); + private static readonly IDictionary names = Platform.CreateHashtable(); + + private static void DefineCurve( + string name, + DerObjectIdentifier oid, + X9ECParametersHolder holder) + { + objIds.Add(name, oid); + names.Add(oid, name); + curves.Add(oid, holder); + } + + static SecNamedCurves() + { + DefineCurve("secp112r1", SecObjectIdentifiers.SecP112r1, Secp112r1Holder.Instance); + DefineCurve("secp112r2", SecObjectIdentifiers.SecP112r2, Secp112r2Holder.Instance); + DefineCurve("secp128r1", SecObjectIdentifiers.SecP128r1, Secp128r1Holder.Instance); + DefineCurve("secp128r2", SecObjectIdentifiers.SecP128r2, Secp128r2Holder.Instance); + DefineCurve("secp160k1", SecObjectIdentifiers.SecP160k1, Secp160k1Holder.Instance); + DefineCurve("secp160r1", SecObjectIdentifiers.SecP160r1, Secp160r1Holder.Instance); + DefineCurve("secp160r2", SecObjectIdentifiers.SecP160r2, Secp160r2Holder.Instance); + DefineCurve("secp192k1", SecObjectIdentifiers.SecP192k1, Secp192k1Holder.Instance); + DefineCurve("secp192r1", SecObjectIdentifiers.SecP192r1, Secp192r1Holder.Instance); + DefineCurve("secp224k1", SecObjectIdentifiers.SecP224k1, Secp224k1Holder.Instance); + DefineCurve("secp224r1", SecObjectIdentifiers.SecP224r1, Secp224r1Holder.Instance); + DefineCurve("secp256k1", SecObjectIdentifiers.SecP256k1, Secp256k1Holder.Instance); + DefineCurve("secp256r1", SecObjectIdentifiers.SecP256r1, Secp256r1Holder.Instance); + DefineCurve("secp384r1", SecObjectIdentifiers.SecP384r1, Secp384r1Holder.Instance); + DefineCurve("secp521r1", SecObjectIdentifiers.SecP521r1, Secp521r1Holder.Instance); + + DefineCurve("sect113r1", SecObjectIdentifiers.SecT113r1, Sect113r1Holder.Instance); + DefineCurve("sect113r2", SecObjectIdentifiers.SecT113r2, Sect113r2Holder.Instance); + DefineCurve("sect131r1", SecObjectIdentifiers.SecT131r1, Sect131r1Holder.Instance); + DefineCurve("sect131r2", SecObjectIdentifiers.SecT131r2, Sect131r2Holder.Instance); + DefineCurve("sect163k1", SecObjectIdentifiers.SecT163k1, Sect163k1Holder.Instance); + DefineCurve("sect163r1", SecObjectIdentifiers.SecT163r1, Sect163r1Holder.Instance); + DefineCurve("sect163r2", SecObjectIdentifiers.SecT163r2, Sect163r2Holder.Instance); + DefineCurve("sect193r1", SecObjectIdentifiers.SecT193r1, Sect193r1Holder.Instance); + DefineCurve("sect193r2", SecObjectIdentifiers.SecT193r2, Sect193r2Holder.Instance); + DefineCurve("sect233k1", SecObjectIdentifiers.SecT233k1, Sect233k1Holder.Instance); + DefineCurve("sect233r1", SecObjectIdentifiers.SecT233r1, Sect233r1Holder.Instance); + DefineCurve("sect239k1", SecObjectIdentifiers.SecT239k1, Sect239k1Holder.Instance); + DefineCurve("sect283k1", SecObjectIdentifiers.SecT283k1, Sect283k1Holder.Instance); + DefineCurve("sect283r1", SecObjectIdentifiers.SecT283r1, Sect283r1Holder.Instance); + DefineCurve("sect409k1", SecObjectIdentifiers.SecT409k1, Sect409k1Holder.Instance); + DefineCurve("sect409r1", SecObjectIdentifiers.SecT409r1, Sect409r1Holder.Instance); + DefineCurve("sect571k1", SecObjectIdentifiers.SecT571k1, Sect571k1Holder.Instance); + DefineCurve("sect571r1", SecObjectIdentifiers.SecT571r1, Sect571r1Holder.Instance); + } + + public static X9ECParameters GetByName( + string name) + { + DerObjectIdentifier oid = (DerObjectIdentifier) + objIds[name.ToLowerInvariant()]; + + return oid == null ? null : GetByOid(oid); + } + + /** + * 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( + DerObjectIdentifier oid) + { + X9ECParametersHolder holder = (X9ECParametersHolder) curves[oid]; + + return holder == null ? null : holder.Parameters; + } + + /** + * 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 DerObjectIdentifier GetOid( + string name) + { + return (DerObjectIdentifier) objIds[name.ToLowerInvariant()]; + } + + /** + * return the named curve name represented by the given object identifier. + */ + public static string GetName( + DerObjectIdentifier oid) + { + return (string) names[oid]; + } + + /** + * returns an enumeration containing the name strings for curves + * contained in this structure. + */ + public static IEnumerable Names + { + get { return new EnumerableProxy(objIds.Keys); } + } + } +} diff --git a/Crypto/src/asn1/sec/SECObjectIdentifiers.cs b/Crypto/src/asn1/sec/SECObjectIdentifiers.cs new file mode 100644
index 000000000..afc10e1d6 --- /dev/null +++ b/Crypto/src/asn1/sec/SECObjectIdentifiers.cs
@@ -0,0 +1,52 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X9; + +namespace Org.BouncyCastle.Asn1.Sec +{ + public abstract class SecObjectIdentifiers + { + /** + * EllipticCurve OBJECT IDENTIFIER ::= { + * iso(1) identified-organization(3) certicom(132) curve(0) + * } + */ + public static readonly DerObjectIdentifier EllipticCurve = new DerObjectIdentifier("1.3.132.0"); + + public static readonly DerObjectIdentifier SecT163k1 = new DerObjectIdentifier(EllipticCurve + ".1"); + public static readonly DerObjectIdentifier SecT163r1 = new DerObjectIdentifier(EllipticCurve + ".2"); + public static readonly DerObjectIdentifier SecT239k1 = new DerObjectIdentifier(EllipticCurve + ".3"); + public static readonly DerObjectIdentifier SecT113r1 = new DerObjectIdentifier(EllipticCurve + ".4"); + public static readonly DerObjectIdentifier SecT113r2 = new DerObjectIdentifier(EllipticCurve + ".5"); + public static readonly DerObjectIdentifier SecP112r1 = new DerObjectIdentifier(EllipticCurve + ".6"); + public static readonly DerObjectIdentifier SecP112r2 = new DerObjectIdentifier(EllipticCurve + ".7"); + public static readonly DerObjectIdentifier SecP160r1 = new DerObjectIdentifier(EllipticCurve + ".8"); + public static readonly DerObjectIdentifier SecP160k1 = new DerObjectIdentifier(EllipticCurve + ".9"); + public static readonly DerObjectIdentifier SecP256k1 = new DerObjectIdentifier(EllipticCurve + ".10"); + public static readonly DerObjectIdentifier SecT163r2 = new DerObjectIdentifier(EllipticCurve + ".15"); + public static readonly DerObjectIdentifier SecT283k1 = new DerObjectIdentifier(EllipticCurve + ".16"); + public static readonly DerObjectIdentifier SecT283r1 = new DerObjectIdentifier(EllipticCurve + ".17"); + public static readonly DerObjectIdentifier SecT131r1 = new DerObjectIdentifier(EllipticCurve + ".22"); + public static readonly DerObjectIdentifier SecT131r2 = new DerObjectIdentifier(EllipticCurve + ".23"); + public static readonly DerObjectIdentifier SecT193r1 = new DerObjectIdentifier(EllipticCurve + ".24"); + public static readonly DerObjectIdentifier SecT193r2 = new DerObjectIdentifier(EllipticCurve + ".25"); + public static readonly DerObjectIdentifier SecT233k1 = new DerObjectIdentifier(EllipticCurve + ".26"); + public static readonly DerObjectIdentifier SecT233r1 = new DerObjectIdentifier(EllipticCurve + ".27"); + public static readonly DerObjectIdentifier SecP128r1 = new DerObjectIdentifier(EllipticCurve + ".28"); + public static readonly DerObjectIdentifier SecP128r2 = new DerObjectIdentifier(EllipticCurve + ".29"); + public static readonly DerObjectIdentifier SecP160r2 = new DerObjectIdentifier(EllipticCurve + ".30"); + public static readonly DerObjectIdentifier SecP192k1 = new DerObjectIdentifier(EllipticCurve + ".31"); + public static readonly DerObjectIdentifier SecP224k1 = new DerObjectIdentifier(EllipticCurve + ".32"); + public static readonly DerObjectIdentifier SecP224r1 = new DerObjectIdentifier(EllipticCurve + ".33"); + public static readonly DerObjectIdentifier SecP384r1 = new DerObjectIdentifier(EllipticCurve + ".34"); + public static readonly DerObjectIdentifier SecP521r1 = new DerObjectIdentifier(EllipticCurve + ".35"); + public static readonly DerObjectIdentifier SecT409k1 = new DerObjectIdentifier(EllipticCurve + ".36"); + public static readonly DerObjectIdentifier SecT409r1 = new DerObjectIdentifier(EllipticCurve + ".37"); + public static readonly DerObjectIdentifier SecT571k1 = new DerObjectIdentifier(EllipticCurve + ".38"); + public static readonly DerObjectIdentifier SecT571r1 = new DerObjectIdentifier(EllipticCurve + ".39"); + + public static readonly DerObjectIdentifier SecP192r1 = X9ObjectIdentifiers.Prime192v1; + public static readonly DerObjectIdentifier SecP256r1 = X9ObjectIdentifiers.Prime256v1; + } +} \ No newline at end of file diff --git a/Crypto/src/asn1/smime/SMIMEAttributes.cs b/Crypto/src/asn1/smime/SMIMEAttributes.cs new file mode 100644
index 000000000..e154e5e74 --- /dev/null +++ b/Crypto/src/asn1/smime/SMIMEAttributes.cs
@@ -0,0 +1,11 @@ +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Pkcs; + +namespace Org.BouncyCastle.Asn1.Smime +{ + public abstract class SmimeAttributes + { + public static readonly DerObjectIdentifier SmimeCapabilities = PkcsObjectIdentifiers.Pkcs9AtSmimeCapabilities; + public static readonly DerObjectIdentifier EncrypKeyPref = PkcsObjectIdentifiers.IdAAEncrypKeyPref; + } +} diff --git a/Crypto/src/asn1/smime/SMIMECapabilities.cs b/Crypto/src/asn1/smime/SMIMECapabilities.cs new file mode 100644
index 000000000..adf254e56 --- /dev/null +++ b/Crypto/src/asn1/smime/SMIMECapabilities.cs
@@ -0,0 +1,128 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1.Smime +{ + /** + * Handler class for dealing with S/MIME Capabilities + */ + public class SmimeCapabilities + : Asn1Encodable + { + /** + * general preferences + */ + public static readonly DerObjectIdentifier PreferSignedData = PkcsObjectIdentifiers.PreferSignedData; + public static readonly DerObjectIdentifier CannotDecryptAny = PkcsObjectIdentifiers.CannotDecryptAny; + public static readonly DerObjectIdentifier SmimeCapabilitesVersions = PkcsObjectIdentifiers.SmimeCapabilitiesVersions; + + /** + * encryption algorithms preferences + */ + public static readonly DerObjectIdentifier DesCbc = new DerObjectIdentifier("1.3.14.3.2.7"); + public static readonly DerObjectIdentifier DesEde3Cbc = PkcsObjectIdentifiers.DesEde3Cbc; + public static readonly DerObjectIdentifier RC2Cbc = PkcsObjectIdentifiers.RC2Cbc; + + private Asn1Sequence capabilities; + + /** + * return an Attr object from the given object. + * + * @param o the object we want converted. + * @exception ArgumentException if the object cannot be converted. + */ + public static SmimeCapabilities GetInstance( + object obj) + { + if (obj == null || obj is SmimeCapabilities) + { + return (SmimeCapabilities) obj; + } + + if (obj is Asn1Sequence) + { + return new SmimeCapabilities((Asn1Sequence) obj); + } + + if (obj is AttributeX509) + { + return new SmimeCapabilities( + (Asn1Sequence)(((AttributeX509) obj).AttrValues[0])); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public SmimeCapabilities( + Asn1Sequence seq) + { + capabilities = seq; + } + +#if !(SILVERLIGHT || PORTABLE) + [Obsolete("Use 'GetCapabilitiesForOid' instead")] + public ArrayList GetCapabilities( + DerObjectIdentifier capability) + { + ArrayList list = new ArrayList(); + DoGetCapabilitiesForOid(capability, list); + return list; + } +#endif + + /** + * returns an ArrayList 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 IList GetCapabilitiesForOid( + DerObjectIdentifier capability) + { + IList list = Platform.CreateArrayList(); + DoGetCapabilitiesForOid(capability, list); + return list; + } + + private void DoGetCapabilitiesForOid(DerObjectIdentifier capability, IList list) + { + if (capability == null) + { + foreach (object o in capabilities) + { + SmimeCapability cap = SmimeCapability.GetInstance(o); + + list.Add(cap); + } + } + else + { + foreach (object o in capabilities) + { + SmimeCapability cap = SmimeCapability.GetInstance(o); + + if (capability.Equals(cap.CapabilityID)) + { + list.Add(cap); + } + } + } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * SMIMECapabilities ::= Sequence OF SMIMECapability + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + return capabilities; + } + } +} diff --git a/Crypto/src/asn1/smime/SMIMECapabilitiesAttribute.cs b/Crypto/src/asn1/smime/SMIMECapabilitiesAttribute.cs new file mode 100644
index 000000000..310c478fe --- /dev/null +++ b/Crypto/src/asn1/smime/SMIMECapabilitiesAttribute.cs
@@ -0,0 +1,16 @@ +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Smime +{ + public class SmimeCapabilitiesAttribute + : AttributeX509 + { + public SmimeCapabilitiesAttribute( + SmimeCapabilityVector capabilities) + : base(SmimeAttributes.SmimeCapabilities, + new DerSet(new DerSequence(capabilities.ToAsn1EncodableVector()))) + { + } + } +} diff --git a/Crypto/src/asn1/smime/SMIMECapability.cs b/Crypto/src/asn1/smime/SMIMECapability.cs new file mode 100644
index 000000000..5709cb815 --- /dev/null +++ b/Crypto/src/asn1/smime/SMIMECapability.cs
@@ -0,0 +1,101 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Pkcs; + +namespace Org.BouncyCastle.Asn1.Smime +{ + public class SmimeCapability + : Asn1Encodable + { + /** + * general preferences + */ + public static readonly DerObjectIdentifier PreferSignedData = PkcsObjectIdentifiers.PreferSignedData; + public static readonly DerObjectIdentifier CannotDecryptAny = PkcsObjectIdentifiers.CannotDecryptAny; + public static readonly DerObjectIdentifier SmimeCapabilitiesVersions = PkcsObjectIdentifiers.SmimeCapabilitiesVersions; + + /** + * encryption algorithms preferences + */ + public static readonly DerObjectIdentifier DesCbc = new DerObjectIdentifier("1.3.14.3.2.7"); + public static readonly DerObjectIdentifier DesEde3Cbc = PkcsObjectIdentifiers.DesEde3Cbc; + public static readonly DerObjectIdentifier RC2Cbc = PkcsObjectIdentifiers.RC2Cbc; + + private DerObjectIdentifier capabilityID; + private Asn1Object parameters; + + public SmimeCapability( + Asn1Sequence seq) + { + capabilityID = (DerObjectIdentifier) seq[0].ToAsn1Object(); + + if (seq.Count > 1) + { + parameters = seq[1].ToAsn1Object(); + } + } + + public SmimeCapability( + DerObjectIdentifier capabilityID, + Asn1Encodable parameters) + { + if (capabilityID == null) + throw new ArgumentNullException("capabilityID"); + + this.capabilityID = capabilityID; + + if (parameters != null) + { + this.parameters = parameters.ToAsn1Object(); + } + } + + public static SmimeCapability GetInstance( + object obj) + { + if (obj == null || obj is SmimeCapability) + { + return (SmimeCapability) obj; + } + + if (obj is Asn1Sequence) + { + return new SmimeCapability((Asn1Sequence) obj); + } + + throw new ArgumentException("Invalid SmimeCapability"); + } + + public DerObjectIdentifier CapabilityID + { + get { return capabilityID; } + } + + public Asn1Object Parameters + { + get { return parameters; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * SMIMECapability ::= Sequence { + * capabilityID OBJECT IDENTIFIER, + * parameters ANY DEFINED BY capabilityID OPTIONAL + * } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(capabilityID); + + if (parameters != null) + { + v.Add(parameters); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/smime/SMIMECapabilityVector.cs b/Crypto/src/asn1/smime/SMIMECapabilityVector.cs new file mode 100644
index 000000000..842825b88 --- /dev/null +++ b/Crypto/src/asn1/smime/SMIMECapabilityVector.cs
@@ -0,0 +1,37 @@ +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.Smime +{ + /** + * Handler for creating a vector S/MIME Capabilities + */ + public class SmimeCapabilityVector + { + private readonly Asn1EncodableVector capabilities = new Asn1EncodableVector(); + + public void AddCapability( + DerObjectIdentifier capability) + { + capabilities.Add(new DerSequence(capability)); + } + + public void AddCapability( + DerObjectIdentifier capability, + int value) + { + capabilities.Add(new DerSequence(capability, new DerInteger(value))); + } + + public void AddCapability( + DerObjectIdentifier capability, + Asn1Encodable parameters) + { + capabilities.Add(new DerSequence(capability, parameters)); + } + + public Asn1EncodableVector ToAsn1EncodableVector() + { + return capabilities; + } + } +} diff --git a/Crypto/src/asn1/smime/SMIMEEncryptionKeyPreferenceAttribute.cs b/Crypto/src/asn1/smime/SMIMEEncryptionKeyPreferenceAttribute.cs new file mode 100644
index 000000000..19c5fd78a --- /dev/null +++ b/Crypto/src/asn1/smime/SMIMEEncryptionKeyPreferenceAttribute.cs
@@ -0,0 +1,44 @@ +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Smime +{ + /** + * The SmimeEncryptionKeyPreference object. + * <pre> + * SmimeEncryptionKeyPreference ::= CHOICE { + * issuerAndSerialNumber [0] IssuerAndSerialNumber, + * receipentKeyId [1] RecipientKeyIdentifier, + * subjectAltKeyIdentifier [2] SubjectKeyIdentifier + * } + * </pre> + */ + public class SmimeEncryptionKeyPreferenceAttribute + : AttributeX509 + { + public SmimeEncryptionKeyPreferenceAttribute( + IssuerAndSerialNumber issAndSer) + : base(SmimeAttributes.EncrypKeyPref, + new DerSet(new DerTaggedObject(false, 0, issAndSer))) + { + } + + public SmimeEncryptionKeyPreferenceAttribute( + RecipientKeyIdentifier rKeyID) + : base(SmimeAttributes.EncrypKeyPref, + new DerSet(new DerTaggedObject(false, 1, rKeyID))) + { + } + + /** + * @param sKeyId the subjectKeyIdentifier value (normally the X.509 one) + */ + public SmimeEncryptionKeyPreferenceAttribute( + Asn1OctetString sKeyID) + : base(SmimeAttributes.EncrypKeyPref, + new DerSet(new DerTaggedObject(false, 2, sKeyID))) + { + } + } +} diff --git a/Crypto/src/asn1/teletrust/TeleTrusTNamedCurves.cs b/Crypto/src/asn1/teletrust/TeleTrusTNamedCurves.cs new file mode 100644
index 000000000..0cfdb8483 --- /dev/null +++ b/Crypto/src/asn1/teletrust/TeleTrusTNamedCurves.cs
@@ -0,0 +1,428 @@ +using System.Collections; +using System.Globalization; + +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Asn1.TeleTrust +{ + /** + * 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 + { + internal class BrainpoolP160r1Holder + : X9ECParametersHolder + { + private BrainpoolP160r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new BrainpoolP160r1Holder(); + + protected override X9ECParameters CreateParameters() + { + ECCurve curve = new FpCurve( + 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 + } + } + + internal class BrainpoolP160t1Holder + : X9ECParametersHolder + { + private BrainpoolP160t1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new BrainpoolP160t1Holder(); + + protected override X9ECParameters CreateParameters() + { + ECCurve curve = new FpCurve( + // 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 + } + } + + internal class BrainpoolP192r1Holder + : X9ECParametersHolder + { + private BrainpoolP192r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new BrainpoolP192r1Holder(); + + protected override X9ECParameters CreateParameters() + { + ECCurve curve = new FpCurve( + 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 + } + } + + internal class BrainpoolP192t1Holder + : X9ECParametersHolder + { + private BrainpoolP192t1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new BrainpoolP192t1Holder(); + + protected override X9ECParameters CreateParameters() + { + ECCurve curve = new FpCurve( + //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 + } + } + + internal class BrainpoolP224r1Holder + : X9ECParametersHolder + { + private BrainpoolP224r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new BrainpoolP224r1Holder(); + + protected override X9ECParameters CreateParameters() + { + ECCurve curve = new FpCurve( + 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 + } + } + + internal class BrainpoolP224t1Holder + : X9ECParametersHolder + { + private BrainpoolP224t1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new BrainpoolP224t1Holder(); + + protected override X9ECParameters CreateParameters() + { + ECCurve curve = new FpCurve( + //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 + } + } + + internal class BrainpoolP256r1Holder + : X9ECParametersHolder + { + private BrainpoolP256r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new BrainpoolP256r1Holder(); + + protected override X9ECParameters CreateParameters() + { + ECCurve curve = new FpCurve( + 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 + } + } + + internal class BrainpoolP256t1Holder + : X9ECParametersHolder + { + private BrainpoolP256t1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new BrainpoolP256t1Holder(); + + protected override X9ECParameters CreateParameters() + { + ECCurve curve = new FpCurve( + //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 + } + } + + internal class BrainpoolP320r1Holder + : X9ECParametersHolder + { + private BrainpoolP320r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new BrainpoolP320r1Holder(); + + protected override X9ECParameters CreateParameters() + { + ECCurve curve = new FpCurve( + 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 + } + } + + internal class BrainpoolP320t1Holder + : X9ECParametersHolder + { + private BrainpoolP320t1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new BrainpoolP320t1Holder(); + + protected override X9ECParameters CreateParameters() + { + ECCurve curve = new FpCurve( + //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 + } + } + + internal class BrainpoolP384r1Holder + : X9ECParametersHolder + { + private BrainpoolP384r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new BrainpoolP384r1Holder(); + + protected override X9ECParameters CreateParameters() + { + ECCurve curve = new FpCurve( + 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 + } + } + + internal class BrainpoolP384t1Holder + : X9ECParametersHolder + { + private BrainpoolP384t1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new BrainpoolP384t1Holder(); + + protected override X9ECParameters CreateParameters() + { + ECCurve curve = new FpCurve( + //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 + } + } + + internal class BrainpoolP512r1Holder + : X9ECParametersHolder + { + private BrainpoolP512r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new BrainpoolP512r1Holder(); + + protected override X9ECParameters CreateParameters() + { + ECCurve curve = new FpCurve( + 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 + } + } + + internal class BrainpoolP512t1Holder + : X9ECParametersHolder + { + private BrainpoolP512t1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new BrainpoolP512t1Holder(); + + protected override X9ECParameters CreateParameters() + { + ECCurve curve = new FpCurve( + //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 + } + } + + + private static readonly IDictionary objIds = Platform.CreateHashtable(); + private static readonly IDictionary curves = Platform.CreateHashtable(); + private static readonly IDictionary names = Platform.CreateHashtable(); + + private static void DefineCurve( + string name, + DerObjectIdentifier oid, + X9ECParametersHolder holder) + { + objIds.Add(name, oid); + names.Add(oid, name); + curves.Add(oid, holder); + } + + static TeleTrusTNamedCurves() + { + DefineCurve("brainpoolp160r1", TeleTrusTObjectIdentifiers.BrainpoolP160R1, BrainpoolP160r1Holder.Instance); + DefineCurve("brainpoolp160t1", TeleTrusTObjectIdentifiers.BrainpoolP160T1, BrainpoolP160t1Holder.Instance); + DefineCurve("brainpoolp192r1", TeleTrusTObjectIdentifiers.BrainpoolP192R1, BrainpoolP192r1Holder.Instance); + DefineCurve("brainpoolp192t1", TeleTrusTObjectIdentifiers.BrainpoolP192T1, BrainpoolP192t1Holder.Instance); + DefineCurve("brainpoolp224r1", TeleTrusTObjectIdentifiers.BrainpoolP224R1, BrainpoolP224r1Holder.Instance); + DefineCurve("brainpoolp224t1", TeleTrusTObjectIdentifiers.BrainpoolP224T1, BrainpoolP224t1Holder.Instance); + DefineCurve("brainpoolp256r1", TeleTrusTObjectIdentifiers.BrainpoolP256R1, BrainpoolP256r1Holder.Instance); + DefineCurve("brainpoolp256t1", TeleTrusTObjectIdentifiers.BrainpoolP256T1, BrainpoolP256t1Holder.Instance); + DefineCurve("brainpoolp320r1", TeleTrusTObjectIdentifiers.BrainpoolP320R1, BrainpoolP320r1Holder.Instance); + DefineCurve("brainpoolp320t1", TeleTrusTObjectIdentifiers.BrainpoolP320T1, BrainpoolP320t1Holder.Instance); + DefineCurve("brainpoolp384r1", TeleTrusTObjectIdentifiers.BrainpoolP384R1, BrainpoolP384r1Holder.Instance); + DefineCurve("brainpoolp384t1", TeleTrusTObjectIdentifiers.BrainpoolP384T1, BrainpoolP384t1Holder.Instance); + DefineCurve("brainpoolp512r1", TeleTrusTObjectIdentifiers.BrainpoolP512R1, BrainpoolP512r1Holder.Instance); + DefineCurve("brainpoolp512t1", TeleTrusTObjectIdentifiers.BrainpoolP512T1, BrainpoolP512t1Holder.Instance); + } + + public static X9ECParameters GetByName( + string name) + { + DerObjectIdentifier oid = (DerObjectIdentifier) + objIds[name.ToLowerInvariant()]; + + return oid == null ? null : GetByOid(oid); + } + + /** + * 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( + DerObjectIdentifier oid) + { + X9ECParametersHolder holder = (X9ECParametersHolder) curves[oid]; + + return holder == null ? null : holder.Parameters; + } + + /** + * 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 DerObjectIdentifier GetOid( + string name) + { + return (DerObjectIdentifier) objIds[name.ToLowerInvariant()]; + } + + /** + * return the named curve name represented by the given object identifier. + */ + public static string GetName( + DerObjectIdentifier oid) + { + return (string) names[oid]; + } + + + /** + * returns an enumeration containing the name strings for curves + * contained in this structure. + */ + public static IEnumerable Names + { + get { return new EnumerableProxy(objIds.Keys); } + } + + public static DerObjectIdentifier GetOid( + short curvesize, + bool twisted) + { + return GetOid("brainpoolP" + curvesize + (twisted ? "t" : "r") + "1"); + } + } +} diff --git a/Crypto/src/asn1/teletrust/TeleTrusTObjectIdentifiers.cs b/Crypto/src/asn1/teletrust/TeleTrusTObjectIdentifiers.cs new file mode 100644
index 000000000..56e70842a --- /dev/null +++ b/Crypto/src/asn1/teletrust/TeleTrusTObjectIdentifiers.cs
@@ -0,0 +1,45 @@ +namespace Org.BouncyCastle.Asn1.TeleTrust +{ + public sealed class TeleTrusTObjectIdentifiers + { + private TeleTrusTObjectIdentifiers() + { + } + + public static readonly DerObjectIdentifier TeleTrusTAlgorithm = new DerObjectIdentifier("1.3.36.3"); + + public static readonly DerObjectIdentifier RipeMD160 = new DerObjectIdentifier(TeleTrusTAlgorithm + ".2.1"); + public static readonly DerObjectIdentifier RipeMD128 = new DerObjectIdentifier(TeleTrusTAlgorithm + ".2.2"); + public static readonly DerObjectIdentifier RipeMD256 = new DerObjectIdentifier(TeleTrusTAlgorithm + ".2.3"); + + public static readonly DerObjectIdentifier TeleTrusTRsaSignatureAlgorithm = new DerObjectIdentifier(TeleTrusTAlgorithm + ".3.1"); + + public static readonly DerObjectIdentifier RsaSignatureWithRipeMD160 = new DerObjectIdentifier(TeleTrusTRsaSignatureAlgorithm + ".2"); + public static readonly DerObjectIdentifier RsaSignatureWithRipeMD128 = new DerObjectIdentifier(TeleTrusTRsaSignatureAlgorithm + ".3"); + public static readonly DerObjectIdentifier RsaSignatureWithRipeMD256 = new DerObjectIdentifier(TeleTrusTRsaSignatureAlgorithm + ".4"); + + public static readonly DerObjectIdentifier ECSign = new DerObjectIdentifier(TeleTrusTAlgorithm + ".3.2"); + + public static readonly DerObjectIdentifier ECSignWithSha1 = new DerObjectIdentifier(ECSign + ".1"); + public static readonly DerObjectIdentifier ECSignWithRipeMD160 = new DerObjectIdentifier(ECSign + ".2"); + + public static readonly DerObjectIdentifier EccBrainpool = new DerObjectIdentifier(TeleTrusTAlgorithm + ".3.2.8"); + public static readonly DerObjectIdentifier EllipticCurve = new DerObjectIdentifier(EccBrainpool + ".1"); + public static readonly DerObjectIdentifier VersionOne = new DerObjectIdentifier(EllipticCurve + ".1"); + + public static readonly DerObjectIdentifier BrainpoolP160R1 = new DerObjectIdentifier(VersionOne + ".1"); + public static readonly DerObjectIdentifier BrainpoolP160T1 = new DerObjectIdentifier(VersionOne + ".2"); + public static readonly DerObjectIdentifier BrainpoolP192R1 = new DerObjectIdentifier(VersionOne + ".3"); + public static readonly DerObjectIdentifier BrainpoolP192T1 = new DerObjectIdentifier(VersionOne + ".4"); + public static readonly DerObjectIdentifier BrainpoolP224R1 = new DerObjectIdentifier(VersionOne + ".5"); + public static readonly DerObjectIdentifier BrainpoolP224T1 = new DerObjectIdentifier(VersionOne + ".6"); + public static readonly DerObjectIdentifier BrainpoolP256R1 = new DerObjectIdentifier(VersionOne + ".7"); + public static readonly DerObjectIdentifier BrainpoolP256T1 = new DerObjectIdentifier(VersionOne + ".8"); + public static readonly DerObjectIdentifier BrainpoolP320R1 = new DerObjectIdentifier(VersionOne + ".9"); + public static readonly DerObjectIdentifier BrainpoolP320T1 = new DerObjectIdentifier(VersionOne + ".10"); + public static readonly DerObjectIdentifier BrainpoolP384R1 = new DerObjectIdentifier(VersionOne + ".11"); + public static readonly DerObjectIdentifier BrainpoolP384T1 = new DerObjectIdentifier(VersionOne + ".12"); + public static readonly DerObjectIdentifier BrainpoolP512R1 = new DerObjectIdentifier(VersionOne + ".13"); + public static readonly DerObjectIdentifier BrainpoolP512T1 = new DerObjectIdentifier(VersionOne + ".14"); + } +} diff --git a/Crypto/src/asn1/tsp/Accuracy.cs b/Crypto/src/asn1/tsp/Accuracy.cs new file mode 100644
index 000000000..a193f52ff --- /dev/null +++ b/Crypto/src/asn1/tsp/Accuracy.cs
@@ -0,0 +1,149 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Tsp +{ + public class Accuracy + : Asn1Encodable + { + private readonly DerInteger seconds; + private readonly DerInteger millis; + private readonly DerInteger micros; + + // constants + protected const int MinMillis = 1; + protected const int MaxMillis = 999; + protected const int MinMicros = 1; + protected const int MaxMicros = 999; + + public Accuracy( + DerInteger seconds, + DerInteger millis, + DerInteger micros) + { + //Verifications + if (millis != null + && (millis.Value.IntValue < MinMillis + || millis.Value.IntValue > MaxMillis)) + { + throw new ArgumentException( + "Invalid millis field : not in (1..999)"); + } + + if (micros != null + && (micros.Value.IntValue < MinMicros + || micros.Value.IntValue > MaxMicros)) + { + throw new ArgumentException( + "Invalid micros field : not in (1..999)"); + } + + this.seconds = seconds; + this.millis = millis; + this.micros = micros; + } + + private Accuracy( + Asn1Sequence seq) + { + for (int i = 0; i < seq.Count; ++i) + { + // seconds + if (seq[i] is DerInteger) + { + seconds = (DerInteger) seq[i]; + } + else if (seq[i] is DerTaggedObject) + { + DerTaggedObject extra = (DerTaggedObject) seq[i]; + + switch (extra.TagNo) + { + case 0: + millis = DerInteger.GetInstance(extra, false); + if (millis.Value.IntValue < MinMillis + || millis.Value.IntValue > MaxMillis) + { + throw new ArgumentException( + "Invalid millis field : not in (1..999)."); + } + break; + case 1: + micros = DerInteger.GetInstance(extra, false); + if (micros.Value.IntValue < MinMicros + || micros.Value.IntValue > MaxMicros) + { + throw new ArgumentException( + "Invalid micros field : not in (1..999)."); + } + break; + default: + throw new ArgumentException("Invalig tag number"); + } + } + } + } + + public static Accuracy GetInstance( + object o) + { + if (o == null || o is Accuracy) + { + return (Accuracy) o; + } + + if (o is Asn1Sequence) + { + return new Accuracy((Asn1Sequence) o); + } + + throw new ArgumentException( + "Unknown object in 'Accuracy' factory: " + o.GetType().FullName); + } + + public DerInteger Seconds + { + get { return seconds; } + } + + public DerInteger Millis + { + get { return millis; } + } + + public DerInteger Micros + { + get { return micros; } + } + + /** + * <pre> + * Accuracy ::= SEQUENCE { + * seconds INTEGER OPTIONAL, + * millis [0] INTEGER (1..999) OPTIONAL, + * micros [1] INTEGER (1..999) OPTIONAL + * } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + 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/Crypto/src/asn1/tsp/MessageImprint.cs b/Crypto/src/asn1/tsp/MessageImprint.cs new file mode 100644
index 000000000..0933bae21 --- /dev/null +++ b/Crypto/src/asn1/tsp/MessageImprint.cs
@@ -0,0 +1,74 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Tsp +{ + public class MessageImprint + : Asn1Encodable + { + private readonly AlgorithmIdentifier hashAlgorithm; + private readonly byte[] hashedMessage; + + /** + * @param o + * @return a MessageImprint object. + */ + public static MessageImprint GetInstance( + object o) + { + if (o == null || o is MessageImprint) + { + return (MessageImprint) o; + } + + if (o is Asn1Sequence) + { + return new MessageImprint((Asn1Sequence) o); + } + + throw new ArgumentException( + "Unknown object in 'MessageImprint' factory: " + o.GetType().FullName); + } + + private MessageImprint( + Asn1Sequence seq) + { + if (seq.Count != 2) + throw new ArgumentException("Wrong number of elements in sequence", "seq"); + + this.hashAlgorithm = AlgorithmIdentifier.GetInstance(seq[0]); + this.hashedMessage = Asn1OctetString.GetInstance(seq[1]).GetOctets(); + } + + public MessageImprint( + AlgorithmIdentifier hashAlgorithm, + byte[] hashedMessage) + { + this.hashAlgorithm = hashAlgorithm; + this.hashedMessage = hashedMessage; + } + + public AlgorithmIdentifier HashAlgorithm + { + get { return hashAlgorithm; } + } + + public byte[] GetHashedMessage() + { + return hashedMessage; + } + + /** + * <pre> + * MessageImprint ::= SEQUENCE { + * hashAlgorithm AlgorithmIdentifier, + * hashedMessage OCTET STRING } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + return new DerSequence(hashAlgorithm, new DerOctetString(hashedMessage)); + } + } +} diff --git a/Crypto/src/asn1/tsp/TSTInfo.cs b/Crypto/src/asn1/tsp/TSTInfo.cs new file mode 100644
index 000000000..61d5399c7 --- /dev/null +++ b/Crypto/src/asn1/tsp/TSTInfo.cs
@@ -0,0 +1,249 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Tsp +{ + public class TstInfo + : Asn1Encodable + { + private readonly DerInteger version; + private readonly DerObjectIdentifier tsaPolicyId; + private readonly MessageImprint messageImprint; + private readonly DerInteger serialNumber; + private readonly DerGeneralizedTime genTime; + private readonly Accuracy accuracy; + private readonly DerBoolean ordering; + private readonly DerInteger nonce; + private readonly GeneralName tsa; + private readonly X509Extensions extensions; + + public static TstInfo GetInstance( + object o) + { + if (o == null || o is TstInfo) + { + return (TstInfo) o; + } + + if (o is Asn1Sequence) + { + return new TstInfo((Asn1Sequence) o); + } + + if (o is Asn1OctetString) + { + try + { + byte[] octets = ((Asn1OctetString)o).GetOctets(); + return GetInstance(Asn1Object.FromByteArray(octets)); + } + catch (IOException) + { + throw new ArgumentException( + "Bad object format in 'TstInfo' factory."); + } + } + + throw new ArgumentException( + "Unknown object in 'TstInfo' factory: " + o.GetType().FullName); + } + + private TstInfo( + Asn1Sequence seq) + { + IEnumerator e = seq.GetEnumerator(); + + // version + e.MoveNext(); + version = DerInteger.GetInstance(e.Current); + + // tsaPolicy + e.MoveNext(); + tsaPolicyId = DerObjectIdentifier.GetInstance(e.Current); + + // messageImprint + e.MoveNext(); + messageImprint = MessageImprint.GetInstance(e.Current); + + // serialNumber + e.MoveNext(); + serialNumber = DerInteger.GetInstance(e.Current); + + // genTime + e.MoveNext(); + genTime = DerGeneralizedTime.GetInstance(e.Current); + + // default for ordering + ordering = DerBoolean.False; + + while (e.MoveNext()) + { + Asn1Object o = (Asn1Object) e.Current; + + if (o is Asn1TaggedObject) + { + DerTaggedObject tagged = (DerTaggedObject) o; + + switch (tagged.TagNo) + { + case 0: + tsa = GeneralName.GetInstance(tagged, true); + break; + case 1: + extensions = X509Extensions.GetInstance(tagged, false); + break; + default: + throw new ArgumentException("Unknown tag value " + tagged.TagNo); + } + } + + if (o is DerSequence) + { + accuracy = Accuracy.GetInstance(o); + } + + if (o is DerBoolean) + { + ordering = DerBoolean.GetInstance(o); + } + + if (o is DerInteger) + { + nonce = DerInteger.GetInstance(o); + } + } + } + + public TstInfo( + DerObjectIdentifier tsaPolicyId, + MessageImprint messageImprint, + DerInteger serialNumber, + DerGeneralizedTime genTime, + Accuracy accuracy, + DerBoolean ordering, + DerInteger nonce, + GeneralName tsa, + X509Extensions extensions) + { + this.version = new DerInteger(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 DerInteger Version + { + get { return version; } + } + + public MessageImprint MessageImprint + { + get { return messageImprint; } + } + + public DerObjectIdentifier Policy + { + get { return tsaPolicyId; } + } + + public DerInteger SerialNumber + { + get { return serialNumber; } + } + + public Accuracy Accuracy + { + get { return accuracy; } + } + + public DerGeneralizedTime GenTime + { + get { return genTime; } + } + + public DerBoolean Ordering + { + get { return ordering; } + } + + public DerInteger Nonce + { + get { return nonce; } + } + + public GeneralName Tsa + { + get { return tsa; } + } + + public X509Extensions Extensions + { + get { 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector( + version, tsaPolicyId, messageImprint, serialNumber, genTime); + + if (accuracy != null) + { + v.Add(accuracy); + } + + if (ordering != null && ordering.IsTrue) + { + v.Add(ordering); + } + + if (nonce != null) + { + v.Add(nonce); + } + + if (tsa != null) + { + v.Add(new DerTaggedObject(true, 0, tsa)); + } + + if (extensions != null) + { + v.Add(new DerTaggedObject(false, 1, extensions)); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/tsp/TimeStampReq.cs b/Crypto/src/asn1/tsp/TimeStampReq.cs new file mode 100644
index 000000000..55e973e76 --- /dev/null +++ b/Crypto/src/asn1/tsp/TimeStampReq.cs
@@ -0,0 +1,164 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.Tsp +{ + public class TimeStampReq + : Asn1Encodable + { + private readonly DerInteger version; + private readonly MessageImprint messageImprint; + private readonly DerObjectIdentifier tsaPolicy; + private readonly DerInteger nonce; + private readonly DerBoolean certReq; + private readonly X509Extensions extensions; + + public static TimeStampReq GetInstance( + object o) + { + if (o == null || o is TimeStampReq) + { + return (TimeStampReq) o; + } + + if (o is Asn1Sequence) + { + return new TimeStampReq((Asn1Sequence) o); + } + + throw new ArgumentException( + "Unknown object in 'TimeStampReq' factory: " + o.GetType().FullName); + } + + private TimeStampReq( + Asn1Sequence seq) + { + int nbObjects = seq.Count; + int seqStart = 0; + + // version + version = DerInteger.GetInstance(seq[seqStart++]); + + // messageImprint + messageImprint = MessageImprint.GetInstance(seq[seqStart++]); + + for (int opt = seqStart; opt < nbObjects; opt++) + { + // tsaPolicy + if (seq[opt] is DerObjectIdentifier) + { + tsaPolicy = DerObjectIdentifier.GetInstance(seq[opt]); + } + // nonce + else if (seq[opt] is DerInteger) + { + nonce = DerInteger.GetInstance(seq[opt]); + } + // certReq + else if (seq[opt] is DerBoolean) + { + certReq = DerBoolean.GetInstance(seq[opt]); + } + // extensions + else if (seq[opt] is Asn1TaggedObject) + { + Asn1TaggedObject tagged = (Asn1TaggedObject) seq[opt]; + if (tagged.TagNo == 0) + { + extensions = X509Extensions.GetInstance(tagged, false); + } + } + } + } + + public TimeStampReq( + MessageImprint messageImprint, + DerObjectIdentifier tsaPolicy, + DerInteger nonce, + DerBoolean certReq, + X509Extensions extensions) + { + // default + this.version = new DerInteger(1); + + this.messageImprint = messageImprint; + this.tsaPolicy = tsaPolicy; + this.nonce = nonce; + this.certReq = certReq; + this.extensions = extensions; + } + + public DerInteger Version + { + get { return version; } + } + + public MessageImprint MessageImprint + { + get { return messageImprint; } + } + + public DerObjectIdentifier ReqPolicy + { + get { return tsaPolicy; } + } + + public DerInteger Nonce + { + get { return nonce; } + } + + public DerBoolean CertReq + { + get { return certReq; } + } + + public X509Extensions Extensions + { + get { 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector( + version, 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/Crypto/src/asn1/tsp/TimeStampResp.cs b/Crypto/src/asn1/tsp/TimeStampResp.cs new file mode 100644
index 000000000..f26fb30bd --- /dev/null +++ b/Crypto/src/asn1/tsp/TimeStampResp.cs
@@ -0,0 +1,80 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1.Cmp; +using Org.BouncyCastle.Asn1.Cms; + +namespace Org.BouncyCastle.Asn1.Tsp +{ + public class TimeStampResp + : Asn1Encodable + { + private readonly PkiStatusInfo pkiStatusInfo; + private readonly ContentInfo timeStampToken; + + public static TimeStampResp GetInstance( + object o) + { + if (o == null || o is TimeStampResp) + { + return (TimeStampResp) o; + } + + if (o is Asn1Sequence) + { + return new TimeStampResp((Asn1Sequence) o); + } + + throw new ArgumentException( + "Unknown object in 'TimeStampResp' factory: " + o.GetType().FullName); + } + + private TimeStampResp( + Asn1Sequence seq) + { + this.pkiStatusInfo = PkiStatusInfo.GetInstance(seq[0]); + + if (seq.Count > 1) + { + this.timeStampToken = ContentInfo.GetInstance(seq[1]); + } + } + + public TimeStampResp( + PkiStatusInfo pkiStatusInfo, + ContentInfo timeStampToken) + { + this.pkiStatusInfo = pkiStatusInfo; + this.timeStampToken = timeStampToken; + } + + public PkiStatusInfo Status + { + get { return pkiStatusInfo; } + } + + public ContentInfo TimeStampToken + { + get { return timeStampToken; } + } + + /** + * <pre> + * TimeStampResp ::= SEQUENCE { + * status PkiStatusInfo, + * timeStampToken TimeStampToken OPTIONAL } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(pkiStatusInfo); + + if (timeStampToken != null) + { + v.Add(timeStampToken); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/util/Asn1Dump.cs b/Crypto/src/asn1/util/Asn1Dump.cs new file mode 100644
index 000000000..43d485500 --- /dev/null +++ b/Crypto/src/asn1/util/Asn1Dump.cs
@@ -0,0 +1,378 @@ +using System; +using System.Collections; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Asn1.Utilities +{ + public sealed class Asn1Dump + { + private static readonly string NewLine = Platform.NewLine; + + private Asn1Dump() + { + } + + private const string Tab = " "; + private const int SampleSize = 32; + + /** + * dump a Der object as a formatted string with indentation + * + * @param obj the Asn1Object to be dumped out. + */ + private static void AsString( + string indent, + bool verbose, + Asn1Object obj, + StringBuilder buf) + { + if (obj is Asn1Sequence) + { + string tab = indent + Tab; + buf.Append(indent); + if (obj is BerSequence) + { + buf.Append("BER Sequence"); + } + else if (obj is DerSequence) + { + buf.Append("DER Sequence"); + } + else + { + buf.Append("Sequence"); + } + + buf.Append(NewLine); + + foreach (Asn1Encodable o in ((Asn1Sequence)obj)) + { + if (o == null || o is Asn1Null) + { + buf.Append(tab); + buf.Append("NULL"); + buf.Append(NewLine); + } + else + { + AsString(tab, verbose, o.ToAsn1Object(), buf); + } + } + } + else if (obj is DerTaggedObject) + { + string tab = indent + Tab; + buf.Append(indent); + if (obj is BerTaggedObject) + { + buf.Append("BER Tagged ["); + } + else + { + buf.Append("Tagged ["); + } + + DerTaggedObject o = (DerTaggedObject)obj; + + buf.Append(((int)o.TagNo).ToString()); + buf.Append(']'); + + if (!o.IsExplicit()) + { + buf.Append(" IMPLICIT "); + } + + buf.Append(NewLine); + + if (o.IsEmpty()) + { + buf.Append(tab); + buf.Append("EMPTY"); + buf.Append(NewLine); + } + else + { + AsString(tab, verbose, o.GetObject(), buf); + } + } + else if (obj is BerSet) + { + string tab = indent + Tab; + + buf.Append(indent); + buf.Append("BER Set"); + buf.Append(NewLine); + + foreach (Asn1Encodable o in ((Asn1Set)obj)) + { + if (o == null) + { + buf.Append(tab); + buf.Append("NULL"); + buf.Append(NewLine); + } + else + { + AsString(tab, verbose, o.ToAsn1Object(), buf); + } + } + } + else if (obj is DerSet) + { + string tab = indent + Tab; + + buf.Append(indent); + buf.Append("DER Set"); + buf.Append(NewLine); + + foreach (Asn1Encodable o in ((Asn1Set)obj)) + { + if (o == null) + { + buf.Append(tab); + buf.Append("NULL"); + buf.Append(NewLine); + } + else + { + AsString(tab, verbose, o.ToAsn1Object(), buf); + } + } + } + else if (obj is DerObjectIdentifier) + { + buf.Append(indent + "ObjectIdentifier(" + ((DerObjectIdentifier)obj).Id + ")" + NewLine); + } + else if (obj is DerBoolean) + { + buf.Append(indent + "Boolean(" + ((DerBoolean)obj).IsTrue + ")" + NewLine); + } + else if (obj is DerInteger) + { + buf.Append(indent + "Integer(" + ((DerInteger)obj).Value + ")" + NewLine); + } + else if (obj is BerOctetString) + { + byte[] octets = ((Asn1OctetString)obj).GetOctets(); + string extra = verbose ? dumpBinaryDataAsString(indent, octets) : ""; + buf.Append(indent + "BER Octet String" + "[" + octets.Length + "] " + extra + NewLine); + } + else if (obj is DerOctetString) + { + byte[] octets = ((Asn1OctetString)obj).GetOctets(); + string extra = verbose ? dumpBinaryDataAsString(indent, octets) : ""; + buf.Append(indent + "DER Octet String" + "[" + octets.Length + "] " + extra + NewLine); + } + else if (obj is DerBitString) + { + DerBitString bt = (DerBitString)obj; + byte[] bytes = bt.GetBytes(); + string extra = verbose ? dumpBinaryDataAsString(indent, bytes) : ""; + buf.Append(indent + "DER Bit String" + "[" + bytes.Length + ", " + bt.PadBits + "] " + extra + NewLine); + } + else if (obj is DerIA5String) + { + buf.Append(indent + "IA5String(" + ((DerIA5String)obj).GetString() + ") " + NewLine); + } + else if (obj is DerUtf8String) + { + buf.Append(indent + "UTF8String(" + ((DerUtf8String)obj).GetString() + ") " + NewLine); + } + else if (obj is DerPrintableString) + { + buf.Append(indent + "PrintableString(" + ((DerPrintableString)obj).GetString() + ") " + NewLine); + } + else if (obj is DerVisibleString) + { + buf.Append(indent + "VisibleString(" + ((DerVisibleString)obj).GetString() + ") " + NewLine); + } + else if (obj is DerBmpString) + { + buf.Append(indent + "BMPString(" + ((DerBmpString)obj).GetString() + ") " + NewLine); + } + else if (obj is DerT61String) + { + buf.Append(indent + "T61String(" + ((DerT61String)obj).GetString() + ") " + NewLine); + } + else if (obj is DerUtcTime) + { + buf.Append(indent + "UTCTime(" + ((DerUtcTime)obj).TimeString + ") " + NewLine); + } + else if (obj is DerGeneralizedTime) + { + buf.Append(indent + "GeneralizedTime(" + ((DerGeneralizedTime)obj).GetTime() + ") " + NewLine); + } + else if (obj is DerUnknownTag) + { + string hex = Hex.ToHexString(((DerUnknownTag)obj).GetData()); + buf.Append(indent + "Unknown " + ((int)((DerUnknownTag)obj).Tag).ToString("X") + " " + hex + NewLine); + } + else if (obj is BerApplicationSpecific) + { + buf.Append(outputApplicationSpecific("BER", indent, verbose, (BerApplicationSpecific)obj)); + } + else if (obj is DerApplicationSpecific) + { + buf.Append(outputApplicationSpecific("DER", indent, verbose, (DerApplicationSpecific)obj)); + } + else if (obj is DerEnumerated) + { + DerEnumerated en = (DerEnumerated)obj; + buf.Append(indent + "DER Enumerated(" + en.Value + ")" + NewLine); + } + else if (obj is DerExternal) + { + DerExternal ext = (DerExternal)obj; + buf.Append(indent + "External " + NewLine); + string tab = indent + Tab; + + if (ext.DirectReference != null) + { + buf.Append(tab + "Direct Reference: " + ext.DirectReference.Id + NewLine); + } + if (ext.IndirectReference != null) + { + buf.Append(tab + "Indirect Reference: " + ext.IndirectReference.ToString() + NewLine); + } + if (ext.DataValueDescriptor != null) + { + AsString(tab, verbose, ext.DataValueDescriptor, buf); + } + buf.Append(tab + "Encoding: " + ext.Encoding + NewLine); + AsString(tab, verbose, ext.ExternalContent, buf); + } + else + { + buf.Append(indent + obj.ToString() + NewLine); + } + } + + private static string outputApplicationSpecific( + string type, + string indent, + bool verbose, + DerApplicationSpecific app) + { + StringBuilder buf = new StringBuilder(); + + if (app.IsConstructed()) + { + try + { + Asn1Sequence s = Asn1Sequence.GetInstance(app.GetObject(Asn1Tags.Sequence)); + buf.Append(indent + type + " ApplicationSpecific[" + app.ApplicationTag + "]" + NewLine); + foreach (Asn1Encodable ae in s) + { + AsString(indent + Tab, verbose, ae.ToAsn1Object(), buf); + } + } + catch (IOException e) + { + buf.Append(e); + } + return buf.ToString(); + } + + return indent + type + " ApplicationSpecific[" + app.ApplicationTag + "] (" + + Hex.ToHexString(app.GetContents()) + ")" + NewLine; + } + + [Obsolete("Use version accepting Asn1Encodable")] + public static string DumpAsString( + object obj) + { + if (obj is Asn1Encodable) + { + StringBuilder buf = new StringBuilder(); + AsString("", false, ((Asn1Encodable)obj).ToAsn1Object(), buf); + return buf.ToString(); + } + + return "unknown object type " + obj.ToString(); + } + + /** + * dump out a DER object as a formatted string, in non-verbose mode + * + * @param obj the Asn1Encodable to be dumped out. + * @return the resulting string. + */ + public static string DumpAsString( + Asn1Encodable obj) + { + return DumpAsString(obj, false); + } + + /** + * Dump out the object as a string + * + * @param obj the Asn1Encodable to be dumped out. + * @param verbose if true, dump out the contents of octet and bit strings. + * @return the resulting string. + */ + public static string DumpAsString( + Asn1Encodable obj, + bool verbose) + { + StringBuilder buf = new StringBuilder(); + AsString("", verbose, obj.ToAsn1Object(), buf); + return buf.ToString(); + } + + private static string dumpBinaryDataAsString(string indent, byte[] bytes) + { + indent += Tab; + + StringBuilder buf = new StringBuilder(NewLine); + + for (int i = 0; i < bytes.Length; i += SampleSize) + { + if (bytes.Length - i > SampleSize) + { + buf.Append(indent); + buf.Append(Hex.ToHexString(bytes, i, SampleSize)); + buf.Append(Tab); + buf.Append(calculateAscString(bytes, i, SampleSize)); + buf.Append(NewLine); + } + else + { + buf.Append(indent); + buf.Append(Hex.ToHexString(bytes, i, bytes.Length - i)); + for (int j = bytes.Length - i; j != SampleSize; j++) + { + buf.Append(" "); + } + buf.Append(Tab); + buf.Append(calculateAscString(bytes, i, bytes.Length - i)); + buf.Append(NewLine); + } + } + + return buf.ToString(); + } + + private static string calculateAscString( + byte[] bytes, + int off, + int len) + { + StringBuilder buf = new StringBuilder(); + + for (int i = off; i != off + len; i++) + { + char c = (char)bytes[i]; + if (c >= ' ' && c <= '~') + { + buf.Append(c); + } + } + + return buf.ToString(); + } + } +} diff --git a/Crypto/src/asn1/util/Dump.cs b/Crypto/src/asn1/util/Dump.cs new file mode 100644
index 000000000..2712ca1b3 --- /dev/null +++ b/Crypto/src/asn1/util/Dump.cs
@@ -0,0 +1,30 @@ +#if !PORTABLE +using Org.BouncyCastle.Asn1; + +using System; +using System.IO; + +namespace Org.BouncyCastle.Asn1.Utilities +{ + public sealed class Dump + { + private Dump() + { + } + + public static void Main(string[] args) + { + FileStream fIn = File.OpenRead(args[0]); + Asn1InputStream bIn = new Asn1InputStream(fIn); + + Asn1Object obj; + while ((obj = bIn.ReadObject()) != null) + { + Console.WriteLine(Asn1Dump.DumpAsString(obj)); + } + + bIn.Dispose(); + } + } +} +#endif \ No newline at end of file diff --git a/Crypto/src/asn1/util/FilterStream.cs b/Crypto/src/asn1/util/FilterStream.cs new file mode 100644
index 000000000..f277e33e6 --- /dev/null +++ b/Crypto/src/asn1/util/FilterStream.cs
@@ -0,0 +1,72 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Asn1.Utilities +{ + public class FilterStream : Stream + { + public FilterStream(Stream s) + { + this.s = s; + } + public override bool CanRead + { + get { return s.CanRead; } + } + public override bool CanSeek + { + get { return s.CanSeek; } + } + public override bool CanWrite + { + get { return s.CanWrite; } + } + public override long Length + { + get { return s.Length; } + } + public override long Position + { + get { return s.Position; } + set { s.Position = value; } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + s.Dispose(); + } + } + + public override void Flush() + { + s.Flush(); + } + public override long Seek(long offset, SeekOrigin origin) + { + return s.Seek(offset, origin); + } + public override void SetLength(long value) + { + s.SetLength(value); + } + public override int Read(byte[] buffer, int offset, int count) + { + return s.Read(buffer, offset, count); + } + public override int ReadByte() + { + return s.ReadByte(); + } + public override void Write(byte[] buffer, int offset, int count) + { + s.Write(buffer, offset, count); + } + public override void WriteByte(byte value) + { + s.WriteByte(value); + } + protected readonly Stream s; + } +} diff --git a/Crypto/src/asn1/x500/DirectoryString.cs b/Crypto/src/asn1/x500/DirectoryString.cs new file mode 100644
index 000000000..78ecc2663 --- /dev/null +++ b/Crypto/src/asn1/x500/DirectoryString.cs
@@ -0,0 +1,75 @@ +using System; + +namespace Org.BouncyCastle.Asn1.X500 +{ + public class DirectoryString + : Asn1Encodable, IAsn1Choice, IAsn1String + { + private readonly DerStringBase str; + + public static DirectoryString GetInstance( + object obj) + { + if (obj is DirectoryString) + { + return (DirectoryString) obj; + } + + if (obj is DerStringBase) + { + if (obj is DerT61String + || obj is DerPrintableString + || obj is DerUniversalString + || obj is DerUtf8String + || obj is DerBmpString) + { + return new DirectoryString((DerStringBase) obj); + } + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public static DirectoryString GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + if (!isExplicit) + throw new ArgumentException("choice item must be explicitly tagged"); + + return GetInstance(obj.GetObject()); + } + + private DirectoryString( + DerStringBase str) + { + this.str = str; + } + + public DirectoryString( + string str) + { + this.str = new DerUtf8String(str); + } + + public string GetString() + { + return str.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 override Asn1Object ToAsn1Object() + { + return str.ToAsn1Object(); + } + } +} diff --git a/Crypto/src/asn1/x509/AccessDescription.cs b/Crypto/src/asn1/x509/AccessDescription.cs new file mode 100644
index 000000000..09b5b5920 --- /dev/null +++ b/Crypto/src/asn1/x509/AccessDescription.cs
@@ -0,0 +1,83 @@ +using System; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * The AccessDescription object. + * <pre> + * AccessDescription ::= SEQUENCE { + * accessMethod OBJECT IDENTIFIER, + * accessLocation GeneralName } + * </pre> + */ + public class AccessDescription + : Asn1Encodable + { + public readonly static DerObjectIdentifier IdADCAIssuers = new DerObjectIdentifier("1.3.6.1.5.5.7.48.2"); + public readonly static DerObjectIdentifier IdADOcsp = new DerObjectIdentifier("1.3.6.1.5.5.7.48.1"); + + private readonly DerObjectIdentifier accessMethod; + private readonly GeneralName accessLocation; + + public static AccessDescription GetInstance( + object obj) + { + if (obj is AccessDescription) + return (AccessDescription) obj; + + if (obj is Asn1Sequence) + return new AccessDescription((Asn1Sequence) obj); + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + private AccessDescription( + Asn1Sequence seq) + { + if (seq.Count != 2) + throw new ArgumentException("wrong number of elements in sequence"); + + accessMethod = DerObjectIdentifier.GetInstance(seq[0]); + accessLocation = GeneralName.GetInstance(seq[1]); + } + + /** + * create an AccessDescription with the oid and location provided. + */ + public AccessDescription( + DerObjectIdentifier oid, + GeneralName location) + { + accessMethod = oid; + accessLocation = location; + } + + /** + * + * @return the access method. + */ + public DerObjectIdentifier AccessMethod + { + get { return accessMethod; } + } + + /** + * + * @return the access location + */ + public GeneralName AccessLocation + { + get { return accessLocation; } + } + + public override Asn1Object ToAsn1Object() + { + return new DerSequence(accessMethod, accessLocation); + } + + public override string ToString() + { + return "AccessDescription: Oid(" + this.accessMethod.Id + ")"; + } + } +} diff --git a/Crypto/src/asn1/x509/AlgorithmIdentifier.cs b/Crypto/src/asn1/x509/AlgorithmIdentifier.cs new file mode 100644
index 000000000..caf9e77c9 --- /dev/null +++ b/Crypto/src/asn1/x509/AlgorithmIdentifier.cs
@@ -0,0 +1,110 @@ +using System; + +namespace Org.BouncyCastle.Asn1.X509 +{ + public class AlgorithmIdentifier + : Asn1Encodable + { + private readonly DerObjectIdentifier objectID; + private readonly Asn1Encodable parameters; + private readonly bool parametersDefined; + + public static AlgorithmIdentifier GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static AlgorithmIdentifier GetInstance( + object obj) + { + if (obj == null || obj is AlgorithmIdentifier) + return (AlgorithmIdentifier) obj; + + if (obj is DerObjectIdentifier) + return new AlgorithmIdentifier((DerObjectIdentifier) obj); + + if (obj is string) + return new AlgorithmIdentifier((string) obj); + + if (obj is Asn1Sequence) + return new AlgorithmIdentifier((Asn1Sequence) obj); + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public AlgorithmIdentifier( + DerObjectIdentifier objectID) + { + this.objectID = objectID; + } + + public AlgorithmIdentifier( + string objectID) + { + this.objectID = new DerObjectIdentifier(objectID); + } + + public AlgorithmIdentifier( + DerObjectIdentifier objectID, + Asn1Encodable parameters) + { + this.objectID = objectID; + this.parameters = parameters; + this.parametersDefined = true; + } + + internal AlgorithmIdentifier( + Asn1Sequence seq) + { + if (seq.Count < 1 || seq.Count > 2) + throw new ArgumentException("Bad sequence size: " + seq.Count); + + this.objectID = DerObjectIdentifier.GetInstance(seq[0]); + this.parametersDefined = (seq.Count == 2); + + if (parametersDefined) + { + this.parameters = seq[1]; + } + } + + public virtual DerObjectIdentifier ObjectID + { + get { return objectID; } + } + + public Asn1Encodable Parameters + { + get { return parameters; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * AlgorithmIdentifier ::= Sequence { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(objectID); + + if (parametersDefined) + { + if (parameters != null) + { + v.Add(parameters); + } + else + { + v.Add(DerNull.Instance); + } + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/x509/AttCertIssuer.cs b/Crypto/src/asn1/x509/AttCertIssuer.cs new file mode 100644
index 000000000..e9314fa92 --- /dev/null +++ b/Crypto/src/asn1/x509/AttCertIssuer.cs
@@ -0,0 +1,86 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.X509 +{ + public class AttCertIssuer + : Asn1Encodable, IAsn1Choice + { + internal readonly Asn1Encodable obj; + internal readonly Asn1Object choiceObj; + + public static AttCertIssuer GetInstance( + object obj) + { + if (obj is AttCertIssuer) + { + return (AttCertIssuer)obj; + } + else if (obj is V2Form) + { + return new AttCertIssuer(V2Form.GetInstance(obj)); + } + else if (obj is GeneralNames) + { + return new AttCertIssuer((GeneralNames)obj); + } + else if (obj is Asn1TaggedObject) + { + return new AttCertIssuer(V2Form.GetInstance((Asn1TaggedObject)obj, false)); + } + else if (obj is Asn1Sequence) + { + return new AttCertIssuer(GeneralNames.GetInstance(obj)); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public static AttCertIssuer GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + return GetInstance(obj.GetObject()); // must be explictly tagged + } + + /// <summary> + /// Don't use this one if you are trying to be RFC 3281 compliant. + /// Use it for v1 attribute certificates only. + /// </summary> + /// <param name="names">Our GeneralNames structure</param> + public AttCertIssuer( + GeneralNames names) + { + obj = names; + choiceObj = obj.ToAsn1Object(); + } + + public AttCertIssuer( + V2Form v2Form) + { + obj = v2Form; + choiceObj = new DerTaggedObject(false, 0, obj); + } + + public Asn1Encodable Issuer + { + get { 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 override Asn1Object ToAsn1Object() + { + return choiceObj; + } + } +} diff --git a/Crypto/src/asn1/x509/AttCertValidityPeriod.cs b/Crypto/src/asn1/x509/AttCertValidityPeriod.cs new file mode 100644
index 000000000..7f86cd0b8 --- /dev/null +++ b/Crypto/src/asn1/x509/AttCertValidityPeriod.cs
@@ -0,0 +1,78 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.X509 +{ + public class AttCertValidityPeriod + : Asn1Encodable + { + private readonly DerGeneralizedTime notBeforeTime; + private readonly DerGeneralizedTime notAfterTime; + + public static AttCertValidityPeriod GetInstance( + object obj) + { + if (obj is AttCertValidityPeriod || obj == null) + { + return (AttCertValidityPeriod) obj; + } + + if (obj is Asn1Sequence) + { + return new AttCertValidityPeriod((Asn1Sequence) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public static AttCertValidityPeriod GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + private AttCertValidityPeriod( + Asn1Sequence seq) + { + if (seq.Count != 2) + throw new ArgumentException("Bad sequence size: " + seq.Count); + + notBeforeTime = DerGeneralizedTime.GetInstance(seq[0]); + notAfterTime = DerGeneralizedTime.GetInstance(seq[1]); + } + + public AttCertValidityPeriod( + DerGeneralizedTime notBeforeTime, + DerGeneralizedTime notAfterTime) + { + this.notBeforeTime = notBeforeTime; + this.notAfterTime = notAfterTime; + } + + public DerGeneralizedTime NotBeforeTime + { + get { return notBeforeTime; } + } + + public DerGeneralizedTime NotAfterTime + { + get { return notAfterTime; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * AttCertValidityPeriod ::= Sequence { + * notBeforeTime GeneralizedTime, + * notAfterTime GeneralizedTime + * } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + return new DerSequence(notBeforeTime, notAfterTime); + } + } +} diff --git a/Crypto/src/asn1/x509/Attribute.cs b/Crypto/src/asn1/x509/Attribute.cs new file mode 100644
index 000000000..d26db93e9 --- /dev/null +++ b/Crypto/src/asn1/x509/Attribute.cs
@@ -0,0 +1,82 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.X509 +{ + public class AttributeX509 + : Asn1Encodable + { + private readonly DerObjectIdentifier attrType; + private readonly Asn1Set attrValues; + + /** + * return an Attr object from the given object. + * + * @param o the object we want converted. + * @exception ArgumentException if the object cannot be converted. + */ + public static AttributeX509 GetInstance( + object obj) + { + if (obj == null || obj is AttributeX509) + { + return (AttributeX509) obj; + } + + if (obj is Asn1Sequence) + { + return new AttributeX509((Asn1Sequence) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + private AttributeX509( + Asn1Sequence seq) + { + if (seq.Count != 2) + throw new ArgumentException("Bad sequence size: " + seq.Count); + + attrType = DerObjectIdentifier.GetInstance(seq[0]); + attrValues = Asn1Set.GetInstance(seq[1]); + } + + public AttributeX509( + DerObjectIdentifier attrType, + Asn1Set attrValues) + { + this.attrType = attrType; + this.attrValues = attrValues; + } + + public DerObjectIdentifier AttrType + { + get { return attrType; } + } + + public Asn1Encodable[] GetAttributeValues() + { + return attrValues.ToArray(); + } + + public Asn1Set AttrValues + { + get { return attrValues; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * Attr ::= Sequence { + * attrType OBJECT IDENTIFIER, + * attrValues Set OF AttributeValue + * } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + return new DerSequence(attrType, attrValues); + } + } +} diff --git a/Crypto/src/asn1/x509/AttributeCertificate.cs b/Crypto/src/asn1/x509/AttributeCertificate.cs new file mode 100644
index 000000000..5f85910da --- /dev/null +++ b/Crypto/src/asn1/x509/AttributeCertificate.cs
@@ -0,0 +1,81 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.X509 +{ + public class AttributeCertificate + : Asn1Encodable + { + private readonly AttributeCertificateInfo acinfo; + private readonly AlgorithmIdentifier signatureAlgorithm; + private readonly DerBitString signatureValue; + + /** + * @param obj + * @return + */ + public static AttributeCertificate GetInstance( + object obj) + { + if (obj is AttributeCertificate) + return (AttributeCertificate) obj; + + 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; + } + + private AttributeCertificate( + Asn1Sequence seq) + { + if (seq.Count != 3) + throw new ArgumentException("Bad sequence size: " + seq.Count); + + this.acinfo = AttributeCertificateInfo.GetInstance(seq[0]); + this.signatureAlgorithm = AlgorithmIdentifier.GetInstance(seq[1]); + this.signatureValue = DerBitString.GetInstance(seq[2]); + } + + public AttributeCertificateInfo ACInfo + { + get { return acinfo; } + } + + public AlgorithmIdentifier SignatureAlgorithm + { + get { return signatureAlgorithm; } + } + + public DerBitString SignatureValue + { + get { return signatureValue; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * AttributeCertificate ::= Sequence { + * acinfo AttributeCertificateInfo, + * signatureAlgorithm AlgorithmIdentifier, + * signatureValue BIT STRING + * } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + return new DerSequence(acinfo, signatureAlgorithm, signatureValue); + } + } +} diff --git a/Crypto/src/asn1/x509/AttributeCertificateInfo.cs b/Crypto/src/asn1/x509/AttributeCertificateInfo.cs new file mode 100644
index 000000000..dcef3d472 --- /dev/null +++ b/Crypto/src/asn1/x509/AttributeCertificateInfo.cs
@@ -0,0 +1,156 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.X509 +{ + public class AttributeCertificateInfo + : Asn1Encodable + { + internal readonly DerInteger version; + internal readonly Holder holder; + internal readonly AttCertIssuer issuer; + internal readonly AlgorithmIdentifier signature; + internal readonly DerInteger serialNumber; + internal readonly AttCertValidityPeriod attrCertValidityPeriod; + internal readonly Asn1Sequence attributes; + internal readonly DerBitString issuerUniqueID; + internal readonly X509Extensions extensions; + + public static AttributeCertificateInfo GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit)); + } + + public static AttributeCertificateInfo GetInstance( + object obj) + { + if (obj is AttributeCertificateInfo) + { + return (AttributeCertificateInfo) obj; + } + + if (obj is Asn1Sequence) + { + return new AttributeCertificateInfo((Asn1Sequence) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + private AttributeCertificateInfo( + Asn1Sequence seq) + { + if (seq.Count < 7 || seq.Count > 9) + { + throw new ArgumentException("Bad sequence size: " + seq.Count); + } + + this.version = DerInteger.GetInstance(seq[0]); + this.holder = Holder.GetInstance(seq[1]); + this.issuer = AttCertIssuer.GetInstance(seq[2]); + this.signature = AlgorithmIdentifier.GetInstance(seq[3]); + this.serialNumber = DerInteger.GetInstance(seq[4]); + this.attrCertValidityPeriod = AttCertValidityPeriod.GetInstance(seq[5]); + this.attributes = Asn1Sequence.GetInstance(seq[6]); + + for (int i = 7; i < seq.Count; i++) + { + Asn1Encodable obj = (Asn1Encodable) seq[i]; + + if (obj is DerBitString) + { + this.issuerUniqueID = DerBitString.GetInstance(seq[i]); + } + else if (obj is Asn1Sequence || obj is X509Extensions) + { + this.extensions = X509Extensions.GetInstance(seq[i]); + } + } + } + + public DerInteger Version + { + get { return version; } + } + + public Holder Holder + { + get { return holder; } + } + + public AttCertIssuer Issuer + { + get { return issuer; } + } + + public AlgorithmIdentifier Signature + { + get { return signature; } + } + + public DerInteger SerialNumber + { + get { return serialNumber; } + } + + public AttCertValidityPeriod AttrCertValidityPeriod + { + get { return attrCertValidityPeriod; } + } + + public Asn1Sequence Attributes + { + get { return attributes; } + } + + public DerBitString IssuerUniqueID + { + get { return issuerUniqueID; } + } + + public X509Extensions Extensions + { + get { 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 Attr, + * issuerUniqueID UniqueIdentifier OPTIONAL, + * extensions Extensions OPTIONAL + * } + * + * AttCertVersion ::= Integer { v2(1) } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector( + version, holder, issuer, signature, serialNumber, + attrCertValidityPeriod, attributes); + + if (issuerUniqueID != null) + { + v.Add(issuerUniqueID); + } + + if (extensions != null) + { + v.Add(extensions); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/x509/AttributeTable.cs b/Crypto/src/asn1/x509/AttributeTable.cs new file mode 100644
index 000000000..87b112f01 --- /dev/null +++ b/Crypto/src/asn1/x509/AttributeTable.cs
@@ -0,0 +1,73 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1.X509 +{ + public class AttributeTable + { + private readonly IDictionary attributes; + + public AttributeTable( + IDictionary attrs) + { + this.attributes = Platform.CreateHashtable(attrs); + } + +#if !(SILVERLIGHT || PORTABLE) + [Obsolete] + public AttributeTable( + Hashtable attrs) + { + this.attributes = Platform.CreateHashtable(attrs); + } +#endif + + public AttributeTable( + Asn1EncodableVector v) + { + this.attributes = Platform.CreateHashtable(v.Count); + + for (int i = 0; i != v.Count; i++) + { + AttributeX509 a = AttributeX509.GetInstance(v[i]); + + attributes.Add(a.AttrType, a); + } + } + + public AttributeTable( + Asn1Set s) + { + this.attributes = Platform.CreateHashtable(s.Count); + + for (int i = 0; i != s.Count; i++) + { + AttributeX509 a = AttributeX509.GetInstance(s[i]); + + attributes.Add(a.AttrType, a); + } + } + + public AttributeX509 Get( + DerObjectIdentifier oid) + { + return (AttributeX509) attributes[oid]; + } + +#if !(SILVERLIGHT || PORTABLE) + [Obsolete("Use 'ToDictionary' instead")] + public Hashtable ToHashtable() + { + return new Hashtable(attributes); + } +#endif + + public IDictionary ToDictionary() + { + return Platform.CreateHashtable(attributes); + } + } +} diff --git a/Crypto/src/asn1/x509/AuthorityInformationAccess.cs b/Crypto/src/asn1/x509/AuthorityInformationAccess.cs new file mode 100644
index 000000000..3eeba8cd2 --- /dev/null +++ b/Crypto/src/asn1/x509/AuthorityInformationAccess.cs
@@ -0,0 +1,105 @@ +using System; +using System.Collections; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * 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 + : Asn1Encodable + { + private readonly AccessDescription[] descriptions; + + public static AuthorityInformationAccess GetInstance( + object obj) + { + if (obj is AuthorityInformationAccess) + return (AuthorityInformationAccess) obj; + + if (obj is Asn1Sequence) + return new AuthorityInformationAccess((Asn1Sequence) obj); + + if (obj is X509Extension) + return GetInstance(X509Extension.ConvertValueToObject((X509Extension) obj)); + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + private AuthorityInformationAccess( + Asn1Sequence seq) + { + if (seq.Count < 1) + throw new ArgumentException("sequence may not be empty"); + + this.descriptions = new AccessDescription[seq.Count]; + + for (int i = 0; i < seq.Count; ++i) + { + descriptions[i] = AccessDescription.GetInstance(seq[i]); + } + } + + /** + * create an AuthorityInformationAccess with the oid and location provided. + */ + [Obsolete("Use version taking an AccessDescription instead")] + public AuthorityInformationAccess( + DerObjectIdentifier oid, + GeneralName location) + { + this.descriptions = new AccessDescription[]{ new AccessDescription(oid, location) }; + } + + public AuthorityInformationAccess( + AccessDescription description) + { + this.descriptions = new AccessDescription[]{ description }; + } + + public AccessDescription[] GetAccessDescriptions() + { + return (AccessDescription[]) descriptions.Clone(); + } + + public override Asn1Object ToAsn1Object() + { + return new DerSequence(descriptions); + } + + public override string ToString() + { + StringBuilder buf = new StringBuilder(); + string sep = Platform.NewLine; + + buf.Append("AuthorityInformationAccess:"); + buf.Append(sep); + + foreach (AccessDescription description in descriptions) + { + buf.Append(" "); + buf.Append(description); + buf.Append(sep); + } + + return buf.ToString(); + } + } +} diff --git a/Crypto/src/asn1/x509/AuthorityKeyIdentifier.cs b/Crypto/src/asn1/x509/AuthorityKeyIdentifier.cs new file mode 100644
index 000000000..12ccacfc7 --- /dev/null +++ b/Crypto/src/asn1/x509/AuthorityKeyIdentifier.cs
@@ -0,0 +1,211 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * 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 + : Asn1Encodable + { + internal readonly Asn1OctetString keyidentifier; + internal readonly GeneralNames certissuer; + internal readonly DerInteger certserno; + + public static AuthorityKeyIdentifier GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static AuthorityKeyIdentifier GetInstance( + object obj) + { + if (obj is AuthorityKeyIdentifier) + { + return (AuthorityKeyIdentifier) obj; + } + + if (obj is Asn1Sequence) + { + return new AuthorityKeyIdentifier((Asn1Sequence) obj); + } + + if (obj is X509Extension) + { + return GetInstance(X509Extension.ConvertValueToObject((X509Extension) obj)); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + protected internal AuthorityKeyIdentifier( + Asn1Sequence seq) + { + foreach (Asn1TaggedObject o in seq) + { + switch (o.TagNo) + { + case 0: + this.keyidentifier = Asn1OctetString.GetInstance(o, false); + break; + case 1: + this.certissuer = GeneralNames.GetInstance(o, false); + break; + case 2: + this.certserno = DerInteger.GetInstance(o, false); + break; + default: + throw new ArgumentException("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) + { + IDigest digest = new Sha1Digest(); + byte[] resBuf = new byte[digest.GetDigestSize()]; + + byte[] bytes = spki.PublicKeyData.GetBytes(); + digest.BlockUpdate(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) + { + IDigest digest = new Sha1Digest(); + byte[] resBuf = new byte[digest.GetDigestSize()]; + + byte[] bytes = spki.PublicKeyData.GetBytes(); + digest.BlockUpdate(bytes, 0, bytes.Length); + digest.DoFinal(resBuf, 0); + + this.keyidentifier = new DerOctetString(resBuf); + this.certissuer = name; + this.certserno = new DerInteger(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.ToAsn1Object()); + this.certserno = new DerInteger(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 precomupted 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.ToAsn1Object()); + this.certserno = new DerInteger(serialNumber); + } + + public byte[] GetKeyIdentifier() + { + return keyidentifier == null ? null : keyidentifier.GetOctets(); + } + + public GeneralNames AuthorityCertIssuer + { + get { return certissuer; } + } + + public BigInteger AuthorityCertSerialNumber + { + get { return certserno == null ? null : certserno.Value; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + */ + public override Asn1Object ToAsn1Object() + { + 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 override string ToString() + { + return ("AuthorityKeyIdentifier: KeyID(" + this.keyidentifier.GetOctets() + ")"); + } + } +} diff --git a/Crypto/src/asn1/x509/BasicConstraints.cs b/Crypto/src/asn1/x509/BasicConstraints.cs new file mode 100644
index 000000000..522cb61cc --- /dev/null +++ b/Crypto/src/asn1/x509/BasicConstraints.cs
@@ -0,0 +1,133 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Asn1.X509 +{ + public class BasicConstraints + : Asn1Encodable + { + private readonly DerBoolean cA; + private readonly DerInteger pathLenConstraint; + + public static BasicConstraints GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static BasicConstraints GetInstance( + object obj) + { + if (obj == null || obj is BasicConstraints) + { + return (BasicConstraints) obj; + } + + if (obj is Asn1Sequence) + { + return new BasicConstraints((Asn1Sequence) obj); + } + + if (obj is X509Extension) + { + return GetInstance(X509Extension.ConvertValueToObject((X509Extension) obj)); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + private BasicConstraints( + Asn1Sequence seq) + { + if (seq.Count > 0) + { + if (seq[0] is DerBoolean) + { + this.cA = DerBoolean.GetInstance(seq[0]); + } + else + { + this.pathLenConstraint = DerInteger.GetInstance(seq[0]); + } + + if (seq.Count > 1) + { + if (this.cA == null) + throw new ArgumentException("wrong sequence in constructor", "seq"); + + this.pathLenConstraint = DerInteger.GetInstance(seq[1]); + } + } + } + + public BasicConstraints( + bool cA) + { + if (cA) + { + this.cA = DerBoolean.True; + } + } + + /** + * create a cA=true object for the given path length constraint. + * + * @param pathLenConstraint + */ + public BasicConstraints( + int pathLenConstraint) + { + this.cA = DerBoolean.True; + this.pathLenConstraint = new DerInteger(pathLenConstraint); + } + + public bool IsCA() + { + return cA != null && cA.IsTrue; + } + + public BigInteger PathLenConstraint + { + get { return pathLenConstraint == null ? null : pathLenConstraint.Value; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * BasicConstraints := Sequence { + * cA Boolean DEFAULT FALSE, + * pathLenConstraint Integer (0..MAX) OPTIONAL + * } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + 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 override string ToString() + { + if (pathLenConstraint == null) + { + return "BasicConstraints: isCa(" + this.IsCA() + ")"; + } + + return "BasicConstraints: isCa(" + this.IsCA() + "), pathLenConstraint = " + pathLenConstraint.Value; + } + } +} diff --git a/Crypto/src/asn1/x509/CRLDistPoint.cs b/Crypto/src/asn1/x509/CRLDistPoint.cs new file mode 100644
index 000000000..2b5c19798 --- /dev/null +++ b/Crypto/src/asn1/x509/CRLDistPoint.cs
@@ -0,0 +1,93 @@ +using System; +using System.Text; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1.X509 +{ + public class CrlDistPoint + : Asn1Encodable + { + internal readonly Asn1Sequence seq; + + public static CrlDistPoint GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static CrlDistPoint GetInstance( + object obj) + { + if (obj is CrlDistPoint || obj == null) + { + return (CrlDistPoint) obj; + } + + if (obj is Asn1Sequence) + { + return new CrlDistPoint((Asn1Sequence) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + private CrlDistPoint( + Asn1Sequence seq) + { + this.seq = seq; + } + + public CrlDistPoint( + DistributionPoint[] points) + { + seq = new DerSequence(points); + } + + /** + * Return the distribution points making up the sequence. + * + * @return DistributionPoint[] + */ + public DistributionPoint[] GetDistributionPoints() + { + DistributionPoint[] dp = new DistributionPoint[seq.Count]; + + for (int i = 0; i != seq.Count; ++i) + { + dp[i] = DistributionPoint.GetInstance(seq[i]); + } + + return dp; + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * CrlDistPoint ::= Sequence SIZE {1..MAX} OF DistributionPoint + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + return seq; + } + + public override string ToString() + { + StringBuilder buf = new StringBuilder(); + string sep = Platform.NewLine; + + 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/Crypto/src/asn1/x509/CRLNumber.cs b/Crypto/src/asn1/x509/CRLNumber.cs new file mode 100644
index 000000000..d744416a5 --- /dev/null +++ b/Crypto/src/asn1/x509/CRLNumber.cs
@@ -0,0 +1,30 @@ +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * The CRLNumber object. + * <pre> + * CRLNumber::= Integer(0..MAX) + * </pre> + */ + public class CrlNumber + : DerInteger + { + public CrlNumber( + BigInteger number) + : base(number) + { + } + + public BigInteger Number + { + get { return PositiveValue; } + } + + public override string ToString() + { + return "CRLNumber: " + Number; + } + } +} diff --git a/Crypto/src/asn1/x509/CRLReason.cs b/Crypto/src/asn1/x509/CRLReason.cs new file mode 100644
index 000000000..e8eb53a59 --- /dev/null +++ b/Crypto/src/asn1/x509/CRLReason.cs
@@ -0,0 +1,61 @@ +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * 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 + : DerEnumerated + { + public const int Unspecified = 0; + public const int KeyCompromise = 1; + public const int CACompromise = 2; + public const int AffiliationChanged = 3; + public const int Superseded = 4; + public const int CessationOfOperation = 5; + public const int CertificateHold = 6; + // 7 -> Unknown + public const int RemoveFromCrl = 8; + public const int PrivilegeWithdrawn = 9; + public const int AACompromise = 10; + + private static readonly string[] ReasonString = new string[] + { + "Unspecified", "KeyCompromise", "CACompromise", "AffiliationChanged", + "Superseded", "CessationOfOperation", "CertificateHold", "Unknown", + "RemoveFromCrl", "PrivilegeWithdrawn", "AACompromise" + }; + + public CrlReason( + int reason) + : base(reason) + { + } + + public CrlReason( + DerEnumerated reason) + : base(reason.Value.IntValue) + { + } + + public override string ToString() + { + int reason = Value.IntValue; + string str = (reason < 0 || reason > 10) ? "Invalid" : ReasonString[reason]; + return "CrlReason: " + str; + } + } +} diff --git a/Crypto/src/asn1/x509/CertPolicyId.cs b/Crypto/src/asn1/x509/CertPolicyId.cs new file mode 100644
index 000000000..11cebcdd7 --- /dev/null +++ b/Crypto/src/asn1/x509/CertPolicyId.cs
@@ -0,0 +1,20 @@ +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * CertPolicyId, used in the CertificatePolicies and PolicyMappings + * X509V3 Extensions. + * + * <pre> + * CertPolicyId ::= OBJECT IDENTIFIER + * </pre> + */ + public class CertPolicyID + : DerObjectIdentifier + { + public CertPolicyID( + string id) + : base(id) + { + } + } +} diff --git a/Crypto/src/asn1/x509/CertificateList.cs b/Crypto/src/asn1/x509/CertificateList.cs new file mode 100644
index 000000000..0412e0816 --- /dev/null +++ b/Crypto/src/asn1/x509/CertificateList.cs
@@ -0,0 +1,108 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * 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 + : Asn1Encodable + { + private readonly TbsCertificateList tbsCertList; + private readonly AlgorithmIdentifier sigAlgID; + private readonly DerBitString sig; + + public static CertificateList GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static CertificateList GetInstance( + object obj) + { + if (obj is CertificateList) + return (CertificateList) obj; + + if (obj != null) + return new CertificateList(Asn1Sequence.GetInstance(obj)); + + return null; + } + + private CertificateList( + Asn1Sequence seq) + { + if (seq.Count != 3) + throw new ArgumentException("sequence wrong size for CertificateList", "seq"); + + tbsCertList = TbsCertificateList.GetInstance(seq[0]); + sigAlgID = AlgorithmIdentifier.GetInstance(seq[1]); + sig = DerBitString.GetInstance(seq[2]); + } + + public TbsCertificateList TbsCertList + { + get { return tbsCertList; } + } + + public CrlEntry[] GetRevokedCertificates() + { + return tbsCertList.GetRevokedCertificates(); + } + + public IEnumerable GetRevokedCertificateEnumeration() + { + return tbsCertList.GetRevokedCertificateEnumeration(); + } + + public AlgorithmIdentifier SignatureAlgorithm + { + get { return sigAlgID; } + } + + public DerBitString Signature + { + get { return sig; } + } + + public int Version + { + get { return tbsCertList.Version; } + } + + public X509Name Issuer + { + get { return tbsCertList.Issuer; } + } + + public Time ThisUpdate + { + get { return tbsCertList.ThisUpdate; } + } + + public Time NextUpdate + { + get { return tbsCertList.NextUpdate; } + } + + public override Asn1Object ToAsn1Object() + { + return new DerSequence(tbsCertList, sigAlgID, sig); + } + } +} diff --git a/Crypto/src/asn1/x509/CertificatePair.cs b/Crypto/src/asn1/x509/CertificatePair.cs new file mode 100644
index 000000000..8baa64719 --- /dev/null +++ b/Crypto/src/asn1/x509/CertificatePair.cs
@@ -0,0 +1,160 @@ +using System; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * 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 + : Asn1Encodable + { + private X509CertificateStructure forward, reverse; + + public static CertificatePair GetInstance( + object obj) + { + if (obj == null || obj is CertificatePair) + { + return (CertificatePair) obj; + } + + if (obj is Asn1Sequence) + { + return new CertificatePair((Asn1Sequence) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + /** + * 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.Count != 1 && seq.Count != 2) + { + throw new ArgumentException("Bad sequence size: " + seq.Count, "seq"); + } + + foreach (object obj in seq) + { + Asn1TaggedObject o = Asn1TaggedObject.GetInstance(obj); + if (o.TagNo == 0) + { + forward = X509CertificateStructure.GetInstance(o, true); + } + else if (o.TagNo == 1) + { + reverse = X509CertificateStructure.GetInstance(o, true); + } + else + { + throw new ArgumentException("Bad tag number: " + o.TagNo); + } + } + } + + /** + * Constructor from a given details. + * + * @param forward Certificates issued to this CA. + * @param reverse Certificates issued by this CA to other CAs. + */ + public CertificatePair( + X509CertificateStructure forward, + X509CertificateStructure 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 DERObject + */ + public override Asn1Object ToAsn1Object() + { + 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 X509CertificateStructure Forward + { + get { return forward; } + } + + /** + * @return Returns the reverse. + */ + public X509CertificateStructure Reverse + { + get { return reverse; } + } + } +} diff --git a/Crypto/src/asn1/x509/DSAParameter.cs b/Crypto/src/asn1/x509/DSAParameter.cs new file mode 100644
index 000000000..b2b325f4d --- /dev/null +++ b/Crypto/src/asn1/x509/DSAParameter.cs
@@ -0,0 +1,77 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Asn1.X509 +{ + public class DsaParameter + : Asn1Encodable + { + internal readonly DerInteger p, q, g; + + public static DsaParameter GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static DsaParameter GetInstance( + object obj) + { + if(obj == null || obj is DsaParameter) + { + return (DsaParameter) obj; + } + + if(obj is Asn1Sequence) + { + return new DsaParameter((Asn1Sequence) obj); + } + + throw new ArgumentException("Invalid DsaParameter: " + obj.GetType().Name); + } + + public DsaParameter( + BigInteger p, + BigInteger q, + BigInteger g) + { + this.p = new DerInteger(p); + this.q = new DerInteger(q); + this.g = new DerInteger(g); + } + + private DsaParameter( + Asn1Sequence seq) + { + if (seq.Count != 3) + throw new ArgumentException("Bad sequence size: " + seq.Count, "seq"); + + this.p = DerInteger.GetInstance(seq[0]); + this.q = DerInteger.GetInstance(seq[1]); + this.g = DerInteger.GetInstance(seq[2]); + } + + public BigInteger P + { + get { return p.PositiveValue; } + } + + public BigInteger Q + { + get { return q.PositiveValue; } + } + + public BigInteger G + { + get { return g.PositiveValue; } + } + + public override Asn1Object ToAsn1Object() + { + return new DerSequence(p, q, g); + } + } +} diff --git a/Crypto/src/asn1/x509/DigestInfo.cs b/Crypto/src/asn1/x509/DigestInfo.cs new file mode 100644
index 000000000..1dec227fa --- /dev/null +++ b/Crypto/src/asn1/x509/DigestInfo.cs
@@ -0,0 +1,76 @@ +using System; +using System.Collections; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * The DigestInfo object. + * <pre> + * DigestInfo::=Sequence{ + * digestAlgorithm AlgorithmIdentifier, + * digest OCTET STRING } + * </pre> + */ + public class DigestInfo + : Asn1Encodable + { + private readonly byte[] digest; + private readonly AlgorithmIdentifier algID; + + public static DigestInfo GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static DigestInfo GetInstance( + object obj) + { + if (obj is DigestInfo) + { + return (DigestInfo) obj; + } + + if (obj is Asn1Sequence) + { + return new DigestInfo((Asn1Sequence) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public DigestInfo( + AlgorithmIdentifier algID, + byte[] digest) + { + this.digest = digest; + this.algID = algID; + } + + private DigestInfo( + Asn1Sequence seq) + { + if (seq.Count != 2) + throw new ArgumentException("Wrong number of elements in sequence", "seq"); + + algID = AlgorithmIdentifier.GetInstance(seq[0]); + digest = Asn1OctetString.GetInstance(seq[1]).GetOctets(); + } + + public AlgorithmIdentifier AlgorithmID + { + get { return algID; } + } + + public byte[] GetDigest() + { + return digest; + } + + public override Asn1Object ToAsn1Object() + { + return new DerSequence(algID, new DerOctetString(digest)); + } + } +} diff --git a/Crypto/src/asn1/x509/DisplayText.cs b/Crypto/src/asn1/x509/DisplayText.cs new file mode 100644
index 000000000..699f39031 --- /dev/null +++ b/Crypto/src/asn1/x509/DisplayText.cs
@@ -0,0 +1,172 @@ +using System; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * <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></p> + * @see PolicyQualifierInfo + * @see PolicyInformation + */ + public class DisplayText + : Asn1Encodable, IAsn1Choice + { + /** + * Constant corresponding to ia5String encoding. + * + */ + public const int ContentTypeIA5String = 0; + /** + * Constant corresponding to bmpString encoding. + * + */ + public const int ContentTypeBmpString = 1; + /** + * Constant corresponding to utf8String encoding. + * + */ + public const int ContentTypeUtf8String = 2; + /** + * Constant corresponding to visibleString encoding. + * + */ + public const int ContentTypeVisibleString = 3; + /** + * Describe constant <code>DisplayTextMaximumSize</code> here. + * + */ + public const int DisplayTextMaximumSize = 200; + + internal readonly int contentType; + internal readonly IAsn1String 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 > DisplayTextMaximumSize) + { + // RFC3280 limits these strings to 200 chars + // truncate the string + text = text.Substring(0, DisplayTextMaximumSize); + } + + contentType = type; + switch (type) + { + case ContentTypeIA5String: + contents = (IAsn1String)new DerIA5String (text); + break; + case ContentTypeUtf8String: + contents = (IAsn1String)new DerUtf8String(text); + break; + case ContentTypeVisibleString: + contents = (IAsn1String)new DerVisibleString(text); + break; + case ContentTypeBmpString: + contents = (IAsn1String)new DerBmpString(text); + break; + default: + contents = (IAsn1String)new DerUtf8String(text); + break; + } + } + +// /** +// * return true if the passed in string can be represented without +// * loss as a PrintableString, false otherwise. +// */ +// private bool CanBePrintable( +// string str) +// { +// for (int i = str.Length - 1; i >= 0; i--) +// { +// if (str[i] > 0x007f) +// { +// return false; +// } +// } +// +// return true; +// } + + /** + * 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 > DisplayTextMaximumSize) + { + text = text.Substring(0, DisplayTextMaximumSize); + } + + contentType = ContentTypeUtf8String; + 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 form.</p> + * + * @param contents an <code>Asn1Encodable</code> instance. + */ + public DisplayText( + IAsn1String contents) + { + this.contents = contents; + } + + public static DisplayText GetInstance( + object obj) + { + if (obj is IAsn1String) + { + return new DisplayText((IAsn1String) obj); + } + + if (obj is DisplayText) + { + return (DisplayText) obj; + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public override Asn1Object ToAsn1Object() + { + return (Asn1Object) 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/Crypto/src/asn1/x509/DistributionPoint.cs b/Crypto/src/asn1/x509/DistributionPoint.cs new file mode 100644
index 000000000..ad1d3989e --- /dev/null +++ b/Crypto/src/asn1/x509/DistributionPoint.cs
@@ -0,0 +1,161 @@ +using System; +using System.Text; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * The DistributionPoint object. + * <pre> + * DistributionPoint ::= Sequence { + * distributionPoint [0] DistributionPointName OPTIONAL, + * reasons [1] ReasonFlags OPTIONAL, + * cRLIssuer [2] GeneralNames OPTIONAL + * } + * </pre> + */ + public class DistributionPoint + : Asn1Encodable + { + internal readonly DistributionPointName distributionPoint; + internal readonly ReasonFlags reasons; + internal readonly GeneralNames cRLIssuer; + + public static DistributionPoint GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static DistributionPoint GetInstance( + object obj) + { + if(obj == null || obj is DistributionPoint) + { + return (DistributionPoint) obj; + } + + if(obj is Asn1Sequence) + { + return new DistributionPoint((Asn1Sequence) obj); + } + + throw new ArgumentException("Invalid DistributionPoint: " + obj.GetType().Name); + } + + private DistributionPoint( + Asn1Sequence seq) + { + for (int i = 0; i != seq.Count; i++) + { + Asn1TaggedObject t = Asn1TaggedObject.GetInstance(seq[i]); + + switch (t.TagNo) + { + 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); + break; + } + } + } + + public DistributionPoint( + DistributionPointName distributionPointName, + ReasonFlags reasons, + GeneralNames crlIssuer) + { + this.distributionPoint = distributionPointName; + this.reasons = reasons; + this.cRLIssuer = crlIssuer; + } + + public DistributionPointName DistributionPointName + { + get { return distributionPoint; } + } + + public ReasonFlags Reasons + { + get { return reasons; } + } + + public GeneralNames CrlIssuer + { + get { return cRLIssuer; } + } + + public override Asn1Object ToAsn1Object() + { + 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 override string ToString() + { + string sep = Platform.NewLine; + StringBuilder buf = new StringBuilder(); + 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( + StringBuilder buf, + string sep, + string name, + string val) + { + string indent = " "; + + buf.Append(indent); + buf.Append(name); + buf.Append(":"); + buf.Append(sep); + buf.Append(indent); + buf.Append(indent); + buf.Append(val); + buf.Append(sep); + } + } +} diff --git a/Crypto/src/asn1/x509/DistributionPointName.cs b/Crypto/src/asn1/x509/DistributionPointName.cs new file mode 100644
index 000000000..1a9d24241 --- /dev/null +++ b/Crypto/src/asn1/x509/DistributionPointName.cs
@@ -0,0 +1,130 @@ +using System; +using System.Text; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * The DistributionPointName object. + * <pre> + * DistributionPointName ::= CHOICE { + * fullName [0] GeneralNames, + * nameRelativeToCRLIssuer [1] RDN + * } + * </pre> + */ + public class DistributionPointName + : Asn1Encodable, IAsn1Choice + { + internal readonly Asn1Encodable name; + internal readonly int type; + + public const int FullName = 0; + public const int NameRelativeToCrlIssuer = 1; + + public static DistributionPointName GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1TaggedObject.GetInstance(obj, true)); + } + + public static DistributionPointName GetInstance( + object obj) + { + if (obj == null || obj is DistributionPointName) + { + return (DistributionPointName) obj; + } + + if (obj is Asn1TaggedObject) + { + return new DistributionPointName((Asn1TaggedObject) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public DistributionPointName( + int type, + Asn1Encodable name) + { + this.type = type; + this.name = name; + } + + public DistributionPointName( + GeneralNames name) + : this(FullName, name) + { + } + + public int PointType + { + get { return type; } + } + + public Asn1Encodable Name + { + get { return name; } + } + + public DistributionPointName( + Asn1TaggedObject obj) + { + this.type = obj.TagNo; + + if (type == FullName) + { + this.name = GeneralNames.GetInstance(obj, false); + } + else + { + this.name = Asn1Set.GetInstance(obj, false); + } + } + + public override Asn1Object ToAsn1Object() + { + return new DerTaggedObject(false, type, name); + } + + public override string ToString() + { + string sep = Platform.NewLine; + StringBuilder buf = new StringBuilder(); + buf.Append("DistributionPointName: ["); + buf.Append(sep); + if (type == FullName) + { + appendObject(buf, sep, "fullName", name.ToString()); + } + else + { + appendObject(buf, sep, "nameRelativeToCRLIssuer", name.ToString()); + } + buf.Append("]"); + buf.Append(sep); + return buf.ToString(); + } + + private void appendObject( + StringBuilder buf, + string sep, + string name, + string val) + { + string indent = " "; + + buf.Append(indent); + buf.Append(name); + buf.Append(":"); + buf.Append(sep); + buf.Append(indent); + buf.Append(indent); + buf.Append(val); + buf.Append(sep); + } + } +} diff --git a/Crypto/src/asn1/x509/ExtendedKeyUsage.cs b/Crypto/src/asn1/x509/ExtendedKeyUsage.cs new file mode 100644
index 000000000..3bf79f392 --- /dev/null +++ b/Crypto/src/asn1/x509/ExtendedKeyUsage.cs
@@ -0,0 +1,131 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * The extendedKeyUsage object. + * <pre> + * extendedKeyUsage ::= Sequence SIZE (1..MAX) OF KeyPurposeId + * </pre> + */ + public class ExtendedKeyUsage + : Asn1Encodable + { + internal readonly IDictionary usageTable = Platform.CreateHashtable(); + internal readonly Asn1Sequence seq; + + public static ExtendedKeyUsage GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static ExtendedKeyUsage GetInstance( + object obj) + { + if (obj is ExtendedKeyUsage) + { + return (ExtendedKeyUsage) obj; + } + + if (obj is Asn1Sequence) + { + return new ExtendedKeyUsage((Asn1Sequence) obj); + } + + if (obj is X509Extension) + { + return GetInstance(X509Extension.ConvertValueToObject((X509Extension) obj)); + } + + throw new ArgumentException("Invalid ExtendedKeyUsage: " + obj.GetType().Name); + } + + private ExtendedKeyUsage( + Asn1Sequence seq) + { + this.seq = seq; + + foreach (object o in seq) + { + if (!(o is DerObjectIdentifier)) + throw new ArgumentException("Only DerObjectIdentifier instances allowed in ExtendedKeyUsage."); + + this.usageTable.Add(o, o); + } + } + + public ExtendedKeyUsage( + params KeyPurposeID[] usages) + { + this.seq = new DerSequence(usages); + + foreach (KeyPurposeID usage in usages) + { + this.usageTable.Add(usage, usage); + } + } + +#if !(SILVERLIGHT || PORTABLE) + [Obsolete] + public ExtendedKeyUsage( + ArrayList usages) + : this((IEnumerable)usages) + { + } +#endif + + public ExtendedKeyUsage( + IEnumerable usages) + { + Asn1EncodableVector v = new Asn1EncodableVector(); + + foreach (Asn1Object o in usages) + { + v.Add(o); + + this.usageTable.Add(o, o); + } + + this.seq = new DerSequence(v); + } + + public bool HasKeyPurposeId( + KeyPurposeID keyPurposeId) + { + return usageTable[keyPurposeId] != null; + } + +#if !(SILVERLIGHT || PORTABLE) + [Obsolete("Use 'GetAllUsages'")] + public ArrayList GetUsages() + { + return new ArrayList(usageTable.Values); + } +#endif + + /** + * Returns all extended key usages. + * The returned ArrayList contains DerObjectIdentifier instances. + * @return An ArrayList with all key purposes. + */ + public IList GetAllUsages() + { + return Platform.CreateArrayList(usageTable.Values); + } + + public int Count + { + get { return usageTable.Count; } + } + + public override Asn1Object ToAsn1Object() + { + return seq; + } + } +} diff --git a/Crypto/src/asn1/x509/GeneralName.cs b/Crypto/src/asn1/x509/GeneralName.cs new file mode 100644
index 000000000..710ddc922 --- /dev/null +++ b/Crypto/src/asn1/x509/GeneralName.cs
@@ -0,0 +1,418 @@ +using System; +using System.Collections; +using System.Globalization; +using System.IO; +using System.Text; + +using NetUtils = Org.BouncyCastle.Utilities.Net; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * 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 } + * </pre> + */ + public class GeneralName + : Asn1Encodable, IAsn1Choice + { + public const int OtherName = 0; + public const int Rfc822Name = 1; + public const int DnsName = 2; + public const int X400Address = 3; + public const int DirectoryName = 4; + public const int EdiPartyName = 5; + public const int UniformResourceIdentifier = 6; + public const int IPAddress = 7; + public const int RegisteredID = 8; + + internal readonly Asn1Encodable obj; + internal readonly int tag; + + public GeneralName( + X509Name directoryName) + { + this.obj = directoryName; + 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( + Asn1Object name, + int tag) + { + this.obj = name; + this.tag = tag; + } + + 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> + * <li>iPAddress</li> + * <li>directoryName</li> + * <li>dNSName</li> + * <li>uniformResourceIdentifier</li> + * <li>registeredID</li> + * </ul> + * For x400Address, otherName and ediPartyName there is no common string + * format defined. + * </p><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. + * </p> + * + * @param tag tag number + * @param name string representation of name + * @throws ArgumentException 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 DerObjectIdentifier(name); + } + else if (tag == DirectoryName) + { + this.obj = new X509Name(name); + } + else if (tag == IPAddress) + { + byte[] enc = toGeneralNameEncoding(name); + if (enc == null) + throw new ArgumentException("IP Address is invalid", "name"); + + this.obj = new DerOctetString(enc); + } + else + { + throw new ArgumentException("can't process string for tag: " + tag, "tag"); + } + } + + public static GeneralName GetInstance( + object obj) + { + if (obj == null || obj is GeneralName) + { + return (GeneralName) obj; + } + + if (obj is Asn1TaggedObject) + { + Asn1TaggedObject tagObj = (Asn1TaggedObject) obj; + int tag = tagObj.TagNo; + + 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 ArgumentException("unknown tag: " + tag); + case DirectoryName: + return new GeneralName(tag, X509Name.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, DerObjectIdentifier.GetInstance(tagObj, false)); + } + } + + if (obj is byte[]) + { + try + { + return GetInstance(Asn1Object.FromByteArray((byte[])obj)); + } + catch (IOException) + { + throw new ArgumentException("unable to parse encoded general name"); + } + } + + throw new ArgumentException("unknown object in GetInstance: " + obj.GetType().FullName, "obj"); + } + + public static GeneralName GetInstance( + Asn1TaggedObject tagObj, + bool explicitly) + { + return GetInstance(Asn1TaggedObject.GetInstance(tagObj, true)); + } + + public int TagNo + { + get { return tag; } + } + + public Asn1Encodable Name + { + get { return obj; } + } + + public override string ToString() + { + StringBuilder buf = new StringBuilder(); + buf.Append(tag); + buf.Append(": "); + + switch (tag) + { + case Rfc822Name: + case DnsName: + case UniformResourceIdentifier: + buf.Append(DerIA5String.GetInstance(obj).GetString()); + break; + case DirectoryName: + buf.Append(X509Name.GetInstance(obj).ToString()); + break; + default: + buf.Append(obj.ToString()); + break; + } + + return buf.ToString(); + } + + private byte[] toGeneralNameEncoding( + string ip) + { + if (NetUtils.IPAddress.IsValidIPv6WithNetmask(ip) || NetUtils.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 (NetUtils.IPAddress.IsValidIPv4WithNetmask(ip) || NetUtils.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 = Int32.Parse(mask); + + for (int i = 0; i != maskVal; i++) + { + addr[(i / 8) + offset] |= (byte)(1 << (i % 8)); + } + } + + private void parseIPv4(string ip, byte[] addr, int offset) + { + foreach (string token in ip.Split('.', '/')) + { + addr[offset++] = (byte)Int32.Parse(token); + } + } + + private int[] parseMask(string mask) + { + int[] res = new int[8]; + int maskVal = Int32.Parse(mask); + + for (int i = 0; i != maskVal; i++) + { + res[i / 16] |= 1 << (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) + { + if (ip.StartsWith("::")) + { + ip = ip.Substring(1); + } + else if (ip.EndsWith("::")) + { + ip = ip.Substring(0, ip.Length - 1); + } + + IEnumerator sEnum = ip.Split(':').GetEnumerator(); + + int index = 0; + int[] val = new int[8]; + + int doubleColon = -1; + + while (sEnum.MoveNext()) + { + string e = (string) sEnum.Current; + + if (e.Length == 0) + { + doubleColon = index; + val[index++] = 0; + } + else + { + if (e.IndexOf('.') < 0) + { + val[index++] = Int32.Parse(e, NumberStyles.AllowHexSpecifier); + } + else + { + string[] tokens = e.Split('.'); + + val[index++] = (Int32.Parse(tokens[0]) << 8) | Int32.Parse(tokens[1]); + val[index++] = (Int32.Parse(tokens[2]) << 8) | Int32.Parse(tokens[3]); + } + } + } + + if (index != val.Length) + { + Array.Copy(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 override Asn1Object ToAsn1Object() + { + // Explicitly tagged if DirectoryName + return new DerTaggedObject(tag == DirectoryName, tag, obj); + } + } +} diff --git a/Crypto/src/asn1/x509/GeneralNames.cs b/Crypto/src/asn1/x509/GeneralNames.cs new file mode 100644
index 000000000..6c5c8e690 --- /dev/null +++ b/Crypto/src/asn1/x509/GeneralNames.cs
@@ -0,0 +1,95 @@ +using System; +using System.Text; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1.X509 +{ + public class GeneralNames + : Asn1Encodable + { + private readonly GeneralName[] names; + + public static GeneralNames GetInstance( + object obj) + { + if (obj == null || obj is GeneralNames) + { + return (GeneralNames) obj; + } + + if (obj is Asn1Sequence) + { + return new GeneralNames((Asn1Sequence) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public static GeneralNames GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + /// <summary>Construct a GeneralNames object containing one GeneralName.</summary> + /// <param name="name">The name to be contained.</param> + public GeneralNames( + GeneralName name) + { + names = new GeneralName[]{ name }; + } + + public GeneralNames( + GeneralName[] names) + { + this.names = (GeneralName[])names.Clone(); + } + + private GeneralNames( + Asn1Sequence seq) + { + this.names = new GeneralName[seq.Count]; + + for (int i = 0; i != seq.Count; i++) + { + names[i] = GeneralName.GetInstance(seq[i]); + } + } + + public GeneralName[] GetNames() + { + return (GeneralName[]) names.Clone(); + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * GeneralNames ::= Sequence SIZE {1..MAX} OF GeneralName + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + return new DerSequence(names); + } + + public override string ToString() + { + StringBuilder buf = new StringBuilder(); + string sep = Platform.NewLine; + + buf.Append("GeneralNames:"); + buf.Append(sep); + + foreach (GeneralName name in names) + { + buf.Append(" "); + buf.Append(name); + buf.Append(sep); + } + + return buf.ToString(); + } + } +} diff --git a/Crypto/src/asn1/x509/GeneralSubtree.cs b/Crypto/src/asn1/x509/GeneralSubtree.cs new file mode 100644
index 000000000..e918a0277 --- /dev/null +++ b/Crypto/src/asn1/x509/GeneralSubtree.cs
@@ -0,0 +1,189 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * Class for containing a restriction object subtrees in NameConstraints. See + * RFC 3280. + * + * <pre> + * + * GeneralSubtree ::= SEQUENCE + * { + * baseName GeneralName, + * minimum [0] BaseDistance DEFAULT 0, + * maximum [1] BaseDistance OPTIONAL + * } + * </pre> + * + * @see org.bouncycastle.asn1.x509.NameConstraints + * + */ + public class GeneralSubtree + : Asn1Encodable + { + private readonly GeneralName baseName; + private readonly DerInteger minimum; + private readonly DerInteger maximum; + + private GeneralSubtree( + Asn1Sequence seq) + { + baseName = GeneralName.GetInstance(seq[0]); + + switch (seq.Count) + { + case 1: + break; + case 2: + { + Asn1TaggedObject o = Asn1TaggedObject.GetInstance(seq[1]); + switch (o.TagNo) + { + case 0: + minimum = DerInteger.GetInstance(o, false); + break; + case 1: + maximum = DerInteger.GetInstance(o, false); + break; + default: + throw new ArgumentException("Bad tag number: " + o.TagNo); + } + break; + } + case 3: + { + { + Asn1TaggedObject oMin = Asn1TaggedObject.GetInstance(seq[1]); + if (oMin.TagNo != 0) + throw new ArgumentException("Bad tag number for 'minimum': " + oMin.TagNo); + minimum = DerInteger.GetInstance(oMin, false); + } + + { + Asn1TaggedObject oMax = Asn1TaggedObject.GetInstance(seq[2]); + if (oMax.TagNo != 1) + throw new ArgumentException("Bad tag number for 'maximum': " + oMax.TagNo); + maximum = DerInteger.GetInstance(oMax, false); + } + + break; + } + default: + throw new ArgumentException("Bad sequence size: " + seq.Count); + } + } + + /** + * 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.</p> + * + * @param baseName + * A restriction. + * @param minimum + * Minimum + * + * @param maximum + * Maximum + */ + public GeneralSubtree( + GeneralName baseName, + BigInteger minimum, + BigInteger maximum) + { + this.baseName = baseName; + if (minimum != null) + { + this.minimum = new DerInteger(minimum); + } + if (maximum != null) + { + this.maximum = new DerInteger(maximum); + } + } + + public GeneralSubtree( + GeneralName baseName) + : this(baseName, null, null) + { + } + + public static GeneralSubtree GetInstance( + Asn1TaggedObject o, + bool isExplicit) + { + return new GeneralSubtree(Asn1Sequence.GetInstance(o, isExplicit)); + } + + public static GeneralSubtree GetInstance( + object obj) + { + if (obj == null) + { + return null; + } + + if (obj is GeneralSubtree) + { + return (GeneralSubtree) obj; + } + + return new GeneralSubtree(Asn1Sequence.GetInstance(obj)); + } + + public GeneralName Base + { + get { return baseName; } + } + + public BigInteger Minimum + { + get { return minimum == null ? BigInteger.Zero : minimum.Value; } + } + + public BigInteger Maximum + { + get { return maximum == null ? null : maximum.Value; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * + * Returns: + * + * <pre> + * GeneralSubtree ::= SEQUENCE + * { + * baseName GeneralName, + * minimum [0] BaseDistance DEFAULT 0, + * maximum [1] BaseDistance OPTIONAL + * } + * </pre> + * + * @return a DERObject + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(baseName); + + if (minimum != null && minimum.Value.SignValue != 0) + { + v.Add(new DerTaggedObject(false, 0, minimum)); + } + + if (maximum != null) + { + v.Add(new DerTaggedObject(false, 1, maximum)); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/x509/Holder.cs b/Crypto/src/asn1/x509/Holder.cs new file mode 100644
index 000000000..d04f1cb60 --- /dev/null +++ b/Crypto/src/asn1/x509/Holder.cs
@@ -0,0 +1,257 @@ +using System; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * 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> + * <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> + * </p> + */ + public class Holder + : Asn1Encodable + { + internal readonly IssuerSerial baseCertificateID; + internal readonly GeneralNames entityName; + internal readonly ObjectDigestInfo objectDigestInfo; + private readonly int version; + + public static Holder GetInstance( + object obj) + { + if (obj is Holder) + { + return (Holder) obj; + } + + if (obj is Asn1Sequence) + { + return new Holder((Asn1Sequence) obj); + } + + if (obj is Asn1TaggedObject) + { + return new Holder((Asn1TaggedObject) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + /** + * Constructor for a holder for an v1 attribute certificate. + * + * @param tagObj The ASN.1 tagged holder object. + */ + public Holder( + Asn1TaggedObject tagObj) + { + switch (tagObj.TagNo) + { + case 0: + baseCertificateID = IssuerSerial.GetInstance(tagObj, false); + break; + case 1: + entityName = GeneralNames.GetInstance(tagObj, false); + break; + default: + throw new ArgumentException("unknown tag in Holder"); + } + + this.version = 0; + } + + /** + * Constructor for a holder for an v2 attribute certificate. * + * + * @param seq The ASN.1 sequence. + */ + private Holder( + Asn1Sequence seq) + { + if (seq.Count > 3) + throw new ArgumentException("Bad sequence size: " + seq.Count); + + for (int i = 0; i != seq.Count; i++) + { + Asn1TaggedObject tObj = Asn1TaggedObject.GetInstance(seq[i]); + + switch (tObj.TagNo) + { + 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 ArgumentException("unknown tag in Holder"); + } + } + + this.version = 1; + } + + public Holder( + IssuerSerial baseCertificateID) + : this(baseCertificateID, 1) + { + } + + /** + * Constructs a holder from a IssuerSerial. + * @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 Version + { + get { return version; } + } + + /** + * 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. + */ + public Holder( + GeneralNames entityName) + : this(entityName, 1) + { + } + + /** + * 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; + this.version = 1; + } + + public IssuerSerial BaseCertificateID + { + get { 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 EntityName + { + get { return entityName; } + } + + public ObjectDigestInfo ObjectDigestInfo + { + get { return objectDigestInfo; } + } + + /** + * The Holder object. + * <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> + */ + public override Asn1Object ToAsn1Object() + { + 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); + } + + if (entityName != null) + { + return new DerTaggedObject(false, 1, entityName); + } + + return new DerTaggedObject(false, 0, baseCertificateID); + } + } +} diff --git a/Crypto/src/asn1/x509/IetfAttrSyntax.cs b/Crypto/src/asn1/x509/IetfAttrSyntax.cs new file mode 100644
index 000000000..e719865b3 --- /dev/null +++ b/Crypto/src/asn1/x509/IetfAttrSyntax.cs
@@ -0,0 +1,161 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * Implementation of <code>IetfAttrSyntax</code> as specified by RFC3281. + */ + public class IetfAttrSyntax + : Asn1Encodable + { + public const int ValueOctets = 1; + public const int ValueOid = 2; + public const int ValueUtf8 = 3; + + internal readonly GeneralNames policyAuthority; + internal readonly Asn1EncodableVector values = new Asn1EncodableVector(); + + internal int valueChoice = -1; + + /** + * + */ + public IetfAttrSyntax( + Asn1Sequence seq) + { + int i = 0; + + if (seq[0] is Asn1TaggedObject) + { + policyAuthority = GeneralNames.GetInstance(((Asn1TaggedObject)seq[0]), false); + i++; + } + else if (seq.Count == 2) + { // VOMS fix + policyAuthority = GeneralNames.GetInstance(seq[0]); + i++; + } + + if (!(seq[i] is Asn1Sequence)) + { + throw new ArgumentException("Non-IetfAttrSyntax encoding"); + } + + seq = (Asn1Sequence) seq[i]; + + foreach (Asn1Object obj in seq) + { + int type; + + if (obj is DerObjectIdentifier) + { + type = ValueOid; + } + else if (obj is DerUtf8String) + { + type = ValueUtf8; + } + else if (obj is DerOctetString) + { + type = ValueOctets; + } + else + { + throw new ArgumentException("Bad value type encoding IetfAttrSyntax"); + } + + if (valueChoice < 0) + { + valueChoice = type; + } + + if (type != valueChoice) + { + throw new ArgumentException("Mix of value types in IetfAttrSyntax"); + } + + values.Add(obj); + } + } + + public GeneralNames PolicyAuthority + { + get { return policyAuthority; } + } + + public int ValueType + { + get { return valueChoice; } + } + + public object[] GetValues() + { + if (this.ValueType == ValueOctets) + { + Asn1OctetString[] tmp = new Asn1OctetString[values.Count]; + + for (int i = 0; i != tmp.Length; i++) + { + tmp[i] = (Asn1OctetString) values[i]; + } + + return tmp; + } + + if (this.ValueType == ValueOid) + { + DerObjectIdentifier[] tmp = new DerObjectIdentifier[values.Count]; + + for (int i = 0; i != tmp.Length; i++) + { + tmp[i] = (DerObjectIdentifier) values[i]; + } + + return tmp; + } + + { + DerUtf8String[] tmp = new DerUtf8String[values.Count]; + + for (int i = 0; i != tmp.Length; i++) + { + tmp[i] = (DerUtf8String) values[i]; + } + + return tmp; + } + } + + /** + * + * <pre> + * + * IetfAttrSyntax ::= Sequence { + * policyAuthority [0] GeneralNames OPTIONAL, + * values Sequence OF CHOICE { + * octets OCTET STRING, + * oid OBJECT IDENTIFIER, + * string UTF8String + * } + * } + * + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(); + + if (policyAuthority != null) + { + v.Add(new DerTaggedObject(0, policyAuthority)); + } + + v.Add(new DerSequence(values)); + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/x509/IssuerSerial.cs b/Crypto/src/asn1/x509/IssuerSerial.cs new file mode 100644
index 000000000..6a24e7333 --- /dev/null +++ b/Crypto/src/asn1/x509/IssuerSerial.cs
@@ -0,0 +1,98 @@ +using System; + +namespace Org.BouncyCastle.Asn1.X509 +{ + public class IssuerSerial + : Asn1Encodable + { + internal readonly GeneralNames issuer; + internal readonly DerInteger serial; + internal readonly DerBitString issuerUid; + + public static IssuerSerial GetInstance( + object obj) + { + if (obj == null || obj is IssuerSerial) + { + return (IssuerSerial) obj; + } + + if (obj is Asn1Sequence) + { + return new IssuerSerial((Asn1Sequence) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public static IssuerSerial GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + private IssuerSerial( + Asn1Sequence seq) + { + if (seq.Count != 2 && seq.Count != 3) + { + throw new ArgumentException("Bad sequence size: " + seq.Count); + } + + issuer = GeneralNames.GetInstance(seq[0]); + serial = DerInteger.GetInstance(seq[1]); + + if (seq.Count == 3) + { + issuerUid = DerBitString.GetInstance(seq[2]); + } + } + + public IssuerSerial( + GeneralNames issuer, + DerInteger serial) + { + this.issuer = issuer; + this.serial = serial; + } + + public GeneralNames Issuer + { + get { return issuer; } + } + + public DerInteger Serial + { + get { return serial; } + } + + public DerBitString IssuerUid + { + get { return issuerUid; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * IssuerSerial ::= Sequence { + * issuer GeneralNames, + * serial CertificateSerialNumber, + * issuerUid UniqueIdentifier OPTIONAL + * } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector( + issuer, serial); + + if (issuerUid != null) + { + v.Add(issuerUid); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/x509/IssuingDistributionPoint.cs b/Crypto/src/asn1/x509/IssuingDistributionPoint.cs new file mode 100644
index 000000000..3af0d565f --- /dev/null +++ b/Crypto/src/asn1/x509/IssuingDistributionPoint.cs
@@ -0,0 +1,247 @@ +using System; +using System.Text; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * <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 + : Asn1Encodable + { + private readonly DistributionPointName _distributionPoint; + private readonly bool _onlyContainsUserCerts; + private readonly bool _onlyContainsCACerts; + private readonly ReasonFlags _onlySomeReasons; + private readonly bool _indirectCRL; + private readonly bool _onlyContainsAttributeCerts; + + private readonly Asn1Sequence seq; + + public static IssuingDistributionPoint GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static IssuingDistributionPoint GetInstance( + object obj) + { + if (obj == null || obj is IssuingDistributionPoint) + { + return (IssuingDistributionPoint) obj; + } + + if (obj is Asn1Sequence) + { + return new IssuingDistributionPoint((Asn1Sequence) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + /** + * 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, + bool onlyContainsUserCerts, + bool onlyContainsCACerts, + ReasonFlags onlySomeReasons, + bool indirectCRL, + bool 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, DerBoolean.True)); + } + if (onlyContainsCACerts) + { + vec.Add(new DerTaggedObject(false, 2, DerBoolean.True)); + } + if (onlySomeReasons != null) + { + vec.Add(new DerTaggedObject(false, 3, onlySomeReasons)); + } + if (indirectCRL) + { + vec.Add(new DerTaggedObject(false, 4, DerBoolean.True)); + } + if (onlyContainsAttributeCerts) + { + vec.Add(new DerTaggedObject(false, 5, DerBoolean.True)); + } + + seq = new DerSequence(vec); + } + + /** + * Constructor from Asn1Sequence + */ + private IssuingDistributionPoint( + Asn1Sequence seq) + { + this.seq = seq; + + for (int i = 0; i != seq.Count; i++) + { + Asn1TaggedObject o = Asn1TaggedObject.GetInstance(seq[i]); + + switch (o.TagNo) + { + case 0: + // CHOICE so explicit + _distributionPoint = DistributionPointName.GetInstance(o, true); + break; + case 1: + _onlyContainsUserCerts = DerBoolean.GetInstance(o, false).IsTrue; + break; + case 2: + _onlyContainsCACerts = DerBoolean.GetInstance(o, false).IsTrue; + break; + case 3: + _onlySomeReasons = new ReasonFlags(ReasonFlags.GetInstance(o, false)); + break; + case 4: + _indirectCRL = DerBoolean.GetInstance(o, false).IsTrue; + break; + case 5: + _onlyContainsAttributeCerts = DerBoolean.GetInstance(o, false).IsTrue; + break; + default: + throw new ArgumentException("unknown tag in IssuingDistributionPoint"); + } + } + } + + public bool OnlyContainsUserCerts + { + get { return _onlyContainsUserCerts; } + } + + public bool OnlyContainsCACerts + { + get { return _onlyContainsCACerts; } + } + + public bool IsIndirectCrl + { + get { return _indirectCRL; } + } + + public bool OnlyContainsAttributeCerts + { + get { return _onlyContainsAttributeCerts; } + } + + /** + * @return Returns the distributionPoint. + */ + public DistributionPointName DistributionPoint + { + get { return _distributionPoint; } + } + + /** + * @return Returns the onlySomeReasons. + */ + public ReasonFlags OnlySomeReasons + { + get { return _onlySomeReasons; } + } + + public override Asn1Object ToAsn1Object() + { + return seq; + } + + public override string ToString() + { + string sep = Platform.NewLine; + StringBuilder buf = new StringBuilder(); + + buf.Append("IssuingDistributionPoint: ["); + buf.Append(sep); + if (_distributionPoint != null) + { + appendObject(buf, sep, "distributionPoint", _distributionPoint.ToString()); + } + if (_onlyContainsUserCerts) + { + appendObject(buf, sep, "onlyContainsUserCerts", _onlyContainsUserCerts.ToString()); + } + if (_onlyContainsCACerts) + { + appendObject(buf, sep, "onlyContainsCACerts", _onlyContainsCACerts.ToString()); + } + if (_onlySomeReasons != null) + { + appendObject(buf, sep, "onlySomeReasons", _onlySomeReasons.ToString()); + } + if (_onlyContainsAttributeCerts) + { + appendObject(buf, sep, "onlyContainsAttributeCerts", _onlyContainsAttributeCerts.ToString()); + } + if (_indirectCRL) + { + appendObject(buf, sep, "indirectCRL", _indirectCRL.ToString()); + } + buf.Append("]"); + buf.Append(sep); + return buf.ToString(); + } + + private void appendObject( + StringBuilder buf, + string sep, + string name, + string val) + { + string indent = " "; + + buf.Append(indent); + buf.Append(name); + buf.Append(":"); + buf.Append(sep); + buf.Append(indent); + buf.Append(indent); + buf.Append(val); + buf.Append(sep); + } + } +} diff --git a/Crypto/src/asn1/x509/KeyPurposeId.cs b/Crypto/src/asn1/x509/KeyPurposeId.cs new file mode 100644
index 000000000..4b48a9b51 --- /dev/null +++ b/Crypto/src/asn1/x509/KeyPurposeId.cs
@@ -0,0 +1,36 @@ +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * The KeyPurposeID object. + * <pre> + * KeyPurposeID ::= OBJECT IDENTIFIER + * </pre> + */ + public sealed class KeyPurposeID + : DerObjectIdentifier + { + private const string IdKP = "1.3.6.1.5.5.7.3"; + + private KeyPurposeID( + string id) + : base(id) + { + } + + public static readonly KeyPurposeID AnyExtendedKeyUsage = new KeyPurposeID(X509Extensions.ExtendedKeyUsage.Id + ".0"); + public static readonly KeyPurposeID IdKPServerAuth = new KeyPurposeID(IdKP + ".1"); + public static readonly KeyPurposeID IdKPClientAuth = new KeyPurposeID(IdKP + ".2"); + public static readonly KeyPurposeID IdKPCodeSigning = new KeyPurposeID(IdKP + ".3"); + public static readonly KeyPurposeID IdKPEmailProtection = new KeyPurposeID(IdKP + ".4"); + public static readonly KeyPurposeID IdKPIpsecEndSystem = new KeyPurposeID(IdKP + ".5"); + public static readonly KeyPurposeID IdKPIpsecTunnel = new KeyPurposeID(IdKP + ".6"); + public static readonly KeyPurposeID IdKPIpsecUser = new KeyPurposeID(IdKP + ".7"); + public static readonly KeyPurposeID IdKPTimeStamping = new KeyPurposeID(IdKP + ".8"); + public static readonly KeyPurposeID IdKPOcspSigning = new KeyPurposeID(IdKP + ".9"); + + // + // microsoft key purpose ids + // + public static readonly KeyPurposeID IdKPSmartCardLogon = new KeyPurposeID("1.3.6.1.4.1.311.20.2.2"); + } +} diff --git a/Crypto/src/asn1/x509/KeyUsage.cs b/Crypto/src/asn1/x509/KeyUsage.cs new file mode 100644
index 000000000..fef04e8b9 --- /dev/null +++ b/Crypto/src/asn1/x509/KeyUsage.cs
@@ -0,0 +1,79 @@ +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * 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 + : DerBitString + { + public const int DigitalSignature = (1 << 7); + public const int NonRepudiation = (1 << 6); + public const int KeyEncipherment = (1 << 5); + public const int DataEncipherment = (1 << 4); + public const int KeyAgreement = (1 << 3); + public const int KeyCertSign = (1 << 2); + public const int CrlSign = (1 << 1); + public const int EncipherOnly = (1 << 0); + public const int DecipherOnly = (1 << 15); + + public static new KeyUsage GetInstance( + object obj) + { + if (obj is KeyUsage) + { + return (KeyUsage)obj; + } + + if (obj is X509Extension) + { + return GetInstance(X509Extension.ConvertValueToObject((X509Extension) obj)); + } + + return new KeyUsage(DerBitString.GetInstance(obj)); + } + + /** + * 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) + : base(GetBytes(usage), GetPadBits(usage)) + { + } + + private KeyUsage( + DerBitString usage) + : base(usage.GetBytes(), usage.PadBits) + { + } + + public override string ToString() + { + byte[] data = GetBytes(); + if (data.Length == 1) + { + return "KeyUsage: 0x" + (data[0] & 0xff).ToString("X"); + } + + return "KeyUsage: 0x" + ((data[1] & 0xff) << 8 | (data[0] & 0xff)).ToString("X"); + } + } +} diff --git a/Crypto/src/asn1/x509/NameConstraints.cs b/Crypto/src/asn1/x509/NameConstraints.cs new file mode 100644
index 000000000..c178f5b45 --- /dev/null +++ b/Crypto/src/asn1/x509/NameConstraints.cs
@@ -0,0 +1,118 @@ +using System; +using System.Collections; + +namespace Org.BouncyCastle.Asn1.X509 +{ + public class NameConstraints + : Asn1Encodable + { + private Asn1Sequence permitted, excluded; + + public static NameConstraints GetInstance( + object obj) + { + if (obj == null || obj is NameConstraints) + { + return (NameConstraints) obj; + } + + if (obj is Asn1Sequence) + { + return new NameConstraints((Asn1Sequence) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public NameConstraints( + Asn1Sequence seq) + { + foreach (Asn1TaggedObject o in seq) + { + switch (o.TagNo) + { + case 0: + permitted = Asn1Sequence.GetInstance(o, false); + break; + case 1: + excluded = Asn1Sequence.GetInstance(o, false); + break; + } + } + } + +#if !(SILVERLIGHT || PORTABLE) + public NameConstraints( + ArrayList permitted, + ArrayList excluded) + : this((IList)permitted, (IList)excluded) + { + } +#endif + + /** + * Constructor from a given details. + * + * <p>permitted and excluded are Vectors of GeneralSubtree objects.</p> + * + * @param permitted Permitted subtrees + * @param excluded Excluded subtrees + */ + public NameConstraints( + IList permitted, + IList excluded) + { + if (permitted != null) + { + this.permitted = CreateSequence(permitted); + } + + if (excluded != null) + { + this.excluded = CreateSequence(excluded); + } + } + + private DerSequence CreateSequence( + IList subtrees) + { + GeneralSubtree[] gsts = new GeneralSubtree[subtrees.Count]; + for (int i = 0; i < subtrees.Count; ++i) + { + gsts[i] = (GeneralSubtree)subtrees[i]; + } + return new DerSequence(gsts); + } + + public Asn1Sequence PermittedSubtrees + { + get { return permitted; } + } + + public Asn1Sequence ExcludedSubtrees + { + get { return excluded; } + } + + /* + * NameConstraints ::= SEQUENCE { permittedSubtrees [0] GeneralSubtrees + * OPTIONAL, excludedSubtrees [1] GeneralSubtrees OPTIONAL } + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(); + + if (permitted != null) + { + v.Add(new DerTaggedObject(false, 0, permitted)); + } + + if (excluded != null) + { + v.Add(new DerTaggedObject(false, 1, excluded)); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/x509/NoticeReference.cs b/Crypto/src/asn1/x509/NoticeReference.cs new file mode 100644
index 000000000..718fe92cf --- /dev/null +++ b/Crypto/src/asn1/x509/NoticeReference.cs
@@ -0,0 +1,138 @@ +using System; +using System.Collections; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * <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 + : Asn1Encodable + { + internal readonly DisplayText organization; + internal readonly Asn1Sequence noticeNumbers; + +#if !(SILVERLIGHT || PORTABLE) + [Obsolete] + public NoticeReference( + string orgName, + ArrayList numbers) + : this(orgName, (IList)numbers) + { + } +#endif + + /** + * Creates a new <code>NoticeReference</code> instance. + * + * @param orgName a <code>string</code> value + * @param numbers a <code>ArrayList</code> value + */ + public NoticeReference( + string orgName, + IList numbers) + { + organization = new DisplayText(orgName); + + object o = numbers[0]; + + Asn1EncodableVector av = new Asn1EncodableVector(); + if (o is int) + { + foreach (int nm in numbers) + { + av.Add(new DerInteger(nm)); + } + } + + noticeNumbers = new DerSequence(av); + } + + /** + * Creates a new <code>NoticeReference</code> instance. + * + * @param orgName a <code>string</code> value + * @param numbers an <code>Asn1Sequence</code> value + */ + public NoticeReference( + string orgName, + Asn1Sequence numbers) + { + organization = new DisplayText(orgName); + noticeNumbers = numbers; + } + + /** + * Creates a new <code>NoticeReference</code> instance. + * + * @param displayTextType an <code>int</code> value + * @param orgName a <code>string</code> value + * @param numbers an <code>Asn1Sequence</code> value + */ + public NoticeReference( + int displayTextType, + string orgName, + Asn1Sequence numbers) + { + organization = new DisplayText(displayTextType, orgName); + noticeNumbers = numbers; + } + + /** + * Creates a new <code>NoticeReference</code> instance. + * <p>Useful for reconstructing a <code>NoticeReference</code> + * instance from its encodable/encoded form.</p> + * + * @param as an <code>Asn1Sequence</code> value obtained from either + * calling @{link ToAsn1Object()} for a <code>NoticeReference</code> + * instance or from parsing it from a Der-encoded stream. + */ + private NoticeReference( + Asn1Sequence seq) + { + if (seq.Count != 2) + throw new ArgumentException("Bad sequence size: " + seq.Count, "seq"); + + organization = DisplayText.GetInstance(seq[0]); + noticeNumbers = Asn1Sequence.GetInstance(seq[1]); + } + + public static NoticeReference GetInstance( + object obj) + { + if (obj is NoticeReference) + { + return (NoticeReference) obj; + } + + if (obj is Asn1Sequence) + { + return new NoticeReference((Asn1Sequence) obj); + } + + throw new ArgumentException("unknown object in GetInstance: " + obj.GetType().FullName, "obj"); + } + + /** + * Describe <code>ToAsn1Object</code> method here. + * + * @return a <code>Asn1Object</code> value + */ + public override Asn1Object ToAsn1Object() + { + return new DerSequence(organization, noticeNumbers); + } + } +} diff --git a/Crypto/src/asn1/x509/ObjectDigestInfo.cs b/Crypto/src/asn1/x509/ObjectDigestInfo.cs new file mode 100644
index 000000000..6d5b9c692 --- /dev/null +++ b/Crypto/src/asn1/x509/ObjectDigestInfo.cs
@@ -0,0 +1,177 @@ +using System; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * 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 + : Asn1Encodable + { + /** + * The public key is hashed. + */ + public const int PublicKey = 0; + + /** + * The public key certificate is hashed. + */ + public const int PublicKeyCert = 1; + + /** + * An other object is hashed. + */ + public const int OtherObjectDigest = 2; + + internal readonly DerEnumerated digestedObjectType; + internal readonly DerObjectIdentifier otherObjectTypeID; + internal readonly AlgorithmIdentifier digestAlgorithm; + internal readonly DerBitString objectDigest; + + public static ObjectDigestInfo GetInstance( + object obj) + { + if (obj == null || obj is ObjectDigestInfo) + { + return (ObjectDigestInfo) obj; + } + + if (obj is Asn1Sequence) + { + return new ObjectDigestInfo((Asn1Sequence) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public static ObjectDigestInfo GetInstance( + Asn1TaggedObject obj, + bool isExplicit) + { + return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit)); + } + + /** + * 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.</p> + * + * @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, + string otherObjectTypeID, + AlgorithmIdentifier digestAlgorithm, + byte[] objectDigest) + { + this.digestedObjectType = new DerEnumerated(digestedObjectType); + + if (digestedObjectType == OtherObjectDigest) + { + this.otherObjectTypeID = new DerObjectIdentifier(otherObjectTypeID); + } + + this.digestAlgorithm = digestAlgorithm; + + this.objectDigest = new DerBitString(objectDigest); + } + + private ObjectDigestInfo( + Asn1Sequence seq) + { + if (seq.Count > 4 || seq.Count < 3) + { + throw new ArgumentException("Bad sequence size: " + seq.Count); + } + + digestedObjectType = DerEnumerated.GetInstance(seq[0]); + + int offset = 0; + + if (seq.Count == 4) + { + otherObjectTypeID = DerObjectIdentifier.GetInstance(seq[1]); + offset++; + } + + digestAlgorithm = AlgorithmIdentifier.GetInstance(seq[1 + offset]); + objectDigest = DerBitString.GetInstance(seq[2 + offset]); + } + + public DerEnumerated DigestedObjectType + { + get { return digestedObjectType; } + } + + public DerObjectIdentifier OtherObjectTypeID + { + get { return otherObjectTypeID; } + } + + public AlgorithmIdentifier DigestAlgorithm + { + get { return digestAlgorithm; } + } + + public DerBitString ObjectDigest + { + get { 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(digestedObjectType); + + if (otherObjectTypeID != null) + { + v.Add(otherObjectTypeID); + } + + v.Add(digestAlgorithm, objectDigest); + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/x509/PolicyInformation.cs b/Crypto/src/asn1/x509/PolicyInformation.cs new file mode 100644
index 000000000..29d245084 --- /dev/null +++ b/Crypto/src/asn1/x509/PolicyInformation.cs
@@ -0,0 +1,80 @@ +using System; + +namespace Org.BouncyCastle.Asn1.X509 +{ + public class PolicyInformation + : Asn1Encodable + { + private readonly DerObjectIdentifier policyIdentifier; + private readonly Asn1Sequence policyQualifiers; + + private PolicyInformation( + Asn1Sequence seq) + { + if (seq.Count < 1 || seq.Count > 2) + { + throw new ArgumentException("Bad sequence size: " + seq.Count); + } + + policyIdentifier = DerObjectIdentifier.GetInstance(seq[0]); + + if (seq.Count > 1) + { + policyQualifiers = Asn1Sequence.GetInstance(seq[1]); + } + } + + public PolicyInformation( + DerObjectIdentifier policyIdentifier) + { + this.policyIdentifier = policyIdentifier; + } + + public PolicyInformation( + DerObjectIdentifier policyIdentifier, + Asn1Sequence policyQualifiers) + { + this.policyIdentifier = policyIdentifier; + this.policyQualifiers = policyQualifiers; + } + + public static PolicyInformation GetInstance( + object obj) + { + if (obj == null || obj is PolicyInformation) + { + return (PolicyInformation) obj; + } + + return new PolicyInformation(Asn1Sequence.GetInstance(obj)); + } + + public DerObjectIdentifier PolicyIdentifier + { + get { return policyIdentifier; } + } + + public Asn1Sequence PolicyQualifiers + { + get { return policyQualifiers; } + } + + /* + * PolicyInformation ::= Sequence { + * policyIdentifier CertPolicyId, + * policyQualifiers Sequence SIZE (1..MAX) OF + * PolicyQualifierInfo OPTIONAL } + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(policyIdentifier); + + if (policyQualifiers != null) + { + v.Add(policyQualifiers); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/x509/PolicyMappings.cs b/Crypto/src/asn1/x509/PolicyMappings.cs new file mode 100644
index 000000000..928ad134d --- /dev/null +++ b/Crypto/src/asn1/x509/PolicyMappings.cs
@@ -0,0 +1,70 @@ +using System.Collections; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * 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 + : Asn1Encodable + { + private readonly Asn1Sequence seq; + + /** + * Creates a new <code>PolicyMappings</code> instance. + * + * @param seq an <code>Asn1Sequence</code> constructed as specified + * in RFC 3280 + */ + public PolicyMappings( + Asn1Sequence seq) + { + this.seq = seq; + } + +#if !(SILVERLIGHT || PORTABLE) + public PolicyMappings( + Hashtable mappings) + : this((IDictionary)mappings) + { + } +#endif + + /** + * 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. + */ + public PolicyMappings( + IDictionary mappings) + { + Asn1EncodableVector v = new Asn1EncodableVector(); + + foreach (string idp in mappings.Keys) + { + string sdp = (string) mappings[idp]; + + v.Add( + new DerSequence( + new DerObjectIdentifier(idp), + new DerObjectIdentifier(sdp))); + } + + seq = new DerSequence(v); + } + + public override Asn1Object ToAsn1Object() + { + return seq; + } + } +} diff --git a/Crypto/src/asn1/x509/PolicyQualifierId.cs b/Crypto/src/asn1/x509/PolicyQualifierId.cs new file mode 100644
index 000000000..c858f0864 --- /dev/null +++ b/Crypto/src/asn1/x509/PolicyQualifierId.cs
@@ -0,0 +1,28 @@ +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * 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 sealed class PolicyQualifierID : DerObjectIdentifier + { + private const string IdQt = "1.3.6.1.5.5.7.2"; + + private PolicyQualifierID( + string id) + : base(id) + { + } + + public static readonly PolicyQualifierID IdQtCps = new PolicyQualifierID(IdQt + ".1"); + public static readonly PolicyQualifierID IdQtUnotice = new PolicyQualifierID(IdQt + ".2"); + } +} diff --git a/Crypto/src/asn1/x509/PolicyQualifierInfo.cs b/Crypto/src/asn1/x509/PolicyQualifierInfo.cs new file mode 100644
index 000000000..187f8d3bb --- /dev/null +++ b/Crypto/src/asn1/x509/PolicyQualifierInfo.cs
@@ -0,0 +1,91 @@ +using System; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * Policy qualifiers, used in the X509V3 CertificatePolicies + * extension. + * + * <pre> + * PolicyQualifierInfo ::= Sequence { + * policyQualifierId PolicyQualifierId, + * qualifier ANY DEFINED BY policyQualifierId } + * </pre> + */ + public class PolicyQualifierInfo + : Asn1Encodable + { + internal readonly DerObjectIdentifier policyQualifierId; + internal readonly 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( + DerObjectIdentifier 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.IdQtCps; + qualifier = new DerIA5String(cps); + } + + /** + * Creates a new <code>PolicyQualifierInfo</code> instance. + * + * @param as <code>PolicyQualifierInfo</code> X509 structure + * encoded as an Asn1Sequence. + */ + private PolicyQualifierInfo( + Asn1Sequence seq) + { + if (seq.Count != 2) + throw new ArgumentException("Bad sequence size: " + seq.Count, "seq"); + + policyQualifierId = DerObjectIdentifier.GetInstance(seq[0]); + qualifier = seq[1]; + } + + public static PolicyQualifierInfo GetInstance( + object obj) + { + if (obj is PolicyQualifierInfo) + { + return (PolicyQualifierInfo) obj; + } + + if (obj is Asn1Sequence) + { + return new PolicyQualifierInfo((Asn1Sequence) obj); + } + + throw new ArgumentException("unknown object in GetInstance: " + obj.GetType().FullName, "obj"); + } + + /** + * Returns a Der-encodable representation of this instance. + * + * @return a <code>Asn1Object</code> value + */ + public override Asn1Object ToAsn1Object() + { + return new DerSequence(policyQualifierId, qualifier); + } + } +} diff --git a/Crypto/src/asn1/x509/PrivateKeyUsagePeriod.cs b/Crypto/src/asn1/x509/PrivateKeyUsagePeriod.cs new file mode 100644
index 000000000..ad2961eb0 --- /dev/null +++ b/Crypto/src/asn1/x509/PrivateKeyUsagePeriod.cs
@@ -0,0 +1,82 @@ +using System; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /// <remarks> + /// <pre> + /// PrivateKeyUsagePeriod ::= SEQUENCE + /// { + /// notBefore [0] GeneralizedTime OPTIONAL, + /// notAfter [1] GeneralizedTime OPTIONAL } + /// </pre> + /// </remarks> + public class PrivateKeyUsagePeriod + : Asn1Encodable + { + public static PrivateKeyUsagePeriod GetInstance( + object obj) + { + if (obj is PrivateKeyUsagePeriod) + { + return (PrivateKeyUsagePeriod) obj; + } + + if (obj is Asn1Sequence) + { + return new PrivateKeyUsagePeriod((Asn1Sequence) obj); + } + + if (obj is X509Extension) + { + return GetInstance(X509Extension.ConvertValueToObject((X509Extension) obj)); + } + + throw new ArgumentException("unknown object in GetInstance: " + obj.GetType().FullName, "obj"); + } + + private DerGeneralizedTime _notBefore, _notAfter; + + private PrivateKeyUsagePeriod( + Asn1Sequence seq) + { + foreach (Asn1TaggedObject tObj in seq) + { + if (tObj.TagNo == 0) + { + _notBefore = DerGeneralizedTime.GetInstance(tObj, false); + } + else if (tObj.TagNo == 1) + { + _notAfter = DerGeneralizedTime.GetInstance(tObj, false); + } + } + } + + public DerGeneralizedTime NotBefore + { + get { return _notBefore; } + } + + public DerGeneralizedTime NotAfter + { + get { return _notAfter; } + } + + public override Asn1Object ToAsn1Object() + { + 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/Crypto/src/asn1/x509/RSAPublicKeyStructure.cs b/Crypto/src/asn1/x509/RSAPublicKeyStructure.cs new file mode 100644
index 000000000..bdcba783e --- /dev/null +++ b/Crypto/src/asn1/x509/RSAPublicKeyStructure.cs
@@ -0,0 +1,92 @@ +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Math; + +using System; +using System.Collections; + +namespace Org.BouncyCastle.Asn1.X509 +{ + public class RsaPublicKeyStructure + : Asn1Encodable + { + private BigInteger modulus; + private BigInteger publicExponent; + + public static RsaPublicKeyStructure GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static RsaPublicKeyStructure GetInstance( + object obj) + { + if (obj == null || obj is RsaPublicKeyStructure) + { + return (RsaPublicKeyStructure) obj; + } + + if (obj is Asn1Sequence) + { + return new RsaPublicKeyStructure((Asn1Sequence) obj); + } + + throw new ArgumentException("Invalid RsaPublicKeyStructure: " + obj.GetType().Name); + } + + public RsaPublicKeyStructure( + BigInteger modulus, + BigInteger publicExponent) + { + if (modulus == null) + throw new ArgumentNullException("modulus"); + if (publicExponent == null) + throw new ArgumentNullException("publicExponent"); + if (modulus.SignValue <= 0) + throw new ArgumentException("Not a valid RSA modulus", "modulus"); + if (publicExponent.SignValue <= 0) + throw new ArgumentException("Not a valid RSA public exponent", "publicExponent"); + + this.modulus = modulus; + this.publicExponent = publicExponent; + } + + private RsaPublicKeyStructure( + Asn1Sequence seq) + { + if (seq.Count != 2) + throw new ArgumentException("Bad sequence size: " + seq.Count); + + // Note: we are accepting technically incorrect (i.e. negative) values here + modulus = DerInteger.GetInstance(seq[0]).PositiveValue; + publicExponent = DerInteger.GetInstance(seq[1]).PositiveValue; + } + + public BigInteger Modulus + { + get { return modulus; } + } + + public BigInteger PublicExponent + { + get { return publicExponent; } + } + + /** + * This outputs the key in Pkcs1v2 format. + * <pre> + * RSAPublicKey ::= Sequence { + * modulus Integer, -- n + * publicExponent Integer, -- e + * } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + return new DerSequence( + new DerInteger(Modulus), + new DerInteger(PublicExponent)); + } + } +} diff --git a/Crypto/src/asn1/x509/ReasonFlags.cs b/Crypto/src/asn1/x509/ReasonFlags.cs new file mode 100644
index 000000000..f204c36aa --- /dev/null +++ b/Crypto/src/asn1/x509/ReasonFlags.cs
@@ -0,0 +1,46 @@ +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * The ReasonFlags object. + * <pre> + * ReasonFlags ::= BIT STRING { + * unused(0), + * keyCompromise(1), + * cACompromise(2), + * affiliationChanged(3), + * superseded(4), + * cessationOfOperation(5), + * certficateHold(6) + * } + * </pre> + */ + public class ReasonFlags + : DerBitString + { + public const int Unused = (1 << 7); + public const int KeyCompromise = (1 << 6); + public const int CACompromise = (1 << 5); + public const int AffiliationChanged = (1 << 4); + public const int Superseded = (1 << 3); + public const int CessationOfOperation = (1 << 2); + public const int CertificateHold = (1 << 1); + public const int PrivilegeWithdrawn = (1 << 0); + public const 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) + : base(GetBytes(reasons), GetPadBits(reasons)) + { + } + + public ReasonFlags( + DerBitString reasons) + : base(reasons.GetBytes(), reasons.PadBits) + { + } + } +} diff --git a/Crypto/src/asn1/x509/RoleSyntax.cs b/Crypto/src/asn1/x509/RoleSyntax.cs new file mode 100644
index 000000000..48c3c6cae --- /dev/null +++ b/Crypto/src/asn1/x509/RoleSyntax.cs
@@ -0,0 +1,230 @@ +using System; +using System.Text; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * Implementation of the RoleSyntax object as specified by the RFC3281. + * + * <pre> + * RoleSyntax ::= SEQUENCE { + * roleAuthority [0] GeneralNames OPTIONAL, + * roleName [1] GeneralName + * } + * </pre> + */ + public class RoleSyntax + : Asn1Encodable + { + private readonly GeneralNames roleAuthority; + private readonly 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.ArgumentException 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 is RoleSyntax) + return (RoleSyntax)obj; + + 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.TagNo != GeneralName.UniformResourceIdentifier + || ((IAsn1String) roleName.Name).GetString().Equals("")) + { + throw new ArgumentException("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.Count < 1 || seq.Count > 2) + { + throw new ArgumentException("Bad sequence size: " + seq.Count); + } + + for (int i = 0; i != seq.Count; i++) + { + Asn1TaggedObject taggedObject = Asn1TaggedObject.GetInstance(seq[i]); + switch (taggedObject.TagNo) + { + case 0: + roleAuthority = GeneralNames.GetInstance(taggedObject, false); + break; + case 1: + roleName = GeneralName.GetInstance(taggedObject, true); + break; + default: + throw new ArgumentException("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 RoleAuthority + { + get { 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 RoleName + { + get { 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>string</code> object. + */ + public string GetRoleNameAsString() + { + return ((IAsn1String) this.roleName.Name).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 asn1Value = names[i].Name; + if (asn1Value is IAsn1String) + { + namesString[i] = ((IAsn1String) asn1Value).GetString(); + } + else + { + namesString[i] = asn1Value.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 override Asn1Object ToAsn1Object() + { + 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 override string ToString() + { + StringBuilder buff = new StringBuilder("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/Crypto/src/asn1/x509/SubjectDirectoryAttributes.cs b/Crypto/src/asn1/x509/SubjectDirectoryAttributes.cs new file mode 100644
index 000000000..c76d94d78 --- /dev/null +++ b/Crypto/src/asn1/x509/SubjectDirectoryAttributes.cs
@@ -0,0 +1,142 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * 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.x509.X509Name for AttributeType ObjectIdentifiers. + */ + public class SubjectDirectoryAttributes + : Asn1Encodable + { + private readonly IList attributes; + + public static SubjectDirectoryAttributes GetInstance( + object obj) + { + if (obj == null || obj is SubjectDirectoryAttributes) + { + return (SubjectDirectoryAttributes) obj; + } + + if (obj is Asn1Sequence) + { + return new SubjectDirectoryAttributes((Asn1Sequence) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + /** + * 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) + { + this.attributes = Platform.CreateArrayList(); + foreach (object o in seq) + { + Asn1Sequence s = Asn1Sequence.GetInstance(o); + attributes.Add(AttributeX509.GetInstance(s)); + } + } + +#if !(SILVERLIGHT || PORTABLE) + [Obsolete] + public SubjectDirectoryAttributes( + ArrayList attributes) + : this((IList)attributes) + { + } +#endif + + /** + * Constructor from an ArrayList of attributes. + * + * The ArrayList consists of attributes of type {@link Attribute Attribute} + * + * @param attributes The attributes. + * + */ + public SubjectDirectoryAttributes( + IList attributes) + { + this.attributes = Platform.CreateArrayList(attributes); + } + + /** + * 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 DERObject + */ + public override Asn1Object ToAsn1Object() + { + AttributeX509[] v = new AttributeX509[attributes.Count]; + for (int i = 0; i < attributes.Count; ++i) + { + v[i] = (AttributeX509)attributes[i]; + } + return new DerSequence(v); + } + + /** + * @return Returns the attributes. + */ + public IEnumerable Attributes + { + get { return new EnumerableProxy(attributes); } + } + } +} diff --git a/Crypto/src/asn1/x509/SubjectKeyIdentifier.cs b/Crypto/src/asn1/x509/SubjectKeyIdentifier.cs new file mode 100644
index 000000000..e640760f3 --- /dev/null +++ b/Crypto/src/asn1/x509/SubjectKeyIdentifier.cs
@@ -0,0 +1,141 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Digests; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * The SubjectKeyIdentifier object. + * <pre> + * SubjectKeyIdentifier::= OCTET STRING + * </pre> + */ + public class SubjectKeyIdentifier + : Asn1Encodable + { + private readonly byte[] keyIdentifier; + + public static SubjectKeyIdentifier GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1OctetString.GetInstance(obj, explicitly)); + } + + public static SubjectKeyIdentifier GetInstance( + object obj) + { + if (obj is SubjectKeyIdentifier) + { + return (SubjectKeyIdentifier) obj; + } + + if (obj is SubjectPublicKeyInfo) + { + return new SubjectKeyIdentifier((SubjectPublicKeyInfo) obj); + } + + if (obj is Asn1OctetString) + { + return new SubjectKeyIdentifier((Asn1OctetString) obj); + } + + if (obj is X509Extension) + { + return GetInstance(X509Extension.ConvertValueToObject((X509Extension) obj)); + } + + throw new ArgumentException("Invalid SubjectKeyIdentifier: " + obj.GetType().Name); + } + + public SubjectKeyIdentifier( + byte[] keyID) + { + if (keyID == null) + throw new ArgumentNullException("keyID"); + + this.keyIdentifier = keyID; + } + + public SubjectKeyIdentifier( + Asn1OctetString keyID) + { + this.keyIdentifier = keyID.GetOctets(); + } + + /** + * Calculates the keyIdentifier using a SHA1 hash over the BIT STRING + * from SubjectPublicKeyInfo as defined in RFC3280. + * + * @param spki the subject public key info. + */ + public SubjectKeyIdentifier( + SubjectPublicKeyInfo spki) + { + this.keyIdentifier = GetDigest(spki); + } + + public byte[] GetKeyIdentifier() + { + return keyIdentifier; + } + + public override Asn1Object ToAsn1Object() + { + return new DerOctetString(keyIdentifier); + } + + /** + * 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. + */ + 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. + */ + public static SubjectKeyIdentifier CreateTruncatedSha1KeyIdentifier( + SubjectPublicKeyInfo keyInfo) + { + byte[] dig = GetDigest(keyInfo); + byte[] id = new byte[8]; + + Array.Copy(dig, dig.Length - 8, id, 0, id.Length); + + id[0] &= 0x0f; + id[0] |= 0x40; + + return new SubjectKeyIdentifier(id); + } + + private static byte[] GetDigest( + SubjectPublicKeyInfo spki) + { + IDigest digest = new Sha1Digest(); + byte[] resBuf = new byte[digest.GetDigestSize()]; + + byte[] bytes = spki.PublicKeyData.GetBytes(); + digest.BlockUpdate(bytes, 0, bytes.Length); + digest.DoFinal(resBuf, 0); + return resBuf; + } + } +} diff --git a/Crypto/src/asn1/x509/SubjectPublicKeyInfo.cs b/Crypto/src/asn1/x509/SubjectPublicKeyInfo.cs new file mode 100644
index 000000000..8ce4b2762 --- /dev/null +++ b/Crypto/src/asn1/x509/SubjectPublicKeyInfo.cs
@@ -0,0 +1,102 @@ +using System; +using System.Collections; +using System.IO; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * 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.</p> + */ + public class SubjectPublicKeyInfo + : Asn1Encodable + { + private readonly AlgorithmIdentifier algID; + private readonly DerBitString keyData; + + public static SubjectPublicKeyInfo GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static SubjectPublicKeyInfo GetInstance( + object obj) + { + if (obj is SubjectPublicKeyInfo) + return (SubjectPublicKeyInfo) obj; + + if (obj != null) + return new SubjectPublicKeyInfo(Asn1Sequence.GetInstance(obj)); + + return null; + } + + public SubjectPublicKeyInfo( + AlgorithmIdentifier algID, + Asn1Encodable publicKey) + { + this.keyData = new DerBitString(publicKey); + this.algID = algID; + } + + public SubjectPublicKeyInfo( + AlgorithmIdentifier algID, + byte[] publicKey) + { + this.keyData = new DerBitString(publicKey); + this.algID = algID; + } + + private SubjectPublicKeyInfo( + Asn1Sequence seq) + { + if (seq.Count != 2) + throw new ArgumentException("Bad sequence size: " + seq.Count, "seq"); + + this.algID = AlgorithmIdentifier.GetInstance(seq[0]); + this.keyData = DerBitString.GetInstance(seq[1]); + } + + public AlgorithmIdentifier AlgorithmID + { + get { return algID; } + } + + /** + * for when the public key is an encoded object - if the bitstring + * can't be decoded this routine raises an IOException. + * + * @exception IOException - if the bit string doesn't represent a Der + * encoded object. + */ + public Asn1Object GetPublicKey() + { + return Asn1Object.FromByteArray(keyData.GetBytes()); + } + + /** + * for when the public key is raw bits... + */ + public DerBitString PublicKeyData + { + get { return keyData; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * SubjectPublicKeyInfo ::= Sequence { + * algorithm AlgorithmIdentifier, + * publicKey BIT STRING } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + return new DerSequence(algID, keyData); + } + } +} diff --git a/Crypto/src/asn1/x509/TBSCertList.cs b/Crypto/src/asn1/x509/TBSCertList.cs new file mode 100644
index 000000000..b5934a230 --- /dev/null +++ b/Crypto/src/asn1/x509/TBSCertList.cs
@@ -0,0 +1,274 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Asn1.X509 +{ + public class CrlEntry + : Asn1Encodable + { + internal Asn1Sequence seq; + internal DerInteger userCertificate; + internal Time revocationDate; + internal X509Extensions crlEntryExtensions; + + public CrlEntry( + Asn1Sequence seq) + { + if (seq.Count < 2 || seq.Count > 3) + { + throw new ArgumentException("Bad sequence size: " + seq.Count); + } + + this.seq = seq; + + userCertificate = DerInteger.GetInstance(seq[0]); + revocationDate = Time.GetInstance(seq[1]); + } + + public DerInteger UserCertificate + { + get { return userCertificate; } + } + + public Time RevocationDate + { + get { return revocationDate; } + } + + public X509Extensions Extensions + { + get + { + if (crlEntryExtensions == null && seq.Count == 3) + { + crlEntryExtensions = X509Extensions.GetInstance(seq[2]); + } + + return crlEntryExtensions; + } + } + + public override Asn1Object ToAsn1Object() + { + return seq; + } + } + + /** + * 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 TbsCertificateList + : Asn1Encodable + { + private class RevokedCertificatesEnumeration + : IEnumerable + { + private readonly IEnumerable en; + + internal RevokedCertificatesEnumeration( + IEnumerable en) + { + this.en = en; + } + + public IEnumerator GetEnumerator() + { + return new RevokedCertificatesEnumerator(en.GetEnumerator()); + } + + private class RevokedCertificatesEnumerator + : IEnumerator + { + private readonly IEnumerator e; + + internal RevokedCertificatesEnumerator( + IEnumerator e) + { + this.e = e; + } + + public bool MoveNext() + { + return e.MoveNext(); + } + + public void Reset() + { + e.Reset(); + } + + public object Current + { + get { return new CrlEntry(Asn1Sequence.GetInstance(e.Current)); } + } + } + } + + internal Asn1Sequence seq; + internal DerInteger version; + internal AlgorithmIdentifier signature; + internal X509Name issuer; + internal Time thisUpdate; + internal Time nextUpdate; + internal Asn1Sequence revokedCertificates; + internal X509Extensions crlExtensions; + + public static TbsCertificateList GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static TbsCertificateList GetInstance( + object obj) + { + TbsCertificateList list = obj as TbsCertificateList; + + if (obj == null || list != null) + { + return list; + } + + if (obj is Asn1Sequence) + { + return new TbsCertificateList((Asn1Sequence) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + internal TbsCertificateList( + Asn1Sequence seq) + { + if (seq.Count < 3 || seq.Count > 7) + { + throw new ArgumentException("Bad sequence size: " + seq.Count); + } + + int seqPos = 0; + + this.seq = seq; + + if (seq[seqPos] is DerInteger) + { + version = DerInteger.GetInstance(seq[seqPos++]); + } + else + { + version = new DerInteger(0); + } + + signature = AlgorithmIdentifier.GetInstance(seq[seqPos++]); + issuer = X509Name.GetInstance(seq[seqPos++]); + thisUpdate = Time.GetInstance(seq[seqPos++]); + + if (seqPos < seq.Count + && (seq[seqPos] is DerUtcTime + || seq[seqPos] is DerGeneralizedTime + || seq[seqPos] is Time)) + { + nextUpdate = Time.GetInstance(seq[seqPos++]); + } + + if (seqPos < seq.Count + && !(seq[seqPos] is DerTaggedObject)) + { + revokedCertificates = Asn1Sequence.GetInstance(seq[seqPos++]); + } + + if (seqPos < seq.Count + && seq[seqPos] is DerTaggedObject) + { + crlExtensions = X509Extensions.GetInstance(seq[seqPos]); + } + } + + public int Version + { + get { return version.Value.IntValue + 1; } + } + + public DerInteger VersionNumber + { + get { return version; } + } + + public AlgorithmIdentifier Signature + { + get { return signature; } + } + + public X509Name Issuer + { + get { return issuer; } + } + + public Time ThisUpdate + { + get { return thisUpdate; } + } + + public Time NextUpdate + { + get { return nextUpdate; } + } + + public CrlEntry[] GetRevokedCertificates() + { + if (revokedCertificates == null) + { + return new CrlEntry[0]; + } + + CrlEntry[] entries = new CrlEntry[revokedCertificates.Count]; + + for (int i = 0; i < entries.Length; i++) + { + entries[i] = new CrlEntry(Asn1Sequence.GetInstance(revokedCertificates[i])); + } + + return entries; + } + + public IEnumerable GetRevokedCertificateEnumeration() + { + if (revokedCertificates == null) + { + return EmptyEnumerable.Instance; + } + + return new RevokedCertificatesEnumeration(revokedCertificates); + } + + public X509Extensions Extensions + { + get { return crlExtensions; } + } + + public override Asn1Object ToAsn1Object() + { + return seq; + } + } +} diff --git a/Crypto/src/asn1/x509/TBSCertificateStructure.cs b/Crypto/src/asn1/x509/TBSCertificateStructure.cs new file mode 100644
index 000000000..fc7c39ba2 --- /dev/null +++ b/Crypto/src/asn1/x509/TBSCertificateStructure.cs
@@ -0,0 +1,185 @@ +using System; + +using Org.BouncyCastle.Asn1.Pkcs; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * 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.</p> + */ + public class TbsCertificateStructure + : Asn1Encodable + { + internal Asn1Sequence seq; + internal DerInteger version; + internal DerInteger serialNumber; + internal AlgorithmIdentifier signature; + internal X509Name issuer; + internal Time startDate, endDate; + internal X509Name subject; + internal SubjectPublicKeyInfo subjectPublicKeyInfo; + internal DerBitString issuerUniqueID; + internal DerBitString subjectUniqueID; + internal X509Extensions extensions; + + public static TbsCertificateStructure GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static TbsCertificateStructure GetInstance( + object obj) + { + if (obj is TbsCertificateStructure) + return (TbsCertificateStructure) obj; + + if (obj != null) + return new TbsCertificateStructure(Asn1Sequence.GetInstance(obj)); + + return null; + } + + internal TbsCertificateStructure( + Asn1Sequence seq) + { + int seqStart = 0; + + this.seq = seq; + + // + // some certficates don't include a version number - we assume v1 + // + if (seq[0] is DerTaggedObject) + { + version = DerInteger.GetInstance((Asn1TaggedObject)seq[0], true); + } + else + { + seqStart = -1; // field 0 is missing! + version = new DerInteger(0); + } + + serialNumber = DerInteger.GetInstance(seq[seqStart + 1]); + + signature = AlgorithmIdentifier.GetInstance(seq[seqStart + 2]); + issuer = X509Name.GetInstance(seq[seqStart + 3]); + + // + // before and after dates + // + Asn1Sequence dates = (Asn1Sequence)seq[seqStart + 4]; + + startDate = Time.GetInstance(dates[0]); + endDate = Time.GetInstance(dates[1]); + + subject = X509Name.GetInstance(seq[seqStart + 5]); + + // + // public key info. + // + subjectPublicKeyInfo = SubjectPublicKeyInfo.GetInstance(seq[seqStart + 6]); + + for (int extras = seq.Count - (seqStart + 6) - 1; extras > 0; extras--) + { + DerTaggedObject extra = (DerTaggedObject) seq[seqStart + 6 + extras]; + + switch (extra.TagNo) + { + case 1: + issuerUniqueID = DerBitString.GetInstance(extra, false); + break; + case 2: + subjectUniqueID = DerBitString.GetInstance(extra, false); + break; + case 3: + extensions = X509Extensions.GetInstance(extra); + break; + } + } + } + + public int Version + { + get { return version.Value.IntValue + 1; } + } + + public DerInteger VersionNumber + { + get { return version; } + } + + public DerInteger SerialNumber + { + get { return serialNumber; } + } + + public AlgorithmIdentifier Signature + { + get { return signature; } + } + + public X509Name Issuer + { + get { return issuer; } + } + + public Time StartDate + { + get { return startDate; } + } + + public Time EndDate + { + get { return endDate; } + } + + public X509Name Subject + { + get { return subject; } + } + + public SubjectPublicKeyInfo SubjectPublicKeyInfo + { + get { return subjectPublicKeyInfo; } + } + + public DerBitString IssuerUniqueID + { + get { return issuerUniqueID; } + } + + public DerBitString SubjectUniqueID + { + get { return subjectUniqueID; } + } + + public X509Extensions Extensions + { + get { return extensions; } + } + + public override Asn1Object ToAsn1Object() + { + return seq; + } + } +} diff --git a/Crypto/src/asn1/x509/Target.cs b/Crypto/src/asn1/x509/Target.cs new file mode 100644
index 000000000..309b28c95 --- /dev/null +++ b/Crypto/src/asn1/x509/Target.cs
@@ -0,0 +1,139 @@ +using System; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * 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.</p> + */ + public class Target + : Asn1Encodable, IAsn1Choice + { + public enum Choice + { + Name = 0, + Group = 1 + }; + + private readonly GeneralName targetName; + private readonly GeneralName targetGroup; + + /** + * Creates an instance of a Target from the given object. + * <p> + * <code>obj</code> can be a Target or a {@link Asn1TaggedObject}</p> + * + * @param obj The object. + * @return A Target instance. + * @throws ArgumentException if the given object cannot be + * interpreted as Target. + */ + public static Target GetInstance( + object obj) + { + if (obj is Target) + { + return (Target) obj; + } + + if (obj is Asn1TaggedObject) + { + return new Target((Asn1TaggedObject) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + /** + * Constructor from Asn1TaggedObject. + * + * @param tagObj The tagged object. + * @throws ArgumentException if the encoding is wrong. + */ + private Target( + Asn1TaggedObject tagObj) + { + switch ((Choice) tagObj.TagNo) + { + case Choice.Name: // GeneralName is already a choice so explicit + targetName = GeneralName.GetInstance(tagObj, true); + break; + case Choice.Group: + targetGroup = GeneralName.GetInstance(tagObj, true); + break; + default: + throw new ArgumentException("unknown tag: " + tagObj.TagNo); + } + } + + /** + * Constructor from given details. + * <p> + * Exactly one of the parameters must be not <code>null</code>.</p> + * + * @param type the choice type to apply to the name. + * @param name the general name. + * @throws ArgumentException if type is invalid. + */ + public Target( + Choice type, + GeneralName name) + : this(new DerTaggedObject((int) type, name)) + { + } + + /** + * @return Returns the targetGroup. + */ + public virtual GeneralName TargetGroup + { + get { return targetGroup; } + } + + /** + * @return Returns the targetName. + */ + public virtual GeneralName TargetName + { + get { return targetName; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * + * Returns: + * + * <pre> + * Target ::= CHOICE { + * targetName [0] GeneralName, + * targetGroup [1] GeneralName, + * targetCert [2] TargetCert + * } + * </pre> + * + * @return an Asn1Object + */ + public override Asn1Object ToAsn1Object() + { + // GeneralName is a choice already so most be explicitly tagged + if (targetName != null) + { + return new DerTaggedObject(true, 0, targetName); + } + + return new DerTaggedObject(true, 1, targetGroup); + } + } +} diff --git a/Crypto/src/asn1/x509/TargetInformation.cs b/Crypto/src/asn1/x509/TargetInformation.cs new file mode 100644
index 000000000..75b18c0c9 --- /dev/null +++ b/Crypto/src/asn1/x509/TargetInformation.cs
@@ -0,0 +1,123 @@ +using System; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * Target information extension for attributes certificates according to RFC + * 3281. + * + * <pre> + * SEQUENCE OF Targets + * </pre> + * + */ + public class TargetInformation + : Asn1Encodable + { + private readonly Asn1Sequence targets; + + /** + * Creates an instance of a TargetInformation from the given object. + * <p> + * <code>obj</code> can be a TargetInformation or a {@link Asn1Sequence}</p> + * + * @param obj The object. + * @return A TargetInformation instance. + * @throws ArgumentException if the given object cannot be interpreted as TargetInformation. + */ + public static TargetInformation GetInstance( + object obj) + { + if (obj is TargetInformation) + { + return (TargetInformation) obj; + } + + if (obj is Asn1Sequence) + { + return new TargetInformation((Asn1Sequence) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + /** + * Constructor from a Asn1Sequence. + * + * @param seq The Asn1Sequence. + * @throws ArgumentException if the sequence does not contain + * correctly encoded Targets elements. + */ + private TargetInformation( + Asn1Sequence targets) + { + this.targets = targets; + } + + /** + * Returns the targets in this target information extension. + * <p> + * The ArrayList is cloned before it is returned.</p> + * + * @return Returns the targets. + */ + public virtual Targets[] GetTargetsObjects() + { + Targets[] result = new Targets[targets.Count]; + + for (int i = 0; i < targets.Count; ++i) + { + result[i] = Targets.GetInstance(targets[i]); + } + + return result; + } + + /** + * 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.</p> + * + * @return an Asn1Object + */ + public override Asn1Object ToAsn1Object() + { + return targets; + } + } +} diff --git a/Crypto/src/asn1/x509/Targets.cs b/Crypto/src/asn1/x509/Targets.cs new file mode 100644
index 000000000..3e436d8d8 --- /dev/null +++ b/Crypto/src/asn1/x509/Targets.cs
@@ -0,0 +1,121 @@ +using System; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * 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 + : Asn1Encodable + { + private readonly Asn1Sequence targets; + + /** + * Creates an instance of a Targets from the given object. + * <p> + * <code>obj</code> can be a Targets or a {@link Asn1Sequence}</p> + * + * @param obj The object. + * @return A Targets instance. + * @throws ArgumentException if the given object cannot be interpreted as Target. + */ + public static Targets GetInstance( + object obj) + { + if (obj is Targets) + { + return (Targets) obj; + } + + if (obj is Asn1Sequence) + { + return new Targets((Asn1Sequence) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + /** + * Constructor from Asn1Sequence. + * + * @param targets The ASN.1 SEQUENCE. + * @throws ArgumentException if the contents of the sequence are + * invalid. + */ + private Targets( + Asn1Sequence targets) + { + this.targets = targets; + } + + /** + * Constructor from given targets. + * <p> + * The ArrayList is copied.</p> + * + * @param targets An <code>ArrayList</code> of {@link Target}s. + * @see Target + * @throws ArgumentException if the ArrayList contains not only Targets. + */ + public Targets( + Target[] targets) + { + this.targets = new DerSequence(targets); + } + + /** + * Returns the targets in an <code>ArrayList</code>. + * <p> + * The ArrayList is cloned before it is returned.</p> + * + * @return Returns the targets. + */ + public virtual Target[] GetTargets() + { + Target[] result = new Target[targets.Count]; + + for (int i = 0; i < targets.Count; ++i) + { + result[i] = Target.GetInstance(targets[i]); + } + + return result; + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * + * Returns: + * + * <pre> + * Targets ::= SEQUENCE OF Target + * </pre> + * + * @return an Asn1Object + */ + public override Asn1Object ToAsn1Object() + { + return targets; + } + } +} diff --git a/Crypto/src/asn1/x509/Time.cs b/Crypto/src/asn1/x509/Time.cs new file mode 100644
index 000000000..ca4e99ac7 --- /dev/null +++ b/Crypto/src/asn1/x509/Time.cs
@@ -0,0 +1,120 @@ +using System; + +namespace Org.BouncyCastle.Asn1.X509 +{ + public class Time + : Asn1Encodable, IAsn1Choice + { + internal Asn1Object time; + + public static Time GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(obj.GetObject()); + } + + public Time( + Asn1Object time) + { + if (time == null) + throw new ArgumentNullException("time"); + + if (!(time is DerUtcTime) && !(time is DerGeneralizedTime)) + { + throw new ArgumentException("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( + DateTime date) + { + string d = date.ToString("yyyyMMddHHmmss") + "Z"; + + int year = Int32.Parse(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 is Time) + return (Time) obj; + + if (obj is DerUtcTime) + return new Time((DerUtcTime) obj); + + if (obj is DerGeneralizedTime) + return new Time((DerGeneralizedTime) obj); + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public string GetTime() + { + if (time is DerUtcTime) + { + return ((DerUtcTime) time).AdjustedTimeString; + } + + return ((DerGeneralizedTime) time).GetTime(); + } + + /// <summary> + /// Return our time as DateTime. + /// </summary> + /// <returns>A date time.</returns> + public DateTime ToDateTime() + { + try + { + if (time is DerUtcTime) + { + return ((DerUtcTime)time).ToAdjustedDateTime(); + } + else + { + return ((DerGeneralizedTime)time).ToDateTime(); + } + } + catch (FormatException e) + { + // this should never happen + throw new InvalidOperationException("invalid date string: " + e.Message); + } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * Time ::= CHOICE { + * utcTime UTCTime, + * generalTime GeneralizedTime } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + return time; + } + + public override string ToString() + { + return GetTime(); + } + } +} diff --git a/Crypto/src/asn1/x509/UserNotice.cs b/Crypto/src/asn1/x509/UserNotice.cs new file mode 100644
index 000000000..2878a180f --- /dev/null +++ b/Crypto/src/asn1/x509/UserNotice.cs
@@ -0,0 +1,104 @@ +using System; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * <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 + : Asn1Encodable + { + internal NoticeReference noticeRef; + internal 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 = noticeRef; + this.explicitText = 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 toASN1Object()} for a <code>UserNotice</code> + * instance or from parsing it from a DER-encoded stream.</p> + */ + public UserNotice( + Asn1Sequence seq) + { + if (seq.Count == 2) + { + noticeRef = NoticeReference.GetInstance(seq[0]); + explicitText = DisplayText.GetInstance(seq[1]); + } + else if (seq.Count == 1) + { + if (seq[0].ToAsn1Object() is Asn1Sequence) + { + noticeRef = NoticeReference.GetInstance(seq[0]); + } + else + { + explicitText = DisplayText.GetInstance(seq[0]); + } + } + else + { + throw new ArgumentException("Bad sequence size: " + seq.Count); + } + } + + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector av = new Asn1EncodableVector(); + + if (noticeRef != null) + { + av.Add(noticeRef); + } + + if (explicitText != null) + { + av.Add(explicitText); + } + + return new DerSequence(av); + } + } +} diff --git a/Crypto/src/asn1/x509/V1TBSCertificateGenerator.cs b/Crypto/src/asn1/x509/V1TBSCertificateGenerator.cs new file mode 100644
index 000000000..20b525a48 --- /dev/null +++ b/Crypto/src/asn1/x509/V1TBSCertificateGenerator.cs
@@ -0,0 +1,108 @@ +using System; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * 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 + { + internal DerTaggedObject version = new DerTaggedObject(0, new DerInteger(0)); + internal DerInteger serialNumber; + internal AlgorithmIdentifier signature; + internal X509Name issuer; + internal Time startDate, endDate; + internal X509Name subject; + internal SubjectPublicKeyInfo subjectPublicKeyInfo; + + public V1TbsCertificateGenerator() + { + } + + public void SetSerialNumber( + DerInteger serialNumber) + { + this.serialNumber = serialNumber; + } + + public void SetSignature( + AlgorithmIdentifier signature) + { + this.signature = signature; + } + + public void SetIssuer( + X509Name issuer) + { + this.issuer = issuer; + } + + public void SetStartDate( + Time startDate) + { + this.startDate = startDate; + } + + public void SetStartDate( + DerUtcTime startDate) + { + this.startDate = new Time(startDate); + } + + public void SetEndDate( + Time endDate) + { + this.endDate = endDate; + } + + public void SetEndDate( + DerUtcTime endDate) + { + this.endDate = new Time(endDate); + } + + public void SetSubject( + X509Name subject) + { + this.subject = subject; + } + + public void SetSubjectPublicKeyInfo( + SubjectPublicKeyInfo pubKeyInfo) + { + this.subjectPublicKeyInfo = pubKeyInfo; + } + + public TbsCertificateStructure GenerateTbsCertificate() + { + if ((serialNumber == null) || (signature == null) + || (issuer == null) || (startDate == null) || (endDate == null) + || (subject == null) || (subjectPublicKeyInfo == null)) + { + throw new InvalidOperationException("not all mandatory fields set in V1 TBScertificate generator"); + } + + return new TbsCertificateStructure( + new DerSequence( + //version, - not required as default value + serialNumber, + signature, + issuer, + new DerSequence(startDate, endDate), // before and after dates + subject, + subjectPublicKeyInfo)); + } + } +} diff --git a/Crypto/src/asn1/x509/V2AttributeCertificateInfoGenerator.cs b/Crypto/src/asn1/x509/V2AttributeCertificateInfoGenerator.cs new file mode 100644
index 000000000..02580b5b8 --- /dev/null +++ b/Crypto/src/asn1/x509/V2AttributeCertificateInfoGenerator.cs
@@ -0,0 +1,137 @@ +using System; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * 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 Attr, + * issuerUniqueID UniqueIdentifier OPTIONAL, + * extensions Extensions OPTIONAL + * } + * </pre> + * + */ + public class V2AttributeCertificateInfoGenerator + { + internal DerInteger version; + internal Holder holder; + internal AttCertIssuer issuer; + internal AlgorithmIdentifier signature; + internal DerInteger serialNumber; +// internal AttCertValidityPeriod attrCertValidityPeriod; + internal Asn1EncodableVector attributes; + internal DerBitString issuerUniqueID; + internal X509Extensions extensions; + internal DerGeneralizedTime startDate, endDate; + + public V2AttributeCertificateInfoGenerator() + { + this.version = new DerInteger(1); + attributes = new Asn1EncodableVector(); + } + + public void SetHolder( + Holder holder) + { + this.holder = holder; + } + + public void AddAttribute( + string oid, + Asn1Encodable value) + { + attributes.Add(new AttributeX509(new DerObjectIdentifier(oid), new DerSet(value))); + } + + /** + * @param attribute + */ + public void AddAttribute(AttributeX509 attribute) + { + attributes.Add(attribute); + } + + public void SetSerialNumber( + DerInteger serialNumber) + { + this.serialNumber = serialNumber; + } + + public void SetSignature( + AlgorithmIdentifier signature) + { + this.signature = signature; + } + + public void SetIssuer( + AttCertIssuer issuer) + { + this.issuer = issuer; + } + + public void SetStartDate( + DerGeneralizedTime startDate) + { + this.startDate = startDate; + } + + public void SetEndDate( + DerGeneralizedTime endDate) + { + this.endDate = endDate; + } + + public void SetIssuerUniqueID( + DerBitString issuerUniqueID) + { + this.issuerUniqueID = issuerUniqueID; + } + + public void SetExtensions( + X509Extensions extensions) + { + this.extensions = extensions; + } + + public AttributeCertificateInfo GenerateAttributeCertificateInfo() + { + if ((serialNumber == null) || (signature == null) + || (issuer == null) || (startDate == null) || (endDate == null) + || (holder == null) || (attributes == null)) + { + throw new InvalidOperationException("not all mandatory fields set in V2 AttributeCertificateInfo generator"); + } + + Asn1EncodableVector v = new Asn1EncodableVector( + version, holder, issuer, signature, serialNumber); + + // + // before and after dates => AttCertValidityPeriod + // + v.Add(new AttCertValidityPeriod(startDate, endDate)); + + // 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/Crypto/src/asn1/x509/V2Form.cs b/Crypto/src/asn1/x509/V2Form.cs new file mode 100644
index 000000000..a9c43357c --- /dev/null +++ b/Crypto/src/asn1/x509/V2Form.cs
@@ -0,0 +1,125 @@ +using System; + +namespace Org.BouncyCastle.Asn1.X509 +{ + public class V2Form + : Asn1Encodable + { + internal GeneralNames issuerName; + internal IssuerSerial baseCertificateID; + internal ObjectDigestInfo objectDigestInfo; + + public static V2Form GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static V2Form GetInstance( + object obj) + { + if (obj is V2Form) + { + return (V2Form) obj; + } + + if (obj is Asn1Sequence) + { + return new V2Form((Asn1Sequence) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + public V2Form( + GeneralNames issuerName) + { + this.issuerName = issuerName; + } + + private V2Form( + Asn1Sequence seq) + { + if (seq.Count > 3) + { + throw new ArgumentException("Bad sequence size: " + seq.Count); + } + + int index = 0; + + if (!(seq[0] is Asn1TaggedObject)) + { + index++; + this.issuerName = GeneralNames.GetInstance(seq[0]); + } + + for (int i = index; i != seq.Count; i++) + { + Asn1TaggedObject o = Asn1TaggedObject.GetInstance(seq[i]); + if (o.TagNo == 0) + { + baseCertificateID = IssuerSerial.GetInstance(o, false); + } + else if (o.TagNo == 1) + { + objectDigestInfo = ObjectDigestInfo.GetInstance(o, false); + } + else + { + throw new ArgumentException("Bad tag number: " + o.TagNo); + } + } + } + + public GeneralNames IssuerName + { + get { return issuerName; } + } + + public IssuerSerial BaseCertificateID + { + get { return baseCertificateID; } + } + + public ObjectDigestInfo ObjectDigestInfo + { + get { 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 override Asn1Object ToAsn1Object() + { + 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/Crypto/src/asn1/x509/V2TBSCertListGenerator.cs b/Crypto/src/asn1/x509/V2TBSCertListGenerator.cs new file mode 100644
index 000000000..2c929188f --- /dev/null +++ b/Crypto/src/asn1/x509/V2TBSCertListGenerator.cs
@@ -0,0 +1,201 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * 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 DerInteger version = new DerInteger(1); + private AlgorithmIdentifier signature; + private X509Name issuer; + private Time thisUpdate, nextUpdate; + private X509Extensions extensions; + private IList crlEntries; + + public V2TbsCertListGenerator() + { + } + + public void SetSignature( + AlgorithmIdentifier signature) + { + this.signature = signature; + } + + public void SetIssuer( + X509Name issuer) + { + this.issuer = issuer; + } + + public void SetThisUpdate( + DerUtcTime thisUpdate) + { + this.thisUpdate = new Time(thisUpdate); + } + + public void SetNextUpdate( + DerUtcTime nextUpdate) + { + this.nextUpdate = (nextUpdate != null) + ? new Time(nextUpdate) + : null; + } + + public void SetThisUpdate( + Time thisUpdate) + { + this.thisUpdate = thisUpdate; + } + + public void SetNextUpdate( + Time nextUpdate) + { + this.nextUpdate = nextUpdate; + } + + public void AddCrlEntry( + Asn1Sequence crlEntry) + { + if (crlEntries == null) + { + crlEntries = Platform.CreateArrayList(); + } + + crlEntries.Add(crlEntry); + } + + public void AddCrlEntry(DerInteger userCertificate, DerUtcTime revocationDate, int reason) + { + AddCrlEntry(userCertificate, new Time(revocationDate), reason); + } + + public void AddCrlEntry(DerInteger userCertificate, Time revocationDate, int reason) + { + AddCrlEntry(userCertificate, revocationDate, reason, null); + } + + public void AddCrlEntry(DerInteger userCertificate, Time revocationDate, int reason, + DerGeneralizedTime invalidityDate) + { + IList extOids = Platform.CreateArrayList(); + IList extValues = Platform.CreateArrayList(); + + if (reason != 0) + { + CrlReason crlReason = new CrlReason(reason); + + try + { + extOids.Add(X509Extensions.ReasonCode); + extValues.Add(new X509Extension(false, new DerOctetString(crlReason.GetEncoded()))); + } + catch (IOException e) + { + throw new ArgumentException("error encoding reason: " + e); + } + } + + if (invalidityDate != null) + { + try + { + extOids.Add(X509Extensions.InvalidityDate); + extValues.Add(new X509Extension(false, new DerOctetString(invalidityDate.GetEncoded()))); + } + catch (IOException e) + { + throw new ArgumentException("error encoding invalidityDate: " + e); + } + } + + if (extOids.Count != 0) + { + AddCrlEntry(userCertificate, revocationDate, new X509Extensions(extOids, extValues)); + } + else + { + AddCrlEntry(userCertificate, revocationDate, null); + } + } + + public void AddCrlEntry(DerInteger userCertificate, Time revocationDate, X509Extensions extensions) + { + Asn1EncodableVector v = new Asn1EncodableVector( + userCertificate, revocationDate); + + if (extensions != null) + { + v.Add(extensions); + } + + AddCrlEntry(new DerSequence(v)); + } + + public void SetExtensions( + X509Extensions extensions) + { + this.extensions = extensions; + } + + public TbsCertificateList GenerateTbsCertList() + { + if ((signature == null) || (issuer == null) || (thisUpdate == null)) + { + throw new InvalidOperationException("Not all mandatory fields set in V2 TbsCertList generator."); + } + + Asn1EncodableVector v = new Asn1EncodableVector( + version, signature, issuer, thisUpdate); + + if (nextUpdate != null) + { + v.Add(nextUpdate); + } + + // Add CRLEntries if they exist + if (crlEntries != null) + { + Asn1Sequence[] certs = new Asn1Sequence[crlEntries.Count]; + for (int i = 0; i < crlEntries.Count; ++i) + { + certs[i] = (Asn1Sequence)crlEntries[i]; + } + v.Add(new DerSequence(certs)); + } + + if (extensions != null) + { + v.Add(new DerTaggedObject(0, extensions)); + } + + return new TbsCertificateList(new DerSequence(v)); + } + } +} diff --git a/Crypto/src/asn1/x509/V3TBSCertificateGenerator.cs b/Crypto/src/asn1/x509/V3TBSCertificateGenerator.cs new file mode 100644
index 000000000..beb469a0d --- /dev/null +++ b/Crypto/src/asn1/x509/V3TBSCertificateGenerator.cs
@@ -0,0 +1,168 @@ +using System; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * 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 + { + internal DerTaggedObject version = new DerTaggedObject(0, new DerInteger(2)); + internal DerInteger serialNumber; + internal AlgorithmIdentifier signature; + internal X509Name issuer; + internal Time startDate, endDate; + internal X509Name subject; + internal SubjectPublicKeyInfo subjectPublicKeyInfo; + internal X509Extensions extensions; + + private bool altNamePresentAndCritical; + private DerBitString issuerUniqueID; + private DerBitString subjectUniqueID; + + public V3TbsCertificateGenerator() + { + } + + public void SetSerialNumber( + DerInteger serialNumber) + { + this.serialNumber = serialNumber; + } + + public void SetSignature( + AlgorithmIdentifier signature) + { + this.signature = signature; + } + + public void SetIssuer( + X509Name 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; + } + + public void SetSubject( + X509Name 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; + } + + public void SetExtensions( + X509Extensions extensions) + { + this.extensions = extensions; + + if (extensions != null) + { + X509Extension altName = extensions.GetExtension(X509Extensions.SubjectAlternativeName); + + if (altName != null && altName.IsCritical) + { + altNamePresentAndCritical = true; + } + } + } + + public TbsCertificateStructure GenerateTbsCertificate() + { + if ((serialNumber == null) || (signature == null) + || (issuer == null) || (startDate == null) || (endDate == null) + || (subject == null && !altNamePresentAndCritical) + || (subjectPublicKeyInfo == null)) + { + throw new InvalidOperationException("not all mandatory fields set in V3 TBScertificate generator"); + } + + DerSequence validity = new DerSequence(startDate, endDate); // before and after dates + + Asn1EncodableVector v = new Asn1EncodableVector( + version, serialNumber, signature, issuer, validity); + + if (subject != null) + { + v.Add(subject); + } + else + { + v.Add(DerSequence.Empty); + } + + 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(3, extensions)); + } + + return new TbsCertificateStructure(new DerSequence(v)); + } + } +} diff --git a/Crypto/src/asn1/x509/X509Attributes.cs b/Crypto/src/asn1/x509/X509Attributes.cs new file mode 100644
index 000000000..291329a62 --- /dev/null +++ b/Crypto/src/asn1/x509/X509Attributes.cs
@@ -0,0 +1,9 @@ +using System; + +namespace Org.BouncyCastle.Asn1.X509 +{ + public class X509Attributes + { + public static readonly DerObjectIdentifier RoleSyntax = new DerObjectIdentifier("2.5.4.72"); + } +} diff --git a/Crypto/src/asn1/x509/X509CertificateStructure.cs b/Crypto/src/asn1/x509/X509CertificateStructure.cs new file mode 100644
index 000000000..e50d3563b --- /dev/null +++ b/Crypto/src/asn1/x509/X509CertificateStructure.cs
@@ -0,0 +1,129 @@ +using System; + +using Org.BouncyCastle.Asn1.Pkcs; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * an X509Certificate structure. + * <pre> + * Certificate ::= Sequence { + * tbsCertificate TbsCertificate, + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING + * } + * </pre> + */ + public class X509CertificateStructure + : Asn1Encodable + { + private readonly TbsCertificateStructure tbsCert; + private readonly AlgorithmIdentifier sigAlgID; + private readonly DerBitString sig; + + public static X509CertificateStructure GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static X509CertificateStructure GetInstance( + object obj) + { + if (obj is X509CertificateStructure) + return (X509CertificateStructure)obj; + + if (obj != null) + return new X509CertificateStructure(Asn1Sequence.GetInstance(obj)); + + return null; + } + + public X509CertificateStructure( + TbsCertificateStructure tbsCert, + AlgorithmIdentifier sigAlgID, + DerBitString sig) + { + if (tbsCert == null) + throw new ArgumentNullException("tbsCert"); + if (sigAlgID == null) + throw new ArgumentNullException("sigAlgID"); + if (sig == null) + throw new ArgumentNullException("sig"); + + this.tbsCert = tbsCert; + this.sigAlgID = sigAlgID; + this.sig = sig; + } + + private X509CertificateStructure( + Asn1Sequence seq) + { + if (seq.Count != 3) + throw new ArgumentException("sequence wrong size for a certificate", "seq"); + + // + // correct x509 certficate + // + tbsCert = TbsCertificateStructure.GetInstance(seq[0]); + sigAlgID = AlgorithmIdentifier.GetInstance(seq[1]); + sig = DerBitString.GetInstance(seq[2]); + } + + public TbsCertificateStructure TbsCertificate + { + get { return tbsCert; } + } + + public int Version + { + get { return tbsCert.Version; } + } + + public DerInteger SerialNumber + { + get { return tbsCert.SerialNumber; } + } + + public X509Name Issuer + { + get { return tbsCert.Issuer; } + } + + public Time StartDate + { + get { return tbsCert.StartDate; } + } + + public Time EndDate + { + get { return tbsCert.EndDate; } + } + + public X509Name Subject + { + get { return tbsCert.Subject; } + } + + public SubjectPublicKeyInfo SubjectPublicKeyInfo + { + get { return tbsCert.SubjectPublicKeyInfo; } + } + + public AlgorithmIdentifier SignatureAlgorithm + { + get { return sigAlgID; } + } + + public DerBitString Signature + { + get { return sig; } + } + + public override Asn1Object ToAsn1Object() + { + return new DerSequence(tbsCert, sigAlgID, sig); + } + } +} diff --git a/Crypto/src/asn1/x509/X509DefaultEntryConverter.cs b/Crypto/src/asn1/x509/X509DefaultEntryConverter.cs new file mode 100644
index 000000000..7282ead26 --- /dev/null +++ b/Crypto/src/asn1/x509/X509DefaultEntryConverter.cs
@@ -0,0 +1,63 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * The default converter for X509 DN entries when going from their + * string value to ASN.1 strings. + */ + public class X509DefaultEntryConverter + : X509NameEntryConverter + { + /** + * Apply default conversion 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 override Asn1Object GetConvertedValue( + DerObjectIdentifier oid, + string value) + { + if (value.Length != 0 && value[0] == '#') + { + try + { + return ConvertHexEncoded(value, 1); + } + catch (IOException) + { + throw new Exception("can't recode value for oid " + oid.Id); + } + } + + if (value.Length != 0 && value[0] == '\\') + { + value = value.Substring(1); + } + + if (oid.Equals(X509Name.EmailAddress) || oid.Equals(X509Name.DC)) + { + return new DerIA5String(value); + } + + if (oid.Equals(X509Name.DateOfBirth)) // accept time string as well as # (for compatibility) + { + return new DerGeneralizedTime(value); + } + + if (oid.Equals(X509Name.C) + || oid.Equals(X509Name.SerialNumber) + || oid.Equals(X509Name.DnQualifier) + || oid.Equals(X509Name.TelephoneNumber)) + { + return new DerPrintableString(value); + } + + return new DerUtf8String(value); + } + } +} diff --git a/Crypto/src/asn1/x509/X509Extension.cs b/Crypto/src/asn1/x509/X509Extension.cs new file mode 100644
index 000000000..430ce4447 --- /dev/null +++ b/Crypto/src/asn1/x509/X509Extension.cs
@@ -0,0 +1,79 @@ +using System; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * an object for the elements in the X.509 V3 extension block. + */ + public class X509Extension + { + internal bool critical; + internal Asn1OctetString value; + + public X509Extension( + DerBoolean critical, + Asn1OctetString value) + { + if (critical == null) + { + throw new ArgumentNullException("critical"); + } + + this.critical = critical.IsTrue; + this.value = value; + } + + public X509Extension( + bool critical, + Asn1OctetString value) + { + this.critical = critical; + this.value = value; + } + + public bool IsCritical { get { return critical; } } + + public Asn1OctetString Value { get { return value; } } + + public Asn1Encodable GetParsedValue() + { + return ConvertValueToObject(this); + } + + public override int GetHashCode() + { + int vh = this.Value.GetHashCode(); + + return IsCritical ? vh : ~vh; + } + + public override bool Equals( + object obj) + { + X509Extension other = obj as X509Extension; + if (other == null) + { + return false; + } + + return Value.Equals(other.Value) && IsCritical == other.IsCritical; + } + + /// <sumary>Convert the value of the passed in extension to an object.</sumary> + /// <param name="ext">The extension to parse.</param> + /// <returns>The object the value string contains.</returns> + /// <exception cref="ArgumentException">If conversion is not possible.</exception> + public static Asn1Object ConvertValueToObject( + X509Extension ext) + { + try + { + return Asn1Object.FromByteArray(ext.Value.GetOctets()); + } + catch (Exception e) + { + throw new ArgumentException("can't convert extension", e); + } + } + } +} diff --git a/Crypto/src/asn1/x509/X509Extensions.cs b/Crypto/src/asn1/x509/X509Extensions.cs new file mode 100644
index 000000000..1896450f5 --- /dev/null +++ b/Crypto/src/asn1/x509/X509Extensions.cs
@@ -0,0 +1,451 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Asn1.X509 +{ + public class X509Extensions + : Asn1Encodable + { + /** + * Subject Directory Attributes + */ + public static readonly DerObjectIdentifier SubjectDirectoryAttributes = new DerObjectIdentifier("2.5.29.9"); + + /** + * Subject Key Identifier + */ + public static readonly DerObjectIdentifier SubjectKeyIdentifier = new DerObjectIdentifier("2.5.29.14"); + + /** + * Key Usage + */ + public static readonly DerObjectIdentifier KeyUsage = new DerObjectIdentifier("2.5.29.15"); + + /** + * Private Key Usage Period + */ + public static readonly DerObjectIdentifier PrivateKeyUsagePeriod = new DerObjectIdentifier("2.5.29.16"); + + /** + * Subject Alternative Name + */ + public static readonly DerObjectIdentifier SubjectAlternativeName = new DerObjectIdentifier("2.5.29.17"); + + /** + * Issuer Alternative Name + */ + public static readonly DerObjectIdentifier IssuerAlternativeName = new DerObjectIdentifier("2.5.29.18"); + + /** + * Basic Constraints + */ + public static readonly DerObjectIdentifier BasicConstraints = new DerObjectIdentifier("2.5.29.19"); + + /** + * CRL Number + */ + public static readonly DerObjectIdentifier CrlNumber = new DerObjectIdentifier("2.5.29.20"); + + /** + * Reason code + */ + public static readonly DerObjectIdentifier ReasonCode = new DerObjectIdentifier("2.5.29.21"); + + /** + * Hold Instruction Code + */ + public static readonly DerObjectIdentifier InstructionCode = new DerObjectIdentifier("2.5.29.23"); + + /** + * Invalidity Date + */ + public static readonly DerObjectIdentifier InvalidityDate = new DerObjectIdentifier("2.5.29.24"); + + /** + * Delta CRL indicator + */ + public static readonly DerObjectIdentifier DeltaCrlIndicator = new DerObjectIdentifier("2.5.29.27"); + + /** + * Issuing Distribution Point + */ + public static readonly DerObjectIdentifier IssuingDistributionPoint = new DerObjectIdentifier("2.5.29.28"); + + /** + * Certificate Issuer + */ + public static readonly DerObjectIdentifier CertificateIssuer = new DerObjectIdentifier("2.5.29.29"); + + /** + * Name Constraints + */ + public static readonly DerObjectIdentifier NameConstraints = new DerObjectIdentifier("2.5.29.30"); + + /** + * CRL Distribution Points + */ + public static readonly DerObjectIdentifier CrlDistributionPoints = new DerObjectIdentifier("2.5.29.31"); + + /** + * Certificate Policies + */ + public static readonly DerObjectIdentifier CertificatePolicies = new DerObjectIdentifier("2.5.29.32"); + + /** + * Policy Mappings + */ + public static readonly DerObjectIdentifier PolicyMappings = new DerObjectIdentifier("2.5.29.33"); + + /** + * Authority Key Identifier + */ + public static readonly DerObjectIdentifier AuthorityKeyIdentifier = new DerObjectIdentifier("2.5.29.35"); + + /** + * Policy Constraints + */ + public static readonly DerObjectIdentifier PolicyConstraints = new DerObjectIdentifier("2.5.29.36"); + + /** + * Extended Key Usage + */ + public static readonly DerObjectIdentifier ExtendedKeyUsage = new DerObjectIdentifier("2.5.29.37"); + + /** + * Freshest CRL + */ + public static readonly DerObjectIdentifier FreshestCrl = new DerObjectIdentifier("2.5.29.46"); + + /** + * Inhibit Any Policy + */ + public static readonly DerObjectIdentifier InhibitAnyPolicy = new DerObjectIdentifier("2.5.29.54"); + + /** + * Authority Info Access + */ + public static readonly DerObjectIdentifier AuthorityInfoAccess = new DerObjectIdentifier("1.3.6.1.5.5.7.1.1"); + + /** + * Subject Info Access + */ + public static readonly DerObjectIdentifier SubjectInfoAccess = new DerObjectIdentifier("1.3.6.1.5.5.7.1.11"); + + /** + * Logo Type + */ + public static readonly DerObjectIdentifier LogoType = new DerObjectIdentifier("1.3.6.1.5.5.7.1.12"); + + /** + * BiometricInfo + */ + public static readonly DerObjectIdentifier BiometricInfo = new DerObjectIdentifier("1.3.6.1.5.5.7.1.2"); + + /** + * QCStatements + */ + public static readonly DerObjectIdentifier QCStatements = new DerObjectIdentifier("1.3.6.1.5.5.7.1.3"); + + /** + * Audit identity extension in attribute certificates. + */ + public static readonly DerObjectIdentifier AuditIdentity = new DerObjectIdentifier("1.3.6.1.5.5.7.1.4"); + + /** + * NoRevAvail extension in attribute certificates. + */ + public static readonly DerObjectIdentifier NoRevAvail = new DerObjectIdentifier("2.5.29.56"); + + /** + * TargetInformation extension in attribute certificates. + */ + public static readonly DerObjectIdentifier TargetInformation = new DerObjectIdentifier("2.5.29.55"); + + private readonly IDictionary extensions = Platform.CreateHashtable(); + private readonly IList ordering; + + public static X509Extensions GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static X509Extensions GetInstance( + object obj) + { + if (obj == null || obj is X509Extensions) + { + return (X509Extensions) obj; + } + + if (obj is Asn1Sequence) + { + return new X509Extensions((Asn1Sequence) obj); + } + + if (obj is Asn1TaggedObject) + { + return GetInstance(((Asn1TaggedObject) obj).GetObject()); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + /** + * Constructor from Asn1Sequence. + * + * the extensions are a list of constructed sequences, either with (Oid, OctetString) or (Oid, Boolean, OctetString) + */ + private X509Extensions( + Asn1Sequence seq) + { + this.ordering = Platform.CreateArrayList(); + + foreach (Asn1Encodable ae in seq) + { + Asn1Sequence s = Asn1Sequence.GetInstance(ae.ToAsn1Object()); + + if (s.Count < 2 || s.Count > 3) + throw new ArgumentException("Bad sequence size: " + s.Count); + + DerObjectIdentifier oid = DerObjectIdentifier.GetInstance(s[0].ToAsn1Object()); + + bool isCritical = s.Count == 3 + && DerBoolean.GetInstance(s[1].ToAsn1Object()).IsTrue; + + Asn1OctetString octets = Asn1OctetString.GetInstance(s[s.Count - 1].ToAsn1Object()); + + extensions.Add(oid, new X509Extension(isCritical, octets)); + ordering.Add(oid); + } + } + + /** + * constructor from a table of extensions. + * <p> + * it's is assumed the table contains Oid/string pairs.</p> + */ + public X509Extensions( + IDictionary extensions) + : this(null, extensions) + { + } + + /** + * Constructor from a table of extensions with ordering. + * <p> + * It's is assumed the table contains Oid/string pairs.</p> + */ + public X509Extensions( + IList ordering, + IDictionary extensions) + { + if (ordering == null) + { + this.ordering = Platform.CreateArrayList(extensions.Keys); + } + else + { + this.ordering = Platform.CreateArrayList(ordering); + } + + foreach (DerObjectIdentifier oid in this.ordering) + { + this.extensions.Add(oid, (X509Extension)extensions[oid]); + } + } + + /** + * Constructor from two vectors + * + * @param objectIDs an ArrayList of the object identifiers. + * @param values an ArrayList of the extension values. + */ + public X509Extensions( + IList oids, + IList values) + { + this.ordering = Platform.CreateArrayList(oids); + + int count = 0; + foreach (DerObjectIdentifier oid in this.ordering) + { + this.extensions.Add(oid, (X509Extension)values[count++]); + } + } + +#if !(SILVERLIGHT || PORTABLE) + /** + * constructor from a table of extensions. + * <p> + * it's is assumed the table contains Oid/string pairs.</p> + */ + [Obsolete] + 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.</p> + */ + [Obsolete] + public X509Extensions( + ArrayList ordering, + Hashtable extensions) + { + if (ordering == null) + { + this.ordering = Platform.CreateArrayList(extensions.Keys); + } + else + { + this.ordering = Platform.CreateArrayList(ordering); + } + + foreach (DerObjectIdentifier oid in this.ordering) + { + this.extensions.Add(oid, (X509Extension) extensions[oid]); + } + } + + /** + * Constructor from two vectors + * + * @param objectIDs an ArrayList of the object identifiers. + * @param values an ArrayList of the extension values. + */ + [Obsolete] + public X509Extensions( + ArrayList oids, + ArrayList values) + { + this.ordering = Platform.CreateArrayList(oids); + + int count = 0; + foreach (DerObjectIdentifier oid in this.ordering) + { + this.extensions.Add(oid, (X509Extension) values[count++]); + } + } +#endif + + [Obsolete("Use ExtensionOids IEnumerable property")] + public IEnumerator Oids() + { + return ExtensionOids.GetEnumerator(); + } + + /** + * return an Enumeration of the extension field's object ids. + */ + public IEnumerable ExtensionOids + { + get { return new EnumerableProxy(ordering); } + } + + /** + * 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[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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector vec = new Asn1EncodableVector(); + + foreach (DerObjectIdentifier oid in ordering) + { + X509Extension ext = (X509Extension) extensions[oid]; + Asn1EncodableVector v = new Asn1EncodableVector(oid); + + if (ext.IsCritical) + { + v.Add(DerBoolean.True); + } + + v.Add(ext.Value); + + vec.Add(new DerSequence(v)); + } + + return new DerSequence(vec); + } + + public bool Equivalent( + X509Extensions other) + { + if (extensions.Count != other.extensions.Count) + return false; + + foreach (DerObjectIdentifier oid in extensions.Keys) + { + if (!extensions[oid].Equals(other.extensions[oid])) + return false; + } + + return true; + } + + public DerObjectIdentifier[] GetExtensionOids() + { + return ToOidArray(ordering); + } + + public DerObjectIdentifier[] GetNonCriticalExtensionOids() + { + return GetExtensionOids(false); + } + + public DerObjectIdentifier[] GetCriticalExtensionOids() + { + return GetExtensionOids(true); + } + + private DerObjectIdentifier[] GetExtensionOids(bool isCritical) + { + IList oids = Platform.CreateArrayList(); + + foreach (DerObjectIdentifier oid in this.ordering) + { + X509Extension ext = (X509Extension)extensions[oid]; + if (ext.IsCritical == isCritical) + { + oids.Add(oid); + } + } + + return ToOidArray(oids); + } + + private static DerObjectIdentifier[] ToOidArray(IList oids) + { + DerObjectIdentifier[] oidArray = new DerObjectIdentifier[oids.Count]; + oids.CopyTo(oidArray, 0); + return oidArray; + } + } +} diff --git a/Crypto/src/asn1/x509/X509ExtensionsGenerator.cs b/Crypto/src/asn1/x509/X509ExtensionsGenerator.cs new file mode 100644
index 000000000..d6f567b22 --- /dev/null +++ b/Crypto/src/asn1/x509/X509ExtensionsGenerator.cs
@@ -0,0 +1,81 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /// <remarks>Generator for X.509 extensions</remarks> + public class X509ExtensionsGenerator + { + private IDictionary extensions = Platform.CreateHashtable(); + private IList extOrdering = Platform.CreateArrayList(); + + /// <summary>Reset the generator</summary> + public void Reset() + { + extensions = Platform.CreateHashtable(); + extOrdering = Platform.CreateArrayList(); + } + + /// <summary> + /// Add an extension with the given oid and the passed in value to be included + /// in the OCTET STRING associated with the extension. + /// </summary> + /// <param name="oid">OID for the extension.</param> + /// <param name="critical">True if critical, false otherwise.</param> + /// <param name="extValue">The ASN.1 object to be included in the extension.</param> + public void AddExtension( + DerObjectIdentifier oid, + bool critical, + Asn1Encodable extValue) + { + byte[] encoded; + try + { + encoded = extValue.GetDerEncoded(); + } + catch (Exception e) + { + throw new ArgumentException("error encoding value: " + e); + } + + this.AddExtension(oid, critical, encoded); + } + + /// <summary> + /// Add an extension with the given oid and the passed in byte array to be wrapped + /// in the OCTET STRING associated with the extension. + /// </summary> + /// <param name="oid">OID for the extension.</param> + /// <param name="critical">True if critical, false otherwise.</param> + /// <param name="extValue">The byte array to be wrapped.</param> + public void AddExtension( + DerObjectIdentifier oid, + bool critical, + byte[] extValue) + { + if (extensions.Contains(oid)) + { + throw new ArgumentException("extension " + oid + " already added"); + } + + extOrdering.Add(oid); + extensions.Add(oid, new X509Extension(critical, new DerOctetString(extValue))); + } + + /// <summary>Return true if there are no extension present in this generator.</summary> + /// <returns>True if empty, false otherwise</returns> + public bool IsEmpty + { + get { return extOrdering.Count < 1; } + } + + /// <summary>Generate an X509Extensions object based on the current state of the generator.</summary> + /// <returns>An <c>X509Extensions</c> object</returns> + public X509Extensions Generate() + { + return new X509Extensions(extOrdering, extensions); + } + } +} diff --git a/Crypto/src/asn1/x509/X509Name.cs b/Crypto/src/asn1/x509/X509Name.cs new file mode 100644
index 000000000..b459cbe1b --- /dev/null +++ b/Crypto/src/asn1/x509/X509Name.cs
@@ -0,0 +1,1189 @@ +using System; +using System.Collections; +using System.Globalization; +using System.IO; +using System.Text; + +#if (SILVERLIGHT || PORTABLE) +using System.Collections.Generic; +#endif + +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * <pre> + * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName + * + * RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue + * + * AttributeTypeAndValue ::= SEQUENCE { + * type OBJECT IDENTIFIER, + * value ANY } + * </pre> + */ + public class X509Name + : Asn1Encodable + { + /** + * country code - StringType(SIZE(2)) + */ + public static readonly DerObjectIdentifier C = new DerObjectIdentifier("2.5.4.6"); + + /** + * organization - StringType(SIZE(1..64)) + */ + public static readonly DerObjectIdentifier O = new DerObjectIdentifier("2.5.4.10"); + + /** + * organizational unit name - StringType(SIZE(1..64)) + */ + public static readonly DerObjectIdentifier OU = new DerObjectIdentifier("2.5.4.11"); + + /** + * Title + */ + public static readonly DerObjectIdentifier T = new DerObjectIdentifier("2.5.4.12"); + + /** + * common name - StringType(SIZE(1..64)) + */ + public static readonly DerObjectIdentifier CN = new DerObjectIdentifier("2.5.4.3"); + + /** + * street - StringType(SIZE(1..64)) + */ + public static readonly DerObjectIdentifier Street = new DerObjectIdentifier("2.5.4.9"); + + /** + * device serial number name - StringType(SIZE(1..64)) + */ + public static readonly DerObjectIdentifier SerialNumber = new DerObjectIdentifier("2.5.4.5"); + + /** + * locality name - StringType(SIZE(1..64)) + */ + public static readonly DerObjectIdentifier L = new DerObjectIdentifier("2.5.4.7"); + + /** + * state, or province name - StringType(SIZE(1..64)) + */ + public static readonly DerObjectIdentifier ST = new DerObjectIdentifier("2.5.4.8"); + + /** + * Naming attributes of type X520name + */ + public static readonly DerObjectIdentifier Surname = new DerObjectIdentifier("2.5.4.4"); + public static readonly DerObjectIdentifier GivenName = new DerObjectIdentifier("2.5.4.42"); + public static readonly DerObjectIdentifier Initials = new DerObjectIdentifier("2.5.4.43"); + public static readonly DerObjectIdentifier Generation = new DerObjectIdentifier("2.5.4.44"); + public static readonly DerObjectIdentifier UniqueIdentifier = new DerObjectIdentifier("2.5.4.45"); + + /** + * businessCategory - DirectoryString(SIZE(1..128) + */ + public static readonly DerObjectIdentifier BusinessCategory = new DerObjectIdentifier( + "2.5.4.15"); + + /** + * postalCode - DirectoryString(SIZE(1..40) + */ + public static readonly DerObjectIdentifier PostalCode = new DerObjectIdentifier( + "2.5.4.17"); + + /** + * dnQualifier - DirectoryString(SIZE(1..64) + */ + public static readonly DerObjectIdentifier DnQualifier = new DerObjectIdentifier( + "2.5.4.46"); + + /** + * RFC 3039 Pseudonym - DirectoryString(SIZE(1..64) + */ + public static readonly DerObjectIdentifier Pseudonym = new DerObjectIdentifier( + "2.5.4.65"); + + /** + * RFC 3039 DateOfBirth - GeneralizedTime - YYYYMMDD000000Z + */ + public static readonly DerObjectIdentifier DateOfBirth = new DerObjectIdentifier( + "1.3.6.1.5.5.7.9.1"); + + /** + * RFC 3039 PlaceOfBirth - DirectoryString(SIZE(1..128) + */ + public static readonly DerObjectIdentifier PlaceOfBirth = new DerObjectIdentifier( + "1.3.6.1.5.5.7.9.2"); + + /** + * RFC 3039 DateOfBirth - PrintableString (SIZE(1)) -- "M", "F", "m" or "f" + */ + public static readonly DerObjectIdentifier Gender = new DerObjectIdentifier( + "1.3.6.1.5.5.7.9.3"); + + /** + * RFC 3039 CountryOfCitizenship - PrintableString (SIZE (2)) -- ISO 3166 + * codes only + */ + public static readonly DerObjectIdentifier CountryOfCitizenship = new DerObjectIdentifier( + "1.3.6.1.5.5.7.9.4"); + + /** + * RFC 3039 CountryOfCitizenship - PrintableString (SIZE (2)) -- ISO 3166 + * codes only + */ + public static readonly DerObjectIdentifier CountryOfResidence = new DerObjectIdentifier( + "1.3.6.1.5.5.7.9.5"); + + /** + * ISIS-MTT NameAtBirth - DirectoryString(SIZE(1..64) + */ + public static readonly DerObjectIdentifier NameAtBirth = new DerObjectIdentifier("1.3.36.8.3.14"); + + /** + * RFC 3039 PostalAddress - SEQUENCE SIZE (1..6) OF + * DirectoryString(SIZE(1..30)) + */ + public static readonly DerObjectIdentifier PostalAddress = new DerObjectIdentifier("2.5.4.16"); + + /** + * RFC 2256 dmdName + */ + public static readonly DerObjectIdentifier DmdName = new DerObjectIdentifier("2.5.4.54"); + + /** + * id-at-telephoneNumber + */ + public static readonly DerObjectIdentifier TelephoneNumber = X509ObjectIdentifiers.id_at_telephoneNumber; + + /** + * id-at-name + */ + public static readonly DerObjectIdentifier 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.</p> + */ + public static readonly DerObjectIdentifier EmailAddress = PkcsObjectIdentifiers.Pkcs9AtEmailAddress; + + /** + * more from PKCS#9 + */ + public static readonly DerObjectIdentifier UnstructuredName = PkcsObjectIdentifiers.Pkcs9AtUnstructuredName; + public static readonly DerObjectIdentifier UnstructuredAddress = PkcsObjectIdentifiers.Pkcs9AtUnstructuredAddress; + + /** + * email address in Verisign certificates + */ + public static readonly DerObjectIdentifier E = EmailAddress; + + /* + * others... + */ + public static readonly DerObjectIdentifier DC = new DerObjectIdentifier("0.9.2342.19200300.100.1.25"); + + /** + * LDAP User id. + */ + public static readonly DerObjectIdentifier UID = new DerObjectIdentifier("0.9.2342.19200300.100.1.1"); + + /** + * determines whether or not strings should be processed and printed + * from back to front. + */ +// public static bool DefaultReverse = false; + public static bool DefaultReverse + { + get { return defaultReverse[0]; } + set { defaultReverse[0] = value; } + } + + private static readonly bool[] defaultReverse = { false }; + +#if (SILVERLIGHT || PORTABLE) + /** + * default look up table translating OID values into their common symbols following + * the convention in RFC 2253 with a few extras + */ + public static readonly IDictionary DefaultSymbols = Platform.CreateHashtable(); + + /** + * look up table translating OID values into their common symbols following the convention in RFC 2253 + */ + public static readonly IDictionary RFC2253Symbols = Platform.CreateHashtable(); + + /** + * look up table translating OID values into their common symbols following the convention in RFC 1779 + * + */ + public static readonly IDictionary RFC1779Symbols = Platform.CreateHashtable(); + + /** + * look up table translating common symbols into their OIDS. + */ + public static readonly IDictionary DefaultLookup = Platform.CreateHashtable(); +#else + /** + * default look up table translating OID values into their common symbols following + * the convention in RFC 2253 with a few extras + */ + public static readonly Hashtable DefaultSymbols = new Hashtable(); + + /** + * look up table translating OID values into their common symbols following the convention in RFC 2253 + */ + public static readonly Hashtable RFC2253Symbols = new Hashtable(); + + /** + * look up table translating OID values into their common symbols following the convention in RFC 1779 + * + */ + public static readonly Hashtable RFC1779Symbols = new Hashtable(); + + /** + * look up table translating common symbols into their OIDS. + */ + public static readonly Hashtable DefaultLookup = new Hashtable(); +#endif + + static X509Name() + { + DefaultSymbols.Add(C, "C"); + DefaultSymbols.Add(O, "O"); + DefaultSymbols.Add(T, "T"); + DefaultSymbols.Add(OU, "OU"); + DefaultSymbols.Add(CN, "CN"); + DefaultSymbols.Add(L, "L"); + DefaultSymbols.Add(ST, "ST"); + DefaultSymbols.Add(SerialNumber, "SERIALNUMBER"); + DefaultSymbols.Add(EmailAddress, "E"); + DefaultSymbols.Add(DC, "DC"); + DefaultSymbols.Add(UID, "UID"); + DefaultSymbols.Add(Street, "STREET"); + DefaultSymbols.Add(Surname, "SURNAME"); + DefaultSymbols.Add(GivenName, "GIVENNAME"); + DefaultSymbols.Add(Initials, "INITIALS"); + DefaultSymbols.Add(Generation, "GENERATION"); + DefaultSymbols.Add(UnstructuredAddress, "unstructuredAddress"); + DefaultSymbols.Add(UnstructuredName, "unstructuredName"); + DefaultSymbols.Add(UniqueIdentifier, "UniqueIdentifier"); + DefaultSymbols.Add(DnQualifier, "DN"); + DefaultSymbols.Add(Pseudonym, "Pseudonym"); + DefaultSymbols.Add(PostalAddress, "PostalAddress"); + DefaultSymbols.Add(NameAtBirth, "NameAtBirth"); + DefaultSymbols.Add(CountryOfCitizenship, "CountryOfCitizenship"); + DefaultSymbols.Add(CountryOfResidence, "CountryOfResidence"); + DefaultSymbols.Add(Gender, "Gender"); + DefaultSymbols.Add(PlaceOfBirth, "PlaceOfBirth"); + DefaultSymbols.Add(DateOfBirth, "DateOfBirth"); + DefaultSymbols.Add(PostalCode, "PostalCode"); + DefaultSymbols.Add(BusinessCategory, "BusinessCategory"); + DefaultSymbols.Add(TelephoneNumber, "TelephoneNumber"); + + RFC2253Symbols.Add(C, "C"); + RFC2253Symbols.Add(O, "O"); + RFC2253Symbols.Add(OU, "OU"); + RFC2253Symbols.Add(CN, "CN"); + RFC2253Symbols.Add(L, "L"); + RFC2253Symbols.Add(ST, "ST"); + RFC2253Symbols.Add(Street, "STREET"); + RFC2253Symbols.Add(DC, "DC"); + RFC2253Symbols.Add(UID, "UID"); + + RFC1779Symbols.Add(C, "C"); + RFC1779Symbols.Add(O, "O"); + RFC1779Symbols.Add(OU, "OU"); + RFC1779Symbols.Add(CN, "CN"); + RFC1779Symbols.Add(L, "L"); + RFC1779Symbols.Add(ST, "ST"); + RFC1779Symbols.Add(Street, "STREET"); + + DefaultLookup.Add("c", C); + DefaultLookup.Add("o", O); + DefaultLookup.Add("t", T); + DefaultLookup.Add("ou", OU); + DefaultLookup.Add("cn", CN); + DefaultLookup.Add("l", L); + DefaultLookup.Add("st", ST); + DefaultLookup.Add("serialnumber", SerialNumber); + DefaultLookup.Add("street", Street); + DefaultLookup.Add("emailaddress", E); + DefaultLookup.Add("dc", DC); + DefaultLookup.Add("e", E); + DefaultLookup.Add("uid", UID); + DefaultLookup.Add("surname", Surname); + DefaultLookup.Add("givenname", GivenName); + DefaultLookup.Add("initials", Initials); + DefaultLookup.Add("generation", Generation); + DefaultLookup.Add("unstructuredaddress", UnstructuredAddress); + DefaultLookup.Add("unstructuredname", UnstructuredName); + DefaultLookup.Add("uniqueidentifier", UniqueIdentifier); + DefaultLookup.Add("dn", DnQualifier); + DefaultLookup.Add("pseudonym", Pseudonym); + DefaultLookup.Add("postaladdress", PostalAddress); + DefaultLookup.Add("nameofbirth", NameAtBirth); + DefaultLookup.Add("countryofcitizenship", CountryOfCitizenship); + DefaultLookup.Add("countryofresidence", CountryOfResidence); + DefaultLookup.Add("gender", Gender); + DefaultLookup.Add("placeofbirth", PlaceOfBirth); + DefaultLookup.Add("dateofbirth", DateOfBirth); + DefaultLookup.Add("postalcode", PostalCode); + DefaultLookup.Add("businesscategory", BusinessCategory); + DefaultLookup.Add("telephonenumber", TelephoneNumber); + } + + private readonly IList ordering = Platform.CreateArrayList(); + private readonly X509NameEntryConverter converter; + + private IList values = Platform.CreateArrayList(); + private IList added = Platform.CreateArrayList(); + private Asn1Sequence seq; + + /** + * Return a X509Name based on the passed in tagged object. + * + * @param obj tag object holding name. + * @param explicitly true if explicitly tagged false otherwise. + * @return the X509Name + */ + public static X509Name GetInstance( + Asn1TaggedObject obj, + bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } + + public static X509Name GetInstance( + object obj) + { + if (obj == null || obj is X509Name) + return (X509Name)obj; + + if (obj != null) + return new X509Name(Asn1Sequence.GetInstance(obj)); + + throw new ArgumentException("null object in factory", "obj"); + } + + protected X509Name() + { + } + + /** + * Constructor from Asn1Sequence + * + * the principal will be a list of constructed sets, each containing an (OID, string) pair. + */ + protected X509Name( + Asn1Sequence seq) + { + this.seq = seq; + + foreach (Asn1Encodable asn1Obj in seq) + { + Asn1Set asn1Set = Asn1Set.GetInstance(asn1Obj.ToAsn1Object()); + + for (int i = 0; i < asn1Set.Count; i++) + { + Asn1Sequence s = Asn1Sequence.GetInstance(asn1Set[i].ToAsn1Object()); + + if (s.Count != 2) + throw new ArgumentException("badly sized pair"); + + ordering.Add(DerObjectIdentifier.GetInstance(s[0].ToAsn1Object())); + + Asn1Object derValue = s[1].ToAsn1Object(); + if (derValue is IAsn1String && !(derValue is DerUniversalString)) + { + string v = ((IAsn1String)derValue).GetString(); + if (v.StartsWith("#")) + { + v = "\\" + v; + } + + values.Add(v); + } + else + { + values.Add("#" + Hex.ToHexString(derValue.GetEncoded())); + } + + added.Add(i != 0); + } + } + } + +#if !(SILVERLIGHT || PORTABLE) + [Obsolete] + public X509Name( + ArrayList ordering, + Hashtable attributes) + : this(ordering, attributes, new X509DefaultEntryConverter()) + { + } +#endif + + /** + * 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 ArrayList should contain the OIDs + * in the order they are meant to be encoded or printed in ToString.</p> + */ + public X509Name( + IList ordering, + IDictionary attributes) + : this(ordering, attributes, new X509DefaultEntryConverter()) + { + } + +#if !(SILVERLIGHT || PORTABLE) + [Obsolete] + public X509Name( + ArrayList ordering, + Hashtable attributes, + X509NameEntryConverter converter) + : this((IList)ordering, (IDictionary)attributes, converter) + { + } +#endif + + /** + * 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 ArrayList should contain the OIDs + * in the order they are meant to be encoded or printed in ToString.</p> + * <p> + * The passed in converter will be used to convert the strings into their + * ASN.1 counterparts.</p> + */ + public X509Name( + IList ordering, + IDictionary attributes, + X509NameEntryConverter converter) + { + this.converter = converter; + + foreach (DerObjectIdentifier oid in ordering) + { + object attribute = attributes[oid]; + if (attribute == null) + { + throw new ArgumentException("No attribute for object id - " + oid + " - passed to distinguished name"); + } + + this.ordering.Add(oid); + this.added.Add(false); + this.values.Add(attribute); // copy the hash table + } + } + +#if !(SILVERLIGHT || PORTABLE) + [Obsolete] + public X509Name( + ArrayList oids, + ArrayList values) + : this(oids, values, new X509DefaultEntryConverter()) + { + } +#endif + + /** + * Takes two vectors one of the oids and the other of the values. + */ + public X509Name( + IList oids, + IList values) + : this(oids, values, new X509DefaultEntryConverter()) + { + } + +#if !(SILVERLIGHT || PORTABLE) + [Obsolete] + public X509Name( + ArrayList oids, + ArrayList values, + X509NameEntryConverter converter) + : this((IList)oids, (IList)values, converter) + { + } +#endif + + /** + * 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.</p> + */ + public X509Name( + IList oids, + IList values, + X509NameEntryConverter converter) + { + this.converter = converter; + + if (oids.Count != values.Count) + { + throw new ArgumentException("'oids' must be same length as 'values'."); + } + + for (int i = 0; i < oids.Count; i++) + { + this.ordering.Add(oids[i]); + this.values.Add(values[i]); + this.added.Add(false); + } + } + +// private static bool IsEncoded( +// string s) +// { +// return s.StartsWith("#"); +// } + + /** + * 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. + */ + public X509Name( + string dirName) + : this(DefaultReverse, (IDictionary)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. + */ + 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. + */ + public X509Name( + bool reverse, + string dirName) + : this(reverse, (IDictionary)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. + */ + public X509Name( + bool reverse, + string dirName, + X509NameEntryConverter converter) + : this(reverse, DefaultLookup, dirName, converter) + { + } + +#if !(SILVERLIGHT || PORTABLE) + [Obsolete] + public X509Name( + bool reverse, + Hashtable lookUp, + string dirName) + : this(reverse, lookUp, dirName, new X509DefaultEntryConverter()) + { + } +#endif + + /** + * 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 DerObjectIdentifier, 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. + */ + public X509Name( + bool reverse, + IDictionary lookUp, + string dirName) + : this(reverse, lookUp, dirName, new X509DefaultEntryConverter()) + { + } + + private DerObjectIdentifier DecodeOid( + string name, + IDictionary lookUp) + { + if (name.ToUpperInvariant().StartsWith("OID.")) + { + return new DerObjectIdentifier(name.Substring(4)); + } + else if (name[0] >= '0' && name[0] <= '9') + { + return new DerObjectIdentifier(name); + } + + DerObjectIdentifier oid = (DerObjectIdentifier)lookUp[name.ToLowerInvariant()]; + if (oid == null) + { + throw new ArgumentException("Unknown object id - " + name + " - passed to distinguished name"); + } + + return oid; + } + + /** + * 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 DerObjectIdentifier, 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( + bool reverse, + IDictionary lookUp, + string dirName, + X509NameEntryConverter converter) + { + this.converter = converter; + X509NameTokenizer nTok = new X509NameTokenizer(dirName); + + while (nTok.HasMoreTokens()) + { + string token = nTok.NextToken(); + int index = token.IndexOf('='); + + if (index == -1) + { + throw new ArgumentException("badly formated directory string"); + } + + string name = token.Substring(0, index); + string value = token.Substring(index + 1); + DerObjectIdentifier oid = DecodeOid(name, lookUp); + + if (value.IndexOf('+') > 0) + { + X509NameTokenizer vTok = new X509NameTokenizer(value, '+'); + string v = vTok.NextToken(); + + this.ordering.Add(oid); + this.values.Add(v); + this.added.Add(false); + + while (vTok.HasMoreTokens()) + { + string sv = vTok.NextToken(); + int ndx = sv.IndexOf('='); + + string nm = sv.Substring(0, ndx); + string vl = sv.Substring(ndx + 1); + this.ordering.Add(DecodeOid(nm, lookUp)); + this.values.Add(vl); + this.added.Add(true); + } + } + else + { + this.ordering.Add(oid); + this.values.Add(value); + this.added.Add(false); + } + } + + if (reverse) + { +// this.ordering.Reverse(); +// this.values.Reverse(); +// this.added.Reverse(); + IList o = Platform.CreateArrayList(); + IList v = Platform.CreateArrayList(); + IList a = Platform.CreateArrayList(); + int count = 1; + + for (int i = 0; i < this.ordering.Count; i++) + { + if (!((bool) this.added[i])) + { + count = 0; + } + + int index = count++; + + o.Insert(index, this.ordering[i]); + v.Insert(index, this.values[i]); + a.Insert(index, this.added[i]); + } + + this.ordering = o; + this.values = v; + this.added = a; + } + } + +#if !(SILVERLIGHT || PORTABLE) + /** + * return an ArrayList of the oids in the name, in the order they were found. + */ + [Obsolete("Use 'GetOidList' instead")] + public ArrayList GetOids() + { + return new ArrayList(ordering); + } +#endif + + /** + * return an IList of the oids in the name, in the order they were found. + */ + public IList GetOidList() + { + return Platform.CreateArrayList(ordering); + } + +#if !(SILVERLIGHT || PORTABLE) + /** + * return an ArrayList of the values found in the name, in the order they + * were found. + */ + [Obsolete("Use 'GetValueList' instead")] + public ArrayList GetValues() + { + return new ArrayList(values); + } +#endif + + /** + * return an IList of the values found in the name, in the order they + * were found. + */ + public IList GetValueList() + { + return Platform.CreateArrayList(values); + } + +#if !(SILVERLIGHT || PORTABLE) + /** + * return an ArrayList of the values found in the name, in the order they + * were found, with the DN label corresponding to passed in oid. + */ + public ArrayList GetValues( + DerObjectIdentifier oid) + { + ArrayList v = new ArrayList(); + DoGetValueList(oid, v); + return v; + } +#endif + + /** + * return an IList of the values found in the name, in the order they + * were found, with the DN label corresponding to passed in oid. + */ + public IList GetValueList(DerObjectIdentifier oid) + { + IList v = Platform.CreateArrayList(); + DoGetValueList(oid, v); + return v; + } + + private void DoGetValueList(DerObjectIdentifier oid, IList v) + { + for (int i = 0; i != values.Count; i++) + { + if (ordering[i].Equals(oid)) + { + string val = (string)values[i]; + + if (val.StartsWith("\\#")) + { + val = val.Substring(1); + } + + v.Add(val); + } + } + } + + public override Asn1Object ToAsn1Object() + { + if (seq == null) + { + Asn1EncodableVector vec = new Asn1EncodableVector(); + Asn1EncodableVector sVec = new Asn1EncodableVector(); + DerObjectIdentifier lstOid = null; + + for (int i = 0; i != ordering.Count; i++) + { + DerObjectIdentifier oid = (DerObjectIdentifier)ordering[i]; + string str = (string)values[i]; + + if (lstOid == null + || ((bool)this.added[i])) + { + } + else + { + vec.Add(new DerSet(sVec)); + sVec = new Asn1EncodableVector(); + } + + sVec.Add( + new DerSequence( + oid, + converter.GetConvertedValue(oid, str))); + + lstOid = oid; + } + + vec.Add(new DerSet(sVec)); + + seq = new DerSequence(vec); + } + + return seq; + } + + /// <param name="other">The X509Name object to test equivalency against.</param> + /// <param name="inOrder">If true, the order of elements must be the same, + /// as well as the values associated with each element.</param> + public bool Equivalent( + X509Name other, + bool inOrder) + { + if (!inOrder) + return this.Equivalent(other); + + if (other == null) + return false; + + if (other == this) + return true; + + int orderingSize = ordering.Count; + + if (orderingSize != other.ordering.Count) + return false; + + for (int i = 0; i < orderingSize; i++) + { + DerObjectIdentifier oid = (DerObjectIdentifier) ordering[i]; + DerObjectIdentifier oOid = (DerObjectIdentifier) other.ordering[i]; + + if (!oid.Equals(oOid)) + return false; + + string val = (string) values[i]; + string oVal = (string) other.values[i]; + + if (!equivalentStrings(val, oVal)) + return false; + } + + return true; + } + + /** + * test for equivalence - note: case is ignored. + */ + public bool Equivalent( + X509Name other) + { + if (other == null) + return false; + + if (other == this) + return true; + + int orderingSize = ordering.Count; + + if (orderingSize != other.ordering.Count) + { + return false; + } + + bool[] indexes = new bool[orderingSize]; + int start, end, delta; + + if (ordering[0].Equals(other.ordering[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) + { + bool found = false; + DerObjectIdentifier oid = (DerObjectIdentifier)ordering[i]; + string value = (string)values[i]; + + for (int j = 0; j < orderingSize; j++) + { + if (indexes[j]) + { + continue; + } + + DerObjectIdentifier oOid = (DerObjectIdentifier)other.ordering[j]; + + if (oid.Equals(oOid)) + { + string oValue = (string)other.values[j]; + + if (equivalentStrings(value, oValue)) + { + indexes[j] = true; + found = true; + break; + } + } + } + + if (!found) + { + return false; + } + } + + return true; + } + + private static bool equivalentStrings( + string s1, + string s2) + { + string v1 = canonicalize(s1); + string v2 = canonicalize(s2); + + if (!v1.Equals(v2)) + { + v1 = stripInternalSpaces(v1); + v2 = stripInternalSpaces(v2); + + if (!v1.Equals(v2)) + { + return false; + } + } + + return true; + } + + private static string canonicalize( + string s) + { + string v = s.ToLowerInvariant().Trim(); + + if (v.StartsWith("#")) + { + Asn1Object obj = decodeObject(v); + + if (obj is IAsn1String) + { + v = ((IAsn1String)obj).GetString().ToLowerInvariant().Trim(); + } + } + + return v; + } + + private static Asn1Object decodeObject( + string v) + { + try + { + return Asn1Object.FromByteArray(Hex.Decode(v.Substring(1))); + } + catch (IOException e) + { + throw new InvalidOperationException("unknown encoding in name: " + e.Message, e); + } + } + + private static string stripInternalSpaces( + string str) + { + StringBuilder res = new StringBuilder(); + + if (str.Length != 0) + { + char c1 = str[0]; + + res.Append(c1); + + for (int k = 1; k < str.Length; k++) + { + char c2 = str[k]; + if (!(c1 == ' ' && c2 == ' ')) + { + res.Append(c2); + } + c1 = c2; + } + } + + return res.ToString(); + } + + private void AppendValue( + StringBuilder buf, + IDictionary oidSymbols, + DerObjectIdentifier oid, + string val) + { + string sym = (string)oidSymbols[oid]; + + if (sym != null) + { + buf.Append(sym); + } + else + { + buf.Append(oid.Id); + } + + buf.Append('='); + + int index = buf.Length; + + buf.Append(val); + + int end = buf.Length; + + if (val.StartsWith("\\#")) + { + index += 2; + } + + while (index != end) + { + if ((buf[index] == ',') + || (buf[index] == '"') + || (buf[index] == '\\') + || (buf[index] == '+') + || (buf[index] == '=') + || (buf[index] == '<') + || (buf[index] == '>') + || (buf[index] == ';')) + { + buf.Insert(index++, "\\"); + end++; + } + + index++; + } + } + +#if !(SILVERLIGHT || PORTABLE) + [Obsolete] + public string ToString( + bool reverse, + Hashtable oidSymbols) + { + return ToString(reverse, (IDictionary)oidSymbols); + } +#endif + + /** + * 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( + bool reverse, + IDictionary oidSymbols) + { +#if (SILVERLIGHT || PORTABLE) + List<object> components = new List<object>(); +#else + ArrayList components = new ArrayList(); +#endif + + StringBuilder ava = null; + + for (int i = 0; i < ordering.Count; i++) + { + if ((bool) added[i]) + { + ava.Append('+'); + AppendValue(ava, oidSymbols, + (DerObjectIdentifier)ordering[i], + (string)values[i]); + } + else + { + ava = new StringBuilder(); + AppendValue(ava, oidSymbols, + (DerObjectIdentifier)ordering[i], + (string)values[i]); + components.Add(ava); + } + } + + if (reverse) + { + components.Reverse(); + } + + StringBuilder buf = new StringBuilder(); + + if (components.Count > 0) + { + buf.Append(components[0].ToString()); + + for (int i = 1; i < components.Count; ++i) + { + buf.Append(','); + buf.Append(components[i].ToString()); + } + } + + return buf.ToString(); + } + + public override string ToString() + { + return ToString(DefaultReverse, (IDictionary)DefaultSymbols); + } + } +} diff --git a/Crypto/src/asn1/x509/X509NameEntryConverter.cs b/Crypto/src/asn1/x509/X509NameEntryConverter.cs new file mode 100644
index 000000000..5872656a9 --- /dev/null +++ b/Crypto/src/asn1/x509/X509NameEntryConverter.cs
@@ -0,0 +1,89 @@ +using System; +using System.Globalization; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Asn1.X509 +{ + /** + * 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 + * : X509NameEntryConverter + * { + * public Asn1Object GetConvertedValue( + * DerObjectIdentifier oid, + * string value) + * { + * if (str.Length() != 0 &amp;&amp; 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); + * } + * } + * } + * </pre> + * </p> + */ + 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 Asn1Object ConvertHexEncoded( + string hexString, + int offset) + { + string str = hexString.Substring(offset); + + return Asn1Object.FromByteArray(Hex.Decode(str)); + } + + /** + * return true if the passed in string can be represented without + * loss as a PrintableString, false otherwise. + */ + protected bool 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 Asn1Object GetConvertedValue(DerObjectIdentifier oid, string value); + } +} diff --git a/Crypto/src/asn1/x509/X509NameTokenizer.cs b/Crypto/src/asn1/x509/X509NameTokenizer.cs new file mode 100644
index 000000000..ab5529535 --- /dev/null +++ b/Crypto/src/asn1/x509/X509NameTokenizer.cs
@@ -0,0 +1,104 @@ +using System.Text; + +namespace 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. + */ + public class X509NameTokenizer + { + private string value; + private int index; + private char separator; + private StringBuilder buffer = new StringBuilder(); + + public X509NameTokenizer( + string oid) + : this(oid, ',') + { + } + + public X509NameTokenizer( + string oid, + char separator) + { + this.value = oid; + this.index = -1; + this.separator = separator; + } + + public bool HasMoreTokens() + { + return index != value.Length; + } + + public string NextToken() + { + if (index == value.Length) + { + return null; + } + + int end = index + 1; + bool quoted = false; + bool escaped = false; + + buffer.Remove(0, buffer.Length); + + while (end != value.Length) + { + char c = value[end]; + + if (c == '"') + { + if (!escaped) + { + quoted = !quoted; + } + else + { + buffer.Append(c); + escaped = false; + } + } + else + { + if (escaped || quoted) + { + if (c == '#' && buffer[buffer.Length - 1] == '=') + { + buffer.Append('\\'); + } + else if (c == '+' && separator != '+') + { + buffer.Append('\\'); + } + buffer.Append(c); + escaped = false; + } + else if (c == '\\') + { + escaped = true; + } + else if (c == separator) + { + break; + } + else + { + buffer.Append(c); + } + } + + end++; + } + + index = end; + + return buffer.ToString().Trim(); + } + } +} diff --git a/Crypto/src/asn1/x509/X509ObjectIdentifiers.cs b/Crypto/src/asn1/x509/X509ObjectIdentifiers.cs new file mode 100644
index 000000000..f00e31475 --- /dev/null +++ b/Crypto/src/asn1/x509/X509ObjectIdentifiers.cs
@@ -0,0 +1,59 @@ +namespace Org.BouncyCastle.Asn1.X509 +{ + public abstract class X509ObjectIdentifiers + { + // + // base id + // + internal const string ID = "2.5.4"; + + public static readonly DerObjectIdentifier CommonName = new DerObjectIdentifier(ID + ".3"); + public static readonly DerObjectIdentifier CountryName = new DerObjectIdentifier(ID + ".6"); + public static readonly DerObjectIdentifier LocalityName = new DerObjectIdentifier(ID + ".7"); + public static readonly DerObjectIdentifier StateOrProvinceName = new DerObjectIdentifier(ID + ".8"); + public static readonly DerObjectIdentifier Organization = new DerObjectIdentifier(ID + ".10"); + public static readonly DerObjectIdentifier OrganizationalUnitName = new DerObjectIdentifier(ID + ".11"); + + public static readonly DerObjectIdentifier id_at_telephoneNumber = new DerObjectIdentifier(ID + ".20"); + public static readonly DerObjectIdentifier id_at_name = new DerObjectIdentifier(ID + ".41"); + + // id-SHA1 OBJECT IDENTIFIER ::= + // {iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 26 } // + public static readonly DerObjectIdentifier IdSha1 = new DerObjectIdentifier("1.3.14.3.2.26"); + + // + // ripemd160 OBJECT IDENTIFIER ::= + // {iso(1) identified-organization(3) TeleTrust(36) algorithm(3) hashAlgorithm(2) RipeMD-160(1)} + // + public static readonly DerObjectIdentifier RipeMD160 = new DerObjectIdentifier("1.3.36.3.2.1"); + + // + // ripemd160WithRSAEncryption OBJECT IDENTIFIER ::= + // {iso(1) identified-organization(3) TeleTrust(36) algorithm(3) signatureAlgorithm(3) rsaSignature(1) rsaSignatureWithripemd160(2) } + // + public static readonly DerObjectIdentifier RipeMD160WithRsaEncryption = new DerObjectIdentifier("1.3.36.3.3.1.2"); + + public static readonly DerObjectIdentifier IdEARsa = new DerObjectIdentifier("2.5.8.1.1"); + + // id-pkix + public static readonly DerObjectIdentifier IdPkix = new DerObjectIdentifier("1.3.6.1.5.5.7"); + + // + // private internet extensions + // + public static readonly DerObjectIdentifier IdPE = new DerObjectIdentifier(IdPkix + ".1"); + + // + // authority information access + // + public static readonly DerObjectIdentifier IdAD = new DerObjectIdentifier(IdPkix + ".48"); + public static readonly DerObjectIdentifier IdADCAIssuers = new DerObjectIdentifier(IdAD + ".2"); + public static readonly DerObjectIdentifier IdADOcsp = new DerObjectIdentifier(IdAD + ".1"); + + // + // OID for ocsp and crl uri in AuthorityInformationAccess extension + // + public static readonly DerObjectIdentifier OcspAccessMethod = IdADOcsp; + public static readonly DerObjectIdentifier CrlAccessMethod = IdADCAIssuers; + } +} diff --git a/Crypto/src/asn1/x509/qualified/BiometricData.cs b/Crypto/src/asn1/x509/qualified/BiometricData.cs new file mode 100644
index 000000000..61d7c99cb --- /dev/null +++ b/Crypto/src/asn1/x509/qualified/BiometricData.cs
@@ -0,0 +1,112 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.X509.Qualified +{ + /** + * The BiometricData object. + * <pre> + * BiometricData ::= SEQUENCE { + * typeOfBiometricData TypeOfBiometricData, + * hashAlgorithm AlgorithmIdentifier, + * biometricDataHash OCTET STRING, + * sourceDataUri IA5String OPTIONAL } + * </pre> + */ + public class BiometricData + : Asn1Encodable + { + private readonly TypeOfBiometricData typeOfBiometricData; + private readonly AlgorithmIdentifier hashAlgorithm; + private readonly Asn1OctetString biometricDataHash; + private readonly DerIA5String sourceDataUri; + + public static BiometricData GetInstance( + object obj) + { + if (obj == null || obj is BiometricData) + { + return (BiometricData)obj; + } + + if (obj is Asn1Sequence) + { + return new BiometricData(Asn1Sequence.GetInstance(obj)); + } + + throw new ArgumentException("unknown object in GetInstance: " + obj.GetType().FullName, "obj"); + } + + private BiometricData( + Asn1Sequence seq) + { + typeOfBiometricData = TypeOfBiometricData.GetInstance(seq[0]); + hashAlgorithm = AlgorithmIdentifier.GetInstance(seq[1]); + biometricDataHash = Asn1OctetString.GetInstance(seq[2]); + + if (seq.Count > 3) + { + sourceDataUri = DerIA5String.GetInstance(seq[3]); + } + } + + 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 TypeOfBiometricData + { + get { return typeOfBiometricData; } + } + + public AlgorithmIdentifier HashAlgorithm + { + get { return hashAlgorithm; } + } + + public Asn1OctetString BiometricDataHash + { + get { return biometricDataHash; } + } + + public DerIA5String SourceDataUri + { + get { return sourceDataUri; } + } + + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector seq = new Asn1EncodableVector( + typeOfBiometricData, hashAlgorithm, biometricDataHash); + + if (sourceDataUri != null) + { + seq.Add(sourceDataUri); + } + + return new DerSequence(seq); + } + } +} diff --git a/Crypto/src/asn1/x509/qualified/ETSIQCObjectIdentifiers.cs b/Crypto/src/asn1/x509/qualified/ETSIQCObjectIdentifiers.cs new file mode 100644
index 000000000..86a4eee0a --- /dev/null +++ b/Crypto/src/asn1/x509/qualified/ETSIQCObjectIdentifiers.cs
@@ -0,0 +1,19 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.X509.Qualified +{ + public abstract class EtsiQCObjectIdentifiers + { + // + // base id + // + public static readonly DerObjectIdentifier IdEtsiQcs = new DerObjectIdentifier("0.4.0.1862.1"); + + public static readonly DerObjectIdentifier IdEtsiQcsQcCompliance = new DerObjectIdentifier(IdEtsiQcs+".1"); + public static readonly DerObjectIdentifier IdEtsiQcsLimitValue = new DerObjectIdentifier(IdEtsiQcs+".2"); + public static readonly DerObjectIdentifier IdEtsiQcsRetentionPeriod = new DerObjectIdentifier(IdEtsiQcs+".3"); + public static readonly DerObjectIdentifier IdEtsiQcsQcSscd = new DerObjectIdentifier(IdEtsiQcs+".4"); + } +} diff --git a/Crypto/src/asn1/x509/qualified/Iso4217CurrencyCode.cs b/Crypto/src/asn1/x509/qualified/Iso4217CurrencyCode.cs new file mode 100644
index 000000000..3300562c8 --- /dev/null +++ b/Crypto/src/asn1/x509/qualified/Iso4217CurrencyCode.cs
@@ -0,0 +1,84 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.X509.Qualified +{ + /** + * 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 + : Asn1Encodable, IAsn1Choice + { + internal const int AlphabeticMaxSize = 3; + internal const int NumericMinSize = 1; + internal const int NumericMaxSize = 999; + + internal Asn1Encodable obj; +// internal int numeric; + + public static Iso4217CurrencyCode GetInstance( + object obj) + { + if (obj == null || obj is Iso4217CurrencyCode) + { + return (Iso4217CurrencyCode) obj; + } + + if (obj is DerInteger) + { + DerInteger numericobj = DerInteger.GetInstance(obj); + int numeric = numericobj.Value.IntValue; + return new Iso4217CurrencyCode(numeric); + } + + if (obj is DerPrintableString) + { + DerPrintableString alphabetic = DerPrintableString.GetInstance(obj); + return new Iso4217CurrencyCode(alphabetic.GetString()); + } + + throw new ArgumentException("unknown object in GetInstance: " + obj.GetType().FullName, "obj"); + } + + public Iso4217CurrencyCode( + int numeric) + { + if (numeric > NumericMaxSize || numeric < NumericMinSize) + { + throw new ArgumentException("wrong size in numeric code : not in (" +NumericMinSize +".."+ NumericMaxSize +")"); + } + + obj = new DerInteger(numeric); + } + + public Iso4217CurrencyCode( + string alphabetic) + { + if (alphabetic.Length > AlphabeticMaxSize) + { + throw new ArgumentException("wrong size in alphabetic code : max size is " + AlphabeticMaxSize); + } + + obj = new DerPrintableString(alphabetic); + } + + public bool IsAlphabetic { get { return obj is DerPrintableString; } } + + public string Alphabetic { get { return ((DerPrintableString) obj).GetString(); } } + + public int Numeric { get { return ((DerInteger)obj).Value.IntValue; } } + + public override Asn1Object ToAsn1Object() + { + return obj.ToAsn1Object(); + } + } +} diff --git a/Crypto/src/asn1/x509/qualified/MonetaryValue.cs b/Crypto/src/asn1/x509/qualified/MonetaryValue.cs new file mode 100644
index 000000000..45e113671 --- /dev/null +++ b/Crypto/src/asn1/x509/qualified/MonetaryValue.cs
@@ -0,0 +1,83 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Asn1.X509.Qualified +{ + /** + * The MonetaryValue object. + * <pre> + * MonetaryValue ::= SEQUENCE { + * currency Iso4217CurrencyCode, + * amount INTEGER, + * exponent INTEGER } + * -- value = amount * 10^exponent + * </pre> + */ + public class MonetaryValue + : Asn1Encodable + { + internal Iso4217CurrencyCode currency; + internal DerInteger amount; + internal DerInteger exponent; + + public static MonetaryValue GetInstance( + object obj) + { + if (obj == null || obj is MonetaryValue) + { + return (MonetaryValue) obj; + } + + if (obj is Asn1Sequence) + { + return new MonetaryValue(Asn1Sequence.GetInstance(obj)); + } + + throw new ArgumentException("unknown object in GetInstance: " + obj.GetType().FullName, "obj"); + } + + private MonetaryValue( + Asn1Sequence seq) + { + if (seq.Count != 3) + throw new ArgumentException("Bad sequence size: " + seq.Count, "seq"); + + currency = Iso4217CurrencyCode.GetInstance(seq[0]); + amount = DerInteger.GetInstance(seq[1]); + exponent = DerInteger.GetInstance(seq[2]); + } + + public MonetaryValue( + Iso4217CurrencyCode currency, + int amount, + int exponent) + { + this.currency = currency; + this.amount = new DerInteger(amount); + this.exponent = new DerInteger(exponent); + } + + public Iso4217CurrencyCode Currency + { + get { return currency; } + } + + public BigInteger Amount + { + get { return amount.Value; } + } + + public BigInteger Exponent + { + get { return exponent.Value; } + } + + public override Asn1Object ToAsn1Object() + { + return new DerSequence(currency, amount, exponent); + } + } +} diff --git a/Crypto/src/asn1/x509/qualified/QCStatement.cs b/Crypto/src/asn1/x509/qualified/QCStatement.cs new file mode 100644
index 000000000..317f03447 --- /dev/null +++ b/Crypto/src/asn1/x509/qualified/QCStatement.cs
@@ -0,0 +1,85 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.X509.Qualified +{ + /** + * The QCStatement object. + * <pre> + * QCStatement ::= SEQUENCE { + * statementId OBJECT IDENTIFIER, + * statementInfo ANY DEFINED BY statementId OPTIONAL} + * </pre> + */ + public class QCStatement + : Asn1Encodable + { + private readonly DerObjectIdentifier qcStatementId; + private readonly Asn1Encodable qcStatementInfo; + + public static QCStatement GetInstance( + object obj) + { + if (obj == null || obj is QCStatement) + { + return (QCStatement) obj; + } + + if (obj is Asn1Sequence) + { + return new QCStatement(Asn1Sequence.GetInstance(obj)); + } + + throw new ArgumentException("unknown object in GetInstance: " + obj.GetType().FullName, "obj"); + } + + private QCStatement( + Asn1Sequence seq) + { + qcStatementId = DerObjectIdentifier.GetInstance(seq[0]); + + if (seq.Count > 1) + { + qcStatementInfo = seq[1]; + } + } + + public QCStatement( + DerObjectIdentifier qcStatementId) + { + this.qcStatementId = qcStatementId; + } + + public QCStatement( + DerObjectIdentifier qcStatementId, + Asn1Encodable qcStatementInfo) + { + this.qcStatementId = qcStatementId; + this.qcStatementInfo = qcStatementInfo; + } + + public DerObjectIdentifier StatementId + { + get { return qcStatementId; } + } + + public Asn1Encodable StatementInfo + { + get { return qcStatementInfo; } + } + + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector seq = new Asn1EncodableVector(qcStatementId); + + if (qcStatementInfo != null) + { + seq.Add(qcStatementInfo); + } + + return new DerSequence(seq); + } + } +} diff --git a/Crypto/src/asn1/x509/qualified/RFC3739QCObjectIdentifiers.cs b/Crypto/src/asn1/x509/qualified/RFC3739QCObjectIdentifiers.cs new file mode 100644
index 000000000..8ebd69edb --- /dev/null +++ b/Crypto/src/asn1/x509/qualified/RFC3739QCObjectIdentifiers.cs
@@ -0,0 +1,21 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.X509.Qualified +{ + public sealed class Rfc3739QCObjectIdentifiers + { + private Rfc3739QCObjectIdentifiers() + { + } + + // + // base id + // + public static readonly DerObjectIdentifier IdQcs = new DerObjectIdentifier("1.3.6.1.5.5.7.11"); + + public static readonly DerObjectIdentifier IdQcsPkixQCSyntaxV1 = new DerObjectIdentifier(IdQcs+".1"); + public static readonly DerObjectIdentifier IdQcsPkixQCSyntaxV2 = new DerObjectIdentifier(IdQcs+".2"); + } +} diff --git a/Crypto/src/asn1/x509/qualified/SemanticsInformation.cs b/Crypto/src/asn1/x509/qualified/SemanticsInformation.cs new file mode 100644
index 000000000..72e7cd0e1 --- /dev/null +++ b/Crypto/src/asn1/x509/qualified/SemanticsInformation.cs
@@ -0,0 +1,124 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Asn1.X509.Qualified +{ + /** + * 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 + : Asn1Encodable + { + private readonly DerObjectIdentifier semanticsIdentifier; + private readonly GeneralName[] nameRegistrationAuthorities; + + public static SemanticsInformation GetInstance( + object obj) + { + if (obj == null || obj is SemanticsInformation) + { + return (SemanticsInformation) obj; + } + + if (obj is Asn1Sequence) + { + return new SemanticsInformation(Asn1Sequence.GetInstance(obj)); + } + + throw new ArgumentException("unknown object in GetInstance: " + obj.GetType().FullName, "obj"); + } + + public SemanticsInformation( + Asn1Sequence seq) + { + if (seq.Count < 1) + { + throw new ArgumentException("no objects in SemanticsInformation"); + } + + IEnumerator e = seq.GetEnumerator(); + e.MoveNext(); + object obj = e.Current; + if (obj is DerObjectIdentifier) + { + semanticsIdentifier = DerObjectIdentifier.GetInstance(obj); + if (e.MoveNext()) + { + obj = e.Current; + } + else + { + obj = null; + } + } + + if (obj != null) + { + Asn1Sequence generalNameSeq = Asn1Sequence.GetInstance(obj ); + nameRegistrationAuthorities = new GeneralName[generalNameSeq.Count]; + for (int i= 0; i < generalNameSeq.Count; i++) + { + nameRegistrationAuthorities[i] = GeneralName.GetInstance(generalNameSeq[i]); + } + } + } + + public SemanticsInformation( + DerObjectIdentifier semanticsIdentifier, + GeneralName[] generalNames) + { + this.semanticsIdentifier = semanticsIdentifier; + this.nameRegistrationAuthorities = generalNames; + } + + public SemanticsInformation( + DerObjectIdentifier semanticsIdentifier) + { + this.semanticsIdentifier = semanticsIdentifier; + } + + public SemanticsInformation( + GeneralName[] generalNames) + { + this.nameRegistrationAuthorities = generalNames; + } + + public DerObjectIdentifier SemanticsIdentifier { get { return semanticsIdentifier; } } + + public GeneralName[] GetNameRegistrationAuthorities() + { + return nameRegistrationAuthorities; + } + + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector seq = new Asn1EncodableVector(); + + if (this.semanticsIdentifier != null) + { + seq.Add(semanticsIdentifier); + } + + if (this.nameRegistrationAuthorities != null) + { + seq.Add(new DerSequence(nameRegistrationAuthorities)); + } + + return new DerSequence(seq); + } + } +} diff --git a/Crypto/src/asn1/x509/qualified/TypeOfBiometricData.cs b/Crypto/src/asn1/x509/qualified/TypeOfBiometricData.cs new file mode 100644
index 000000000..a77e54acb --- /dev/null +++ b/Crypto/src/asn1/x509/qualified/TypeOfBiometricData.cs
@@ -0,0 +1,91 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.X509.Qualified +{ + /** + * 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 + : Asn1Encodable, IAsn1Choice + { + public const int Picture = 0; + public const int HandwrittenSignature = 1; + + internal Asn1Encodable obj; + + public static TypeOfBiometricData GetInstance( + object obj) + { + if (obj == null || obj is TypeOfBiometricData) + { + return (TypeOfBiometricData) obj; + } + + if (obj is DerInteger) + { + DerInteger predefinedBiometricTypeObj = DerInteger.GetInstance(obj); + int predefinedBiometricType = predefinedBiometricTypeObj.Value.IntValue; + + return new TypeOfBiometricData(predefinedBiometricType); + } + + if (obj is DerObjectIdentifier) + { + DerObjectIdentifier BiometricDataOid = DerObjectIdentifier.GetInstance(obj); + return new TypeOfBiometricData(BiometricDataOid); + } + + throw new ArgumentException("unknown object in GetInstance: " + obj.GetType().FullName, "obj"); + } + + public TypeOfBiometricData( + int predefinedBiometricType) + { + if (predefinedBiometricType == Picture || predefinedBiometricType == HandwrittenSignature) + { + obj = new DerInteger(predefinedBiometricType); + } + else + { + throw new ArgumentException("unknow PredefinedBiometricType : " + predefinedBiometricType); + } + } + + public TypeOfBiometricData( + DerObjectIdentifier biometricDataOid) + { + obj = biometricDataOid; + } + + public bool IsPredefined + { + get { return obj is DerInteger; } + } + + public int PredefinedBiometricType + { + get { return ((DerInteger) obj).Value.IntValue; } + } + + public DerObjectIdentifier BiometricDataOid + { + get { return (DerObjectIdentifier) obj; } + } + + public override Asn1Object ToAsn1Object() + { + return obj.ToAsn1Object(); + } + } +} diff --git a/Crypto/src/asn1/x509/sigi/NameOrPseudonym.cs b/Crypto/src/asn1/x509/sigi/NameOrPseudonym.cs new file mode 100644
index 000000000..222895cf1 --- /dev/null +++ b/Crypto/src/asn1/x509/sigi/NameOrPseudonym.cs
@@ -0,0 +1,177 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1.X500; + +namespace Org.BouncyCastle.Asn1.X509.SigI +{ + /** + * 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 + : Asn1Encodable, IAsn1Choice + { + private readonly DirectoryString pseudonym; + private readonly DirectoryString surname; + private readonly Asn1Sequence givenName; + + public static NameOrPseudonym GetInstance( + object obj) + { + if (obj == null || obj is NameOrPseudonym) + { + return (NameOrPseudonym)obj; + } + + if (obj is IAsn1String) + { + return new NameOrPseudonym(DirectoryString.GetInstance(obj)); + } + + if (obj is Asn1Sequence) + { + return new NameOrPseudonym((Asn1Sequence) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + /** + * Constructor from DERString. + * <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.Count != 2) + throw new ArgumentException("Bad sequence size: " + seq.Count); + + if (!(seq[0] is IAsn1String)) + throw new ArgumentException("Bad object encountered: " + seq[0].GetType().Name); + + surname = DirectoryString.GetInstance(seq[0]); + givenName = Asn1Sequence.GetInstance(seq[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 Pseudonym + { + get { return pseudonym; } + } + + public DirectoryString Surname + { + get { return surname; } + } + + public DirectoryString[] GetGivenName() + { + DirectoryString[] items = new DirectoryString[givenName.Count]; + int count = 0; + foreach (object o in givenName) + { + items[count++] = DirectoryString.GetInstance(o); + } + 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 an Asn1Object + */ + public override Asn1Object ToAsn1Object() + { + if (pseudonym != null) + { + return pseudonym.ToAsn1Object(); + } + + return new DerSequence(surname, givenName); + } + } +} diff --git a/Crypto/src/asn1/x509/sigi/PersonalData.cs b/Crypto/src/asn1/x509/sigi/PersonalData.cs new file mode 100644
index 000000000..6acdc7308 --- /dev/null +++ b/Crypto/src/asn1/x509/sigi/PersonalData.cs
@@ -0,0 +1,210 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1.X500; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Asn1.X509.SigI +{ + /** + * 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 + : Asn1Encodable + { + private readonly NameOrPseudonym nameOrPseudonym; + private readonly BigInteger nameDistinguisher; + private readonly DerGeneralizedTime dateOfBirth; + private readonly DirectoryString placeOfBirth; + private readonly string gender; + private readonly DirectoryString postalAddress; + + public static PersonalData GetInstance( + object obj) + { + if (obj == null || obj is PersonalData) + { + return (PersonalData) obj; + } + + if (obj is Asn1Sequence) + { + return new PersonalData((Asn1Sequence) obj); + } + + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + } + + /** + * 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.Count < 1) + throw new ArgumentException("Bad sequence size: " + seq.Count); + + IEnumerator e = seq.GetEnumerator(); + e.MoveNext(); + + nameOrPseudonym = NameOrPseudonym.GetInstance(e.Current); + + while (e.MoveNext()) + { + Asn1TaggedObject o = Asn1TaggedObject.GetInstance(e.Current); + int tag = o.TagNo; + switch (tag) + { + case 0: + nameDistinguisher = DerInteger.GetInstance(o, false).Value; + break; + case 1: + dateOfBirth = DerGeneralizedTime.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 ArgumentException("Bad tag number: " + o.TagNo); + } + } + } + + /** + * 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, + DerGeneralizedTime 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 NameOrPseudonym + { + get { return nameOrPseudonym; } + } + + public BigInteger NameDistinguisher + { + get { return nameDistinguisher; } + } + + public DerGeneralizedTime DateOfBirth + { + get { return dateOfBirth; } + } + + public DirectoryString PlaceOfBirth + { + get { return placeOfBirth; } + } + + public string Gender + { + get { return gender; } + } + + public DirectoryString PostalAddress + { + get { 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 an Asn1Object + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector vec = new Asn1EncodableVector(); + vec.Add(nameOrPseudonym); + if (nameDistinguisher != null) + { + vec.Add(new DerTaggedObject(false, 0, new DerInteger(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/Crypto/src/asn1/x509/sigi/SigIObjectIdentifiers.cs b/Crypto/src/asn1/x509/sigi/SigIObjectIdentifiers.cs new file mode 100644
index 000000000..682311adc --- /dev/null +++ b/Crypto/src/asn1/x509/sigi/SigIObjectIdentifiers.cs
@@ -0,0 +1,49 @@ +using System; + +namespace Org.BouncyCastle.Asn1.X509.SigI +{ + /** + * Object Identifiers of SigI specifciation (German Signature Law + * Interoperability specification). + */ + public sealed class SigIObjectIdentifiers + { + private SigIObjectIdentifiers() + { + } + + public readonly static DerObjectIdentifier IdSigI = new DerObjectIdentifier("1.3.36.8"); + + /** + * Key purpose IDs for German SigI (Signature Interoperability + * Specification) + */ + public readonly static DerObjectIdentifier IdSigIKP = new DerObjectIdentifier(IdSigI + ".2"); + + /** + * Certificate policy IDs for German SigI (Signature Interoperability + * Specification) + */ + public readonly static DerObjectIdentifier IdSigICP = new DerObjectIdentifier(IdSigI + ".1"); + + /** + * Other Name IDs for German SigI (Signature Interoperability Specification) + */ + public readonly static DerObjectIdentifier IdSigION = new DerObjectIdentifier(IdSigI + ".4"); + + /** + * To be used for for the generation of directory service certificates. + */ + public static readonly DerObjectIdentifier IdSigIKPDirectoryService = new DerObjectIdentifier(IdSigIKP + ".1"); + + /** + * ID for PersonalData + */ + public static readonly DerObjectIdentifier IdSigIONPersonalData = new DerObjectIdentifier(IdSigION + ".1"); + + /** + * Certificate is conform to german signature law. + */ + public static readonly DerObjectIdentifier IdSigICPSigConform = new DerObjectIdentifier(IdSigICP + ".1"); + } +} diff --git a/Crypto/src/asn1/x9/DHDomainParameters.cs b/Crypto/src/asn1/x9/DHDomainParameters.cs new file mode 100644
index 000000000..8de869694 --- /dev/null +++ b/Crypto/src/asn1/x9/DHDomainParameters.cs
@@ -0,0 +1,116 @@ +using System; +using System.Collections; + +namespace Org.BouncyCastle.Asn1.X9 +{ + public class DHDomainParameters + : Asn1Encodable + { + private readonly DerInteger p, g, q, j; + private readonly DHValidationParms validationParms; + + public static DHDomainParameters GetInstance(Asn1TaggedObject obj, bool isExplicit) + { + return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit)); + } + + public static DHDomainParameters GetInstance(object obj) + { + if (obj == null || obj is DHDomainParameters) + return (DHDomainParameters)obj; + + if (obj is Asn1Sequence) + return new DHDomainParameters((Asn1Sequence)obj); + + throw new ArgumentException("Invalid DHDomainParameters: " + obj.GetType().FullName, "obj"); + } + + public DHDomainParameters(DerInteger p, DerInteger g, DerInteger q, DerInteger j, + DHValidationParms validationParms) + { + if (p == null) + throw new ArgumentNullException("p"); + if (g == null) + throw new ArgumentNullException("g"); + if (q == null) + throw new ArgumentNullException("q"); + + this.p = p; + this.g = g; + this.q = q; + this.j = j; + this.validationParms = validationParms; + } + + private DHDomainParameters(Asn1Sequence seq) + { + if (seq.Count < 3 || seq.Count > 5) + throw new ArgumentException("Bad sequence size: " + seq.Count, "seq"); + + IEnumerator e = seq.GetEnumerator(); + this.p = DerInteger.GetInstance(GetNext(e)); + this.g = DerInteger.GetInstance(GetNext(e)); + this.q = DerInteger.GetInstance(GetNext(e)); + + Asn1Encodable next = GetNext(e); + + if (next != null && next is DerInteger) + { + this.j = DerInteger.GetInstance(next); + next = GetNext(e); + } + + if (next != null) + { + this.validationParms = DHValidationParms.GetInstance(next.ToAsn1Object()); + } + } + + private static Asn1Encodable GetNext(IEnumerator e) + { + return e.MoveNext() ? (Asn1Encodable)e.Current : null; + } + + public DerInteger P + { + get { return this.p; } + } + + public DerInteger G + { + get { return this.g; } + } + + public DerInteger Q + { + get { return this.q; } + } + + public DerInteger J + { + get { return this.j; } + } + + public DHValidationParms ValidationParms + { + get { return this.validationParms; } + } + + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(p, g, q); + + if (this.j != null) + { + v.Add(this.j); + } + + if (this.validationParms != null) + { + v.Add(this.validationParms); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/x9/DHPublicKey.cs b/Crypto/src/asn1/x9/DHPublicKey.cs new file mode 100644
index 000000000..1a20a8a16 --- /dev/null +++ b/Crypto/src/asn1/x9/DHPublicKey.cs
@@ -0,0 +1,44 @@ +using System; + +namespace Org.BouncyCastle.Asn1.X9 +{ + public class DHPublicKey + : Asn1Encodable + { + private readonly DerInteger y; + + public static DHPublicKey GetInstance(Asn1TaggedObject obj, bool isExplicit) + { + return GetInstance(DerInteger.GetInstance(obj, isExplicit)); + } + + public static DHPublicKey GetInstance(object obj) + { + if (obj == null || obj is DHPublicKey) + return (DHPublicKey)obj; + + if (obj is DerInteger) + return new DHPublicKey((DerInteger)obj); + + throw new ArgumentException("Invalid DHPublicKey: " + obj.GetType().FullName, "obj"); + } + + public DHPublicKey(DerInteger y) + { + if (y == null) + throw new ArgumentNullException("y"); + + this.y = y; + } + + public DerInteger Y + { + get { return this.y; } + } + + public override Asn1Object ToAsn1Object() + { + return this.y; + } + } +} diff --git a/Crypto/src/asn1/x9/DHValidationParms.cs b/Crypto/src/asn1/x9/DHValidationParms.cs new file mode 100644
index 000000000..a37964cfb --- /dev/null +++ b/Crypto/src/asn1/x9/DHValidationParms.cs
@@ -0,0 +1,62 @@ +using System; + +namespace Org.BouncyCastle.Asn1.X9 +{ + public class DHValidationParms + : Asn1Encodable + { + private readonly DerBitString seed; + private readonly DerInteger pgenCounter; + + public static DHValidationParms GetInstance(Asn1TaggedObject obj, bool isExplicit) + { + return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit)); + } + + public static DHValidationParms GetInstance(object obj) + { + if (obj == null || obj is DHDomainParameters) + return (DHValidationParms)obj; + + if (obj is Asn1Sequence) + return new DHValidationParms((Asn1Sequence)obj); + + throw new ArgumentException("Invalid DHValidationParms: " + obj.GetType().FullName, "obj"); + } + + public DHValidationParms(DerBitString seed, DerInteger pgenCounter) + { + if (seed == null) + throw new ArgumentNullException("seed"); + if (pgenCounter == null) + throw new ArgumentNullException("pgenCounter"); + + this.seed = seed; + this.pgenCounter = pgenCounter; + } + + private DHValidationParms(Asn1Sequence seq) + { + if (seq.Count != 2) + throw new ArgumentException("Bad sequence size: " + seq.Count, "seq"); + + this.seed = DerBitString.GetInstance(seq[0]); + this.pgenCounter = DerInteger.GetInstance(seq[1]); + } + + public DerBitString Seed + { + get { return this.seed; } + } + + public DerInteger PgenCounter + { + get { return this.pgenCounter; } + } + + public override Asn1Object ToAsn1Object() + { + return new DerSequence(seed, pgenCounter); + } + } +} diff --git a/Crypto/src/asn1/x9/KeySpecificInfo.cs b/Crypto/src/asn1/x9/KeySpecificInfo.cs new file mode 100644
index 000000000..46298646b --- /dev/null +++ b/Crypto/src/asn1/x9/KeySpecificInfo.cs
@@ -0,0 +1,58 @@ +using System.Collections; + +namespace Org.BouncyCastle.Asn1.X9 +{ + /** + * ASN.1 def for Diffie-Hellman key exchange KeySpecificInfo structure. See + * RFC 2631, or X9.42, for further details. + */ + public class KeySpecificInfo + : Asn1Encodable + { + private DerObjectIdentifier algorithm; + private Asn1OctetString counter; + + public KeySpecificInfo( + DerObjectIdentifier algorithm, + Asn1OctetString counter) + { + this.algorithm = algorithm; + this.counter = counter; + } + + public KeySpecificInfo( + Asn1Sequence seq) + { + IEnumerator e = seq.GetEnumerator(); + + e.MoveNext(); + algorithm = (DerObjectIdentifier)e.Current; + e.MoveNext(); + counter = (Asn1OctetString)e.Current; + } + + public DerObjectIdentifier Algorithm + { + get { return algorithm; } + } + + public Asn1OctetString Counter + { + get { return counter; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * KeySpecificInfo ::= Sequence { + * algorithm OBJECT IDENTIFIER, + * counter OCTET STRING SIZE (4..4) + * } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + return new DerSequence(algorithm, counter); + } + } +} diff --git a/Crypto/src/asn1/x9/OtherInfo.cs b/Crypto/src/asn1/x9/OtherInfo.cs new file mode 100644
index 000000000..21863bd17 --- /dev/null +++ b/Crypto/src/asn1/x9/OtherInfo.cs
@@ -0,0 +1,88 @@ +using System.Collections; + +namespace Org.BouncyCastle.Asn1.X9 +{ + /** + * ANS.1 def for Diffie-Hellman key exchange OtherInfo structure. See + * RFC 2631, or X9.42, for further details. + */ + public class OtherInfo + : Asn1Encodable + { + 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) + { + IEnumerator e = seq.GetEnumerator(); + + e.MoveNext(); + keyInfo = new KeySpecificInfo((Asn1Sequence) e.Current); + + while (e.MoveNext()) + { + DerTaggedObject o = (DerTaggedObject) e.Current; + + if (o.TagNo == 0) + { + partyAInfo = (Asn1OctetString) o.GetObject(); + } + else if ((int) o.TagNo == 2) + { + suppPubInfo = (Asn1OctetString) o.GetObject(); + } + } + } + + public KeySpecificInfo KeyInfo + { + get { return keyInfo; } + } + + public Asn1OctetString PartyAInfo + { + get { return partyAInfo; } + } + + public Asn1OctetString SuppPubInfo + { + get { 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(keyInfo); + + if (partyAInfo != null) + { + v.Add(new DerTaggedObject(0, partyAInfo)); + } + + v.Add(new DerTaggedObject(2, suppPubInfo)); + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/x9/X962NamedCurves.cs b/Crypto/src/asn1/x9/X962NamedCurves.cs new file mode 100644
index 000000000..de80ca280 --- /dev/null +++ b/Crypto/src/asn1/x9/X962NamedCurves.cs
@@ -0,0 +1,733 @@ +using System; +using System.Collections; +using System.Globalization; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Asn1.X9 +{ + /** + * table of the current named curves defined in X.962 EC-DSA. + */ + public sealed class X962NamedCurves + { + private X962NamedCurves() + { + } + + internal class Prime192v1Holder + : X9ECParametersHolder + { + private Prime192v1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Prime192v1Holder(); + + protected override X9ECParameters CreateParameters() + { + ECCurve cFp192v1 = new FpCurve( + 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.One, + Hex.Decode("3045AE6FC8422f64ED579528D38120EAE12196D5")); + } + } + + internal class Prime192v2Holder + : X9ECParametersHolder + { + private Prime192v2Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Prime192v2Holder(); + + protected override X9ECParameters CreateParameters() + { + ECCurve cFp192v2 = new FpCurve( + 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.One, + Hex.Decode("31a92ee2029fd10d901b113e990710f0d21ac6b6")); + } + } + + internal class Prime192v3Holder + : X9ECParametersHolder + { + private Prime192v3Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Prime192v3Holder(); + + protected override X9ECParameters CreateParameters() + { + ECCurve cFp192v3 = new FpCurve( + 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.One, + Hex.Decode("c469684435deb378c4b65ca9591e2a5763059a2e")); + } + } + + internal class Prime239v1Holder + : X9ECParametersHolder + { + private Prime239v1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Prime239v1Holder(); + + protected override X9ECParameters CreateParameters() + { + ECCurve cFp239v1 = new FpCurve( + 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.One, + Hex.Decode("e43bb460f0b80cc0c0b075798e948060f8321b7d")); + } + } + + internal class Prime239v2Holder + : X9ECParametersHolder + { + private Prime239v2Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Prime239v2Holder(); + + protected override X9ECParameters CreateParameters() + { + ECCurve cFp239v2 = new FpCurve( + 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.One, + Hex.Decode("e8b4011604095303ca3b8099982be09fcb9ae616")); + } + } + + internal class Prime239v3Holder + : X9ECParametersHolder + { + private Prime239v3Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Prime239v3Holder(); + + protected override X9ECParameters CreateParameters() + { + ECCurve cFp239v3 = new FpCurve( + 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.One, + Hex.Decode("7d7374168ffe3471b60a857686a19475d3bfa2ff")); + } + } + + internal class Prime256v1Holder + : X9ECParametersHolder + { + private Prime256v1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Prime256v1Holder(); + + protected override X9ECParameters CreateParameters() + { + ECCurve cFp256v1 = new FpCurve( + 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.One, + Hex.Decode("c49d360886e704936a6678e1139d26b7819f7e90")); + } + } + + /* + * F2m Curves + */ + internal class C2pnb163v1Holder + : X9ECParametersHolder + { + private C2pnb163v1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new C2pnb163v1Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("0400000000000000000001E60FC8821CC74DAEAFC1", 16); + BigInteger h = BigInteger.ValueOf(2); + + ECCurve c2m163v1 = new F2mCurve( + 163, + 1, 2, 8, + new BigInteger("072546B5435234A422E0789675F432C89435DE5242", 16), + new BigInteger("00C9517D06D5240D3CFF38C74B20B6CD4D6F9DD4D9", 16), + n, h); + + return new X9ECParameters( + c2m163v1, + c2m163v1.DecodePoint( + Hex.Decode("0307AF69989546103D79329FCC3D74880F33BBE803CB")), + n, h, + Hex.Decode("D2COFB15760860DEF1EEF4D696E6768756151754")); + } + } + + internal class C2pnb163v2Holder + : X9ECParametersHolder + { + private C2pnb163v2Holder() {} + + internal static readonly X9ECParametersHolder Instance = new C2pnb163v2Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("03FFFFFFFFFFFFFFFFFFFDF64DE1151ADBB78F10A7", 16); + BigInteger h = BigInteger.ValueOf(2); + + ECCurve c2m163v2 = new F2mCurve( + 163, + 1, 2, 8, + new BigInteger("0108B39E77C4B108BED981ED0E890E117C511CF072", 16), + new BigInteger("0667ACEB38AF4E488C407433FFAE4F1C811638DF20", 16), + n, h); + + return new X9ECParameters( + c2m163v2, + c2m163v2.DecodePoint( + Hex.Decode("030024266E4EB5106D0A964D92C4860E2671DB9B6CC5")), + n, h, + null); + } + } + + internal class C2pnb163v3Holder + : X9ECParametersHolder + { + private C2pnb163v3Holder() {} + + internal static readonly X9ECParametersHolder Instance = new C2pnb163v3Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("03FFFFFFFFFFFFFFFFFFFE1AEE140F110AFF961309", 16); + BigInteger h = BigInteger.ValueOf(2); + + ECCurve c2m163v3 = new F2mCurve( + 163, + 1, 2, 8, + new BigInteger("07A526C63D3E25A256A007699F5447E32AE456B50E", 16), + new BigInteger("03F7061798EB99E238FD6F1BF95B48FEEB4854252B", 16), + n, h); + + return new X9ECParameters( + c2m163v3, + c2m163v3.DecodePoint(Hex.Decode("0202F9F87B7C574D0BDECF8A22E6524775F98CDEBDCB")), + n, h, + null); + } + } + + internal class C2pnb176w1Holder + : X9ECParametersHolder + { + private C2pnb176w1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new C2pnb176w1Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("010092537397ECA4F6145799D62B0A19CE06FE26AD", 16); + BigInteger h = BigInteger.ValueOf(0xFF6E); + + ECCurve c2m176w1 = new F2mCurve( + 176, + 1, 2, 43, + new BigInteger("00E4E6DB2995065C407D9D39B8D0967B96704BA8E9C90B", 16), + new BigInteger("005DDA470ABE6414DE8EC133AE28E9BBD7FCEC0AE0FFF2", 16), + n, h); + + return new X9ECParameters( + c2m176w1, + c2m176w1.DecodePoint( + Hex.Decode("038D16C2866798B600F9F08BB4A8E860F3298CE04A5798")), + n, h, + null); + } + } + + internal class C2tnb191v1Holder + : X9ECParametersHolder + { + private C2tnb191v1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new C2tnb191v1Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("40000000000000000000000004A20E90C39067C893BBB9A5", 16); + BigInteger h = BigInteger.ValueOf(2); + + ECCurve c2m191v1 = new F2mCurve( + 191, + 9, + new BigInteger("2866537B676752636A68F56554E12640276B649EF7526267", 16), + new BigInteger("2E45EF571F00786F67B0081B9495A3D95462F5DE0AA185EC", 16), + n, h); + + return new X9ECParameters( + c2m191v1, + c2m191v1.DecodePoint( + Hex.Decode("0236B3DAF8A23206F9C4F299D7B21A9C369137F2C84AE1AA0D")), + n, h, + Hex.Decode("4E13CA542744D696E67687561517552F279A8C84")); + } + } + + internal class C2tnb191v2Holder + : X9ECParametersHolder + { + private C2tnb191v2Holder() {} + + internal static readonly X9ECParametersHolder Instance = new C2tnb191v2Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("20000000000000000000000050508CB89F652824E06B8173", 16); + BigInteger h = BigInteger.ValueOf(4); + + ECCurve c2m191v2 = new F2mCurve( + 191, + 9, + new BigInteger("401028774D7777C7B7666D1366EA432071274F89FF01E718", 16), + new BigInteger("0620048D28BCBD03B6249C99182B7C8CD19700C362C46A01", 16), + n, h); + + return new X9ECParameters( + c2m191v2, + c2m191v2.DecodePoint( + Hex.Decode("023809B2B7CC1B28CC5A87926AAD83FD28789E81E2C9E3BF10")), + n, h, + null); + } + } + + internal class C2tnb191v3Holder + : X9ECParametersHolder + { + private C2tnb191v3Holder() {} + + internal static readonly X9ECParametersHolder Instance = new C2tnb191v3Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("155555555555555555555555610C0B196812BFB6288A3EA3", 16); + BigInteger h = BigInteger.ValueOf(6); + + ECCurve c2m191v3 = new F2mCurve( + 191, + 9, + new BigInteger("6C01074756099122221056911C77D77E77A777E7E7E77FCB", 16), + new BigInteger("71FE1AF926CF847989EFEF8DB459F66394D90F32AD3F15E8", 16), + n, h); + + return new X9ECParameters( + c2m191v3, + c2m191v3.DecodePoint( + Hex.Decode("03375D4CE24FDE434489DE8746E71786015009E66E38A926DD")), + n, h, + null); + } + } + + internal class C2pnb208w1Holder + : X9ECParametersHolder + { + private C2pnb208w1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new C2pnb208w1Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("0101BAF95C9723C57B6C21DA2EFF2D5ED588BDD5717E212F9D", 16); + BigInteger h = BigInteger.ValueOf(0xFE48); + + ECCurve c2m208w1 = new F2mCurve( + 208, + 1, 2, 83, + new BigInteger("0", 16), + new BigInteger("00C8619ED45A62E6212E1160349E2BFA844439FAFC2A3FD1638F9E", 16), + n, h); + + return new X9ECParameters( + c2m208w1, + c2m208w1.DecodePoint( + Hex.Decode("0289FDFBE4ABE193DF9559ECF07AC0CE78554E2784EB8C1ED1A57A")), + n, h, + null); + } + } + + internal class C2tnb239v1Holder + : X9ECParametersHolder + { + private C2tnb239v1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new C2tnb239v1Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("2000000000000000000000000000000F4D42FFE1492A4993F1CAD666E447", 16); + BigInteger h = BigInteger.ValueOf(4); + + ECCurve c2m239v1 = new F2mCurve( + 239, + 36, + new BigInteger("32010857077C5431123A46B808906756F543423E8D27877578125778AC76", 16), + new BigInteger("790408F2EEDAF392B012EDEFB3392F30F4327C0CA3F31FC383C422AA8C16", 16), + n, h); + + return new X9ECParameters( + c2m239v1, + c2m239v1.DecodePoint( + Hex.Decode("0257927098FA932E7C0A96D3FD5B706EF7E5F5C156E16B7E7C86038552E91D")), + n, h, + null); + } + } + + internal class C2tnb239v2Holder + : X9ECParametersHolder + { + private C2tnb239v2Holder() {} + + internal static readonly X9ECParametersHolder Instance = new C2tnb239v2Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("1555555555555555555555555555553C6F2885259C31E3FCDF154624522D", 16); + BigInteger h = BigInteger.ValueOf(6); + + ECCurve c2m239v2 = new F2mCurve( + 239, + 36, + new BigInteger("4230017757A767FAE42398569B746325D45313AF0766266479B75654E65F", 16), + new BigInteger("5037EA654196CFF0CD82B2C14A2FCF2E3FF8775285B545722F03EACDB74B", 16), + n, h); + + return new X9ECParameters( + c2m239v2, + c2m239v2.DecodePoint( + Hex.Decode("0228F9D04E900069C8DC47A08534FE76D2B900B7D7EF31F5709F200C4CA205")), + n, h, + null); + } + } + + internal class C2tnb239v3Holder + : X9ECParametersHolder + { + private C2tnb239v3Holder() {} + + internal static readonly X9ECParametersHolder Instance = new C2tnb239v3Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("0CCCCCCCCCCCCCCCCCCCCCCCCCCCCCAC4912D2D9DF903EF9888B8A0E4CFF", 16); + BigInteger h = BigInteger.ValueOf(10); + + ECCurve c2m239v3 = new F2mCurve( + 239, + 36, + new BigInteger("01238774666A67766D6676F778E676B66999176666E687666D8766C66A9F", 16), + new BigInteger("6A941977BA9F6A435199ACFC51067ED587F519C5ECB541B8E44111DE1D40", 16), + n, h); + + return new X9ECParameters( + c2m239v3, + c2m239v3.DecodePoint( + Hex.Decode("0370F6E9D04D289C4E89913CE3530BFDE903977D42B146D539BF1BDE4E9C92")), + n, h, + null); + } + } + + internal class C2pnb272w1Holder + : X9ECParametersHolder + { + private C2pnb272w1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new C2pnb272w1Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("0100FAF51354E0E39E4892DF6E319C72C8161603FA45AA7B998A167B8F1E629521", 16); + BigInteger h = BigInteger.ValueOf(0xFF06); + + ECCurve c2m272w1 = new F2mCurve( + 272, + 1, 3, 56, + new BigInteger("0091A091F03B5FBA4AB2CCF49C4EDD220FB028712D42BE752B2C40094DBACDB586FB20", 16), + new BigInteger("7167EFC92BB2E3CE7C8AAAFF34E12A9C557003D7C73A6FAF003F99F6CC8482E540F7", 16), + n, h); + + return new X9ECParameters( + c2m272w1, + c2m272w1.DecodePoint( + Hex.Decode("026108BABB2CEEBCF787058A056CBE0CFE622D7723A289E08A07AE13EF0D10D171DD8D")), + n, h, + null); + } + } + + internal class C2pnb304w1Holder + : X9ECParametersHolder + { + private C2pnb304w1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new C2pnb304w1Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("0101D556572AABAC800101D556572AABAC8001022D5C91DD173F8FB561DA6899164443051D", 16); + BigInteger h = BigInteger.ValueOf(0xFE2E); + + ECCurve c2m304w1 = new F2mCurve( + 304, + 1, 2, 11, + new BigInteger("00FD0D693149A118F651E6DCE6802085377E5F882D1B510B44160074C1288078365A0396C8E681", 16), + new BigInteger("00BDDB97E555A50A908E43B01C798EA5DAA6788F1EA2794EFCF57166B8C14039601E55827340BE", 16), + n, h); + + return new X9ECParameters( + c2m304w1, + c2m304w1.DecodePoint( + Hex.Decode("02197B07845E9BE2D96ADB0F5F3C7F2CFFBD7A3EB8B6FEC35C7FD67F26DDF6285A644F740A2614")), + n, h, + null); + } + } + + internal class C2tnb359v1Holder + : X9ECParametersHolder + { + private C2tnb359v1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new C2tnb359v1Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("01AF286BCA1AF286BCA1AF286BCA1AF286BCA1AF286BC9FB8F6B85C556892C20A7EB964FE7719E74F490758D3B", 16); + BigInteger h = BigInteger.ValueOf(0x4C); + + ECCurve c2m359v1 = new F2mCurve( + 359, + 68, + new BigInteger("5667676A654B20754F356EA92017D946567C46675556F19556A04616B567D223A5E05656FB549016A96656A557", 16), + new BigInteger("2472E2D0197C49363F1FE7F5B6DB075D52B6947D135D8CA445805D39BC345626089687742B6329E70680231988", 16), + n, h); + + return new X9ECParameters( + c2m359v1, + c2m359v1.DecodePoint( + Hex.Decode("033C258EF3047767E7EDE0F1FDAA79DAEE3841366A132E163ACED4ED2401DF9C6BDCDE98E8E707C07A2239B1B097")), + n, h, + null); + } + } + + internal class C2pnb368w1Holder + : X9ECParametersHolder + { + private C2pnb368w1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new C2pnb368w1Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("010090512DA9AF72B08349D98A5DD4C7B0532ECA51CE03E2D10F3B7AC579BD87E909AE40A6F131E9CFCE5BD967", 16); + BigInteger h = BigInteger.ValueOf(0xFF70); + + ECCurve c2m368w1 = new F2mCurve( + 368, + 1, 2, 85, + new BigInteger("00E0D2EE25095206F5E2A4F9ED229F1F256E79A0E2B455970D8D0D865BD94778C576D62F0AB7519CCD2A1A906AE30D", 16), + new BigInteger("00FC1217D4320A90452C760A58EDCD30C8DD069B3C34453837A34ED50CB54917E1C2112D84D164F444F8F74786046A", 16), + n, h); + + return new X9ECParameters( + c2m368w1, + c2m368w1.DecodePoint( + Hex.Decode("021085E2755381DCCCE3C1557AFA10C2F0C0C2825646C5B34A394CBCFA8BC16B22E7E789E927BE216F02E1FB136A5F")), + n, h, + null); + } + } + + internal class C2tnb431r1Holder + : X9ECParametersHolder + { + private C2tnb431r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new C2tnb431r1Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("0340340340340340340340340340340340340340340340340340340323C313FAB50589703B5EC68D3587FEC60D161CC149C1AD4A91", 16); + BigInteger h = BigInteger.ValueOf(0x2760); + + ECCurve c2m431r1 = new F2mCurve( + 431, + 120, + new BigInteger("1A827EF00DD6FC0E234CAF046C6A5D8A85395B236CC4AD2CF32A0CADBDC9DDF620B0EB9906D0957F6C6FEACD615468DF104DE296CD8F", 16), + new BigInteger("10D9B4A3D9047D8B154359ABFB1B7F5485B04CEB868237DDC9DEDA982A679A5A919B626D4E50A8DD731B107A9962381FB5D807BF2618", 16), + n, h); + + return new X9ECParameters( + c2m431r1, + c2m431r1.DecodePoint( + Hex.Decode("02120FC05D3C67A99DE161D2F4092622FECA701BE4F50F4758714E8A87BBF2A658EF8C21E7C5EFE965361F6C2999C0C247B0DBD70CE6B7")), + n, h, + null); + } + } + + private static readonly IDictionary objIds = Platform.CreateHashtable(); + private static readonly IDictionary curves = Platform.CreateHashtable(); + private static readonly IDictionary names = Platform.CreateHashtable(); + + private static void DefineCurve( + string name, + DerObjectIdentifier oid, + X9ECParametersHolder holder) + { + objIds.Add(name, oid); + names.Add(oid, name); + curves.Add(oid, holder); + } + + static X962NamedCurves() + { + DefineCurve("prime192v1", X9ObjectIdentifiers.Prime192v1, Prime192v1Holder.Instance); + DefineCurve("prime192v2", X9ObjectIdentifiers.Prime192v2, Prime192v2Holder.Instance); + DefineCurve("prime192v3", X9ObjectIdentifiers.Prime192v3, Prime192v3Holder.Instance); + DefineCurve("prime239v1", X9ObjectIdentifiers.Prime239v1, Prime239v1Holder.Instance); + DefineCurve("prime239v2", X9ObjectIdentifiers.Prime239v2, Prime239v2Holder.Instance); + DefineCurve("prime239v3", X9ObjectIdentifiers.Prime239v3, Prime239v3Holder.Instance); + DefineCurve("prime256v1", X9ObjectIdentifiers.Prime256v1, Prime256v1Holder.Instance); + DefineCurve("c2pnb163v1", X9ObjectIdentifiers.C2Pnb163v1, C2pnb163v1Holder.Instance); + DefineCurve("c2pnb163v2", X9ObjectIdentifiers.C2Pnb163v2, C2pnb163v2Holder.Instance); + DefineCurve("c2pnb163v3", X9ObjectIdentifiers.C2Pnb163v3, C2pnb163v3Holder.Instance); + DefineCurve("c2pnb176w1", X9ObjectIdentifiers.C2Pnb176w1, C2pnb176w1Holder.Instance); + DefineCurve("c2tnb191v1", X9ObjectIdentifiers.C2Tnb191v1, C2tnb191v1Holder.Instance); + DefineCurve("c2tnb191v2", X9ObjectIdentifiers.C2Tnb191v2, C2tnb191v2Holder.Instance); + DefineCurve("c2tnb191v3", X9ObjectIdentifiers.C2Tnb191v3, C2tnb191v3Holder.Instance); + DefineCurve("c2pnb208w1", X9ObjectIdentifiers.C2Pnb208w1, C2pnb208w1Holder.Instance); + DefineCurve("c2tnb239v1", X9ObjectIdentifiers.C2Tnb239v1, C2tnb239v1Holder.Instance); + DefineCurve("c2tnb239v2", X9ObjectIdentifiers.C2Tnb239v2, C2tnb239v2Holder.Instance); + DefineCurve("c2tnb239v3", X9ObjectIdentifiers.C2Tnb239v3, C2tnb239v3Holder.Instance); + DefineCurve("c2pnb272w1", X9ObjectIdentifiers.C2Pnb272w1, C2pnb272w1Holder.Instance); + DefineCurve("c2pnb304w1", X9ObjectIdentifiers.C2Pnb304w1, C2pnb304w1Holder.Instance); + DefineCurve("c2tnb359v1", X9ObjectIdentifiers.C2Tnb359v1, C2tnb359v1Holder.Instance); + DefineCurve("c2pnb368w1", X9ObjectIdentifiers.C2Pnb368w1, C2pnb368w1Holder.Instance); + DefineCurve("c2tnb431r1", X9ObjectIdentifiers.C2Tnb431r1, C2tnb431r1Holder.Instance); + } + + public static X9ECParameters GetByName( + string name) + { + DerObjectIdentifier oid = (DerObjectIdentifier) objIds[name.ToLowerInvariant()]; + + return oid == null ? null : GetByOid(oid); + } + + /** + * 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( + DerObjectIdentifier oid) + { + X9ECParametersHolder holder = (X9ECParametersHolder) curves[oid]; + + return holder == null ? null : holder.Parameters; + } + + /** + * 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 DerObjectIdentifier GetOid( + string name) + { + return (DerObjectIdentifier) objIds[name.ToLowerInvariant()]; + } + + /** + * return the named curve name represented by the given object identifier. + */ + public static string GetName( + DerObjectIdentifier oid) + { + return (string) names[oid]; + } + + /** + * returns an enumeration containing the name strings for curves + * contained in this structure. + */ + public static IEnumerable Names + { + get { return new EnumerableProxy(objIds.Keys); } + } + } +} diff --git a/Crypto/src/asn1/x9/X962Parameters.cs b/Crypto/src/asn1/x9/X962Parameters.cs new file mode 100644
index 000000000..5b7eaa1de --- /dev/null +++ b/Crypto/src/asn1/x9/X962Parameters.cs
@@ -0,0 +1,53 @@ +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Asn1.X9 +{ + public class X962Parameters + : Asn1Encodable, IAsn1Choice + { + private readonly Asn1Object _params; + + public X962Parameters( + X9ECParameters ecParameters) + { + this._params = ecParameters.ToAsn1Object(); + } + + public X962Parameters( + DerObjectIdentifier namedCurve) + { + this._params = namedCurve; + } + + public X962Parameters( + Asn1Object obj) + { + this._params = obj; + } + + public bool IsNamedCurve + { + get { return (_params is DerObjectIdentifier); } + } + + public Asn1Object Parameters + { + get { return _params; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * Parameters ::= CHOICE { + * ecParameters ECParameters, + * namedCurve CURVES.&amp;id({CurveNames}), + * implicitlyCA Null + * } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + return _params; + } + } +} diff --git a/Crypto/src/asn1/x9/X9Curve.cs b/Crypto/src/asn1/x9/X9Curve.cs new file mode 100644
index 000000000..b92e7b3b5 --- /dev/null +++ b/Crypto/src/asn1/x9/X9Curve.cs
@@ -0,0 +1,147 @@ +using System; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1.X9 +{ + /** + * ASN.1 def for Elliptic-Curve Curve structure. See + * X9.62, for further details. + */ + public class X9Curve + : Asn1Encodable + { + private readonly ECCurve curve; + private readonly byte[] seed; + private readonly DerObjectIdentifier fieldIdentifier; + + public X9Curve( + ECCurve curve) + : this(curve, null) + { + this.curve = curve; + } + + public X9Curve( + ECCurve curve, + byte[] seed) + { + if (curve == null) + throw new ArgumentNullException("curve"); + + this.curve = curve; + this.seed = Arrays.Clone(seed); + + if (curve is FpCurve) + { + this.fieldIdentifier = X9ObjectIdentifiers.PrimeField; + } + else if (curve is F2mCurve) + { + this.fieldIdentifier = X9ObjectIdentifiers.CharacteristicTwoField; + } + else + { + throw new ArgumentException("This type of ECCurve is not implemented"); + } + } + + public X9Curve( + X9FieldID fieldID, + Asn1Sequence seq) + { + if (fieldID == null) + throw new ArgumentNullException("fieldID"); + if (seq == null) + throw new ArgumentNullException("seq"); + + this.fieldIdentifier = fieldID.Identifier; + + if (fieldIdentifier.Equals(X9ObjectIdentifiers.PrimeField)) + { + BigInteger q = ((DerInteger) fieldID.Parameters).Value; + X9FieldElement x9A = new X9FieldElement(q, (Asn1OctetString) seq[0]); + X9FieldElement x9B = new X9FieldElement(q, (Asn1OctetString) seq[1]); + curve = new FpCurve(q, x9A.Value.ToBigInteger(), x9B.Value.ToBigInteger()); + } + else + { + if (fieldIdentifier.Equals(X9ObjectIdentifiers.CharacteristicTwoField)) + { + // Characteristic two field + DerSequence parameters = (DerSequence)fieldID.Parameters; + int m = ((DerInteger)parameters[0]).Value.IntValue; + DerObjectIdentifier representation + = (DerObjectIdentifier)parameters[1]; + + int k1 = 0; + int k2 = 0; + int k3 = 0; + if (representation.Equals(X9ObjectIdentifiers.TPBasis)) + { + // Trinomial basis representation + k1 = ((DerInteger)parameters[2]).Value.IntValue; + } + else + { + // Pentanomial basis representation + DerSequence pentanomial = (DerSequence) parameters[2]; + k1 = ((DerInteger) pentanomial[0]).Value.IntValue; + k2 = ((DerInteger) pentanomial[1]).Value.IntValue; + k3 = ((DerInteger) pentanomial[2]).Value.IntValue; + } + X9FieldElement x9A = new X9FieldElement(m, k1, k2, k3, (Asn1OctetString)seq[0]); + X9FieldElement x9B = new X9FieldElement(m, k1, k2, k3, (Asn1OctetString)seq[1]); + // TODO Is it possible to get the order (n) and cofactor(h) too? + curve = new F2mCurve(m, k1, k2, k3, x9A.Value.ToBigInteger(), x9B.Value.ToBigInteger()); + } + } + + if (seq.Count == 3) + { + seed = ((DerBitString) seq[2]).GetBytes(); + } + } + + public ECCurve Curve + { + get { return curve; } + } + + public byte[] GetSeed() + { + return Arrays.Clone(seed); + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * Curve ::= Sequence { + * a FieldElement, + * b FieldElement, + * seed BIT STRING OPTIONAL + * } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(); + + if (fieldIdentifier.Equals(X9ObjectIdentifiers.PrimeField) + || fieldIdentifier.Equals(X9ObjectIdentifiers.CharacteristicTwoField)) + { + v.Add(new X9FieldElement(curve.A).ToAsn1Object()); + v.Add(new X9FieldElement(curve.B).ToAsn1Object()); + } + + if (seed != null) + { + v.Add(new DerBitString(seed)); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/x9/X9ECParameters.cs b/Crypto/src/asn1/x9/X9ECParameters.cs new file mode 100644
index 000000000..d025b36ce --- /dev/null +++ b/Crypto/src/asn1/x9/X9ECParameters.cs
@@ -0,0 +1,170 @@ +using System; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; + +namespace Org.BouncyCastle.Asn1.X9 +{ + /** + * ASN.1 def for Elliptic-Curve ECParameters structure. See + * X9.62, for further details. + */ + public class X9ECParameters + : Asn1Encodable + { + private X9FieldID fieldID; + private ECCurve curve; + private ECPoint g; + private BigInteger n; + private BigInteger h; + private byte[] seed; + + public X9ECParameters( + Asn1Sequence seq) + { + if (!(seq[0] is DerInteger) + || !((DerInteger) seq[0]).Value.Equals(BigInteger.One)) + { + throw new ArgumentException("bad version in X9ECParameters"); + } + + X9Curve x9c = null; + if (seq[2] is X9Curve) + { + x9c = (X9Curve) seq[2]; + } + else + { + x9c = new X9Curve( + new X9FieldID( + (Asn1Sequence) seq[1]), + (Asn1Sequence) seq[2]); + } + + this.curve = x9c.Curve; + + if (seq[3] is X9ECPoint) + { + this.g = ((X9ECPoint) seq[3]).Point; + } + else + { + this.g = new X9ECPoint(curve, (Asn1OctetString) seq[3]).Point; + } + + this.n = ((DerInteger) seq[4]).Value; + this.seed = x9c.GetSeed(); + + if (seq.Count == 6) + { + this.h = ((DerInteger) seq[5]).Value; + } + } + + public X9ECParameters( + ECCurve curve, + ECPoint g, + BigInteger n) + : this(curve, g, n, BigInteger.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 is FpCurve) + { + this.fieldID = new X9FieldID(((FpCurve) curve).Q); + } + else if (curve is F2mCurve) + { + F2mCurve curveF2m = (F2mCurve) curve; + this.fieldID = new X9FieldID(curveF2m.M, curveF2m.K1, + curveF2m.K2, curveF2m.K3); + } + } + + public ECCurve Curve + { + get { return curve; } + } + + public ECPoint G + { + get { return g; } + } + + public BigInteger N + { + get { return n; } + } + + public BigInteger H + { + get + { + if (h == null) + { + // TODO - this should be calculated, it will cause issues with custom curves. + return BigInteger.One; + } + + 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 override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector( + new DerInteger(1), + fieldID, + new X9Curve(curve, seed), + new X9ECPoint(g), + new DerInteger(n)); + + if (h != null) + { + v.Add(new DerInteger(h)); + } + + return new DerSequence(v); + } + } +} diff --git a/Crypto/src/asn1/x9/X9ECParametersHolder.cs b/Crypto/src/asn1/x9/X9ECParametersHolder.cs new file mode 100644
index 000000000..b3455709c --- /dev/null +++ b/Crypto/src/asn1/x9/X9ECParametersHolder.cs
@@ -0,0 +1,22 @@ +namespace Org.BouncyCastle.Asn1.X9 +{ + public abstract class X9ECParametersHolder + { + private X9ECParameters parameters; + + public X9ECParameters Parameters + { + get + { + if (parameters == null) + { + parameters = CreateParameters(); + } + + return parameters; + } + } + + protected abstract X9ECParameters CreateParameters(); + } +} diff --git a/Crypto/src/asn1/x9/X9ECPoint.cs b/Crypto/src/asn1/x9/X9ECPoint.cs new file mode 100644
index 000000000..ba2b2bcbf --- /dev/null +++ b/Crypto/src/asn1/x9/X9ECPoint.cs
@@ -0,0 +1,44 @@ +using Org.BouncyCastle.Math.EC; + +namespace Org.BouncyCastle.Asn1.X9 +{ + /** + * class for describing an ECPoint as a Der object. + */ + public class X9ECPoint + : Asn1Encodable + { + private readonly ECPoint p; + + public X9ECPoint( + ECPoint p) + { + this.p = p; + } + + public X9ECPoint( + ECCurve c, + Asn1OctetString s) + { + this.p = c.DecodePoint(s.GetOctets()); + } + + public ECPoint Point + { + get { return p; } + } + + /** + * Produce an object suitable for an Asn1OutputStream. + * <pre> + * ECPoint ::= OCTET STRING + * </pre> + * <p> + * Octet string produced using ECPoint.GetEncoded().</p> + */ + public override Asn1Object ToAsn1Object() + { + return new DerOctetString(p.GetEncoded()); + } + } +} diff --git a/Crypto/src/asn1/x9/X9FieldElement.cs b/Crypto/src/asn1/x9/X9FieldElement.cs new file mode 100644
index 000000000..06fa0e3dc --- /dev/null +++ b/Crypto/src/asn1/x9/X9FieldElement.cs
@@ -0,0 +1,69 @@ +using System; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; + +namespace Org.BouncyCastle.Asn1.X9 +{ + /** + * Class for processing an ECFieldElement as a DER object. + */ + public class X9FieldElement + : Asn1Encodable + { + private ECFieldElement f; + + public X9FieldElement( + ECFieldElement f) + { + this.f = f; + } + + public X9FieldElement( + BigInteger p, + Asn1OctetString s) + : this(new FpFieldElement(p, new BigInteger(1, s.GetOctets()))) + { + } + + public X9FieldElement( + int m, + int k1, + int k2, + int k3, + Asn1OctetString s) + : this(new F2mFieldElement(m, k1, k2, k3, new BigInteger(1, s.GetOctets()))) + { + } + + public ECFieldElement Value + { + get { 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> + * </p> + */ + public override Asn1Object ToAsn1Object() + { + int byteCount = X9IntegerConverter.GetByteLength(f); + byte[] paddedBigInteger = X9IntegerConverter.IntegerToBytes(f.ToBigInteger(), byteCount); + + return new DerOctetString(paddedBigInteger); + } + } +} diff --git a/Crypto/src/asn1/x9/X9FieldID.cs b/Crypto/src/asn1/x9/X9FieldID.cs new file mode 100644
index 000000000..c51cc4df2 --- /dev/null +++ b/Crypto/src/asn1/x9/X9FieldID.cs
@@ -0,0 +1,102 @@ +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Asn1.X9 +{ + /** + * ASN.1 def for Elliptic-Curve Field ID structure. See + * X9.62, for further details. + */ + public class X9FieldID + : Asn1Encodable + { + private readonly DerObjectIdentifier id; + private readonly Asn1Object 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 = X9ObjectIdentifiers.PrimeField; + this.parameters = new DerInteger(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 = X9ObjectIdentifiers.CharacteristicTwoField; + + Asn1EncodableVector fieldIdParams = new Asn1EncodableVector(new DerInteger(m)); + + if (k2 == 0) + { + fieldIdParams.Add( + X9ObjectIdentifiers.TPBasis, + new DerInteger(k1)); + } + else + { + fieldIdParams.Add( + X9ObjectIdentifiers.PPBasis, + new DerSequence( + new DerInteger(k1), + new DerInteger(k2), + new DerInteger(k3))); + } + + this.parameters = new DerSequence(fieldIdParams); + } + + internal X9FieldID( + Asn1Sequence seq) + { + this.id = (DerObjectIdentifier) seq[0]; + this.parameters = (Asn1Object) seq[1]; + } + + public DerObjectIdentifier Identifier + { + get { return id; } + } + + public Asn1Object Parameters + { + get { 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 override Asn1Object ToAsn1Object() + { + return new DerSequence(id, parameters); + } + } +} diff --git a/Crypto/src/asn1/x9/X9IntegerConverter.cs b/Crypto/src/asn1/x9/X9IntegerConverter.cs new file mode 100644
index 000000000..2bce20488 --- /dev/null +++ b/Crypto/src/asn1/x9/X9IntegerConverter.cs
@@ -0,0 +1,48 @@ +using System; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; + +namespace Org.BouncyCastle.Asn1.X9 +{ + public sealed class X9IntegerConverter + { + private X9IntegerConverter() + { + } + + public static int GetByteLength( + ECFieldElement fe) + { + return (fe.FieldSize + 7) / 8; + } + + public static int GetByteLength( + ECCurve c) + { + return (c.FieldSize + 7) / 8; + } + + public static byte[] IntegerToBytes( + BigInteger s, + int qLength) + { + byte[] bytes = s.ToByteArrayUnsigned(); + + if (qLength < bytes.Length) + { + byte[] tmp = new byte[qLength]; + Array.Copy(bytes, bytes.Length - tmp.Length, tmp, 0, tmp.Length); + return tmp; + } + else if (qLength > bytes.Length) + { + byte[] tmp = new byte[qLength]; + Array.Copy(bytes, 0, tmp, tmp.Length - bytes.Length, bytes.Length); + return tmp; + } + + return bytes; + } + } +} diff --git a/Crypto/src/asn1/x9/X9ObjectIdentifiers.cs b/Crypto/src/asn1/x9/X9ObjectIdentifiers.cs new file mode 100644
index 000000000..5b2fdbd2d --- /dev/null +++ b/Crypto/src/asn1/x9/X9ObjectIdentifiers.cs
@@ -0,0 +1,135 @@ +namespace Org.BouncyCastle.Asn1.X9 +{ + public abstract class X9ObjectIdentifiers + { + // + // X9.62 + // + // ansi-X9-62 OBJECT IDENTIFIER ::= { iso(1) member-body(2) + // us(840) ansi-x962(10045) } + // + internal const string AnsiX962 = "1.2.840.10045"; + internal const string IdFieldType = AnsiX962 + ".1"; + + public static readonly DerObjectIdentifier PrimeField + = new DerObjectIdentifier(IdFieldType + ".1"); + + public static readonly DerObjectIdentifier CharacteristicTwoField + = new DerObjectIdentifier(IdFieldType + ".2"); + + public static readonly DerObjectIdentifier GNBasis + = new DerObjectIdentifier(IdFieldType + ".2.3.1"); + + public static readonly DerObjectIdentifier TPBasis + = new DerObjectIdentifier(IdFieldType + ".2.3.2"); + + public static readonly DerObjectIdentifier PPBasis + = new DerObjectIdentifier(IdFieldType + ".2.3.3"); + + public const string IdECSigType = AnsiX962 + ".4"; + + public static readonly DerObjectIdentifier ECDsaWithSha1 + = new DerObjectIdentifier(IdECSigType + ".1"); + + public const string IdPublicKeyType = AnsiX962 + ".2"; + + public static readonly DerObjectIdentifier IdECPublicKey + = new DerObjectIdentifier(IdPublicKeyType + ".1"); + + public static readonly DerObjectIdentifier ECDsaWithSha2 = new DerObjectIdentifier(IdECSigType + ".3"); + public static readonly DerObjectIdentifier ECDsaWithSha224 = new DerObjectIdentifier(ECDsaWithSha2 + ".1"); + public static readonly DerObjectIdentifier ECDsaWithSha256 = new DerObjectIdentifier(ECDsaWithSha2 + ".2"); + public static readonly DerObjectIdentifier ECDsaWithSha384 = new DerObjectIdentifier(ECDsaWithSha2 + ".3"); + public static readonly DerObjectIdentifier ECDsaWithSha512 = new DerObjectIdentifier(ECDsaWithSha2 + ".4"); + + + // + // named curves + // + public static readonly DerObjectIdentifier EllipticCurve = new DerObjectIdentifier(AnsiX962 + ".3"); + + // + // Two Curves + // + public static readonly DerObjectIdentifier CTwoCurve = new DerObjectIdentifier(EllipticCurve + ".0"); + + public static readonly DerObjectIdentifier C2Pnb163v1 = new DerObjectIdentifier(CTwoCurve + ".1"); + public static readonly DerObjectIdentifier C2Pnb163v2 = new DerObjectIdentifier(CTwoCurve + ".2"); + public static readonly DerObjectIdentifier C2Pnb163v3 = new DerObjectIdentifier(CTwoCurve + ".3"); + public static readonly DerObjectIdentifier C2Pnb176w1 = new DerObjectIdentifier(CTwoCurve + ".4"); + public static readonly DerObjectIdentifier C2Tnb191v1 = new DerObjectIdentifier(CTwoCurve + ".5"); + public static readonly DerObjectIdentifier C2Tnb191v2 = new DerObjectIdentifier(CTwoCurve + ".6"); + public static readonly DerObjectIdentifier C2Tnb191v3 = new DerObjectIdentifier(CTwoCurve + ".7"); + public static readonly DerObjectIdentifier C2Onb191v4 = new DerObjectIdentifier(CTwoCurve + ".8"); + public static readonly DerObjectIdentifier C2Onb191v5 = new DerObjectIdentifier(CTwoCurve + ".9"); + public static readonly DerObjectIdentifier C2Pnb208w1 = new DerObjectIdentifier(CTwoCurve + ".10"); + public static readonly DerObjectIdentifier C2Tnb239v1 = new DerObjectIdentifier(CTwoCurve + ".11"); + public static readonly DerObjectIdentifier C2Tnb239v2 = new DerObjectIdentifier(CTwoCurve + ".12"); + public static readonly DerObjectIdentifier C2Tnb239v3 = new DerObjectIdentifier(CTwoCurve + ".13"); + public static readonly DerObjectIdentifier C2Onb239v4 = new DerObjectIdentifier(CTwoCurve + ".14"); + public static readonly DerObjectIdentifier C2Onb239v5 = new DerObjectIdentifier(CTwoCurve + ".15"); + public static readonly DerObjectIdentifier C2Pnb272w1 = new DerObjectIdentifier(CTwoCurve + ".16"); + public static readonly DerObjectIdentifier C2Pnb304w1 = new DerObjectIdentifier(CTwoCurve + ".17"); + public static readonly DerObjectIdentifier C2Tnb359v1 = new DerObjectIdentifier(CTwoCurve + ".18"); + public static readonly DerObjectIdentifier C2Pnb368w1 = new DerObjectIdentifier(CTwoCurve + ".19"); + public static readonly DerObjectIdentifier C2Tnb431r1 = new DerObjectIdentifier(CTwoCurve + ".20"); + + // + // Prime + // + public static readonly DerObjectIdentifier PrimeCurve = new DerObjectIdentifier(EllipticCurve + ".1"); + + public static readonly DerObjectIdentifier Prime192v1 = new DerObjectIdentifier(PrimeCurve + ".1"); + public static readonly DerObjectIdentifier Prime192v2 = new DerObjectIdentifier(PrimeCurve + ".2"); + public static readonly DerObjectIdentifier Prime192v3 = new DerObjectIdentifier(PrimeCurve + ".3"); + public static readonly DerObjectIdentifier Prime239v1 = new DerObjectIdentifier(PrimeCurve + ".4"); + public static readonly DerObjectIdentifier Prime239v2 = new DerObjectIdentifier(PrimeCurve + ".5"); + public static readonly DerObjectIdentifier Prime239v3 = new DerObjectIdentifier(PrimeCurve + ".6"); + public static readonly DerObjectIdentifier Prime256v1 = new DerObjectIdentifier(PrimeCurve + ".7"); + + // + // DSA + // + // dsapublicnumber OBJECT IDENTIFIER ::= { iso(1) member-body(2) + // us(840) ansi-x957(10040) number-type(4) 1 } + public static readonly DerObjectIdentifier IdDsa = new DerObjectIdentifier("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 readonly DerObjectIdentifier IdDsaWithSha1 = new DerObjectIdentifier("1.2.840.10040.4.3"); + + /** + * X9.63 + */ + public static readonly DerObjectIdentifier X9x63Scheme = new DerObjectIdentifier("1.3.133.16.840.63.0"); + public static readonly DerObjectIdentifier DHSinglePassStdDHSha1KdfScheme = new DerObjectIdentifier(X9x63Scheme + ".2"); + public static readonly DerObjectIdentifier DHSinglePassCofactorDHSha1KdfScheme = new DerObjectIdentifier(X9x63Scheme + ".3"); + public static readonly DerObjectIdentifier MqvSinglePassSha1KdfScheme = new DerObjectIdentifier(X9x63Scheme + ".16"); + + /** + * X9.42 + */ + + internal const string AnsiX942 = "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 readonly DerObjectIdentifier DHPublicNumber = new DerObjectIdentifier(AnsiX942 + ".2.1"); + + public static readonly DerObjectIdentifier X9x42Schemes = new DerObjectIdentifier(AnsiX942 + ".3"); + public static readonly DerObjectIdentifier DHStatic = new DerObjectIdentifier(X9x42Schemes + ".1"); + public static readonly DerObjectIdentifier DHEphem = new DerObjectIdentifier(X9x42Schemes + ".2"); + public static readonly DerObjectIdentifier DHOneFlow = new DerObjectIdentifier(X9x42Schemes + ".3"); + public static readonly DerObjectIdentifier DHHybrid1 = new DerObjectIdentifier(X9x42Schemes + ".4"); + public static readonly DerObjectIdentifier DHHybrid2 = new DerObjectIdentifier(X9x42Schemes + ".5"); + public static readonly DerObjectIdentifier DHHybridOneFlow = new DerObjectIdentifier(X9x42Schemes + ".6"); + public static readonly DerObjectIdentifier Mqv2 = new DerObjectIdentifier(X9x42Schemes + ".7"); + public static readonly DerObjectIdentifier Mqv1 = new DerObjectIdentifier(X9x42Schemes + ".8"); + } +} diff --git a/Crypto/src/bcpg/ArmoredInputStream.cs b/Crypto/src/bcpg/ArmoredInputStream.cs new file mode 100644
index 000000000..0dcc948d3 --- /dev/null +++ b/Crypto/src/bcpg/ArmoredInputStream.cs
@@ -0,0 +1,517 @@ +using System; +using System.Collections; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Bcpg +{ + /** + * reader for Base64 armored objects - read the headers and then start returning + * bytes when the data is reached. An IOException is thrown if the CRC check + * fails. + */ + public class ArmoredInputStream + : BaseInputStream + { + /* + * set up the decoding table. + */ + private readonly static byte[] decodingTable; + static ArmoredInputStream() + { + decodingTable = new byte[128]; + for (int i = 'A'; i <= 'Z'; i++) + { + decodingTable[i] = (byte)(i - 'A'); + } + for (int i = 'a'; i <= 'z'; i++) + { + decodingTable[i] = (byte)(i - 'a' + 26); + } + for (int i = '0'; i <= '9'; i++) + { + decodingTable[i] = (byte)(i - '0' + 52); + } + decodingTable['+'] = 62; + decodingTable['/'] = 63; + } + + /** + * decode the base 64 encoded input data. + * + * @return the offset the data starts in out. + */ + private int Decode( + int in0, + int in1, + int in2, + int in3, + int[] result) + { + if (in3 < 0) + { + throw new EndOfStreamException("unexpected end of file in armored stream."); + } + + int b1, b2, b3, b4; + if (in2 == '=') + { + b1 = decodingTable[in0] &0xff; + b2 = decodingTable[in1] & 0xff; + result[2] = ((b1 << 2) | (b2 >> 4)) & 0xff; + return 2; + } + else if (in3 == '=') + { + b1 = decodingTable[in0]; + b2 = decodingTable[in1]; + b3 = decodingTable[in2]; + result[1] = ((b1 << 2) | (b2 >> 4)) & 0xff; + result[2] = ((b2 << 4) | (b3 >> 2)) & 0xff; + return 1; + } + else + { + b1 = decodingTable[in0]; + b2 = decodingTable[in1]; + b3 = decodingTable[in2]; + b4 = decodingTable[in3]; + result[0] = ((b1 << 2) | (b2 >> 4)) & 0xff; + result[1] = ((b2 << 4) | (b3 >> 2)) & 0xff; + result[2] = ((b3 << 6) | b4) & 0xff; + return 0; + } + } + + Stream input; + bool start = true; + int[] outBuf = new int[3]; + int bufPtr = 3; + Crc24 crc = new Crc24(); + bool crcFound = false; + bool hasHeaders = true; + string header = null; + bool newLineFound = false; + bool clearText = false; + bool restart = false; + IList headerList= Platform.CreateArrayList(); + int lastC = 0; + bool isEndOfStream; + + /** + * Create a stream for reading a PGP armoured message, parsing up to a header + * and then reading the data that follows. + * + * @param input + */ + public ArmoredInputStream( + Stream input) + : this(input, true) + { + } + + /** + * Create an armoured input stream which will assume the data starts + * straight away, or parse for headers first depending on the value of + * hasHeaders. + * + * @param input + * @param hasHeaders true if headers are to be looked for, false otherwise. + */ + public ArmoredInputStream( + Stream input, + bool hasHeaders) + { + this.input = input; + this.hasHeaders = hasHeaders; + + if (hasHeaders) + { + ParseHeaders(); + } + + start = false; + } + + private bool ParseHeaders() + { + header = null; + + int c; + int last = 0; + bool headerFound = false; + + headerList = Platform.CreateArrayList(); + + // + // if restart we already have a header + // + if (restart) + { + headerFound = true; + } + else + { + while ((c = input.ReadByte()) >= 0) + { + if (c == '-' && (last == 0 || last == '\n' || last == '\r')) + { + headerFound = true; + break; + } + + last = c; + } + } + + if (headerFound) + { + StringBuilder Buffer = new StringBuilder("-"); + bool eolReached = false; + bool crLf = false; + + if (restart) // we've had to look ahead two '-' + { + Buffer.Append('-'); + } + + while ((c = input.ReadByte()) >= 0) + { + if (last == '\r' && c == '\n') + { + crLf = true; + } + if (eolReached && (last != '\r' && c == '\n')) + { + break; + } + if (eolReached && c == '\r') + { + break; + } + if (c == '\r' || (last != '\r' && c == '\n')) + { + string line = Buffer.ToString(); + if (line.Trim().Length < 1) + break; + headerList.Add(line); + Buffer.Length = 0; + } + + if (c != '\n' && c != '\r') + { + Buffer.Append((char)c); + eolReached = false; + } + else + { + if (c == '\r' || (last != '\r' && c == '\n')) + { + eolReached = true; + } + } + + last = c; + } + + if (crLf) + { + input.ReadByte(); // skip last \n + } + } + + if (headerList.Count > 0) + { + header = (string) headerList[0]; + } + + clearText = "-----BEGIN PGP SIGNED MESSAGE-----".Equals(header); + newLineFound = true; + + return headerFound; + } + + /** + * @return true if we are inside the clear text section of a PGP + * signed message. + */ + public bool IsClearText() + { + return clearText; + } + + /** + * @return true if the stream is actually at end of file. + */ + public bool IsEndOfStream() + { + return isEndOfStream; + } + + /** + * Return the armor header line (if there is one) + * @return the armor header line, null if none present. + */ + public string GetArmorHeaderLine() + { + return header; + } + + /** + * Return the armor headers (the lines after the armor header line), + * @return an array of armor headers, null if there aren't any. + */ + public string[] GetArmorHeaders() + { + if (headerList.Count <= 1) + { + return null; + } + + string[] hdrs = new string[headerList.Count - 1]; + for (int i = 0; i != hdrs.Length; i++) + { + hdrs[i] = (string) headerList[i + 1]; + } + + return hdrs; + } + + private int ReadIgnoreSpace() + { + int c; + do + { + c = input.ReadByte(); + } + while (c == ' ' || c == '\t'); + + return c; + } + + private int ReadIgnoreWhitespace() + { + int c; + do + { + c = input.ReadByte(); + } + while (c == ' ' || c == '\t' || c == '\r' || c == '\n'); + + return c; + } + + private int ReadByteClearText() + { + int c = input.ReadByte(); + + if (c == '\r' || (c == '\n' && lastC != '\r')) + { + newLineFound = true; + } + else if (newLineFound && c == '-') + { + c = input.ReadByte(); + if (c == '-') // a header, not dash escaped + { + clearText = false; + start = true; + restart = true; + } + else // a space - must be a dash escape + { + c = input.ReadByte(); + } + newLineFound = false; + } + else + { + if (c != '\n' && lastC != '\r') + { + newLineFound = false; + } + } + + lastC = c; + + if (c < 0) + { + isEndOfStream = true; + } + + return c; + } + + private int ReadClearText(byte[] buffer, int offset, int count) + { + int pos = offset; + try + { + int end = offset + count; + while (pos < end) + { + int c = ReadByteClearText(); + if (c == -1) + { + break; + } + buffer[pos++] = (byte) c; + } + } + catch (IOException ioe) + { + if (pos == offset) throw ioe; + } + + return pos - offset; + } + + private int DoReadByte() + { + if (bufPtr > 2 || crcFound) + { + int c = ReadIgnoreSpace(); + if (c == '\n' || c == '\r') + { + c = ReadIgnoreWhitespace(); + if (c == '=') // crc reached + { + bufPtr = Decode(ReadIgnoreSpace(), ReadIgnoreSpace(), ReadIgnoreSpace(), ReadIgnoreSpace(), outBuf); + + if (bufPtr != 0) + { + throw new IOException("no crc found in armored message."); + } + + crcFound = true; + + int i = ((outBuf[0] & 0xff) << 16) + | ((outBuf[1] & 0xff) << 8) + | (outBuf[2] & 0xff); + + if (i != crc.Value) + { + throw new IOException("crc check failed in armored message."); + } + + return ReadByte(); + } + + if (c == '-') // end of record reached + { + while ((c = input.ReadByte()) >= 0) + { + if (c == '\n' || c == '\r') + { + break; + } + } + + if (!crcFound) + { + throw new IOException("crc check not found."); + } + + crcFound = false; + start = true; + bufPtr = 3; + + if (c < 0) + { + isEndOfStream = true; + } + + return -1; + } + } + + if (c < 0) + { + isEndOfStream = true; + return -1; + } + + bufPtr = Decode(c, ReadIgnoreSpace(), ReadIgnoreSpace(), ReadIgnoreSpace(), outBuf); + } + + return outBuf[bufPtr++]; + } + + public override int ReadByte() + { + if (start) + { + if (hasHeaders) + { + ParseHeaders(); + } + + crc.Reset(); + start = false; + } + + if (clearText) + { + return ReadByteClearText(); + } + + int c = DoReadByte(); + + crc.Update(c); + + return c; + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (start && count > 0) + { + if (hasHeaders) + { + ParseHeaders(); + } + start = false; + } + + if (clearText) + { + return ReadClearText(buffer, offset, count); + } + + int pos = offset; + try + { + int end = offset + count; + while (pos < end) + { + int c = DoReadByte(); + crc.Update(c); + if (c == -1) + { + break; + } + buffer[pos++] = (byte) c; + } + } + catch (IOException ioe) + { + if (pos == offset) throw ioe; + } + + return pos - offset; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + input.Dispose(); + } + + base.Dispose(disposing); + } + } +} diff --git a/Crypto/src/bcpg/ArmoredOutputStream.cs b/Crypto/src/bcpg/ArmoredOutputStream.cs new file mode 100644
index 000000000..7ee26886a --- /dev/null +++ b/Crypto/src/bcpg/ArmoredOutputStream.cs
@@ -0,0 +1,333 @@ +using System; +using System.Collections; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Text; + +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Bcpg +{ + /** + * Basic output stream. + */ + public class ArmoredOutputStream + : BaseOutputStream + { + private static readonly 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)'/' + }; + + /** + * encode the input data producing a base 64 encoded byte array. + */ + private static void Encode( + Stream outStream, + int[] data, + int len) + { + Debug.Assert(len > 0); + Debug.Assert(len < 4); + + byte[] bs = new byte[4]; + int d1 = data[0]; + bs[0] = encodingTable[(d1 >> 2) & 0x3f]; + + switch (len) + { + case 1: + { + bs[1] = encodingTable[(d1 << 4) & 0x3f]; + bs[2] = (byte)'='; + bs[3] = (byte)'='; + break; + } + case 2: + { + int d2 = data[1]; + bs[1] = encodingTable[((d1 << 4) | (d2 >> 4)) & 0x3f]; + bs[2] = encodingTable[(d2 << 2) & 0x3f]; + bs[3] = (byte)'='; + break; + } + case 3: + { + int d2 = data[1]; + int d3 = data[2]; + bs[1] = encodingTable[((d1 << 4) | (d2 >> 4)) & 0x3f]; + bs[2] = encodingTable[((d2 << 2) | (d3 >> 6)) & 0x3f]; + bs[3] = encodingTable[d3 & 0x3f]; + break; + } + } + + outStream.Write(bs, 0, bs.Length); + } + + private readonly Stream outStream; + private int[] buf = new int[3]; + private int bufPtr = 0; + private Crc24 crc = new Crc24(); + private int chunkCount = 0; + private int lastb; + + private bool start = true; + private bool clearText = false; + private bool newLine = false; + + private string type; + + private static readonly string nl = Platform.NewLine; + private static readonly string headerStart = "-----BEGIN PGP "; + private static readonly string headerTail = "-----"; + private static readonly string footerStart = "-----END PGP "; + private static readonly string footerTail = "-----"; + + private static readonly string version = "BCPG C# v" + AssemblyInfo.Version; + + private readonly IDictionary headers; + + public ArmoredOutputStream(Stream outStream) + { + this.outStream = outStream; + this.headers = Platform.CreateHashtable(); + this.headers["Version"] = version; + } + + public ArmoredOutputStream(Stream outStream, IDictionary headers) + { + this.outStream = outStream; + this.headers = Platform.CreateHashtable(headers); + this.headers["Version"] = version; + } + + /** + * Set an additional header entry. + * + * @param name the name of the header entry. + * @param v the value of the header entry. + */ + public void SetHeader( + string name, + string v) + { + headers[name] = v; + } + + /** + * Reset the headers to only contain a Version string. + */ + public void ResetHeaders() + { + headers.Clear(); + headers["Version"] = version; + } + + /** + * Start a clear text signed message. + * @param hashAlgorithm + */ + public void BeginClearText( + HashAlgorithmTag hashAlgorithm) + { + string hash; + + switch (hashAlgorithm) + { + case HashAlgorithmTag.Sha1: + hash = "SHA1"; + break; + case HashAlgorithmTag.Sha256: + hash = "SHA256"; + break; + case HashAlgorithmTag.Sha384: + hash = "SHA384"; + break; + case HashAlgorithmTag.Sha512: + hash = "SHA512"; + break; + case HashAlgorithmTag.MD2: + hash = "MD2"; + break; + case HashAlgorithmTag.MD5: + hash = "MD5"; + break; + case HashAlgorithmTag.RipeMD160: + hash = "RIPEMD160"; + break; + default: + throw new IOException("unknown hash algorithm tag in beginClearText: " + hashAlgorithm); + } + + DoWrite("-----BEGIN PGP SIGNED MESSAGE-----" + nl); + DoWrite("Hash: " + hash + nl + nl); + + clearText = true; + newLine = true; + lastb = 0; + } + + public void EndClearText() + { + clearText = false; + } + + public override void WriteByte( + byte b) + { + if (clearText) + { + outStream.WriteByte(b); + + if (newLine) + { + if (!(b == '\n' && lastb == '\r')) + { + newLine = false; + } + if (b == '-') + { + outStream.WriteByte((byte)' '); + outStream.WriteByte((byte)'-'); // dash escape + } + } + if (b == '\r' || (b == '\n' && lastb != '\r')) + { + newLine = true; + } + lastb = b; + return; + } + + if (start) + { + bool newPacket = (b & 0x40) != 0; + + int tag; + if (newPacket) + { + tag = b & 0x3f; + } + else + { + tag = (b & 0x3f) >> 2; + } + + switch ((PacketTag)tag) + { + case PacketTag.PublicKey: + type = "PUBLIC KEY BLOCK"; + break; + case PacketTag.SecretKey: + type = "PRIVATE KEY BLOCK"; + break; + case PacketTag.Signature: + type = "SIGNATURE"; + break; + default: + type = "MESSAGE"; + break; + } + + DoWrite(headerStart + type + headerTail + nl); + WriteHeaderEntry("Version", (string) headers["Version"]); + + foreach (DictionaryEntry de in headers) + { + string k = (string) de.Key; + if (k != "Version") + { + string v = (string) de.Value; + WriteHeaderEntry(k, v); + } + } + + DoWrite(nl); + + start = false; + } + + if (bufPtr == 3) + { + Encode(outStream, buf, bufPtr); + bufPtr = 0; + if ((++chunkCount & 0xf) == 0) + { + DoWrite(nl); + } + } + + crc.Update(b); + buf[bufPtr++] = b & 0xff; + } + + /** + * <b>Note</b>: Dispose does nor Dispose the underlying stream. So it is possible to write + * multiple objects using armoring to a single stream. + */ + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (type != null) + { + if (bufPtr > 0) + { + Encode(outStream, buf, bufPtr); + } + + DoWrite(nl + '='); + + int crcV = crc.Value; + + buf[0] = ((crcV >> 16) & 0xff); + buf[1] = ((crcV >> 8) & 0xff); + buf[2] = (crcV & 0xff); + + Encode(outStream, buf, 3); + + DoWrite(nl); + DoWrite(footerStart); + DoWrite(type); + DoWrite(footerTail); + DoWrite(nl); + + outStream.Flush(); + + type = null; + start = true; + } + + base.Dispose(disposing); + } + } + + private void WriteHeaderEntry( + string name, + string v) + { + DoWrite(name + ": " + v + nl); + } + + private void DoWrite( + string s) + { + byte[] bs = Strings.ToAsciiByteArray(s); + outStream.Write(bs, 0, bs.Length); + } + } +} diff --git a/Crypto/src/bcpg/BcpgInputStream.cs b/Crypto/src/bcpg/BcpgInputStream.cs new file mode 100644
index 000000000..b4afab901 --- /dev/null +++ b/Crypto/src/bcpg/BcpgInputStream.cs
@@ -0,0 +1,359 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Reader for PGP objects.</remarks> + public class BcpgInputStream + : BaseInputStream + { + private Stream m_in; + private bool next = false; + private int nextB; + + internal static BcpgInputStream Wrap( + Stream inStr) + { + if (inStr is BcpgInputStream) + { + return (BcpgInputStream) inStr; + } + + return new BcpgInputStream(inStr); + } + + private BcpgInputStream( + Stream inputStream) + { + this.m_in = inputStream; + } + + public override int ReadByte() + { + if (next) + { + next = false; + return nextB; + } + + return m_in.ReadByte(); + } + + public override int Read( + byte[] buffer, + int offset, + int count) + { + // Strangely, when count == 0, we should still attempt to read a byte +// if (count == 0) +// return 0; + + if (!next) + return m_in.Read(buffer, offset, count); + + // We have next byte waiting, so return it + + if (nextB < 0) + return 0; // EndOfStream + + if (buffer == null) + throw new ArgumentNullException("buffer"); + + buffer[offset] = (byte) nextB; + next = false; + + return 1; + } + + public byte[] ReadAll() + { + return Streams.ReadAll(this); + } + + public void ReadFully( + byte[] buffer, + int off, + int len) + { + if (Streams.ReadFully(this, buffer, off, len) < len) + throw new EndOfStreamException(); + } + + public void ReadFully( + byte[] buffer) + { + ReadFully(buffer, 0, buffer.Length); + } + + /// <summary>Returns the next packet tag in the stream.</summary> + public PacketTag NextPacketTag() + { + if (!next) + { + try + { + nextB = m_in.ReadByte(); + } + catch (EndOfStreamException) + { + nextB = -1; + } + + next = true; + } + + if (nextB >= 0) + { + if ((nextB & 0x40) != 0) // new + { + return (PacketTag)(nextB & 0x3f); + } + else // old + { + return (PacketTag)((nextB & 0x3f) >> 2); + } + } + + return (PacketTag) nextB; + } + + public Packet ReadPacket() + { + int hdr = this.ReadByte(); + + if (hdr < 0) + { + return null; + } + + if ((hdr & 0x80) == 0) + { + throw new IOException("invalid header encountered"); + } + + bool newPacket = (hdr & 0x40) != 0; + PacketTag tag = 0; + int bodyLen = 0; + bool partial = false; + + if (newPacket) + { + tag = (PacketTag)(hdr & 0x3f); + + int l = this.ReadByte(); + + if (l < 192) + { + bodyLen = l; + } + else if (l <= 223) + { + int b = m_in.ReadByte(); + bodyLen = ((l - 192) << 8) + (b) + 192; + } + else if (l == 255) + { + bodyLen = (m_in.ReadByte() << 24) | (m_in.ReadByte() << 16) + | (m_in.ReadByte() << 8) | m_in.ReadByte(); + } + else + { + partial = true; + bodyLen = 1 << (l & 0x1f); + } + } + else + { + int lengthType = hdr & 0x3; + + tag = (PacketTag)((hdr & 0x3f) >> 2); + + switch (lengthType) + { + case 0: + bodyLen = this.ReadByte(); + break; + case 1: + bodyLen = (this.ReadByte() << 8) | this.ReadByte(); + break; + case 2: + bodyLen = (this.ReadByte() << 24) | (this.ReadByte() << 16) + | (this.ReadByte() << 8) | this.ReadByte(); + break; + case 3: + partial = true; + break; + default: + throw new IOException("unknown length type encountered"); + } + } + + BcpgInputStream objStream; + if (bodyLen == 0 && partial) + { + objStream = this; + } + else + { + PartialInputStream pis = new PartialInputStream(this, partial, bodyLen); + objStream = new BcpgInputStream(pis); + } + + switch (tag) + { + case PacketTag.Reserved: + return new InputStreamPacket(objStream); + case PacketTag.PublicKeyEncryptedSession: + return new PublicKeyEncSessionPacket(objStream); + case PacketTag.Signature: + return new SignaturePacket(objStream); + case PacketTag.SymmetricKeyEncryptedSessionKey: + return new SymmetricKeyEncSessionPacket(objStream); + case PacketTag.OnePassSignature: + return new OnePassSignaturePacket(objStream); + case PacketTag.SecretKey: + return new SecretKeyPacket(objStream); + case PacketTag.PublicKey: + return new PublicKeyPacket(objStream); + case PacketTag.SecretSubkey: + return new SecretSubkeyPacket(objStream); + case PacketTag.CompressedData: + return new CompressedDataPacket(objStream); + case PacketTag.SymmetricKeyEncrypted: + return new SymmetricEncDataPacket(objStream); + case PacketTag.Marker: + return new MarkerPacket(objStream); + case PacketTag.LiteralData: + return new LiteralDataPacket(objStream); + case PacketTag.Trust: + return new TrustPacket(objStream); + case PacketTag.UserId: + return new UserIdPacket(objStream); + case PacketTag.UserAttribute: + return new UserAttributePacket(objStream); + case PacketTag.PublicSubkey: + return new PublicSubkeyPacket(objStream); + case PacketTag.SymmetricEncryptedIntegrityProtected: + return new SymmetricEncIntegrityPacket(objStream); + case PacketTag.ModificationDetectionCode: + return new ModDetectionCodePacket(objStream); + case PacketTag.Experimental1: + case PacketTag.Experimental2: + case PacketTag.Experimental3: + case PacketTag.Experimental4: + return new ExperimentalPacket(tag, objStream); + default: + throw new IOException("unknown packet type encountered: " + tag); + } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + m_in.Dispose(); + } + + base.Dispose(disposing); + } + + /// <summary> + /// A stream that overlays our input stream, allowing the user to only read a segment of it. + /// NB: dataLength will be negative if the segment length is in the upper range above 2**31. + /// </summary> + private class PartialInputStream + : BaseInputStream + { + private BcpgInputStream m_in; + private bool partial; + private int dataLength; + + internal PartialInputStream( + BcpgInputStream bcpgIn, + bool partial, + int dataLength) + { + this.m_in = bcpgIn; + this.partial = partial; + this.dataLength = dataLength; + } + + public override int ReadByte() + { + do + { + if (dataLength != 0) + { + int ch = m_in.ReadByte(); + if (ch < 0) + { + throw new EndOfStreamException("Premature end of stream in PartialInputStream"); + } + dataLength--; + return ch; + } + } + while (partial && ReadPartialDataLength() >= 0); + + return -1; + } + + public override int Read(byte[] buffer, int offset, int count) + { + do + { + if (dataLength != 0) + { + int readLen = (dataLength > count || dataLength < 0) ? count : dataLength; + int len = m_in.Read(buffer, offset, readLen); + if (len < 1) + { + throw new EndOfStreamException("Premature end of stream in PartialInputStream"); + } + dataLength -= len; + return len; + } + } + while (partial && ReadPartialDataLength() >= 0); + + return 0; + } + + private int ReadPartialDataLength() + { + int l = m_in.ReadByte(); + + if (l < 0) + { + return -1; + } + + partial = false; + + if (l < 192) + { + dataLength = l; + } + else if (l <= 223) + { + dataLength = ((l - 192) << 8) + (m_in.ReadByte()) + 192; + } + else if (l == 255) + { + dataLength = (m_in.ReadByte() << 24) | (m_in.ReadByte() << 16) + | (m_in.ReadByte() << 8) | m_in.ReadByte(); + } + else + { + partial = true; + dataLength = 1 << (l & 0x1f); + } + + return 0; + } + } + } +} diff --git a/Crypto/src/bcpg/BcpgObject.cs b/Crypto/src/bcpg/BcpgObject.cs new file mode 100644
index 000000000..92811c3d3 --- /dev/null +++ b/Crypto/src/bcpg/BcpgObject.cs
@@ -0,0 +1,22 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Base class for a PGP object.</remarks> + public abstract class BcpgObject + { + public virtual byte[] GetEncoded() + { + MemoryStream bOut = new MemoryStream(); + BcpgOutputStream pOut = new BcpgOutputStream(bOut); + + pOut.WriteObject(this); + + return bOut.ToArray(); + } + + public abstract void Encode(BcpgOutputStream bcpgOut); + } +} + diff --git a/Crypto/src/bcpg/BcpgOutputStream.cs b/Crypto/src/bcpg/BcpgOutputStream.cs new file mode 100644
index 000000000..365053e11 --- /dev/null +++ b/Crypto/src/bcpg/BcpgOutputStream.cs
@@ -0,0 +1,394 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Basic output stream.</remarks> + public class BcpgOutputStream + : BaseOutputStream + { + internal static BcpgOutputStream Wrap( + Stream outStr) + { + if (outStr is BcpgOutputStream) + { + return (BcpgOutputStream) outStr; + } + + return new BcpgOutputStream(outStr); + } + + private Stream outStr; + private byte[] partialBuffer; + private int partialBufferLength; + private int partialPower; + private int partialOffset; + private const int BufferSizePower = 16; // 2^16 size buffer on long files + + /// <summary>Create a stream representing a general packet.</summary> + /// <param name="outStr">Output stream to write to.</param> + public BcpgOutputStream( + Stream outStr) + { + if (outStr == null) + throw new ArgumentNullException("outStr"); + + this.outStr = outStr; + } + + /// <summary>Create a stream representing an old style partial object.</summary> + /// <param name="outStr">Output stream to write to.</param> + /// <param name="tag">The packet tag for the object.</param> + public BcpgOutputStream( + Stream outStr, + PacketTag tag) + { + if (outStr == null) + throw new ArgumentNullException("outStr"); + + this.outStr = outStr; + this.WriteHeader(tag, true, true, 0); + } + + /// <summary>Create a stream representing a general packet.</summary> + /// <param name="outStr">Output stream to write to.</param> + /// <param name="tag">Packet tag.</param> + /// <param name="length">Size of chunks making up the packet.</param> + /// <param name="oldFormat">If true, the header is written out in old format.</param> + public BcpgOutputStream( + Stream outStr, + PacketTag tag, + long length, + bool oldFormat) + { + if (outStr == null) + throw new ArgumentNullException("outStr"); + + this.outStr = outStr; + + if (length > 0xFFFFFFFFL) + { + this.WriteHeader(tag, false, true, 0); + this.partialBufferLength = 1 << BufferSizePower; + this.partialBuffer = new byte[partialBufferLength]; + this.partialPower = BufferSizePower; + this.partialOffset = 0; + } + else + { + this.WriteHeader(tag, oldFormat, false, length); + } + } + + /// <summary>Create a new style partial input stream buffered into chunks.</summary> + /// <param name="outStr">Output stream to write to.</param> + /// <param name="tag">Packet tag.</param> + /// <param name="length">Size of chunks making up the packet.</param> + public BcpgOutputStream( + Stream outStr, + PacketTag tag, + long length) + { + if (outStr == null) + throw new ArgumentNullException("outStr"); + + this.outStr = outStr; + this.WriteHeader(tag, false, false, length); + } + + /// <summary>Create a new style partial input stream buffered into chunks.</summary> + /// <param name="outStr">Output stream to write to.</param> + /// <param name="tag">Packet tag.</param> + /// <param name="buffer">Buffer to use for collecting chunks.</param> + public BcpgOutputStream( + Stream outStr, + PacketTag tag, + byte[] buffer) + { + if (outStr == null) + throw new ArgumentNullException("outStr"); + + this.outStr = outStr; + this.WriteHeader(tag, false, true, 0); + + this.partialBuffer = buffer; + + uint length = (uint) partialBuffer.Length; + for (partialPower = 0; length != 1; partialPower++) + { + length >>= 1; + } + + if (partialPower > 30) + { + throw new IOException("Buffer cannot be greater than 2^30 in length."); + } + this.partialBufferLength = 1 << partialPower; + this.partialOffset = 0; + } + + private void WriteNewPacketLength( + long bodyLen) + { + if (bodyLen < 192) + { + outStr.WriteByte((byte)bodyLen); + } + else if (bodyLen <= 8383) + { + bodyLen -= 192; + + outStr.WriteByte((byte)(((bodyLen >> 8) & 0xff) + 192)); + outStr.WriteByte((byte)bodyLen); + } + else + { + outStr.WriteByte(0xff); + outStr.WriteByte((byte)(bodyLen >> 24)); + outStr.WriteByte((byte)(bodyLen >> 16)); + outStr.WriteByte((byte)(bodyLen >> 8)); + outStr.WriteByte((byte)bodyLen); + } + } + + private void WriteHeader( + PacketTag tag, + bool oldPackets, + bool partial, + long bodyLen) + { + int hdr = 0x80; + + if (partialBuffer != null) + { + PartialFlush(true); + partialBuffer = null; + } + + if (oldPackets) + { + hdr |= ((int) tag) << 2; + + if (partial) + { + this.WriteByte((byte)(hdr | 0x03)); + } + else + { + if (bodyLen <= 0xff) + { + this.WriteByte((byte) hdr); + this.WriteByte((byte)bodyLen); + } + else if (bodyLen <= 0xffff) + { + this.WriteByte((byte)(hdr | 0x01)); + this.WriteByte((byte)(bodyLen >> 8)); + this.WriteByte((byte)(bodyLen)); + } + else + { + this.WriteByte((byte)(hdr | 0x02)); + this.WriteByte((byte)(bodyLen >> 24)); + this.WriteByte((byte)(bodyLen >> 16)); + this.WriteByte((byte)(bodyLen >> 8)); + this.WriteByte((byte)bodyLen); + } + } + } + else + { + hdr |= 0x40 | (int) tag; + this.WriteByte((byte) hdr); + + if (partial) + { + partialOffset = 0; + } + else + { + this.WriteNewPacketLength(bodyLen); + } + } + } + + private void PartialFlush( + bool isLast) + { + if (isLast) + { + WriteNewPacketLength(partialOffset); + outStr.Write(partialBuffer, 0, partialOffset); + } + else + { + outStr.WriteByte((byte)(0xE0 | partialPower)); + outStr.Write(partialBuffer, 0, partialBufferLength); + } + + partialOffset = 0; + } + + private void WritePartial( + byte b) + { + if (partialOffset == partialBufferLength) + { + PartialFlush(false); + } + + partialBuffer[partialOffset++] = b; + } + + private void WritePartial( + byte[] buffer, + int off, + int len) + { + if (partialOffset == partialBufferLength) + { + PartialFlush(false); + } + + if (len <= (partialBufferLength - partialOffset)) + { + Array.Copy(buffer, off, partialBuffer, partialOffset, len); + partialOffset += len; + } + else + { + int diff = partialBufferLength - partialOffset; + Array.Copy(buffer, off, partialBuffer, partialOffset, diff); + off += diff; + len -= diff; + PartialFlush(false); + while (len > partialBufferLength) + { + Array.Copy(buffer, off, partialBuffer, 0, partialBufferLength); + off += partialBufferLength; + len -= partialBufferLength; + PartialFlush(false); + } + Array.Copy(buffer, off, partialBuffer, 0, len); + partialOffset += len; + } + } + public override void WriteByte( + byte value) + { + if (partialBuffer != null) + { + WritePartial(value); + } + else + { + outStr.WriteByte(value); + } + } + public override void Write( + byte[] buffer, + int offset, + int count) + { + if (partialBuffer != null) + { + WritePartial(buffer, offset, count); + } + else + { + outStr.Write(buffer, offset, count); + } + } + + // Additional helper methods to write primitive types + internal virtual void WriteShort( + short n) + { + this.Write( + (byte)(n >> 8), + (byte)n); + } + internal virtual void WriteInt( + int n) + { + this.Write( + (byte)(n >> 24), + (byte)(n >> 16), + (byte)(n >> 8), + (byte)n); + } + internal virtual void WriteLong( + long n) + { + this.Write( + (byte)(n >> 56), + (byte)(n >> 48), + (byte)(n >> 40), + (byte)(n >> 32), + (byte)(n >> 24), + (byte)(n >> 16), + (byte)(n >> 8), + (byte)n); + } + + public void WritePacket( + ContainedPacket p) + { + p.Encode(this); + } + + internal void WritePacket( + PacketTag tag, + byte[] body, + bool oldFormat) + { + this.WriteHeader(tag, oldFormat, false, body.Length); + this.Write(body); + } + + public void WriteObject( + BcpgObject bcpgObject) + { + bcpgObject.Encode(this); + } + + public void WriteObjects( + params BcpgObject[] v) + { + foreach (BcpgObject o in v) + { + o.Encode(this); + } + } + + /// <summary>Flush the underlying stream.</summary> + public override void Flush() + { + outStr.Flush(); + } + + /// <summary>Finish writing out the current packet without closing the underlying stream.</summary> + public void Finish() + { + if (partialBuffer != null) + { + PartialFlush(true); + partialBuffer = null; + } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + this.Finish(); + outStr.Flush(); + outStr.Dispose(); + } + + base.Dispose(disposing); + } + } +} diff --git a/Crypto/src/bcpg/CompressedDataPacket.cs b/Crypto/src/bcpg/CompressedDataPacket.cs new file mode 100644
index 000000000..2432825eb --- /dev/null +++ b/Crypto/src/bcpg/CompressedDataPacket.cs
@@ -0,0 +1,24 @@ +using System.IO; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Generic compressed data object.</remarks> + public class CompressedDataPacket + : InputStreamPacket + { + private readonly CompressionAlgorithmTag algorithm; + + internal CompressedDataPacket( + BcpgInputStream bcpgIn) + : base(bcpgIn) + { + this.algorithm = (CompressionAlgorithmTag) bcpgIn.ReadByte(); + } + + /// <summary>The algorithm tag value.</summary> + public CompressionAlgorithmTag Algorithm + { + get { return algorithm; } + } + } +} diff --git a/Crypto/src/bcpg/CompressionAlgorithmTags.cs b/Crypto/src/bcpg/CompressionAlgorithmTags.cs new file mode 100644
index 000000000..0e452298e --- /dev/null +++ b/Crypto/src/bcpg/CompressionAlgorithmTags.cs
@@ -0,0 +1,11 @@ +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Basic tags for compression algorithms.</remarks> + public enum CompressionAlgorithmTag + { + Uncompressed = 0, // Uncompressed + Zip = 1, // ZIP (RFC 1951) + ZLib = 2, // ZLIB (RFC 1950) + BZip2 = 3, // BZ2 + } +} diff --git a/Crypto/src/bcpg/ContainedPacket.cs b/Crypto/src/bcpg/ContainedPacket.cs new file mode 100644
index 000000000..e8f387ca4 --- /dev/null +++ b/Crypto/src/bcpg/ContainedPacket.cs
@@ -0,0 +1,22 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Basic type for a PGP packet.</remarks> + public abstract class ContainedPacket + : Packet + { + public byte[] GetEncoded() + { + MemoryStream bOut = new MemoryStream(); + BcpgOutputStream pOut = new BcpgOutputStream(bOut); + + pOut.WritePacket(this); + + return bOut.ToArray(); + } + + public abstract void Encode(BcpgOutputStream bcpgOut); + } +} diff --git a/Crypto/src/bcpg/Crc24.cs b/Crypto/src/bcpg/Crc24.cs new file mode 100644
index 000000000..97846f4fb --- /dev/null +++ b/Crypto/src/bcpg/Crc24.cs
@@ -0,0 +1,46 @@ +using System; + +namespace Org.BouncyCastle.Bcpg +{ + public class Crc24 + { + private const int Crc24Init = 0x0b704ce; + private const int Crc24Poly = 0x1864cfb; + + private int crc = Crc24Init; + + public Crc24() + { + } + + public void Update( + int b) + { + crc ^= b << 16; + for (int i = 0; i < 8; i++) + { + crc <<= 1; + if ((crc & 0x1000000) != 0) + { + crc ^= Crc24Poly; + } + } + } + + [Obsolete("Use 'Value' property instead")] + public int GetValue() + { + return crc; + } + + public int Value + { + get { return crc; } + } + + public void Reset() + { + crc = Crc24Init; + } + } +} diff --git a/Crypto/src/bcpg/DsaPublicBcpgKey.cs b/Crypto/src/bcpg/DsaPublicBcpgKey.cs new file mode 100644
index 000000000..61159567c --- /dev/null +++ b/Crypto/src/bcpg/DsaPublicBcpgKey.cs
@@ -0,0 +1,80 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Base class for a DSA public key.</remarks> + public class DsaPublicBcpgKey + : BcpgObject, IBcpgKey + { + private readonly MPInteger p, q, g, y; + + /// <param name="bcpgIn">The stream to read the packet from.</param> + public DsaPublicBcpgKey( + BcpgInputStream bcpgIn) + { + this.p = new MPInteger(bcpgIn); + this.q = new MPInteger(bcpgIn); + this.g = new MPInteger(bcpgIn); + this.y = new MPInteger(bcpgIn); + } + + public DsaPublicBcpgKey( + BigInteger p, + BigInteger q, + BigInteger g, + BigInteger y) + { + this.p = new MPInteger(p); + this.q = new MPInteger(q); + this.g = new MPInteger(g); + this.y = new MPInteger(y); + } + + /// <summary>The format, as a string, always "PGP".</summary> + public string Format + { + get { return "PGP"; } + } + + /// <summary>Return the standard PGP encoding of the key.</summary> + public override byte[] GetEncoded() + { + try + { + return base.GetEncoded(); + } + catch (Exception) + { + return null; + } + } + + public override void Encode( + BcpgOutputStream bcpgOut) + { + bcpgOut.WriteObjects(p, q, g, y); + } + + public BigInteger G + { + get { return g.Value; } + } + + public BigInteger P + { + get { return p.Value; } + } + + public BigInteger Q + { + get { return q.Value; } + } + + public BigInteger Y + { + get { return y.Value; } + } + } +} diff --git a/Crypto/src/bcpg/DsaSecretBcpgKey.cs b/Crypto/src/bcpg/DsaSecretBcpgKey.cs new file mode 100644
index 000000000..41835d419 --- /dev/null +++ b/Crypto/src/bcpg/DsaSecretBcpgKey.cs
@@ -0,0 +1,61 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Base class for a DSA secret key.</remarks> + public class DsaSecretBcpgKey + : BcpgObject, IBcpgKey + { + internal MPInteger x; + + /** + * @param in + */ + public DsaSecretBcpgKey( + BcpgInputStream bcpgIn) + { + this.x = new MPInteger(bcpgIn); + } + + public DsaSecretBcpgKey( + BigInteger x) + { + this.x = new MPInteger(x); + } + + /// <summary>The format, as a string, always "PGP".</summary> + public string Format + { + get { return "PGP"; } + } + + /// <summary>Return the standard PGP encoding of the key.</summary> + public override byte[] GetEncoded() + { + try + { + return base.GetEncoded(); + } + catch (Exception) + { + return null; + } + } + + public override void Encode( + BcpgOutputStream bcpgOut) + { + bcpgOut.WriteObject(x); + } + + /** + * @return x + */ + public BigInteger X + { + get { return x.Value; } + } + } +} diff --git a/Crypto/src/bcpg/ElGamalPublicBcpgKey.cs b/Crypto/src/bcpg/ElGamalPublicBcpgKey.cs new file mode 100644
index 000000000..808e427b2 --- /dev/null +++ b/Crypto/src/bcpg/ElGamalPublicBcpgKey.cs
@@ -0,0 +1,71 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Base class for an ElGamal public key.</remarks> + public class ElGamalPublicBcpgKey + : BcpgObject, IBcpgKey + { + internal MPInteger p, g, y; + + public ElGamalPublicBcpgKey( + BcpgInputStream bcpgIn) + { + this.p = new MPInteger(bcpgIn); + this.g = new MPInteger(bcpgIn); + this.y = new MPInteger(bcpgIn); + } + + public ElGamalPublicBcpgKey( + BigInteger p, + BigInteger g, + BigInteger y) + { + this.p = new MPInteger(p); + this.g = new MPInteger(g); + this.y = new MPInteger(y); + } + + /// <summary>The format, as a string, always "PGP".</summary> + public string Format + { + get { return "PGP"; } + } + + /// <summary>Return the standard PGP encoding of the key.</summary> + public override byte[] GetEncoded() + { + try + { + return base.GetEncoded(); + } + catch (Exception) + { + return null; + } + } + + public BigInteger P + { + get { return p.Value; } + } + + public BigInteger G + { + get { return g.Value; } + } + + public BigInteger Y + { + get { return y.Value; } + } + + public override void Encode( + BcpgOutputStream bcpgOut) + { + bcpgOut.WriteObjects(p, g, y); + } + } +} diff --git a/Crypto/src/bcpg/ElGamalSecretBcpgKey.cs b/Crypto/src/bcpg/ElGamalSecretBcpgKey.cs new file mode 100644
index 000000000..2d95b29b1 --- /dev/null +++ b/Crypto/src/bcpg/ElGamalSecretBcpgKey.cs
@@ -0,0 +1,61 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Base class for an ElGamal secret key.</remarks> + public class ElGamalSecretBcpgKey + : BcpgObject, IBcpgKey + { + internal MPInteger x; + + /** + * @param in + */ + public ElGamalSecretBcpgKey( + BcpgInputStream bcpgIn) + { + this.x = new MPInteger(bcpgIn); + } + + /** + * @param x + */ + public ElGamalSecretBcpgKey( + BigInteger x) + { + this.x = new MPInteger(x); + } + + /// <summary>The format, as a string, always "PGP".</summary> + public string Format + { + get { return "PGP"; } + } + + public BigInteger X + { + get { return x.Value; } + } + + /// <summary>Return the standard PGP encoding of the key.</summary> + public override byte[] GetEncoded() + { + try + { + return base.GetEncoded(); + } + catch (Exception) + { + return null; + } + } + + public override void Encode( + BcpgOutputStream bcpgOut) + { + bcpgOut.WriteObject(x); + } + } +} diff --git a/Crypto/src/bcpg/ExperimentalPacket.cs b/Crypto/src/bcpg/ExperimentalPacket.cs new file mode 100644
index 000000000..36a254be1 --- /dev/null +++ b/Crypto/src/bcpg/ExperimentalPacket.cs
@@ -0,0 +1,38 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Basic packet for an experimental packet.</remarks> + public class ExperimentalPacket + : ContainedPacket //, PublicKeyAlgorithmTag + { + private readonly PacketTag tag; + private readonly byte[] contents; + + internal ExperimentalPacket( + PacketTag tag, + BcpgInputStream bcpgIn) + { + this.tag = tag; + + this.contents = bcpgIn.ReadAll(); + } + + public PacketTag Tag + { + get { return tag; } + } + + public byte[] GetContents() + { + return (byte[]) contents.Clone(); + } + + public override void Encode( + BcpgOutputStream bcpgOut) + { + bcpgOut.WritePacket(tag, contents, true); + } + } +} diff --git a/Crypto/src/bcpg/HashAlgorithmTags.cs b/Crypto/src/bcpg/HashAlgorithmTags.cs new file mode 100644
index 000000000..96c009153 --- /dev/null +++ b/Crypto/src/bcpg/HashAlgorithmTags.cs
@@ -0,0 +1,19 @@ +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Basic tags for hash algorithms.</remarks> + public enum HashAlgorithmTag + { + MD5 = 1, // MD5 + Sha1 = 2, // SHA-1 + RipeMD160 = 3, // RIPE-MD/160 + DoubleSha = 4, // Reserved for double-width SHA (experimental) + MD2 = 5, // MD2 + Tiger192 = 6, // Reserved for TIGER/192 + Haval5pass160 = 7, // Reserved for HAVAL (5 pass, 160-bit) + + Sha256 = 8, // SHA-256 + Sha384 = 9, // SHA-384 + Sha512 = 10, // SHA-512 + Sha224 = 11, // SHA-224 + } +} diff --git a/Crypto/src/bcpg/IBcpgKey.cs b/Crypto/src/bcpg/IBcpgKey.cs new file mode 100644
index 000000000..275461772 --- /dev/null +++ b/Crypto/src/bcpg/IBcpgKey.cs
@@ -0,0 +1,16 @@ +using System; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Base interface for a PGP key.</remarks> + public interface IBcpgKey + { + /// <summary> + /// The base format for this key - in the case of the symmetric keys it will generally + /// be raw indicating that the key is just a straight byte representation, for an asymmetric + /// key the format will be PGP, indicating the key is a string of MPIs encoded in PGP format. + /// </summary> + /// <returns>"RAW" or "PGP".</returns> + string Format { get; } + } +} diff --git a/Crypto/src/bcpg/InputStreamPacket.cs b/Crypto/src/bcpg/InputStreamPacket.cs new file mode 100644
index 000000000..c45efab7b --- /dev/null +++ b/Crypto/src/bcpg/InputStreamPacket.cs
@@ -0,0 +1,20 @@ +namespace Org.BouncyCastle.Bcpg +{ + public class InputStreamPacket + : Packet + { + private readonly BcpgInputStream bcpgIn; + + public InputStreamPacket( + BcpgInputStream bcpgIn) + { + this.bcpgIn = bcpgIn; + } + + /// <summary>Note: you can only read from this once...</summary> + public BcpgInputStream GetInputStream() + { + return bcpgIn; + } + } +} diff --git a/Crypto/src/bcpg/LiteralDataPacket.cs b/Crypto/src/bcpg/LiteralDataPacket.cs new file mode 100644
index 000000000..63a2c6d44 --- /dev/null +++ b/Crypto/src/bcpg/LiteralDataPacket.cs
@@ -0,0 +1,57 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Generic literal data packet.</remarks> + public class LiteralDataPacket + : InputStreamPacket + { + private int format; + private byte[] fileName; + private long modDate; + + internal LiteralDataPacket( + BcpgInputStream bcpgIn) + : base(bcpgIn) + { + format = bcpgIn.ReadByte(); + int len = bcpgIn.ReadByte(); + + fileName = new byte[len]; + for (int i = 0; i != len; ++i) + { + fileName[i] = (byte)bcpgIn.ReadByte(); + } + + modDate = (((uint)bcpgIn.ReadByte() << 24) + | ((uint)bcpgIn.ReadByte() << 16) + | ((uint)bcpgIn.ReadByte() << 8) + | (uint)bcpgIn.ReadByte()) * 1000L; + } + + /// <summary>The format tag value.</summary> + public int Format + { + get { return format; } + } + + /// <summary>The modification time of the file in milli-seconds (since Jan 1, 1970 UTC)</summary> + public long ModificationTime + { + get { return modDate; } + } + + public string FileName + { + get { return Strings.FromUtf8ByteArray(fileName); } + } + + public byte[] GetRawFileName() + { + return Arrays.Clone(fileName); + } + } +} diff --git a/Crypto/src/bcpg/MPInteger.cs b/Crypto/src/bcpg/MPInteger.cs new file mode 100644
index 000000000..441407244 --- /dev/null +++ b/Crypto/src/bcpg/MPInteger.cs
@@ -0,0 +1,59 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>A multiple precision integer</remarks> + public class MPInteger + : BcpgObject + { + private readonly BigInteger val; + + public MPInteger( + BcpgInputStream bcpgIn) + { + if (bcpgIn == null) + throw new ArgumentNullException("bcpgIn"); + + int length = (bcpgIn.ReadByte() << 8) | bcpgIn.ReadByte(); + byte[] bytes = new byte[(length + 7) / 8]; + + bcpgIn.ReadFully(bytes); + + this.val = new BigInteger(1, bytes); + } + + public MPInteger( + BigInteger val) + { + if (val == null) + throw new ArgumentNullException("val"); + if (val.SignValue < 0) + throw new ArgumentException("Values must be positive", "val"); + + this.val = val; + } + + public BigInteger Value + { + get { return val; } + } + + public override void Encode( + BcpgOutputStream bcpgOut) + { + bcpgOut.WriteShort((short) val.BitLength); + bcpgOut.Write(val.ToByteArrayUnsigned()); + } + + internal static void Encode( + BcpgOutputStream bcpgOut, + BigInteger val) + { + bcpgOut.WriteShort((short) val.BitLength); + bcpgOut.Write(val.ToByteArrayUnsigned()); + } + } +} diff --git a/Crypto/src/bcpg/MarkerPacket.cs b/Crypto/src/bcpg/MarkerPacket.cs new file mode 100644
index 000000000..4dc4b5a83 --- /dev/null +++ b/Crypto/src/bcpg/MarkerPacket.cs
@@ -0,0 +1,24 @@ +using System.IO; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Basic type for a marker packet.</remarks> + public class MarkerPacket + : ContainedPacket + { + // "PGP" + byte[] marker = { (byte)0x50, (byte)0x47, (byte)0x50 }; + + public MarkerPacket( + BcpgInputStream bcpgIn) + { + bcpgIn.ReadFully(marker); + } + + public override void Encode( + BcpgOutputStream bcpgOut) + { + bcpgOut.WritePacket(PacketTag.Marker, marker, true); + } + } +} diff --git a/Crypto/src/bcpg/ModDetectionCodePacket.cs b/Crypto/src/bcpg/ModDetectionCodePacket.cs new file mode 100644
index 000000000..6bb23645a --- /dev/null +++ b/Crypto/src/bcpg/ModDetectionCodePacket.cs
@@ -0,0 +1,42 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Basic packet for a modification detection code packet.</remarks> + public class ModDetectionCodePacket + : ContainedPacket + { + private readonly byte[] digest; + + internal ModDetectionCodePacket( + BcpgInputStream bcpgIn) + { + if (bcpgIn == null) + throw new ArgumentNullException("bcpgIn"); + + this.digest = new byte[20]; + bcpgIn.ReadFully(this.digest); + } + + public ModDetectionCodePacket( + byte[] digest) + { + if (digest == null) + throw new ArgumentNullException("digest"); + + this.digest = (byte[]) digest.Clone(); + } + + public byte[] GetDigest() + { + return (byte[]) digest.Clone(); + } + + public override void Encode( + BcpgOutputStream bcpgOut) + { + bcpgOut.WritePacket(PacketTag.ModificationDetectionCode, digest, false); + } + } +} diff --git a/Crypto/src/bcpg/OnePassSignaturePacket.cs b/Crypto/src/bcpg/OnePassSignaturePacket.cs new file mode 100644
index 000000000..b67df0a52 --- /dev/null +++ b/Crypto/src/bcpg/OnePassSignaturePacket.cs
@@ -0,0 +1,93 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Generic signature object</remarks> + public class OnePassSignaturePacket + : ContainedPacket + { + private int version; + private int sigType; + private HashAlgorithmTag hashAlgorithm; + private PublicKeyAlgorithmTag keyAlgorithm; + private long keyId; + private int nested; + + internal OnePassSignaturePacket( + BcpgInputStream bcpgIn) + { + version = bcpgIn.ReadByte(); + sigType = bcpgIn.ReadByte(); + hashAlgorithm = (HashAlgorithmTag) bcpgIn.ReadByte(); + keyAlgorithm = (PublicKeyAlgorithmTag) bcpgIn.ReadByte(); + + keyId |= (long)bcpgIn.ReadByte() << 56; + keyId |= (long)bcpgIn.ReadByte() << 48; + keyId |= (long)bcpgIn.ReadByte() << 40; + keyId |= (long)bcpgIn.ReadByte() << 32; + keyId |= (long)bcpgIn.ReadByte() << 24; + keyId |= (long)bcpgIn.ReadByte() << 16; + keyId |= (long)bcpgIn.ReadByte() << 8; + keyId |= (uint)bcpgIn.ReadByte(); + + nested = bcpgIn.ReadByte(); + } + + public OnePassSignaturePacket( + int sigType, + HashAlgorithmTag hashAlgorithm, + PublicKeyAlgorithmTag keyAlgorithm, + long keyId, + bool isNested) + { + this.version = 3; + this.sigType = sigType; + this.hashAlgorithm = hashAlgorithm; + this.keyAlgorithm = keyAlgorithm; + this.keyId = keyId; + this.nested = (isNested) ? 0 : 1; + } + + public int SignatureType + { + get { return sigType; } + } + + /// <summary>The encryption algorithm tag.</summary> + public PublicKeyAlgorithmTag KeyAlgorithm + { + get { return keyAlgorithm; } + } + + /// <summary>The hash algorithm tag.</summary> + public HashAlgorithmTag HashAlgorithm + { + get { return hashAlgorithm; } + } + + public long KeyId + { + get { return keyId; } + } + + public override void Encode( + BcpgOutputStream bcpgOut) + { + MemoryStream bOut = new MemoryStream(); + BcpgOutputStream pOut = new BcpgOutputStream(bOut); + + pOut.Write( + (byte) version, + (byte) sigType, + (byte) hashAlgorithm, + (byte) keyAlgorithm); + + pOut.WriteLong(keyId); + + pOut.WriteByte((byte) nested); + + bcpgOut.WritePacket(PacketTag.OnePassSignature, bOut.ToArray(), true); + } + } +} diff --git a/Crypto/src/bcpg/OutputStreamPacket.cs b/Crypto/src/bcpg/OutputStreamPacket.cs new file mode 100644
index 000000000..aa8316dcb --- /dev/null +++ b/Crypto/src/bcpg/OutputStreamPacket.cs
@@ -0,0 +1,24 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Bcpg +{ + public abstract class OutputStreamPacket + { + private readonly BcpgOutputStream bcpgOut; + + internal OutputStreamPacket( + BcpgOutputStream bcpgOut) + { + if (bcpgOut == null) + throw new ArgumentNullException("bcpgOut"); + + this.bcpgOut = bcpgOut; + } + + public abstract BcpgOutputStream Open(); + + public abstract void Close(); + } +} + diff --git a/Crypto/src/bcpg/Packet.cs b/Crypto/src/bcpg/Packet.cs new file mode 100644
index 000000000..83f6d1f74 --- /dev/null +++ b/Crypto/src/bcpg/Packet.cs
@@ -0,0 +1,7 @@ +namespace Org.BouncyCastle.Bcpg +{ + public class Packet + //: PacketTag + { + } +} diff --git a/Crypto/src/bcpg/PacketTags.cs b/Crypto/src/bcpg/PacketTags.cs new file mode 100644
index 000000000..5a53d4e95 --- /dev/null +++ b/Crypto/src/bcpg/PacketTags.cs
@@ -0,0 +1,30 @@ +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Basic PGP packet tag types.</remarks> + public enum PacketTag + { + Reserved = 0, // Reserved - a packet tag must not have this value + PublicKeyEncryptedSession = 1, // Public-Key Encrypted Session Key Packet + Signature = 2, // Signature Packet + SymmetricKeyEncryptedSessionKey = 3, // Symmetric-Key Encrypted Session Key Packet + OnePassSignature = 4, // One-Pass Signature Packet + SecretKey = 5, // Secret Key Packet + PublicKey = 6, // Public Key Packet + SecretSubkey = 7, // Secret Subkey Packet + CompressedData = 8, // Compressed Data Packet + SymmetricKeyEncrypted = 9, // Symmetrically Encrypted Data Packet + Marker = 10, // Marker Packet + LiteralData = 11, // Literal Data Packet + Trust = 12, // Trust Packet + UserId = 13, // User ID Packet + PublicSubkey = 14, // Public Subkey Packet + UserAttribute = 17, // User attribute + SymmetricEncryptedIntegrityProtected = 18, // Symmetric encrypted, integrity protected + ModificationDetectionCode = 19, // Modification detection code + + Experimental1 = 60, // Private or Experimental Values + Experimental2 = 61, + Experimental3 = 62, + Experimental4 = 63 + } +} diff --git a/Crypto/src/bcpg/PublicKeyAlgorithmTags.cs b/Crypto/src/bcpg/PublicKeyAlgorithmTags.cs new file mode 100644
index 000000000..85ae548eb --- /dev/null +++ b/Crypto/src/bcpg/PublicKeyAlgorithmTags.cs
@@ -0,0 +1,28 @@ +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Public Key Algorithm tag numbers.</remarks> + public enum PublicKeyAlgorithmTag + { + RsaGeneral = 1, // RSA (Encrypt or Sign) + RsaEncrypt = 2, // RSA Encrypt-Only + RsaSign = 3, // RSA Sign-Only + ElGamalEncrypt = 16, // Elgamal (Encrypt-Only), see [ELGAMAL] + Dsa = 17, // DSA (Digital Signature Standard) + EC = 18, // Reserved for Elliptic Curve + ECDsa = 19, // Reserved for ECDSA + ElGamalGeneral = 20, // Elgamal (Encrypt or Sign) + DiffieHellman = 21, // Reserved for Diffie-Hellman (X9.42, as defined for IETF-S/MIME) + + Experimental_1 = 100, + Experimental_2 = 101, + Experimental_3 = 102, + Experimental_4 = 103, + Experimental_5 = 104, + Experimental_6 = 105, + Experimental_7 = 106, + Experimental_8 = 107, + Experimental_9 = 108, + Experimental_10 = 109, + Experimental_11 = 110, + } +} diff --git a/Crypto/src/bcpg/PublicKeyEncSessionPacket.cs b/Crypto/src/bcpg/PublicKeyEncSessionPacket.cs new file mode 100644
index 000000000..d10605f1d --- /dev/null +++ b/Crypto/src/bcpg/PublicKeyEncSessionPacket.cs
@@ -0,0 +1,103 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Basic packet for a PGP public key.</remarks> + public class PublicKeyEncSessionPacket + : ContainedPacket //, PublicKeyAlgorithmTag + { + private int version; + private long keyId; + private PublicKeyAlgorithmTag algorithm; + private BigInteger[] data; + + internal PublicKeyEncSessionPacket( + BcpgInputStream bcpgIn) + { + version = bcpgIn.ReadByte(); + + keyId |= (long)bcpgIn.ReadByte() << 56; + keyId |= (long)bcpgIn.ReadByte() << 48; + keyId |= (long)bcpgIn.ReadByte() << 40; + keyId |= (long)bcpgIn.ReadByte() << 32; + keyId |= (long)bcpgIn.ReadByte() << 24; + keyId |= (long)bcpgIn.ReadByte() << 16; + keyId |= (long)bcpgIn.ReadByte() << 8; + keyId |= (uint)bcpgIn.ReadByte(); + + algorithm = (PublicKeyAlgorithmTag) bcpgIn.ReadByte(); + + switch ((PublicKeyAlgorithmTag) algorithm) + { + case PublicKeyAlgorithmTag.RsaEncrypt: + case PublicKeyAlgorithmTag.RsaGeneral: + data = new BigInteger[]{ new MPInteger(bcpgIn).Value }; + break; + case PublicKeyAlgorithmTag.ElGamalEncrypt: + case PublicKeyAlgorithmTag.ElGamalGeneral: + data = new BigInteger[] + { + new MPInteger(bcpgIn).Value, + new MPInteger(bcpgIn).Value + }; + break; + default: + throw new IOException("unknown PGP public key algorithm encountered"); + } + } + + public PublicKeyEncSessionPacket( + long keyId, + PublicKeyAlgorithmTag algorithm, + BigInteger[] data) + { + this.version = 3; + this.keyId = keyId; + this.algorithm = algorithm; + this.data = (BigInteger[]) data.Clone(); + } + + public int Version + { + get { return version; } + } + + public long KeyId + { + get { return keyId; } + } + + public PublicKeyAlgorithmTag Algorithm + { + get { return algorithm; } + } + + public BigInteger[] GetEncSessionKey() + { + return (BigInteger[]) data.Clone(); + } + + public override void Encode( + BcpgOutputStream bcpgOut) + { + MemoryStream bOut = new MemoryStream(); + BcpgOutputStream pOut = new BcpgOutputStream(bOut); + + pOut.WriteByte((byte) version); + + pOut.WriteLong(keyId); + + pOut.WriteByte((byte)algorithm); + + for (int i = 0; i != data.Length; i++) + { + MPInteger.Encode(pOut, data[i]); + } + + bcpgOut.WritePacket(PacketTag.PublicKeyEncryptedSession , bOut.ToArray(), true); + } + } +} diff --git a/Crypto/src/bcpg/PublicKeyPacket.cs b/Crypto/src/bcpg/PublicKeyPacket.cs new file mode 100644
index 000000000..32d43149b --- /dev/null +++ b/Crypto/src/bcpg/PublicKeyPacket.cs
@@ -0,0 +1,115 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Utilities.Date; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Basic packet for a PGP public key.</remarks> + public class PublicKeyPacket + : ContainedPacket //, PublicKeyAlgorithmTag + { + private int version; + private long time; + private int validDays; + private PublicKeyAlgorithmTag algorithm; + private IBcpgKey key; + + internal PublicKeyPacket( + BcpgInputStream bcpgIn) + { + version = bcpgIn.ReadByte(); + + time = ((uint)bcpgIn.ReadByte() << 24) | ((uint)bcpgIn.ReadByte() << 16) + | ((uint)bcpgIn.ReadByte() << 8) | (uint)bcpgIn.ReadByte(); + + if (version <= 3) + { + validDays = (bcpgIn.ReadByte() << 8) | bcpgIn.ReadByte(); + } + + algorithm = (PublicKeyAlgorithmTag) bcpgIn.ReadByte(); + + switch ((PublicKeyAlgorithmTag) algorithm) + { + case PublicKeyAlgorithmTag.RsaEncrypt: + case PublicKeyAlgorithmTag.RsaGeneral: + case PublicKeyAlgorithmTag.RsaSign: + key = new RsaPublicBcpgKey(bcpgIn); + break; + case PublicKeyAlgorithmTag.Dsa: + key = new DsaPublicBcpgKey(bcpgIn); + break; + case PublicKeyAlgorithmTag.ElGamalEncrypt: + case PublicKeyAlgorithmTag.ElGamalGeneral: + key = new ElGamalPublicBcpgKey(bcpgIn); + break; + default: + throw new IOException("unknown PGP public key algorithm encountered"); + } + } + + /// <summary>Construct a version 4 public key packet.</summary> + public PublicKeyPacket( + PublicKeyAlgorithmTag algorithm, + DateTime time, + IBcpgKey key) + { + this.version = 4; + this.time = DateTimeUtilities.DateTimeToUnixMs(time) / 1000L; + this.algorithm = algorithm; + this.key = key; + } + + public int Version + { + get { return version; } + } + + public PublicKeyAlgorithmTag Algorithm + { + get { return algorithm; } + } + + public int ValidDays + { + get { return validDays; } + } + + public DateTime GetTime() + { + return DateTimeUtilities.UnixMsToDateTime(time * 1000L); + } + + public IBcpgKey Key + { + get { return key; } + } + + public byte[] GetEncodedContents() + { + MemoryStream bOut = new MemoryStream(); + BcpgOutputStream pOut = new BcpgOutputStream(bOut); + + pOut.WriteByte((byte) version); + pOut.WriteInt((int) time); + + if (version <= 3) + { + pOut.WriteShort((short) validDays); + } + + pOut.WriteByte((byte) algorithm); + + pOut.WriteObject((BcpgObject)key); + + return bOut.ToArray(); + } + + public override void Encode( + BcpgOutputStream bcpgOut) + { + bcpgOut.WritePacket(PacketTag.PublicKey, GetEncodedContents(), true); + } + } +} diff --git a/Crypto/src/bcpg/PublicSubkeyPacket.cs b/Crypto/src/bcpg/PublicSubkeyPacket.cs new file mode 100644
index 000000000..6e1aeda98 --- /dev/null +++ b/Crypto/src/bcpg/PublicSubkeyPacket.cs
@@ -0,0 +1,30 @@ +using System; +using System.IO; +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Basic packet for a PGP public subkey</remarks> + public class PublicSubkeyPacket + : PublicKeyPacket + { + internal PublicSubkeyPacket( + BcpgInputStream bcpgIn) + : base(bcpgIn) + { + } + + /// <summary>Construct a version 4 public subkey packet.</summary> + public PublicSubkeyPacket( + PublicKeyAlgorithmTag algorithm, + DateTime time, + IBcpgKey key) + : base(algorithm, time, key) + { + } + + public override void Encode( + BcpgOutputStream bcpgOut) + { + bcpgOut.WritePacket(PacketTag.PublicSubkey, GetEncodedContents(), true); + } + } +} diff --git a/Crypto/src/bcpg/RsaPublicBcpgKey.cs b/Crypto/src/bcpg/RsaPublicBcpgKey.cs new file mode 100644
index 000000000..fd2313c89 --- /dev/null +++ b/Crypto/src/bcpg/RsaPublicBcpgKey.cs
@@ -0,0 +1,66 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Base class for an RSA public key.</remarks> + public class RsaPublicBcpgKey + : BcpgObject, IBcpgKey + { + private readonly MPInteger n, e; + + /// <summary>Construct an RSA public key from the passed in stream.</summary> + public RsaPublicBcpgKey( + BcpgInputStream bcpgIn) + { + this.n = new MPInteger(bcpgIn); + this.e = new MPInteger(bcpgIn); + } + + /// <param name="n">The modulus.</param> + /// <param name="e">The public exponent.</param> + public RsaPublicBcpgKey( + BigInteger n, + BigInteger e) + { + this.n = new MPInteger(n); + this.e = new MPInteger(e); + } + + public BigInteger PublicExponent + { + get { return e.Value; } + } + + public BigInteger Modulus + { + get { return n.Value; } + } + + /// <summary>The format, as a string, always "PGP".</summary> + public string Format + { + get { return "PGP"; } + } + + /// <summary>Return the standard PGP encoding of the key.</summary> + public override byte[] GetEncoded() + { + try + { + return base.GetEncoded(); + } + catch (Exception) + { + return null; + } + } + + public override void Encode( + BcpgOutputStream bcpgOut) + { + bcpgOut.WriteObjects(n, e); + } + } +} diff --git a/Crypto/src/bcpg/RsaSecretBcpgKey.cs b/Crypto/src/bcpg/RsaSecretBcpgKey.cs new file mode 100644
index 000000000..5c04d9f85 --- /dev/null +++ b/Crypto/src/bcpg/RsaSecretBcpgKey.cs
@@ -0,0 +1,114 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Base class for an RSA secret (or priate) key.</remarks> + public class RsaSecretBcpgKey + : BcpgObject, IBcpgKey + { + private readonly MPInteger d, p, q, u; + private readonly BigInteger expP, expQ, crt; + + public RsaSecretBcpgKey( + BcpgInputStream bcpgIn) + { + this.d = new MPInteger(bcpgIn); + this.p = new MPInteger(bcpgIn); + this.q = new MPInteger(bcpgIn); + this.u = new MPInteger(bcpgIn); + + this.expP = d.Value.Remainder(p.Value.Subtract(BigInteger.One)); + this.expQ = d.Value.Remainder(q.Value.Subtract(BigInteger.One)); + this.crt = q.Value.ModInverse(p.Value); + } + + public RsaSecretBcpgKey( + BigInteger d, + BigInteger p, + BigInteger q) + { + // PGP requires (p < q) + int cmp = p.CompareTo(q); + if (cmp >= 0) + { + if (cmp == 0) + throw new ArgumentException("p and q cannot be equal"); + + BigInteger tmp = p; + p = q; + q = tmp; + } + + this.d = new MPInteger(d); + this.p = new MPInteger(p); + this.q = new MPInteger(q); + this.u = new MPInteger(p.ModInverse(q)); + + this.expP = d.Remainder(p.Subtract(BigInteger.One)); + this.expQ = d.Remainder(q.Subtract(BigInteger.One)); + this.crt = q.ModInverse(p); + } + + public BigInteger Modulus + { + get { return p.Value.Multiply(q.Value); } + } + + public BigInteger PrivateExponent + { + get { return d.Value; } + } + + public BigInteger PrimeP + { + get { return p.Value; } + } + + public BigInteger PrimeQ + { + get { return q.Value; } + } + + public BigInteger PrimeExponentP + { + get { return expP; } + } + + public BigInteger PrimeExponentQ + { + get { return expQ; } + } + + public BigInteger CrtCoefficient + { + get { return crt; } + } + + /// <summary>The format, as a string, always "PGP".</summary> + public string Format + { + get { return "PGP"; } + } + + /// <summary>Return the standard PGP encoding of the key.</summary> + public override byte[] GetEncoded() + { + try + { + return base.GetEncoded(); + } + catch (Exception) + { + return null; + } + } + + public override void Encode( + BcpgOutputStream bcpgOut) + { + bcpgOut.WriteObjects(d, p, q, u); + } + } +} diff --git a/Crypto/src/bcpg/S2k.cs b/Crypto/src/bcpg/S2k.cs new file mode 100644
index 000000000..de08c016c --- /dev/null +++ b/Crypto/src/bcpg/S2k.cs
@@ -0,0 +1,147 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>The string to key specifier class.</remarks> + public class S2k + : BcpgObject + { + private const int ExpBias = 6; + + public const int Simple = 0; + public const int Salted = 1; + public const int SaltedAndIterated = 3; + public const int GnuDummyS2K = 101; + + internal int type; + internal HashAlgorithmTag algorithm; + internal byte[] iv; + internal int itCount = -1; + internal int protectionMode = -1; + + internal S2k( + Stream inStr) + { + type = inStr.ReadByte(); + algorithm = (HashAlgorithmTag) inStr.ReadByte(); + + // + // if this happens we have a dummy-S2k packet. + // + if (type != GnuDummyS2K) + { + if (type != 0) + { + iv = new byte[8]; + if (Streams.ReadFully(inStr, iv, 0, iv.Length) < iv.Length) + throw new EndOfStreamException(); + + if (type == 3) + { + itCount = inStr.ReadByte(); + } + } + } + else + { + inStr.ReadByte(); // G + inStr.ReadByte(); // N + inStr.ReadByte(); // U + protectionMode = inStr.ReadByte(); // protection mode + } + } + + public S2k( + HashAlgorithmTag algorithm) + { + this.type = 0; + this.algorithm = algorithm; + } + + public S2k( + HashAlgorithmTag algorithm, + byte[] iv) + { + this.type = 1; + this.algorithm = algorithm; + this.iv = iv; + } + + public S2k( + HashAlgorithmTag algorithm, + byte[] iv, + int itCount) + { + this.type = 3; + this.algorithm = algorithm; + this.iv = iv; + this.itCount = itCount; + } + + public int Type + { + get { return type; } + } + + /// <summary>The hash algorithm.</summary> + public HashAlgorithmTag HashAlgorithm + { + get { return algorithm; } + } + + /// <summary>The IV for the key generation algorithm.</summary> + public byte[] GetIV() + { + return Arrays.Clone(iv); + } + + [Obsolete("Use 'IterationCount' property instead")] + public long GetIterationCount() + { + return IterationCount; + } + + /// <summary>The iteration count</summary> + public long IterationCount + { + get { return (16 + (itCount & 15)) << ((itCount >> 4) + ExpBias); } + } + + /// <summary>The protection mode - only if GnuDummyS2K</summary> + public int ProtectionMode + { + get { return protectionMode; } + } + + public override void Encode( + BcpgOutputStream bcpgOut) + { + bcpgOut.WriteByte((byte) type); + bcpgOut.WriteByte((byte) algorithm); + + if (type != GnuDummyS2K) + { + if (type != 0) + { + bcpgOut.Write(iv); + } + + if (type == 3) + { + bcpgOut.WriteByte((byte) itCount); + } + } + else + { + bcpgOut.WriteByte((byte) 'G'); + bcpgOut.WriteByte((byte) 'N'); + bcpgOut.WriteByte((byte) 'U'); + bcpgOut.WriteByte((byte) protectionMode); + } + } + } +} diff --git a/Crypto/src/bcpg/SecretKeyPacket.cs b/Crypto/src/bcpg/SecretKeyPacket.cs new file mode 100644
index 000000000..d9ceab4f1 --- /dev/null +++ b/Crypto/src/bcpg/SecretKeyPacket.cs
@@ -0,0 +1,170 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Basic packet for a PGP secret key.</remarks> + public class SecretKeyPacket + : ContainedPacket //, PublicKeyAlgorithmTag + { + public const int UsageNone = 0x00; + public const int UsageChecksum = 0xff; + public const int UsageSha1 = 0xfe; + + private PublicKeyPacket pubKeyPacket; + private readonly byte[] secKeyData; + private int s2kUsage; + private SymmetricKeyAlgorithmTag encAlgorithm; + private S2k s2k; + private byte[] iv; + + internal SecretKeyPacket( + BcpgInputStream bcpgIn) + { + if (this is SecretSubkeyPacket) + { + pubKeyPacket = new PublicSubkeyPacket(bcpgIn); + } + else + { + pubKeyPacket = new PublicKeyPacket(bcpgIn); + } + + s2kUsage = bcpgIn.ReadByte(); + + if (s2kUsage == UsageChecksum || s2kUsage == UsageSha1) + { + encAlgorithm = (SymmetricKeyAlgorithmTag) bcpgIn.ReadByte(); + s2k = new S2k(bcpgIn); + } + else + { + encAlgorithm = (SymmetricKeyAlgorithmTag) s2kUsage; + } + + if (!(s2k != null && s2k.Type == S2k.GnuDummyS2K && s2k.ProtectionMode == 0x01)) + { + if (s2kUsage != 0) + { + if (((int) encAlgorithm) < 7) + { + iv = new byte[8]; + } + else + { + iv = new byte[16]; + } + bcpgIn.ReadFully(iv); + } + } + + secKeyData = bcpgIn.ReadAll(); + } + + public SecretKeyPacket( + PublicKeyPacket pubKeyPacket, + SymmetricKeyAlgorithmTag encAlgorithm, + S2k s2k, + byte[] iv, + byte[] secKeyData) + { + this.pubKeyPacket = pubKeyPacket; + this.encAlgorithm = encAlgorithm; + + if (encAlgorithm != SymmetricKeyAlgorithmTag.Null) + { + this.s2kUsage = UsageChecksum; + } + else + { + this.s2kUsage = UsageNone; + } + + this.s2k = s2k; + this.iv = Arrays.Clone(iv); + this.secKeyData = secKeyData; + } + + public SecretKeyPacket( + PublicKeyPacket pubKeyPacket, + SymmetricKeyAlgorithmTag encAlgorithm, + int s2kUsage, + S2k s2k, + byte[] iv, + byte[] secKeyData) + { + this.pubKeyPacket = pubKeyPacket; + this.encAlgorithm = encAlgorithm; + this.s2kUsage = s2kUsage; + this.s2k = s2k; + this.iv = Arrays.Clone(iv); + this.secKeyData = secKeyData; + } + + public SymmetricKeyAlgorithmTag EncAlgorithm + { + get { return encAlgorithm; } + } + + public int S2kUsage + { + get { return s2kUsage; } + } + + public byte[] GetIV() + { + return Arrays.Clone(iv); + } + + public S2k S2k + { + get { return s2k; } + } + + public PublicKeyPacket PublicKeyPacket + { + get { return pubKeyPacket; } + } + + public byte[] GetSecretKeyData() + { + return secKeyData; + } + + public byte[] GetEncodedContents() + { + MemoryStream bOut = new MemoryStream(); + BcpgOutputStream pOut = new BcpgOutputStream(bOut); + + pOut.Write(pubKeyPacket.GetEncodedContents()); + + pOut.WriteByte((byte) s2kUsage); + + if (s2kUsage == UsageChecksum || s2kUsage == UsageSha1) + { + pOut.WriteByte((byte) encAlgorithm); + pOut.WriteObject(s2k); + } + + if (iv != null) + { + pOut.Write(iv); + } + + if (secKeyData != null && secKeyData.Length > 0) + { + pOut.Write(secKeyData); + } + + return bOut.ToArray(); + } + + public override void Encode( + BcpgOutputStream bcpgOut) + { + bcpgOut.WritePacket(PacketTag.SecretKey, GetEncodedContents(), true); + } + } +} diff --git a/Crypto/src/bcpg/SecretSubkeyPacket.cs b/Crypto/src/bcpg/SecretSubkeyPacket.cs new file mode 100644
index 000000000..8f1746942 --- /dev/null +++ b/Crypto/src/bcpg/SecretSubkeyPacket.cs
@@ -0,0 +1,43 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Basic packet for a PGP secret key.</remarks> + public class SecretSubkeyPacket + : SecretKeyPacket + { + internal SecretSubkeyPacket( + BcpgInputStream bcpgIn) + : base(bcpgIn) + { + } + + public SecretSubkeyPacket( + PublicKeyPacket pubKeyPacket, + SymmetricKeyAlgorithmTag encAlgorithm, + S2k s2k, + byte[] iv, + byte[] secKeyData) + : base(pubKeyPacket, encAlgorithm, s2k, iv, secKeyData) + { + } + + public SecretSubkeyPacket( + PublicKeyPacket pubKeyPacket, + SymmetricKeyAlgorithmTag encAlgorithm, + int s2kUsage, + S2k s2k, + byte[] iv, + byte[] secKeyData) + : base(pubKeyPacket, encAlgorithm, s2kUsage, s2k, iv, secKeyData) + { + } + + public override void Encode( + BcpgOutputStream bcpgOut) + { + bcpgOut.WritePacket(PacketTag.SecretSubkey, GetEncodedContents(), true); + } + } +} diff --git a/Crypto/src/bcpg/SignaturePacket.cs b/Crypto/src/bcpg/SignaturePacket.cs new file mode 100644
index 000000000..605ce84c4 --- /dev/null +++ b/Crypto/src/bcpg/SignaturePacket.cs
@@ -0,0 +1,472 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Bcpg.Sig; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Date; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Generic signature packet.</remarks> + public class SignaturePacket + : ContainedPacket //, PublicKeyAlgorithmTag + { + private int version; + private int signatureType; + private long creationTime; + private long keyId; + private PublicKeyAlgorithmTag keyAlgorithm; + private HashAlgorithmTag hashAlgorithm; + private MPInteger[] signature; + private byte[] fingerprint; + private SignatureSubpacket[] hashedData; + private SignatureSubpacket[] unhashedData; + private byte[] signatureEncoding; + + internal SignaturePacket( + BcpgInputStream bcpgIn) + { + version = bcpgIn.ReadByte(); + + if (version == 3 || version == 2) + { +// int l = + bcpgIn.ReadByte(); + + signatureType = bcpgIn.ReadByte(); + creationTime = (((long)bcpgIn.ReadByte() << 24) | ((long)bcpgIn.ReadByte() << 16) + | ((long)bcpgIn.ReadByte() << 8) | (uint)bcpgIn.ReadByte()) * 1000L; + + keyId |= (long)bcpgIn.ReadByte() << 56; + keyId |= (long)bcpgIn.ReadByte() << 48; + keyId |= (long)bcpgIn.ReadByte() << 40; + keyId |= (long)bcpgIn.ReadByte() << 32; + keyId |= (long)bcpgIn.ReadByte() << 24; + keyId |= (long)bcpgIn.ReadByte() << 16; + keyId |= (long)bcpgIn.ReadByte() << 8; + keyId |= (uint)bcpgIn.ReadByte(); + + keyAlgorithm = (PublicKeyAlgorithmTag) bcpgIn.ReadByte(); + hashAlgorithm = (HashAlgorithmTag) bcpgIn.ReadByte(); + } + else if (version == 4) + { + signatureType = bcpgIn.ReadByte(); + keyAlgorithm = (PublicKeyAlgorithmTag) bcpgIn.ReadByte(); + hashAlgorithm = (HashAlgorithmTag) bcpgIn.ReadByte(); + + int hashedLength = (bcpgIn.ReadByte() << 8) | bcpgIn.ReadByte(); + byte[] hashed = new byte[hashedLength]; + + bcpgIn.ReadFully(hashed); + + // + // read the signature sub packet data. + // + SignatureSubpacketsParser sIn = new SignatureSubpacketsParser( + new MemoryStream(hashed, false)); + + IList v = Platform.CreateArrayList(); + SignatureSubpacket sub; + while ((sub = sIn.ReadPacket()) != null) + { + v.Add(sub); + } + + hashedData = new SignatureSubpacket[v.Count]; + + for (int i = 0; i != hashedData.Length; i++) + { + SignatureSubpacket p = (SignatureSubpacket)v[i]; + if (p is IssuerKeyId) + { + keyId = ((IssuerKeyId)p).KeyId; + } + else if (p is SignatureCreationTime) + { + creationTime = DateTimeUtilities.DateTimeToUnixMs( + ((SignatureCreationTime)p).GetTime()); + } + + hashedData[i] = p; + } + + int unhashedLength = (bcpgIn.ReadByte() << 8) | bcpgIn.ReadByte(); + byte[] unhashed = new byte[unhashedLength]; + + bcpgIn.ReadFully(unhashed); + + sIn = new SignatureSubpacketsParser(new MemoryStream(unhashed, false)); + + v.Clear(); + + while ((sub = sIn.ReadPacket()) != null) + { + v.Add(sub); + } + + unhashedData = new SignatureSubpacket[v.Count]; + + for (int i = 0; i != unhashedData.Length; i++) + { + SignatureSubpacket p = (SignatureSubpacket)v[i]; + if (p is IssuerKeyId) + { + keyId = ((IssuerKeyId)p).KeyId; + } + + unhashedData[i] = p; + } + } + else + { + throw new Exception("unsupported version: " + version); + } + + fingerprint = new byte[2]; + bcpgIn.ReadFully(fingerprint); + + switch (keyAlgorithm) + { + case PublicKeyAlgorithmTag.RsaGeneral: + case PublicKeyAlgorithmTag.RsaSign: + MPInteger v = new MPInteger(bcpgIn); + signature = new MPInteger[]{ v }; + break; + case PublicKeyAlgorithmTag.Dsa: + MPInteger r = new MPInteger(bcpgIn); + MPInteger s = new MPInteger(bcpgIn); + signature = new MPInteger[]{ r, s }; + break; + case PublicKeyAlgorithmTag.ElGamalEncrypt: // yep, this really does happen sometimes. + case PublicKeyAlgorithmTag.ElGamalGeneral: + MPInteger p = new MPInteger(bcpgIn); + MPInteger g = new MPInteger(bcpgIn); + MPInteger y = new MPInteger(bcpgIn); + signature = new MPInteger[]{ p, g, y }; + break; + default: + if (keyAlgorithm >= PublicKeyAlgorithmTag.Experimental_1 && keyAlgorithm <= PublicKeyAlgorithmTag.Experimental_11) + { + signature = null; + MemoryStream bOut = new MemoryStream(); + int ch; + while ((ch = bcpgIn.ReadByte()) >= 0) + { + bOut.WriteByte((byte) ch); + } + signatureEncoding = bOut.ToArray(); + } + else + { + throw new IOException("unknown signature key algorithm: " + keyAlgorithm); + } + break; + } + } + + /** + * Generate a version 4 signature packet. + * + * @param signatureType + * @param keyAlgorithm + * @param hashAlgorithm + * @param hashedData + * @param unhashedData + * @param fingerprint + * @param signature + */ + public SignaturePacket( + int signatureType, + long keyId, + PublicKeyAlgorithmTag keyAlgorithm, + HashAlgorithmTag hashAlgorithm, + SignatureSubpacket[] hashedData, + SignatureSubpacket[] unhashedData, + byte[] fingerprint, + MPInteger[] signature) + : this(4, signatureType, keyId, keyAlgorithm, hashAlgorithm, hashedData, unhashedData, fingerprint, signature) + { + } + + /** + * Generate a version 2/3 signature packet. + * + * @param signatureType + * @param keyAlgorithm + * @param hashAlgorithm + * @param fingerprint + * @param signature + */ + public SignaturePacket( + int version, + int signatureType, + long keyId, + PublicKeyAlgorithmTag keyAlgorithm, + HashAlgorithmTag hashAlgorithm, + long creationTime, + byte[] fingerprint, + MPInteger[] signature) + : this(version, signatureType, keyId, keyAlgorithm, hashAlgorithm, null, null, fingerprint, signature) + { + this.creationTime = creationTime; + } + + public SignaturePacket( + int version, + int signatureType, + long keyId, + PublicKeyAlgorithmTag keyAlgorithm, + HashAlgorithmTag hashAlgorithm, + SignatureSubpacket[] hashedData, + SignatureSubpacket[] unhashedData, + byte[] fingerprint, + MPInteger[] signature) + { + this.version = version; + this.signatureType = signatureType; + this.keyId = keyId; + this.keyAlgorithm = keyAlgorithm; + this.hashAlgorithm = hashAlgorithm; + this.hashedData = hashedData; + this.unhashedData = unhashedData; + this.fingerprint = fingerprint; + this.signature = signature; + + if (hashedData != null) + { + setCreationTime(); + } + } + + public int Version + { + get { return version; } + } + + public int SignatureType + { + get { return signatureType; } + } + + /** + * return the keyId + * @return the keyId that created the signature. + */ + public long KeyId + { + get { return keyId; } + } + + /** + * return the signature trailer that must be included with the data + * to reconstruct the signature + * + * @return byte[] + */ + public byte[] GetSignatureTrailer() + { + byte[] trailer = null; + + if (version == 3) + { + trailer = new byte[5]; + + long time = creationTime / 1000L; + + trailer[0] = (byte)signatureType; + trailer[1] = (byte)(time >> 24); + trailer[2] = (byte)(time >> 16); + trailer[3] = (byte)(time >> 8); + trailer[4] = (byte)(time); + } + else + { + MemoryStream sOut = new MemoryStream(); + + sOut.WriteByte((byte)this.Version); + sOut.WriteByte((byte)this.SignatureType); + sOut.WriteByte((byte)this.KeyAlgorithm); + sOut.WriteByte((byte)this.HashAlgorithm); + + MemoryStream hOut = new MemoryStream(); + SignatureSubpacket[] hashed = this.GetHashedSubPackets(); + + for (int i = 0; i != hashed.Length; i++) + { + hashed[i].Encode(hOut); + } + + byte[] data = hOut.ToArray(); + + sOut.WriteByte((byte)(data.Length >> 8)); + sOut.WriteByte((byte)data.Length); + sOut.Write(data, 0, data.Length); + + byte[] hData = sOut.ToArray(); + + sOut.WriteByte((byte)this.Version); + sOut.WriteByte((byte)0xff); + sOut.WriteByte((byte)(hData.Length>> 24)); + sOut.WriteByte((byte)(hData.Length >> 16)); + sOut.WriteByte((byte)(hData.Length >> 8)); + sOut.WriteByte((byte)(hData.Length)); + + trailer = sOut.ToArray(); + } + + return trailer; + } + + public PublicKeyAlgorithmTag KeyAlgorithm + { + get { return keyAlgorithm; } + } + + public HashAlgorithmTag HashAlgorithm + { + get { return hashAlgorithm; } + } + + /** + * return the signature as a set of integers - note this is normalised to be the + * ASN.1 encoding of what appears in the signature packet. + */ + public MPInteger[] GetSignature() + { + return signature; + } + + /** + * Return the byte encoding of the signature section. + * @return uninterpreted signature bytes. + */ + public byte[] GetSignatureBytes() + { + if (signatureEncoding != null) + { + return (byte[]) signatureEncoding.Clone(); + } + + MemoryStream bOut = new MemoryStream(); + BcpgOutputStream bcOut = new BcpgOutputStream(bOut); + + foreach (MPInteger sigObj in signature) + { + try + { + bcOut.WriteObject(sigObj); + } + catch (IOException e) + { + throw new Exception("internal error: " + e); + } + } + + return bOut.ToArray(); + } + + public SignatureSubpacket[] GetHashedSubPackets() + { + return hashedData; + } + + public SignatureSubpacket[] GetUnhashedSubPackets() + { + return unhashedData; + } + + /// <summary>Return the creation time in milliseconds since 1 Jan., 1970 UTC.</summary> + public long CreationTime + { + get { return creationTime; } + } + + public override void Encode( + BcpgOutputStream bcpgOut) + { + MemoryStream bOut = new MemoryStream(); + BcpgOutputStream pOut = new BcpgOutputStream(bOut); + + pOut.WriteByte((byte) version); + + if (version == 3 || version == 2) + { + pOut.Write( + 5, // the length of the next block + (byte) signatureType); + + pOut.WriteInt((int)(creationTime / 1000L)); + + pOut.WriteLong(keyId); + + pOut.Write( + (byte) keyAlgorithm, + (byte) hashAlgorithm); + } + else if (version == 4) + { + pOut.Write( + (byte) signatureType, + (byte) keyAlgorithm, + (byte) hashAlgorithm); + + EncodeLengthAndData(pOut, GetEncodedSubpackets(hashedData)); + + EncodeLengthAndData(pOut, GetEncodedSubpackets(unhashedData)); + } + else + { + throw new IOException("unknown version: " + version); + } + + pOut.Write(fingerprint); + + if (signature != null) + { + pOut.WriteObjects(signature); + } + else + { + pOut.Write(signatureEncoding); + } + + bcpgOut.WritePacket(PacketTag.Signature, bOut.ToArray(), true); + } + + private static void EncodeLengthAndData( + BcpgOutputStream pOut, + byte[] data) + { + pOut.WriteShort((short) data.Length); + pOut.Write(data); + } + + private static byte[] GetEncodedSubpackets( + SignatureSubpacket[] ps) + { + MemoryStream sOut = new MemoryStream(); + + foreach (SignatureSubpacket p in ps) + { + p.Encode(sOut); + } + + return sOut.ToArray(); + } + + private void setCreationTime() + { + foreach (SignatureSubpacket p in hashedData) + { + if (p is SignatureCreationTime) + { + creationTime = DateTimeUtilities.DateTimeToUnixMs( + ((SignatureCreationTime)p).GetTime()); + break; + } + } + } + } +} diff --git a/Crypto/src/bcpg/SignatureSubpacket.cs b/Crypto/src/bcpg/SignatureSubpacket.cs new file mode 100644
index 000000000..ac26f8a3c --- /dev/null +++ b/Crypto/src/bcpg/SignatureSubpacket.cs
@@ -0,0 +1,76 @@ +using System.IO; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Basic type for a PGP Signature sub-packet.</remarks> + public class SignatureSubpacket + { + private readonly SignatureSubpacketTag type; + private readonly bool critical; + + internal readonly byte[] data; + + protected internal SignatureSubpacket( + SignatureSubpacketTag type, + bool critical, + byte[] data) + { + this.type = type; + this.critical = critical; + this.data = data; + } + + public SignatureSubpacketTag SubpacketType + { + get { return type; } + } + + public bool IsCritical() + { + return critical; + } + + /// <summary>Return the generic data making up the packet.</summary> + public byte[] GetData() + { + return (byte[]) data.Clone(); + } + + public void Encode( + Stream os) + { + int bodyLen = data.Length + 1; + + if (bodyLen < 192) + { + os.WriteByte((byte)bodyLen); + } + else if (bodyLen <= 8383) + { + bodyLen -= 192; + + os.WriteByte((byte)(((bodyLen >> 8) & 0xff) + 192)); + os.WriteByte((byte)bodyLen); + } + else + { + os.WriteByte(0xff); + os.WriteByte((byte)(bodyLen >> 24)); + os.WriteByte((byte)(bodyLen >> 16)); + os.WriteByte((byte)(bodyLen >> 8)); + os.WriteByte((byte)bodyLen); + } + + if (critical) + { + os.WriteByte((byte)(0x80 | (int) type)); + } + else + { + os.WriteByte((byte) type); + } + + os.Write(data, 0, data.Length); + } + } +} diff --git a/Crypto/src/bcpg/SignatureSubpacketTags.cs b/Crypto/src/bcpg/SignatureSubpacketTags.cs new file mode 100644
index 000000000..1a8e254c0 --- /dev/null +++ b/Crypto/src/bcpg/SignatureSubpacketTags.cs
@@ -0,0 +1,33 @@ +namespace Org.BouncyCastle.Bcpg +{ + /** + * Basic PGP signature sub-packet tag types. + */ + public enum SignatureSubpacketTag + { + CreationTime = 2, // signature creation time + ExpireTime = 3, // signature expiration time + Exportable = 4, // exportable certification + TrustSig = 5, // trust signature + RegExp = 6, // regular expression + Revocable = 7, // revocable + KeyExpireTime = 9, // key expiration time + Placeholder = 10, // placeholder for backward compatibility + PreferredSymmetricAlgorithms = 11, // preferred symmetric algorithms + RevocationKey = 12, // revocation key + IssuerKeyId = 16, // issuer key ID + NotationData = 20, // notation data + PreferredHashAlgorithms = 21, // preferred hash algorithms + PreferredCompressionAlgorithms = 22, // preferred compression algorithms + KeyServerPreferences = 23, // key server preferences + PreferredKeyServer = 24, // preferred key server + PrimaryUserId = 25, // primary user id + PolicyUrl = 26, // policy URL + KeyFlags = 27, // key flags + SignerUserId = 28, // signer's user id + RevocationReason = 29, // reason for revocation + Features = 30, // features + SignatureTarget = 31, // signature target + EmbeddedSignature = 32 // embedded signature + } +} diff --git a/Crypto/src/bcpg/SignatureSubpacketsReader.cs b/Crypto/src/bcpg/SignatureSubpacketsReader.cs new file mode 100644
index 000000000..8dd1af332 --- /dev/null +++ b/Crypto/src/bcpg/SignatureSubpacketsReader.cs
@@ -0,0 +1,88 @@ +using System; +using System.IO; +using Org.BouncyCastle.Bcpg.Sig; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Bcpg +{ + /** + * reader for signature sub-packets + */ + public class SignatureSubpacketsParser + { + private readonly Stream input; + + public SignatureSubpacketsParser( + Stream input) + { + this.input = input; + } + + public SignatureSubpacket ReadPacket() + { + int l = input.ReadByte(); + if (l < 0) + return null; + + int bodyLen = 0; + if (l < 192) + { + bodyLen = l; + } + else if (l <= 223) + { + bodyLen = ((l - 192) << 8) + (input.ReadByte()) + 192; + } + else if (l == 255) + { + bodyLen = (input.ReadByte() << 24) | (input.ReadByte() << 16) + | (input.ReadByte() << 8) | input.ReadByte(); + } + else + { + // TODO Error? + } + + int tag = input.ReadByte(); + if (tag < 0) + throw new EndOfStreamException("unexpected EOF reading signature sub packet"); + + byte[] data = new byte[bodyLen - 1]; + if (Streams.ReadFully(input, data) < data.Length) + throw new EndOfStreamException(); + + bool isCritical = ((tag & 0x80) != 0); + SignatureSubpacketTag type = (SignatureSubpacketTag)(tag & 0x7f); + switch (type) + { + case SignatureSubpacketTag.CreationTime: + return new SignatureCreationTime(isCritical, data); + case SignatureSubpacketTag.KeyExpireTime: + return new KeyExpirationTime(isCritical, data); + case SignatureSubpacketTag.ExpireTime: + return new SignatureExpirationTime(isCritical, data); + case SignatureSubpacketTag.Revocable: + return new Revocable(isCritical, data); + case SignatureSubpacketTag.Exportable: + return new Exportable(isCritical, data); + case SignatureSubpacketTag.IssuerKeyId: + return new IssuerKeyId(isCritical, data); + case SignatureSubpacketTag.TrustSig: + return new TrustSignature(isCritical, data); + case SignatureSubpacketTag.PreferredCompressionAlgorithms: + case SignatureSubpacketTag.PreferredHashAlgorithms: + case SignatureSubpacketTag.PreferredSymmetricAlgorithms: + return new PreferredAlgorithms(type, isCritical, data); + case SignatureSubpacketTag.KeyFlags: + return new KeyFlags(isCritical, data); + case SignatureSubpacketTag.PrimaryUserId: + return new PrimaryUserId(isCritical, data); + case SignatureSubpacketTag.SignerUserId: + return new SignerUserId(isCritical, data); + case SignatureSubpacketTag.NotationData: + return new NotationData(isCritical, data); + } + return new SignatureSubpacket(type, isCritical, data); + } + } +} diff --git a/Crypto/src/bcpg/SymmetricEncDataPacket.cs b/Crypto/src/bcpg/SymmetricEncDataPacket.cs new file mode 100644
index 000000000..17ee55bb7 --- /dev/null +++ b/Crypto/src/bcpg/SymmetricEncDataPacket.cs
@@ -0,0 +1,15 @@ +using System; + +namespace Org.BouncyCastle.Bcpg +{ + /// <remarks>Basic type for a symmetric key encrypted packet.</remarks> + public class SymmetricEncDataPacket + : InputStreamPacket + { + public SymmetricEncDataPacket( + BcpgInputStream bcpgIn) + : base(bcpgIn) + { + } + } +} diff --git a/Crypto/src/bcpg/SymmetricEncIntegrityPacket.cs b/Crypto/src/bcpg/SymmetricEncIntegrityPacket.cs new file mode 100644
index 000000000..a9b6d0678 --- /dev/null +++ b/Crypto/src/bcpg/SymmetricEncIntegrityPacket.cs
@@ -0,0 +1,18 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Bcpg +{ + public class SymmetricEncIntegrityPacket + : InputStreamPacket + { + internal readonly int version; + + internal SymmetricEncIntegrityPacket( + BcpgInputStream bcpgIn) + : base(bcpgIn) + { + version = bcpgIn.ReadByte(); + } + } +} diff --git a/Crypto/src/bcpg/SymmetricKeyAlgorithmTags.cs b/Crypto/src/bcpg/SymmetricKeyAlgorithmTags.cs new file mode 100644
index 000000000..7633b1dba --- /dev/null +++ b/Crypto/src/bcpg/SymmetricKeyAlgorithmTags.cs
@@ -0,0 +1,20 @@ +namespace Org.BouncyCastle.Bcpg +{ + /** + * Basic tags for symmetric key algorithms + */ + public enum SymmetricKeyAlgorithmTag + { + Null = 0, // Plaintext or unencrypted data + Idea = 1, // IDEA [IDEA] + TripleDes = 2, // Triple-DES (DES-EDE, as per spec -168 bit key derived from 192) + Cast5 = 3, // Cast5 (128 bit key, as per RFC 2144) + Blowfish = 4, // Blowfish (128 bit key, 16 rounds) [Blowfish] + Safer = 5, // Safer-SK128 (13 rounds) [Safer] + Des = 6, // Reserved for DES/SK + Aes128 = 7, // Reserved for AES with 128-bit key + Aes192 = 8, // Reserved for AES with 192-bit key + Aes256 = 9, // Reserved for AES with 256-bit key + Twofish = 10 // Reserved for Twofish + } +} diff --git a/Crypto/src/bcpg/SymmetricKeyEncSessionPacket.cs b/Crypto/src/bcpg/SymmetricKeyEncSessionPacket.cs new file mode 100644
index 000000000..0381fa386 --- /dev/null +++ b/Crypto/src/bcpg/SymmetricKeyEncSessionPacket.cs
@@ -0,0 +1,91 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Bcpg +{ + /** + * Basic type for a symmetric encrypted session key packet + */ + public class SymmetricKeyEncSessionPacket + : ContainedPacket + { + private int version; + private SymmetricKeyAlgorithmTag encAlgorithm; + private S2k s2k; + private readonly byte[] secKeyData; + + public SymmetricKeyEncSessionPacket( + BcpgInputStream bcpgIn) + { + version = bcpgIn.ReadByte(); + encAlgorithm = (SymmetricKeyAlgorithmTag) bcpgIn.ReadByte(); + + s2k = new S2k(bcpgIn); + + secKeyData = bcpgIn.ReadAll(); + } + + public SymmetricKeyEncSessionPacket( + SymmetricKeyAlgorithmTag encAlgorithm, + S2k s2k, + byte[] secKeyData) + { + this.version = 4; + this.encAlgorithm = encAlgorithm; + this.s2k = s2k; + this.secKeyData = secKeyData; + } + + /** + * @return int + */ + public SymmetricKeyAlgorithmTag EncAlgorithm + { + get { return encAlgorithm; } + } + + /** + * @return S2k + */ + public S2k S2k + { + get { return s2k; } + } + + /** + * @return byte[] + */ + public byte[] GetSecKeyData() + { + return secKeyData; + } + + /** + * @return int + */ + public int Version + { + get { return version; } + } + + public override void Encode( + BcpgOutputStream bcpgOut) + { + MemoryStream bOut = new MemoryStream(); + BcpgOutputStream pOut = new BcpgOutputStream(bOut); + + pOut.Write( + (byte) version, + (byte) encAlgorithm); + + pOut.WriteObject(s2k); + + if (secKeyData != null && secKeyData.Length > 0) + { + pOut.Write(secKeyData); + } + + bcpgOut.WritePacket(PacketTag.SymmetricKeyEncryptedSessionKey, bOut.ToArray(), true); + } + } +} diff --git a/Crypto/src/bcpg/TrustPacket.cs b/Crypto/src/bcpg/TrustPacket.cs new file mode 100644
index 000000000..6f1969c2a --- /dev/null +++ b/Crypto/src/bcpg/TrustPacket.cs
@@ -0,0 +1,43 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Bcpg +{ + /// <summary>Basic type for a trust packet.</summary> + public class TrustPacket + : ContainedPacket + { + private readonly byte[] levelAndTrustAmount; + + public TrustPacket( + BcpgInputStream bcpgIn) + { + MemoryStream bOut = new MemoryStream(); + + int ch; + while ((ch = bcpgIn.ReadByte()) >= 0) + { + bOut.WriteByte((byte) ch); + } + + levelAndTrustAmount = bOut.ToArray(); + } + + public TrustPacket( + int trustCode) + { + this.levelAndTrustAmount = new byte[]{ (byte) trustCode }; + } + + public byte[] GetLevelAndTrustAmount() + { + return (byte[]) levelAndTrustAmount.Clone(); + } + + public override void Encode( + BcpgOutputStream bcpgOut) + { + bcpgOut.WritePacket(PacketTag.Trust, levelAndTrustAmount, true); + } + } +} diff --git a/Crypto/src/bcpg/UserAttributePacket.cs b/Crypto/src/bcpg/UserAttributePacket.cs new file mode 100644
index 000000000..20e3598ab --- /dev/null +++ b/Crypto/src/bcpg/UserAttributePacket.cs
@@ -0,0 +1,61 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Bcpg +{ + /** + * Basic type for a user attribute packet. + */ + public class UserAttributePacket + : ContainedPacket + { + private readonly UserAttributeSubpacket[] subpackets; + + public UserAttributePacket( + BcpgInputStream bcpgIn) + { + UserAttributeSubpacketsParser sIn = new UserAttributeSubpacketsParser(bcpgIn); + UserAttributeSubpacket sub; + + IList v = Platform.CreateArrayList(); + while ((sub = sIn.ReadPacket()) != null) + { + v.Add(sub); + } + + subpackets = new UserAttributeSubpacket[v.Count]; + + for (int i = 0; i != subpackets.Length; i++) + { + subpackets[i] = (UserAttributeSubpacket)v[i]; + } + } + + public UserAttributePacket( + UserAttributeSubpacket[] subpackets) + { + this.subpackets = subpackets; + } + + public UserAttributeSubpacket[] GetSubpackets() + { + return subpackets; + } + + public override void Encode( + BcpgOutputStream bcpgOut) + { + MemoryStream bOut = new MemoryStream(); + + for (int i = 0; i != subpackets.Length; i++) + { + subpackets[i].Encode(bOut); + } + + bcpgOut.WritePacket(PacketTag.UserAttribute, bOut.ToArray(), false); + } + } +} diff --git a/Crypto/src/bcpg/UserAttributeSubpacket.cs b/Crypto/src/bcpg/UserAttributeSubpacket.cs new file mode 100644
index 000000000..bd49d2150 --- /dev/null +++ b/Crypto/src/bcpg/UserAttributeSubpacket.cs
@@ -0,0 +1,86 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Bcpg +{ + /** + * Basic type for a user attribute sub-packet. + */ + public class UserAttributeSubpacket + { + private readonly UserAttributeSubpacketTag type; + private readonly byte[] data; + + internal UserAttributeSubpacket( + UserAttributeSubpacketTag type, + byte[] data) + { + this.type = type; + this.data = data; + } + + public UserAttributeSubpacketTag SubpacketType + { + get { return type; } + } + + /** + * return the generic data making up the packet. + */ + public byte[] GetData() + { + return data; + } + + public void Encode( + Stream os) + { + int bodyLen = data.Length + 1; + + if (bodyLen < 192) + { + os.WriteByte((byte)bodyLen); + } + else if (bodyLen <= 8383) + { + bodyLen -= 192; + + os.WriteByte((byte)(((bodyLen >> 8) & 0xff) + 192)); + os.WriteByte((byte)bodyLen); + } + else + { + os.WriteByte(0xff); + os.WriteByte((byte)(bodyLen >> 24)); + os.WriteByte((byte)(bodyLen >> 16)); + os.WriteByte((byte)(bodyLen >> 8)); + os.WriteByte((byte)bodyLen); + } + + os.WriteByte((byte) type); + os.Write(data, 0, data.Length); + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + UserAttributeSubpacket other = obj as UserAttributeSubpacket; + + if (other == null) + return false; + + return type == other.type + && Arrays.AreEqual(data, other.data); + } + + public override int GetHashCode() + { + return type.GetHashCode() ^ Arrays.GetHashCode(data); + } + } +} diff --git a/Crypto/src/bcpg/UserAttributeSubpacketTags.cs b/Crypto/src/bcpg/UserAttributeSubpacketTags.cs new file mode 100644
index 000000000..7a9cd1d5d --- /dev/null +++ b/Crypto/src/bcpg/UserAttributeSubpacketTags.cs
@@ -0,0 +1,10 @@ +namespace Org.BouncyCastle.Bcpg +{ + /** + * Basic PGP user attribute sub-packet tag types. + */ + public enum UserAttributeSubpacketTag + { + ImageAttribute = 1 + } +} diff --git a/Crypto/src/bcpg/UserAttributeSubpacketsReader.cs b/Crypto/src/bcpg/UserAttributeSubpacketsReader.cs new file mode 100644
index 000000000..2e5ea0f3e --- /dev/null +++ b/Crypto/src/bcpg/UserAttributeSubpacketsReader.cs
@@ -0,0 +1,63 @@ +using System; +using System.IO; +using Org.BouncyCastle.Bcpg.Attr; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Bcpg +{ + /** + * reader for user attribute sub-packets + */ + public class UserAttributeSubpacketsParser + { + private readonly Stream input; + + public UserAttributeSubpacketsParser( + Stream input) + { + this.input = input; + } + + public UserAttributeSubpacket ReadPacket() + { + int l = input.ReadByte(); + if (l < 0) + return null; + + int bodyLen = 0; + if (l < 192) + { + bodyLen = l; + } + else if (l <= 223) + { + bodyLen = ((l - 192) << 8) + (input.ReadByte()) + 192; + } + else if (l == 255) + { + bodyLen = (input.ReadByte() << 24) | (input.ReadByte() << 16) + | (input.ReadByte() << 8) | input.ReadByte(); + } + else + { + // TODO Error? + } + + int tag = input.ReadByte(); + if (tag < 0) + throw new EndOfStreamException("unexpected EOF reading user attribute sub packet"); + + byte[] data = new byte[bodyLen - 1]; + if (Streams.ReadFully(input, data) < data.Length) + throw new EndOfStreamException(); + + UserAttributeSubpacketTag type = (UserAttributeSubpacketTag) tag; + switch (type) + { + case UserAttributeSubpacketTag.ImageAttribute: + return new ImageAttrib(data); + } + return new UserAttributeSubpacket(type, data); + } + } +} diff --git a/Crypto/src/bcpg/UserIdPacket.cs b/Crypto/src/bcpg/UserIdPacket.cs new file mode 100644
index 000000000..a175e74a6 --- /dev/null +++ b/Crypto/src/bcpg/UserIdPacket.cs
@@ -0,0 +1,37 @@ +using System; +using System.Text; + +namespace Org.BouncyCastle.Bcpg +{ + /** + * Basic type for a user ID packet. + */ + public class UserIdPacket + : ContainedPacket + { + private readonly byte[] idData; + + public UserIdPacket( + BcpgInputStream bcpgIn) + { + this.idData = bcpgIn.ReadAll(); + } + + public UserIdPacket( + string id) + { + this.idData = Encoding.UTF8.GetBytes(id); + } + + public string GetId() + { + return Encoding.UTF8.GetString(idData, 0, idData.Length); + } + + public override void Encode( + BcpgOutputStream bcpgOut) + { + bcpgOut.WritePacket(PacketTag.UserId, idData, true); + } + } +} diff --git a/Crypto/src/bcpg/attr/ImageAttrib.cs b/Crypto/src/bcpg/attr/ImageAttrib.cs new file mode 100644
index 000000000..73490791c --- /dev/null +++ b/Crypto/src/bcpg/attr/ImageAttrib.cs
@@ -0,0 +1,68 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Bcpg.Attr +{ + /// <remarks>Basic type for a image attribute packet.</remarks> + public class ImageAttrib + : UserAttributeSubpacket + { + public enum Format : byte + { + Jpeg = 1 + } + + private static readonly byte[] Zeroes = new byte[12]; + + private int hdrLength; + private int _version; + private int _encoding; + private byte[] imageData; + + public ImageAttrib( + byte[] data) + : base(UserAttributeSubpacketTag.ImageAttribute, data) + { + hdrLength = ((data[1] & 0xff) << 8) | (data[0] & 0xff); + _version = data[2] & 0xff; + _encoding = data[3] & 0xff; + + imageData = new byte[data.Length - hdrLength]; + Array.Copy(data, hdrLength, imageData, 0, imageData.Length); + } + + public ImageAttrib( + Format imageType, + byte[] imageData) + : this(ToByteArray(imageType, imageData)) + { + } + + private static byte[] ToByteArray( + Format imageType, + byte[] imageData) + { + MemoryStream bOut = new MemoryStream(); + bOut.WriteByte(0x10); bOut.WriteByte(0x00); bOut.WriteByte(0x01); + bOut.WriteByte((byte) imageType); + bOut.Write(Zeroes, 0, Zeroes.Length); + bOut.Write(imageData, 0, imageData.Length); + return bOut.ToArray(); + } + + public int Version + { + get { return _version; } + } + + public int Encoding + { + get { return _encoding; } + } + + public byte[] GetImageData() + { + return imageData; + } + } +} diff --git a/Crypto/src/bcpg/sig/EmbeddedSignature.cs b/Crypto/src/bcpg/sig/EmbeddedSignature.cs new file mode 100644
index 000000000..e47604ac8 --- /dev/null +++ b/Crypto/src/bcpg/sig/EmbeddedSignature.cs
@@ -0,0 +1,18 @@ +using System; + +namespace Org.BouncyCastle.Bcpg.Sig +{ + /** + * Packet embedded signature + */ + public class EmbeddedSignature + : SignatureSubpacket + { + public EmbeddedSignature( + bool critical, + byte[] data) + : base(SignatureSubpacketTag.EmbeddedSignature, critical, data) + { + } + } +} diff --git a/Crypto/src/bcpg/sig/Exportable.cs b/Crypto/src/bcpg/sig/Exportable.cs new file mode 100644
index 000000000..4455c3814 --- /dev/null +++ b/Crypto/src/bcpg/sig/Exportable.cs
@@ -0,0 +1,47 @@ +using System; + + + +namespace Org.BouncyCastle.Bcpg.Sig +{ + /** + * packet giving signature creation time. + */ + public class Exportable + : SignatureSubpacket + { + private static byte[] BooleanToByteArray(bool val) + { + byte[] data = new byte[1]; + + if (val) + { + data[0] = 1; + return data; + } + else + { + return data; + } + } + + public Exportable( + bool critical, + byte[] data) + : base(SignatureSubpacketTag.Exportable, critical, data) + { + } + + public Exportable( + bool critical, + bool isExportable) + : base(SignatureSubpacketTag.Exportable, critical, BooleanToByteArray(isExportable)) + { + } + + public bool IsExportable() + { + return data[0] != 0; + } + } +} diff --git a/Crypto/src/bcpg/sig/IssuerKeyId.cs b/Crypto/src/bcpg/sig/IssuerKeyId.cs new file mode 100644
index 000000000..91490d33b --- /dev/null +++ b/Crypto/src/bcpg/sig/IssuerKeyId.cs
@@ -0,0 +1,61 @@ +using System; + + + +namespace Org.BouncyCastle.Bcpg.Sig +{ + /** + * packet giving signature creation time. + */ + public class IssuerKeyId + : SignatureSubpacket + { + protected static byte[] KeyIdToBytes( + long keyId) + { + byte[] data = new byte[8]; + + data[0] = (byte)(keyId >> 56); + data[1] = (byte)(keyId >> 48); + data[2] = (byte)(keyId >> 40); + data[3] = (byte)(keyId >> 32); + data[4] = (byte)(keyId >> 24); + data[5] = (byte)(keyId >> 16); + data[6] = (byte)(keyId >> 8); + data[7] = (byte)keyId; + + return data; + } + + public IssuerKeyId( + bool critical, + byte[] data) + : base(SignatureSubpacketTag.IssuerKeyId, critical, data) + { + } + + public IssuerKeyId( + bool critical, + long keyId) + : base(SignatureSubpacketTag.IssuerKeyId, critical, KeyIdToBytes(keyId)) + { + } + + public long KeyId + { + get + { + long keyId = ((long)(data[0] & 0xff) << 56) + | ((long)(data[1] & 0xff) << 48) + | ((long)(data[2] & 0xff) << 40) + | ((long)(data[3] & 0xff) << 32) + | ((long)(data[4] & 0xff) << 24) + | ((long)(data[5] & 0xff) << 16) + | ((long)(data[6] & 0xff) << 8) + | ((long)data[7] & 0xff); + + return keyId; + } + } + } +} diff --git a/Crypto/src/bcpg/sig/KeyExpirationTime.cs b/Crypto/src/bcpg/sig/KeyExpirationTime.cs new file mode 100644
index 000000000..23b4cac29 --- /dev/null +++ b/Crypto/src/bcpg/sig/KeyExpirationTime.cs
@@ -0,0 +1,56 @@ +using System; + + + +namespace Org.BouncyCastle.Bcpg.Sig +{ + /** + * packet giving time after creation at which the key expires. + */ + public class KeyExpirationTime + : SignatureSubpacket + { + protected static byte[] TimeToBytes( + long t) + { + byte[] data = new byte[4]; + + data[0] = (byte)(t >> 24); + data[1] = (byte)(t >> 16); + data[2] = (byte)(t >> 8); + data[3] = (byte)t; + + return data; + } + + public KeyExpirationTime( + bool critical, + byte[] data) + : base(SignatureSubpacketTag.KeyExpireTime, critical, data) + { + } + + public KeyExpirationTime( + bool critical, + long seconds) + : base(SignatureSubpacketTag.KeyExpireTime, critical, TimeToBytes(seconds)) + { + } + + /** + * Return the number of seconds after creation time a key is valid for. + * + * @return second count for key validity. + */ + public long Time + { + get + { + long time = ((long)(data[0] & 0xff) << 24) | ((long)(data[1] & 0xff) << 16) + | ((long)(data[2] & 0xff) << 8) | ((long)data[3] & 0xff); + + return time; + } + } + } +} diff --git a/Crypto/src/bcpg/sig/KeyFlags.cs b/Crypto/src/bcpg/sig/KeyFlags.cs new file mode 100644
index 000000000..0592301b3 --- /dev/null +++ b/Crypto/src/bcpg/sig/KeyFlags.cs
@@ -0,0 +1,74 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Bcpg.Sig +{ + /** + * Packet holding the key flag values. + */ + public class KeyFlags + : SignatureSubpacket + { + public const int CertifyOther = 0x01; + public const int SignData = 0x02; + public const int EncryptComms = 0x04; + public const int EncryptStorage = 0x08; + public const int Split = 0x10; + public const int Authentication = 0x20; + public const int Shared = 0x80; + + private static byte[] IntToByteArray( + int v) + { + byte[] tmp = new byte[4]; + int size = 0; + + for (int i = 0; i != 4; i++) + { + tmp[i] = (byte)(v >> (i * 8)); + if (tmp[i] != 0) + { + size = i; + } + } + + byte[] data = new byte[size + 1]; + Array.Copy(tmp, 0, data, 0, data.Length); + return data; + } + + public KeyFlags( + bool critical, + byte[] data) + : base(SignatureSubpacketTag.KeyFlags, critical, data) + { + } + + public KeyFlags( + bool critical, + int flags) + : base(SignatureSubpacketTag.KeyFlags, critical, IntToByteArray(flags)) + { + } + + /// <summary> + /// Return the flag values contained in the first 4 octets (note: at the moment + /// the standard only uses the first one). + /// </summary> + public int Flags + { + get + { + int flags = 0; + + for (int i = 0; i != data.Length; i++) + { + flags |= (data[i] & 0xff) << (i * 8); + } + + return flags; + } + } + } +} diff --git a/Crypto/src/bcpg/sig/NotationData.cs b/Crypto/src/bcpg/sig/NotationData.cs new file mode 100644
index 000000000..ccc9aa745 --- /dev/null +++ b/Crypto/src/bcpg/sig/NotationData.cs
@@ -0,0 +1,112 @@ +using System; +using System.IO; +using System.Text; + +namespace Org.BouncyCastle.Bcpg.Sig +{ + /** + * Class provided a NotationData object according to + * RFC2440, Chapter 5.2.3.15. Notation Data + */ + public class NotationData + : SignatureSubpacket + { + public const int HeaderFlagLength = 4; + public const int HeaderNameLength = 2; + public const int HeaderValueLength = 2; + + public NotationData( + bool critical, + byte[] data) + : base(SignatureSubpacketTag.NotationData, critical, data) + { + } + + public NotationData( + bool critical, + bool humanReadable, + string notationName, + string notationValue) + : base(SignatureSubpacketTag.NotationData, critical, + createData(humanReadable, notationName, notationValue)) + { + } + + private static byte[] createData( + bool humanReadable, + string notationName, + string notationValue) + { + MemoryStream os = new MemoryStream(); + + // (4 octets of flags, 2 octets of name length (M), + // 2 octets of value length (N), + // M octets of name data, + // N octets of value data) + + // flags + os.WriteByte(humanReadable ? (byte)0x80 : (byte)0x00); + os.WriteByte(0x0); + os.WriteByte(0x0); + os.WriteByte(0x0); + + byte[] nameData, valueData = null; + int nameLength, valueLength; + + nameData = Encoding.UTF8.GetBytes(notationName); + nameLength = System.Math.Min(nameData.Length, 0xFF); + + valueData = Encoding.UTF8.GetBytes(notationValue); + valueLength = System.Math.Min(valueData.Length, 0xFF); + + // name length + os.WriteByte((byte)(nameLength >> 8)); + os.WriteByte((byte)(nameLength >> 0)); + + // value length + os.WriteByte((byte)(valueLength >> 8)); + os.WriteByte((byte)(valueLength >> 0)); + + // name + os.Write(nameData, 0, nameLength); + + // value + os.Write(valueData, 0, valueLength); + + return os.ToArray(); + } + + public bool IsHumanReadable + { + get { return data[0] == (byte)0x80; } + } + + public string GetNotationName() + { + int nameLength = ((data[HeaderFlagLength] << 8) + (data[HeaderFlagLength + 1] << 0)); + int namePos = HeaderFlagLength + HeaderNameLength + HeaderValueLength; + + return Encoding.UTF8.GetString(data, namePos, nameLength); + } + + public string GetNotationValue() + { + int nameLength = ((data[HeaderFlagLength] << 8) + (data[HeaderFlagLength + 1] << 0)); + int valueLength = ((data[HeaderFlagLength + HeaderNameLength] << 8) + (data[HeaderFlagLength + HeaderNameLength + 1] << 0)); + int valuePos = HeaderFlagLength + HeaderNameLength + HeaderValueLength + nameLength; + + return Encoding.UTF8.GetString(data, valuePos, valueLength); + } + + public byte[] GetNotationValueBytes() + { + int nameLength = ((data[HeaderFlagLength] << 8) + (data[HeaderFlagLength + 1] << 0)); + int valueLength = ((data[HeaderFlagLength + HeaderNameLength] << 8) + (data[HeaderFlagLength + HeaderNameLength + 1] << 0)); + int valuePos = HeaderFlagLength + HeaderNameLength + HeaderValueLength + nameLength; + + byte[] bytes = new byte[valueLength]; + Array.Copy(data, valuePos, bytes, 0, valueLength); + return bytes; + } + } +} diff --git a/Crypto/src/bcpg/sig/PreferredAlgorithms.cs b/Crypto/src/bcpg/sig/PreferredAlgorithms.cs new file mode 100644
index 000000000..0f282a38c --- /dev/null +++ b/Crypto/src/bcpg/sig/PreferredAlgorithms.cs
@@ -0,0 +1,54 @@ +using System; + + + +namespace Org.BouncyCastle.Bcpg.Sig +{ + /** + * packet giving signature creation time. + */ + public class PreferredAlgorithms + : SignatureSubpacket + { + private static byte[] IntToByteArray( + int[] v) + { + byte[] data = new byte[v.Length]; + + for (int i = 0; i != v.Length; i++) + { + data[i] = (byte)v[i]; + } + + return data; + } + + public PreferredAlgorithms( + SignatureSubpacketTag type, + bool critical, + byte[] data) + : base(type, critical, data) + { + } + + public PreferredAlgorithms( + SignatureSubpacketTag type, + bool critical, + int[] preferences) + : base(type, critical, IntToByteArray(preferences)) + { + } + + public int[] GetPreferences() + { + int[] v = new int[data.Length]; + + for (int i = 0; i != v.Length; i++) + { + v[i] = data[i] & 0xff; + } + + return v; + } + } +} diff --git a/Crypto/src/bcpg/sig/PrimaryUserId.cs b/Crypto/src/bcpg/sig/PrimaryUserId.cs new file mode 100644
index 000000000..fc0353afd --- /dev/null +++ b/Crypto/src/bcpg/sig/PrimaryUserId.cs
@@ -0,0 +1,48 @@ +using System; + + + +namespace Org.BouncyCastle.Bcpg.Sig +{ + /** + * packet giving whether or not the signature is signed using the primary user ID for the key. + */ + public class PrimaryUserId + : SignatureSubpacket + { + private static byte[] BooleanToByteArray( + bool val) + { + byte[] data = new byte[1]; + + if (val) + { + data[0] = 1; + return data; + } + else + { + return data; + } + } + + public PrimaryUserId( + bool critical, + byte[] data) + : base(SignatureSubpacketTag.PrimaryUserId, critical, data) + { + } + + public PrimaryUserId( + bool critical, + bool isPrimaryUserId) + : base(SignatureSubpacketTag.PrimaryUserId, critical, BooleanToByteArray(isPrimaryUserId)) + { + } + + public bool IsPrimaryUserId() + { + return data[0] != 0; + } + } +} diff --git a/Crypto/src/bcpg/sig/Revocable.cs b/Crypto/src/bcpg/sig/Revocable.cs new file mode 100644
index 000000000..b5e94feec --- /dev/null +++ b/Crypto/src/bcpg/sig/Revocable.cs
@@ -0,0 +1,48 @@ +using System; + + + +namespace Org.BouncyCastle.Bcpg.Sig +{ + /** + * packet giving whether or not is revocable. + */ + public class Revocable + : SignatureSubpacket + { + private static byte[] BooleanToByteArray( + bool value) + { + byte[] data = new byte[1]; + + if (value) + { + data[0] = 1; + return data; + } + else + { + return data; + } + } + + public Revocable( + bool critical, + byte[] data) + : base(SignatureSubpacketTag.Revocable, critical, data) + { + } + + public Revocable( + bool critical, + bool isRevocable) + : base(SignatureSubpacketTag.Revocable, critical, BooleanToByteArray(isRevocable)) + { + } + + public bool IsRevocable() + { + return data[0] != 0; + } + } +} diff --git a/Crypto/src/bcpg/sig/RevocationKey.cs b/Crypto/src/bcpg/sig/RevocationKey.cs new file mode 100644
index 000000000..5e388246b --- /dev/null +++ b/Crypto/src/bcpg/sig/RevocationKey.cs
@@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Org.BouncyCastle.Bcpg +{ + /// <summary> + /// Represents revocation key OpenPGP signature sub packet. + /// </summary> + public class RevocationKey + : SignatureSubpacket + { + // 1 octet of class, + // 1 octet of public-key algorithm ID, + // 20 octets of fingerprint + public RevocationKey( + bool isCritical, + byte[] data) + : base(SignatureSubpacketTag.RevocationKey, isCritical, data) + { + } + + public RevocationKey( + bool isCritical, + RevocationKeyTag signatureClass, + PublicKeyAlgorithmTag keyAlgorithm, + byte[] fingerprint) + : base(SignatureSubpacketTag.RevocationKey, isCritical, + CreateData(signatureClass, keyAlgorithm, fingerprint)) + { + } + + private static byte[] CreateData( + RevocationKeyTag signatureClass, + PublicKeyAlgorithmTag keyAlgorithm, + byte[] fingerprint) + { + byte[] data = new byte[2 + fingerprint.Length]; + data[0] = (byte)signatureClass; + data[1] = (byte)keyAlgorithm; + Array.Copy(fingerprint, 0, data, 2, fingerprint.Length); + return data; + } + + public virtual RevocationKeyTag SignatureClass + { + get { return (RevocationKeyTag)this.GetData()[0]; } + } + + public virtual PublicKeyAlgorithmTag Algorithm + { + get { return (PublicKeyAlgorithmTag)this.GetData()[1]; } + } + + public virtual byte[] GetFingerprint() + { + byte[] data = this.GetData(); + byte[] fingerprint = new byte[data.Length - 2]; + Array.Copy(data, 2, fingerprint, 0, fingerprint.Length); + return fingerprint; + } + } +} diff --git a/Crypto/src/bcpg/sig/RevocationKeyTags.cs b/Crypto/src/bcpg/sig/RevocationKeyTags.cs new file mode 100644
index 000000000..d76d1dcf4 --- /dev/null +++ b/Crypto/src/bcpg/sig/RevocationKeyTags.cs
@@ -0,0 +1,9 @@ +namespace Org.BouncyCastle.Bcpg +{ + public enum RevocationKeyTag + : byte + { + ClassDefault = 0x80, + ClassSensitive = 0x40 + } +} diff --git a/Crypto/src/bcpg/sig/RevocationReason.cs b/Crypto/src/bcpg/sig/RevocationReason.cs new file mode 100644
index 000000000..f32c1f64a --- /dev/null +++ b/Crypto/src/bcpg/sig/RevocationReason.cs
@@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Text; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Bcpg +{ + /// <summary> + /// Represents revocation reason OpenPGP signature sub packet. + /// </summary> + public class RevocationReason + : SignatureSubpacket + { + public RevocationReason(bool isCritical, byte[] data) + : base(SignatureSubpacketTag.RevocationReason, isCritical, data) + { + } + + public RevocationReason( + bool isCritical, + RevocationReasonTag reason, + string description) + : base(SignatureSubpacketTag.RevocationReason, isCritical, CreateData(reason, description)) + { + } + + private static byte[] CreateData( + RevocationReasonTag reason, + string description) + { + byte[] descriptionBytes = Strings.ToUtf8ByteArray(description); + byte[] data = new byte[1 + descriptionBytes.Length]; + + data[0] = (byte)reason; + Array.Copy(descriptionBytes, 0, data, 1, descriptionBytes.Length); + + return data; + } + + public virtual RevocationReasonTag GetRevocationReason() + { + return (RevocationReasonTag)GetData()[0]; + } + + public virtual string GetRevocationDescription() + { + byte[] data = GetData(); + if (data.Length == 1) + { + return string.Empty; + } + + byte[] description = new byte[data.Length - 1]; + Array.Copy(data, 1, description, 0, description.Length); + + return Strings.FromUtf8ByteArray(description); + } + } +} diff --git a/Crypto/src/bcpg/sig/RevocationReasonTags.cs b/Crypto/src/bcpg/sig/RevocationReasonTags.cs new file mode 100644
index 000000000..524a58c49 --- /dev/null +++ b/Crypto/src/bcpg/sig/RevocationReasonTags.cs
@@ -0,0 +1,14 @@ +namespace Org.BouncyCastle.Bcpg +{ + public enum RevocationReasonTag + : byte + { + NoReason = 0, // No reason specified (key revocations or cert revocations) + KeySuperseded = 1, // Key is superseded (key revocations) + KeyCompromised = 2, // Key material has been compromised (key revocations) + KeyRetired = 3, // Key is retired and no longer used (key revocations) + UserNoLongerValid = 32, // User ID information is no longer valid (cert revocations) + + // 100-110 - Private Use + } +} diff --git a/Crypto/src/bcpg/sig/SignatureCreationTime.cs b/Crypto/src/bcpg/sig/SignatureCreationTime.cs new file mode 100644
index 000000000..e6f241f11 --- /dev/null +++ b/Crypto/src/bcpg/sig/SignatureCreationTime.cs
@@ -0,0 +1,47 @@ +using System; + +using Org.BouncyCastle.Utilities.Date; + +namespace Org.BouncyCastle.Bcpg.Sig +{ + /** + * packet giving signature creation time. + */ + public class SignatureCreationTime + : SignatureSubpacket + { + protected static byte[] TimeToBytes( + DateTime time) + { + long t = DateTimeUtilities.DateTimeToUnixMs(time) / 1000L; + byte[] data = new byte[4]; + data[0] = (byte)(t >> 24); + data[1] = (byte)(t >> 16); + data[2] = (byte)(t >> 8); + data[3] = (byte)t; + return data; + } + public SignatureCreationTime( + bool critical, + byte[] data) + : base(SignatureSubpacketTag.CreationTime, critical, data) + { + } + public SignatureCreationTime( + bool critical, + DateTime date) + : base(SignatureSubpacketTag.CreationTime, critical, TimeToBytes(date)) + { + } + public DateTime GetTime() + { + long time = (long)( + ((uint)data[0] << 24) + | ((uint)data[1] << 16) + | ((uint)data[2] << 8) + | ((uint)data[3]) + ); + return DateTimeUtilities.UnixMsToDateTime(time * 1000L); + } + } +} diff --git a/Crypto/src/bcpg/sig/SignatureExpirationTime.cs b/Crypto/src/bcpg/sig/SignatureExpirationTime.cs new file mode 100644
index 000000000..7fddf5743 --- /dev/null +++ b/Crypto/src/bcpg/sig/SignatureExpirationTime.cs
@@ -0,0 +1,54 @@ +using System; + + + +namespace Org.BouncyCastle.Bcpg.Sig +{ + /** + * packet giving signature expiration time. + */ + public class SignatureExpirationTime + : SignatureSubpacket + { + protected static byte[] TimeToBytes( + long t) + { + byte[] data = new byte[4]; + + data[0] = (byte)(t >> 24); + data[1] = (byte)(t >> 16); + data[2] = (byte)(t >> 8); + data[3] = (byte)t; + + return data; + } + + public SignatureExpirationTime( + bool critical, + byte[] data) + : base(SignatureSubpacketTag.ExpireTime, critical, data) + { + } + + public SignatureExpirationTime( + bool critical, + long seconds) + : base(SignatureSubpacketTag.ExpireTime, critical, TimeToBytes(seconds)) + { + } + + /** + * return time in seconds before signature expires after creation time. + */ + public long Time + { + get + { + long time = ((long)(data[0] & 0xff) << 24) | ((long)(data[1] & 0xff) << 16) + | ((long)(data[2] & 0xff) << 8) | ((long)data[3] & 0xff); + + return time; + } + } + } +} diff --git a/Crypto/src/bcpg/sig/SignerUserId.cs b/Crypto/src/bcpg/sig/SignerUserId.cs new file mode 100644
index 000000000..98cc808e7 --- /dev/null +++ b/Crypto/src/bcpg/sig/SignerUserId.cs
@@ -0,0 +1,52 @@ +using System; + + + +namespace Org.BouncyCastle.Bcpg.Sig +{ + /** + * packet giving the User ID of the signer. + */ + public class SignerUserId + : SignatureSubpacket + { + private static byte[] UserIdToBytes( + string id) + { + byte[] idData = new byte[id.Length]; + + for (int i = 0; i != id.Length; i++) + { + idData[i] = (byte)id[i]; + } + + return idData; + } + + public SignerUserId( + bool critical, + byte[] data) + : base(SignatureSubpacketTag.SignerUserId, critical, data) + { + } + + public SignerUserId( + bool critical, + string userId) + : base(SignatureSubpacketTag.SignerUserId, critical, UserIdToBytes(userId)) + { + } + + public string GetId() + { + char[] chars = new char[data.Length]; + + for (int i = 0; i != chars.Length; i++) + { + chars[i] = (char)(data[i] & 0xff); + } + + return new string(chars); + } + } +} diff --git a/Crypto/src/bcpg/sig/TrustSignature.cs b/Crypto/src/bcpg/sig/TrustSignature.cs new file mode 100644
index 000000000..bbadd3067 --- /dev/null +++ b/Crypto/src/bcpg/sig/TrustSignature.cs
@@ -0,0 +1,43 @@ +using System; + +namespace Org.BouncyCastle.Bcpg.Sig +{ + /** + * packet giving trust. + */ + public class TrustSignature + : SignatureSubpacket + { + private static byte[] IntToByteArray( + int v1, + int v2) + { + return new byte[]{ (byte)v1, (byte)v2 }; + } + + public TrustSignature( + bool critical, + byte[] data) + : base(SignatureSubpacketTag.TrustSig, critical, data) + { + } + + public TrustSignature( + bool critical, + int depth, + int trustAmount) + : base(SignatureSubpacketTag.TrustSig, critical, IntToByteArray(depth, trustAmount)) + { + } + + public int Depth + { + get { return data[0] & 0xff; } + } + + public int TrustAmount + { + get { return data[1] & 0xff; } + } + } +} diff --git a/Crypto/src/cms/BaseDigestCalculator.cs b/Crypto/src/cms/BaseDigestCalculator.cs new file mode 100644
index 000000000..3dcbca753 --- /dev/null +++ b/Crypto/src/cms/BaseDigestCalculator.cs
@@ -0,0 +1,23 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Cms +{ + internal class BaseDigestCalculator + : IDigestCalculator + { + private readonly byte[] digest; + + internal BaseDigestCalculator( + byte[] digest) + { + this.digest = digest; + } + + public byte[] GetDigest() + { + return Arrays.Clone(digest); + } + } +} diff --git a/Crypto/src/cms/CMSAttributeTableGenerationException.cs b/Crypto/src/cms/CMSAttributeTableGenerationException.cs new file mode 100644
index 000000000..2bc8ce5d1 --- /dev/null +++ b/Crypto/src/cms/CMSAttributeTableGenerationException.cs
@@ -0,0 +1,25 @@ +using System; + +namespace Org.BouncyCastle.Cms +{ + public class CmsAttributeTableGenerationException + : CmsException + { + public CmsAttributeTableGenerationException() + { + } + + public CmsAttributeTableGenerationException( + string name) + : base(name) + { + } + + public CmsAttributeTableGenerationException( + string name, + Exception e) + : base(name, e) + { + } + } +} diff --git a/Crypto/src/cms/CMSAttributeTableGenerator.cs b/Crypto/src/cms/CMSAttributeTableGenerator.cs new file mode 100644
index 000000000..92c9a29d9 --- /dev/null +++ b/Crypto/src/cms/CMSAttributeTableGenerator.cs
@@ -0,0 +1,25 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1.Cms; + +namespace Org.BouncyCastle.Cms +{ + /// <remarks> + /// The 'Signature' parameter is only available when generating unsigned attributes. + /// </remarks> + public enum CmsAttributeTableParameter + { +// const string ContentType = "contentType"; +// const string Digest = "digest"; +// const string Signature = "encryptedDigest"; +// const string DigestAlgorithmIdentifier = "digestAlgID"; + + ContentType, Digest, Signature, DigestAlgorithmIdentifier + } + + public interface CmsAttributeTableGenerator + { + AttributeTable GetAttributes(IDictionary parameters); + } +} diff --git a/Crypto/src/cms/CMSAuthEnvelopedData.cs b/Crypto/src/cms/CMSAuthEnvelopedData.cs new file mode 100644
index 000000000..d35e946ae --- /dev/null +++ b/Crypto/src/cms/CMSAuthEnvelopedData.cs
@@ -0,0 +1,112 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Cms +{ + /** + * containing class for an CMS AuthEnveloped Data object + */ + internal class CmsAuthEnvelopedData + { + internal RecipientInformationStore recipientInfoStore; + internal ContentInfo contentInfo; + + private OriginatorInfo originator; + private AlgorithmIdentifier authEncAlg; + private Asn1Set authAttrs; + private byte[] mac; + private Asn1Set unauthAttrs; + + public CmsAuthEnvelopedData( + byte[] authEnvData) + : this(CmsUtilities.ReadContentInfo(authEnvData)) + { + } + + public CmsAuthEnvelopedData( + Stream authEnvData) + : this(CmsUtilities.ReadContentInfo(authEnvData)) + { + } + + public CmsAuthEnvelopedData( + ContentInfo contentInfo) + { + this.contentInfo = contentInfo; + + AuthEnvelopedData authEnvData = AuthEnvelopedData.GetInstance(contentInfo.Content); + + this.originator = authEnvData.OriginatorInfo; + + // + // read the recipients + // + Asn1Set recipientInfos = authEnvData.RecipientInfos; + + // + // read the auth-encrypted content info + // + EncryptedContentInfo authEncInfo = authEnvData.AuthEncryptedContentInfo; + this.authEncAlg = authEncInfo.ContentEncryptionAlgorithm; + CmsSecureReadable secureReadable = new AuthEnvelopedSecureReadable(this); + + // + // build the RecipientInformationStore + // + this.recipientInfoStore = CmsEnvelopedHelper.BuildRecipientInformationStore( + recipientInfos, secureReadable); + + // FIXME These need to be passed to the AEAD cipher as AAD (Additional Authenticated Data) + this.authAttrs = authEnvData.AuthAttrs; + this.mac = authEnvData.Mac.GetOctets(); + this.unauthAttrs = authEnvData.UnauthAttrs; + } + + private class AuthEnvelopedSecureReadable : CmsSecureReadable + { + private readonly CmsAuthEnvelopedData parent; + + internal AuthEnvelopedSecureReadable(CmsAuthEnvelopedData parent) + { + this.parent = parent; + } + + public AlgorithmIdentifier Algorithm + { + get { return parent.authEncAlg; } + } + + public object CryptoObject + { + get { return null; } + } + + public CmsReadable GetReadable(KeyParameter key) + { + // TODO Create AEAD cipher instance to decrypt and calculate tag ( MAC) + throw new CmsException("AuthEnveloped data decryption not yet implemented"); + +// RFC 5084 ASN.1 Module +// -- Parameters for AlgorithmIdentifier +// +// CCMParameters ::= SEQUENCE { +// aes-nonce OCTET STRING (SIZE(7..13)), +// aes-ICVlen AES-CCM-ICVlen DEFAULT 12 } +// +// AES-CCM-ICVlen ::= INTEGER (4 | 6 | 8 | 10 | 12 | 14 | 16) +// +// GCMParameters ::= SEQUENCE { +// aes-nonce OCTET STRING, -- recommended size is 12 octets +// aes-ICVlen AES-GCM-ICVlen DEFAULT 12 } +// +// AES-GCM-ICVlen ::= INTEGER (12 | 13 | 14 | 15 | 16) + } + } + } +} diff --git a/Crypto/src/cms/CMSAuthEnvelopedGenerator.cs b/Crypto/src/cms/CMSAuthEnvelopedGenerator.cs new file mode 100644
index 000000000..4273cff29 --- /dev/null +++ b/Crypto/src/cms/CMSAuthEnvelopedGenerator.cs
@@ -0,0 +1,16 @@ +using System; + +using Org.BouncyCastle.Asn1.Nist; + +namespace Org.BouncyCastle.Cms +{ + internal class CmsAuthEnvelopedGenerator + { + public static readonly string Aes128Ccm = NistObjectIdentifiers.IdAes128Ccm.Id; + public static readonly string Aes192Ccm = NistObjectIdentifiers.IdAes192Ccm.Id; + public static readonly string Aes256Ccm = NistObjectIdentifiers.IdAes256Ccm.Id; + public static readonly string Aes128Gcm = NistObjectIdentifiers.IdAes128Gcm.Id; + public static readonly string Aes192Gcm = NistObjectIdentifiers.IdAes192Gcm.Id; + public static readonly string Aes256Gcm = NistObjectIdentifiers.IdAes256Gcm.Id; + } +} diff --git a/Crypto/src/cms/CMSAuthenticatedData.cs b/Crypto/src/cms/CMSAuthenticatedData.cs new file mode 100644
index 000000000..5e234da2b --- /dev/null +++ b/Crypto/src/cms/CMSAuthenticatedData.cs
@@ -0,0 +1,137 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Cms +{ + /** + * containing class for an CMS Authenticated Data object + */ + public class CmsAuthenticatedData + { + internal RecipientInformationStore recipientInfoStore; + internal ContentInfo contentInfo; + + private AlgorithmIdentifier macAlg; + private Asn1Set authAttrs; + private Asn1Set unauthAttrs; + private byte[] mac; + + public CmsAuthenticatedData( + byte[] authData) + : this(CmsUtilities.ReadContentInfo(authData)) + { + } + + public CmsAuthenticatedData( + Stream authData) + : this(CmsUtilities.ReadContentInfo(authData)) + { + } + + public CmsAuthenticatedData( + ContentInfo contentInfo) + { + this.contentInfo = contentInfo; + + AuthenticatedData authData = AuthenticatedData.GetInstance(contentInfo.Content); + + // + // read the recipients + // + Asn1Set recipientInfos = authData.RecipientInfos; + + this.macAlg = authData.MacAlgorithm; + + // + // read the authenticated content info + // + ContentInfo encInfo = authData.EncapsulatedContentInfo; + CmsReadable readable = new CmsProcessableByteArray( + Asn1OctetString.GetInstance(encInfo.Content).GetOctets()); + CmsSecureReadable secureReadable = new CmsEnvelopedHelper.CmsAuthenticatedSecureReadable( + this.macAlg, readable); + + // + // build the RecipientInformationStore + // + this.recipientInfoStore = CmsEnvelopedHelper.BuildRecipientInformationStore( + recipientInfos, secureReadable); + + this.authAttrs = authData.AuthAttrs; + this.mac = authData.Mac.GetOctets(); + this.unauthAttrs = authData.UnauthAttrs; + } + + public byte[] GetMac() + { + return Arrays.Clone(mac); + } + + public AlgorithmIdentifier MacAlgorithmID + { + get { return macAlg; } + } + + /** + * return the object identifier for the content MAC algorithm. + */ + public string MacAlgOid + { + get { return macAlg.ObjectID.Id; } + } + + /** + * return a store of the intended recipients for this message + */ + public RecipientInformationStore GetRecipientInfos() + { + return recipientInfoStore; + } + + /** + * return the ContentInfo + */ + public ContentInfo ContentInfo + { + get { return contentInfo; } + } + + /** + * return a table of the digested attributes indexed by + * the OID of the attribute. + */ + public Asn1.Cms.AttributeTable GetAuthAttrs() + { + if (authAttrs == null) + return null; + + return new Asn1.Cms.AttributeTable(authAttrs); + } + + /** + * return a table of the undigested attributes indexed by + * the OID of the attribute. + */ + public Asn1.Cms.AttributeTable GetUnauthAttrs() + { + if (unauthAttrs == null) + return null; + + return new Asn1.Cms.AttributeTable(unauthAttrs); + } + + /** + * return the ASN.1 encoded representation of this object. + */ + public byte[] GetEncoded() + { + return contentInfo.GetEncoded(); + } + } +} diff --git a/Crypto/src/cms/CMSAuthenticatedDataGenerator.cs b/Crypto/src/cms/CMSAuthenticatedDataGenerator.cs new file mode 100644
index 000000000..0a37ca4f5 --- /dev/null +++ b/Crypto/src/cms/CMSAuthenticatedDataGenerator.cs
@@ -0,0 +1,156 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Cms +{ + /** + * General class for generating a CMS authenticated-data message. + * + * A simple example of usage. + * + * <pre> + * CMSAuthenticatedDataGenerator fact = new CMSAuthenticatedDataGenerator(); + * + * fact.addKeyTransRecipient(cert); + * + * CMSAuthenticatedData data = fact.generate(content, algorithm, "BC"); + * </pre> + */ + public class CmsAuthenticatedDataGenerator + : CmsAuthenticatedGenerator + { + /** + * base constructor + */ + public CmsAuthenticatedDataGenerator() + { + } + + /** + * constructor allowing specific source of randomness + * @param rand instance of SecureRandom to use + */ + public CmsAuthenticatedDataGenerator( + SecureRandom rand) + : base(rand) + { + } + + /** + * generate an enveloped object that contains an CMS Enveloped Data + * object using the given provider and the passed in key generator. + */ + private CmsAuthenticatedData Generate( + CmsProcessable content, + string macOid, + CipherKeyGenerator keyGen) + { + AlgorithmIdentifier macAlgId; + KeyParameter encKey; + Asn1OctetString encContent; + Asn1OctetString macResult; + + try + { + // FIXME Will this work for macs? + byte[] encKeyBytes = keyGen.GenerateKey(); + encKey = ParameterUtilities.CreateKeyParameter(macOid, encKeyBytes); + + Asn1Encodable asn1Params = GenerateAsn1Parameters(macOid, encKeyBytes); + + ICipherParameters cipherParameters; + macAlgId = GetAlgorithmIdentifier( + macOid, encKey, asn1Params, out cipherParameters); + + IMac mac = MacUtilities.GetMac(macOid); + // TODO Confirm no ParametersWithRandom needed + // FIXME Only passing key at the moment +// mac.Init(cipherParameters); + mac.Init(encKey); + + MemoryStream bOut = new MemoryStream(); + Stream mOut = new TeeOutputStream(bOut, new MacOutputStream(mac)); + + content.Write(mOut); + + mOut.Dispose(); + bOut.Dispose(); + + encContent = new BerOctetString(bOut.ToArray()); + + byte[] macOctets = MacUtilities.DoFinal(mac); + macResult = new DerOctetString(macOctets); + } + catch (SecurityUtilityException e) + { + throw new CmsException("couldn't create cipher.", e); + } + catch (InvalidKeyException e) + { + throw new CmsException("key invalid in message.", e); + } + catch (IOException e) + { + throw new CmsException("exception decoding algorithm parameters.", e); + } + + Asn1EncodableVector recipientInfos = new Asn1EncodableVector(); + + foreach (RecipientInfoGenerator rig in recipientInfoGenerators) + { + try + { + recipientInfos.Add(rig.Generate(encKey, rand)); + } + catch (InvalidKeyException e) + { + throw new CmsException("key inappropriate for algorithm.", e); + } + catch (GeneralSecurityException e) + { + throw new CmsException("error making encrypted content.", e); + } + } + + ContentInfo eci = new ContentInfo(CmsObjectIdentifiers.Data, encContent); + + ContentInfo contentInfo = new ContentInfo( + CmsObjectIdentifiers.AuthenticatedData, + new AuthenticatedData(null, new DerSet(recipientInfos), macAlgId, null, eci, null, macResult, null)); + + return new CmsAuthenticatedData(contentInfo); + } + + /** + * generate an authenticated object that contains an CMS Authenticated Data object + */ + public CmsAuthenticatedData Generate( + CmsProcessable content, + string encryptionOid) + { + try + { + // FIXME Will this work for macs? + CipherKeyGenerator keyGen = GeneratorUtilities.GetKeyGenerator(encryptionOid); + + keyGen.Init(new KeyGenerationParameters(rand, keyGen.DefaultStrength)); + + return Generate(content, encryptionOid, keyGen); + } + catch (SecurityUtilityException e) + { + throw new CmsException("can't find key generation algorithm.", e); + } + } + } +} diff --git a/Crypto/src/cms/CMSAuthenticatedDataParser.cs b/Crypto/src/cms/CMSAuthenticatedDataParser.cs new file mode 100644
index 000000000..c99aac61c --- /dev/null +++ b/Crypto/src/cms/CMSAuthenticatedDataParser.cs
@@ -0,0 +1,214 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Cms +{ + /** + * Parsing class for an CMS Authenticated Data object from an input stream. + * <p> + * Note: that because we are in a streaming mode only one recipient can be tried and it is important + * that the methods on the parser are called in the appropriate order. + * </p> + * <p> + * Example of use - assuming the first recipient matches the private key we have. + * <pre> + * CMSAuthenticatedDataParser ad = new CMSAuthenticatedDataParser(inputStream); + * + * RecipientInformationStore recipients = ad.getRecipientInfos(); + * + * Collection c = recipients.getRecipients(); + * Iterator it = c.iterator(); + * + * if (it.hasNext()) + * { + * RecipientInformation recipient = (RecipientInformation)it.next(); + * + * CMSTypedStream recData = recipient.getContentStream(privateKey, "BC"); + * + * processDataStream(recData.getContentStream()); + * + * if (!Arrays.equals(ad.getMac(), recipient.getMac()) + * { + * System.err.println("Data corrupted!!!!"); + * } + * } + * </pre> + * Note: this class does not introduce buffering - if you are processing large files you should create + * the parser with: + * <pre> + * CMSAuthenticatedDataParser ep = new CMSAuthenticatedDataParser(new BufferedInputStream(inputStream, bufSize)); + * </pre> + * where bufSize is a suitably large buffer size. + * </p> + */ + public class CmsAuthenticatedDataParser + : CmsContentInfoParser + { + internal RecipientInformationStore _recipientInfoStore; + internal AuthenticatedDataParser authData; + + private AlgorithmIdentifier macAlg; + private byte[] mac; + private Asn1.Cms.AttributeTable authAttrs; + private Asn1.Cms.AttributeTable unauthAttrs; + + private bool authAttrNotRead; + private bool unauthAttrNotRead; + + public CmsAuthenticatedDataParser( + byte[] envelopedData) + : this(new MemoryStream(envelopedData, false)) + { + } + + public CmsAuthenticatedDataParser( + Stream envelopedData) + : base(envelopedData) + { + this.authAttrNotRead = true; + this.authData = new AuthenticatedDataParser( + (Asn1SequenceParser)contentInfo.GetContent(Asn1Tags.Sequence)); + + // TODO Validate version? + //DerInteger version = this.authData.getVersion(); + + // + // read the recipients + // + Asn1Set recipientInfos = Asn1Set.GetInstance(authData.GetRecipientInfos().ToAsn1Object()); + + this.macAlg = authData.GetMacAlgorithm(); + + // + // read the authenticated content info + // + ContentInfoParser data = authData.GetEnapsulatedContentInfo(); + CmsReadable readable = new CmsProcessableInputStream( + ((Asn1OctetStringParser)data.GetContent(Asn1Tags.OctetString)).GetOctetStream()); + CmsSecureReadable secureReadable = new CmsEnvelopedHelper.CmsAuthenticatedSecureReadable( + this.macAlg, readable); + + // + // build the RecipientInformationStore + // + this._recipientInfoStore = CmsEnvelopedHelper.BuildRecipientInformationStore( + recipientInfos, secureReadable); + } + + public AlgorithmIdentifier MacAlgorithmID + { + get { return macAlg; } + } + + /** + * return the object identifier for the mac algorithm. + */ + public string MacAlgOid + { + get { return macAlg.ObjectID.Id; } + } + + + /** + * return the ASN.1 encoded encryption algorithm parameters, or null if + * there aren't any. + */ + public Asn1Object MacAlgParams + { + get + { + Asn1Encodable ae = macAlg.Parameters; + + return ae == null ? null : ae.ToAsn1Object(); + } + } + + /** + * return a store of the intended recipients for this message + */ + public RecipientInformationStore GetRecipientInfos() + { + return _recipientInfoStore; + } + + public byte[] GetMac() + { + if (mac == null) + { + GetAuthAttrs(); + mac = authData.GetMac().GetOctets(); + } + return Arrays.Clone(mac); + } + + /** + * return a table of the unauthenticated attributes indexed by + * the OID of the attribute. + * @exception java.io.IOException + */ + public Asn1.Cms.AttributeTable GetAuthAttrs() + { + if (authAttrs == null && authAttrNotRead) + { + Asn1SetParser s = authData.GetAuthAttrs(); + + authAttrNotRead = false; + + if (s != null) + { + Asn1EncodableVector v = new Asn1EncodableVector(); + + IAsn1Convertible o; + while ((o = s.ReadObject()) != null) + { + Asn1SequenceParser seq = (Asn1SequenceParser)o; + + v.Add(seq.ToAsn1Object()); + } + + authAttrs = new Asn1.Cms.AttributeTable(new DerSet(v)); + } + } + + return authAttrs; + } + + /** + * return a table of the unauthenticated attributes indexed by + * the OID of the attribute. + * @exception java.io.IOException + */ + public Asn1.Cms.AttributeTable GetUnauthAttrs() + { + if (unauthAttrs == null && unauthAttrNotRead) + { + Asn1SetParser s = authData.GetUnauthAttrs(); + + unauthAttrNotRead = false; + + if (s != null) + { + Asn1EncodableVector v = new Asn1EncodableVector(); + + IAsn1Convertible o; + while ((o = s.ReadObject()) != null) + { + Asn1SequenceParser seq = (Asn1SequenceParser)o; + + v.Add(seq.ToAsn1Object()); + } + + unauthAttrs = new Asn1.Cms.AttributeTable(new DerSet(v)); + } + } + + return unauthAttrs; + } + } +} diff --git a/Crypto/src/cms/CMSAuthenticatedDataStreamGenerator.cs b/Crypto/src/cms/CMSAuthenticatedDataStreamGenerator.cs new file mode 100644
index 000000000..c133ae240 --- /dev/null +++ b/Crypto/src/cms/CMSAuthenticatedDataStreamGenerator.cs
@@ -0,0 +1,275 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Cms +{ + /** + * General class for generating a CMS authenticated-data message stream. + * <p> + * A simple example of usage. + * <pre> + * CMSAuthenticatedDataStreamGenerator edGen = new CMSAuthenticatedDataStreamGenerator(); + * + * edGen.addKeyTransRecipient(cert); + * + * ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + * + * OutputStream out = edGen.open( + * bOut, CMSAuthenticatedDataGenerator.AES128_CBC, "BC");* + * out.write(data); + * + * out.close(); + * </pre> + * </p> + */ + public class CmsAuthenticatedDataStreamGenerator + : CmsAuthenticatedGenerator + { + // TODO Add support +// private object _originatorInfo = null; +// private object _unprotectedAttributes = null; + private int _bufferSize; + private bool _berEncodeRecipientSet; + + /** + * base constructor + */ + public CmsAuthenticatedDataStreamGenerator() + { + } + + /** + * constructor allowing specific source of randomness + * @param rand instance of SecureRandom to use + */ + public CmsAuthenticatedDataStreamGenerator( + SecureRandom rand) + : base(rand) + { + } + + /** + * Set the underlying string size for encapsulated data + * + * @param bufferSize length of octet strings to buffer the data. + */ + public void SetBufferSize( + int bufferSize) + { + _bufferSize = bufferSize; + } + + /** + * Use a BER Set to store the recipient information + */ + public void SetBerEncodeRecipients( + bool berEncodeRecipientSet) + { + _berEncodeRecipientSet = berEncodeRecipientSet; + } + + /** + * generate an enveloped object that contains an CMS Enveloped Data + * object using the given provider and the passed in key generator. + * @throws java.io.IOException + */ + private Stream Open( + Stream outStr, + string macOid, + CipherKeyGenerator keyGen) + { + // FIXME Will this work for macs? + byte[] encKeyBytes = keyGen.GenerateKey(); + KeyParameter encKey = ParameterUtilities.CreateKeyParameter(macOid, encKeyBytes); + + Asn1Encodable asn1Params = GenerateAsn1Parameters(macOid, encKeyBytes); + + ICipherParameters cipherParameters; + AlgorithmIdentifier macAlgId = GetAlgorithmIdentifier( + macOid, encKey, asn1Params, out cipherParameters); + + Asn1EncodableVector recipientInfos = new Asn1EncodableVector(); + + foreach (RecipientInfoGenerator rig in recipientInfoGenerators) + { + try + { + recipientInfos.Add(rig.Generate(encKey, rand)); + } + catch (InvalidKeyException e) + { + throw new CmsException("key inappropriate for algorithm.", e); + } + catch (GeneralSecurityException e) + { + throw new CmsException("error making encrypted content.", e); + } + } + + // FIXME Only passing key at the moment +// return Open(outStr, macAlgId, cipherParameters, recipientInfos); + return Open(outStr, macAlgId, encKey, recipientInfos); + } + + protected Stream Open( + Stream outStr, + AlgorithmIdentifier macAlgId, + ICipherParameters cipherParameters, + Asn1EncodableVector recipientInfos) + { + try + { + // + // ContentInfo + // + BerSequenceGenerator cGen = new BerSequenceGenerator(outStr); + + cGen.AddObject(CmsObjectIdentifiers.AuthenticatedData); + + // + // Authenticated Data + // + BerSequenceGenerator authGen = new BerSequenceGenerator( + cGen.GetRawOutputStream(), 0, true); + + authGen.AddObject(new DerInteger(AuthenticatedData.CalculateVersion(null))); + + Stream authRaw = authGen.GetRawOutputStream(); + Asn1Generator recipGen = _berEncodeRecipientSet + ? (Asn1Generator) new BerSetGenerator(authRaw) + : new DerSetGenerator(authRaw); + + foreach (Asn1Encodable ae in recipientInfos) + { + recipGen.AddObject(ae); + } + + recipGen.Close(); + + authGen.AddObject(macAlgId); + + BerSequenceGenerator eiGen = new BerSequenceGenerator(authRaw); + eiGen.AddObject(CmsObjectIdentifiers.Data); + + Stream octetOutputStream = CmsUtilities.CreateBerOctetOutputStream( + eiGen.GetRawOutputStream(), 0, false, _bufferSize); + + IMac mac = MacUtilities.GetMac(macAlgId.ObjectID); + // TODO Confirm no ParametersWithRandom needed + mac.Init(cipherParameters); + Stream mOut = new TeeOutputStream(octetOutputStream, new MacOutputStream(mac)); + + return new CmsAuthenticatedDataOutputStream(mOut, mac, cGen, authGen, eiGen); + } + catch (SecurityUtilityException e) + { + throw new CmsException("couldn't create cipher.", e); + } + catch (InvalidKeyException e) + { + throw new CmsException("key invalid in message.", e); + } + catch (IOException e) + { + throw new CmsException("exception decoding algorithm parameters.", e); + } + } + + /** + * generate an enveloped object that contains an CMS Enveloped Data object + */ + public Stream Open( + Stream outStr, + string encryptionOid) + { + CipherKeyGenerator keyGen = GeneratorUtilities.GetKeyGenerator(encryptionOid); + + keyGen.Init(new KeyGenerationParameters(rand, keyGen.DefaultStrength)); + + return Open(outStr, encryptionOid, keyGen); + } + + /** + * generate an enveloped object that contains an CMS Enveloped Data object + */ + public Stream Open( + Stream outStr, + string encryptionOid, + int keySize) + { + CipherKeyGenerator keyGen = GeneratorUtilities.GetKeyGenerator(encryptionOid); + + keyGen.Init(new KeyGenerationParameters(rand, keySize)); + + return Open(outStr, encryptionOid, keyGen); + } + + private class CmsAuthenticatedDataOutputStream + : BaseOutputStream + { + private readonly Stream macStream; + private readonly IMac mac; + private readonly BerSequenceGenerator cGen; + private readonly BerSequenceGenerator authGen; + private readonly BerSequenceGenerator eiGen; + + public CmsAuthenticatedDataOutputStream( + Stream macStream, + IMac mac, + BerSequenceGenerator cGen, + BerSequenceGenerator authGen, + BerSequenceGenerator eiGen) + { + this.macStream = macStream; + this.mac = mac; + this.cGen = cGen; + this.authGen = authGen; + this.eiGen = eiGen; + } + + public override void WriteByte( + byte b) + { + macStream.WriteByte(b); + } + + public override void Write( + byte[] bytes, + int off, + int len) + { + macStream.Write(bytes, off, len); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + macStream.Dispose(); + + // TODO Parent context(s) should really be be closed explicitly + + eiGen.Close(); + + // [TODO] auth attributes go here + byte[] macOctets = MacUtilities.DoFinal(mac); + authGen.AddObject(new DerOctetString(macOctets)); + // [TODO] unauth attributes go here + + authGen.Close(); + cGen.Close(); + } + } + } + } +} diff --git a/Crypto/src/cms/CMSAuthenticatedGenerator.cs b/Crypto/src/cms/CMSAuthenticatedGenerator.cs new file mode 100644
index 000000000..8824d1913 --- /dev/null +++ b/Crypto/src/cms/CMSAuthenticatedGenerator.cs
@@ -0,0 +1,35 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities.Date; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Cms +{ + public class CmsAuthenticatedGenerator + : CmsEnvelopedGenerator + { + /** + * base constructor + */ + public CmsAuthenticatedGenerator() + { + } + + /** + * constructor allowing specific source of randomness + * + * @param rand instance of SecureRandom to use + */ + public CmsAuthenticatedGenerator( + SecureRandom rand) + : base(rand) + { + } + } +} diff --git a/Crypto/src/cms/CMSCompressedData.cs b/Crypto/src/cms/CMSCompressedData.cs new file mode 100644
index 000000000..55d06b5b8 --- /dev/null +++ b/Crypto/src/cms/CMSCompressedData.cs
@@ -0,0 +1,107 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Utilities.Zlib; + +namespace Org.BouncyCastle.Cms +{ + /** + * containing class for an CMS Compressed Data object + */ + public class CmsCompressedData + { + internal ContentInfo contentInfo; + + public CmsCompressedData( + byte[] compressedData) + : this(CmsUtilities.ReadContentInfo(compressedData)) + { + } + + public CmsCompressedData( + Stream compressedDataStream) + : this(CmsUtilities.ReadContentInfo(compressedDataStream)) + { + } + + public CmsCompressedData( + ContentInfo contentInfo) + { + this.contentInfo = contentInfo; + } + + /** + * Return the uncompressed content. + * + * @return the uncompressed content + * @throws CmsException if there is an exception uncompressing the data. + */ + public byte[] GetContent() + { + CompressedData comData = CompressedData.GetInstance(contentInfo.Content); + ContentInfo content = comData.EncapContentInfo; + + Asn1OctetString bytes = (Asn1OctetString) content.Content; + ZInputStream zIn = new ZInputStream(bytes.GetOctetStream()); + + try + { + return CmsUtilities.StreamToByteArray(zIn); + } + catch (IOException e) + { + throw new CmsException("exception reading compressed stream.", e); + } + finally + { + zIn.Dispose(); + } + } + + /** + * Return the uncompressed content, throwing an exception if the data size + * is greater than the passed in limit. If the content is exceeded getCause() + * on the CMSException will contain a StreamOverflowException + * + * @param limit maximum number of bytes to read + * @return the content read + * @throws CMSException if there is an exception uncompressing the data. + */ + public byte[] GetContent(int limit) + { + CompressedData comData = CompressedData.GetInstance(contentInfo.Content); + ContentInfo content = comData.EncapContentInfo; + + Asn1OctetString bytes = (Asn1OctetString)content.Content; + + ZInputStream zIn = new ZInputStream(new MemoryStream(bytes.GetOctets(), false)); + + try + { + return CmsUtilities.StreamToByteArray(zIn, limit); + } + catch (IOException e) + { + throw new CmsException("exception reading compressed stream.", e); + } + } + + /** + * return the ContentInfo + */ + public ContentInfo ContentInfo + { + get { return contentInfo; } + } + + /** + * return the ASN.1 encoded representation of this object. + */ + public byte[] GetEncoded() + { + return contentInfo.GetEncoded(); + } + } +} diff --git a/Crypto/src/cms/CMSCompressedDataGenerator.cs b/Crypto/src/cms/CMSCompressedDataGenerator.cs new file mode 100644
index 000000000..a6381ced6 --- /dev/null +++ b/Crypto/src/cms/CMSCompressedDataGenerator.cs
@@ -0,0 +1,66 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Utilities.Zlib; + +namespace Org.BouncyCastle.Cms +{ + /** + * General class for generating a compressed CMS message. + * <p> + * A simple example of usage.</p> + * <p> + * <pre> + * CMSCompressedDataGenerator fact = new CMSCompressedDataGenerator(); + * CMSCompressedData data = fact.Generate(content, algorithm); + * </pre> + * </p> + */ + public class CmsCompressedDataGenerator + { + public const string ZLib = "1.2.840.113549.1.9.16.3.8"; + + public CmsCompressedDataGenerator() + { + } + + /** + * Generate an object that contains an CMS Compressed Data + */ + public CmsCompressedData Generate( + CmsProcessable content, + string compressionOid) + { + AlgorithmIdentifier comAlgId; + Asn1OctetString comOcts; + + try + { + MemoryStream bOut = new MemoryStream(); + ZOutputStream zOut = new ZOutputStream(bOut, JZlib.Z_DEFAULT_COMPRESSION); + + content.Write(zOut); + + zOut.Dispose(); + + comAlgId = new AlgorithmIdentifier(new DerObjectIdentifier(compressionOid)); + comOcts = new BerOctetString(bOut.ToArray()); + } + catch (IOException e) + { + throw new CmsException("exception encoding data.", e); + } + + ContentInfo comContent = new ContentInfo(CmsObjectIdentifiers.Data, comOcts); + ContentInfo contentInfo = new ContentInfo( + CmsObjectIdentifiers.CompressedData, + new CompressedData(comAlgId, comContent)); + + return new CmsCompressedData(contentInfo); + } + } +} diff --git a/Crypto/src/cms/CMSCompressedDataParser.cs b/Crypto/src/cms/CMSCompressedDataParser.cs new file mode 100644
index 000000000..93dfa1286 --- /dev/null +++ b/Crypto/src/cms/CMSCompressedDataParser.cs
@@ -0,0 +1,57 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Utilities.Zlib; + +namespace Org.BouncyCastle.Cms +{ + /** + * Class for reading a CMS Compressed Data stream. + * <pre> + * CMSCompressedDataParser cp = new CMSCompressedDataParser(inputStream); + * + * process(cp.GetContent().GetContentStream()); + * </pre> + * Note: this class does not introduce buffering - if you are processing large files you should create + * the parser with: + * <pre> + * CMSCompressedDataParser ep = new CMSCompressedDataParser(new BufferedInputStream(inputStream, bufSize)); + * </pre> + * where bufSize is a suitably large buffer size. + */ + public class CmsCompressedDataParser + : CmsContentInfoParser + { + public CmsCompressedDataParser( + byte[] compressedData) + : this(new MemoryStream(compressedData, false)) + { + } + + public CmsCompressedDataParser( + Stream compressedData) + : base(compressedData) + { + } + + public CmsTypedStream GetContent() + { + try + { + CompressedDataParser comData = new CompressedDataParser((Asn1SequenceParser)this.contentInfo.GetContent(Asn1Tags.Sequence)); + ContentInfoParser content = comData.GetEncapContentInfo(); + + Asn1OctetStringParser bytes = (Asn1OctetStringParser)content.GetContent(Asn1Tags.OctetString); + + return new CmsTypedStream(content.ContentType.ToString(), new ZInputStream(bytes.GetOctetStream())); + } + catch (IOException e) + { + throw new CmsException("IOException reading compressed content.", e); + } + } + } +} diff --git a/Crypto/src/cms/CMSCompressedDataStreamGenerator.cs b/Crypto/src/cms/CMSCompressedDataStreamGenerator.cs new file mode 100644
index 000000000..13006d4b0 --- /dev/null +++ b/Crypto/src/cms/CMSCompressedDataStreamGenerator.cs
@@ -0,0 +1,144 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Utilities.IO; +using Org.BouncyCastle.Utilities.Zlib; + +namespace Org.BouncyCastle.Cms +{ + /** + * General class for generating a compressed CMS message stream. + * <p> + * A simple example of usage. + * </p> + * <pre> + * CMSCompressedDataStreamGenerator gen = new CMSCompressedDataStreamGenerator(); + * + * Stream cOut = gen.Open(outputStream, CMSCompressedDataStreamGenerator.ZLIB); + * + * cOut.Write(data); + * + * cOut.Close(); + * </pre> + */ + public class CmsCompressedDataStreamGenerator + { + public const string ZLib = "1.2.840.113549.1.9.16.3.8"; + + private int _bufferSize; + + /** + * base constructor + */ + public CmsCompressedDataStreamGenerator() + { + } + + /** + * Set the underlying string size for encapsulated data + * + * @param bufferSize length of octet strings to buffer the data. + */ + public void SetBufferSize( + int bufferSize) + { + _bufferSize = bufferSize; + } + + public Stream Open( + Stream outStream, + string compressionOID) + { + return Open(outStream, CmsObjectIdentifiers.Data.Id, compressionOID); + } + + public Stream Open( + Stream outStream, + string contentOID, + string compressionOID) + { + BerSequenceGenerator sGen = new BerSequenceGenerator(outStream); + + sGen.AddObject(CmsObjectIdentifiers.CompressedData); + + // + // Compressed Data + // + BerSequenceGenerator cGen = new BerSequenceGenerator( + sGen.GetRawOutputStream(), 0, true); + + // CMSVersion + cGen.AddObject(new DerInteger(0)); + + // CompressionAlgorithmIdentifier + cGen.AddObject(new AlgorithmIdentifier(new DerObjectIdentifier(ZLib))); + + // + // Encapsulated ContentInfo + // + BerSequenceGenerator eiGen = new BerSequenceGenerator(cGen.GetRawOutputStream()); + + eiGen.AddObject(new DerObjectIdentifier(contentOID)); + + Stream octetStream = CmsUtilities.CreateBerOctetOutputStream( + eiGen.GetRawOutputStream(), 0, true, _bufferSize); + + return new CmsCompressedOutputStream( + new ZOutputStream(octetStream, JZlib.Z_DEFAULT_COMPRESSION), sGen, cGen, eiGen); + } + + private class CmsCompressedOutputStream + : BaseOutputStream + { + private ZOutputStream _out; + private BerSequenceGenerator _sGen; + private BerSequenceGenerator _cGen; + private BerSequenceGenerator _eiGen; + + internal CmsCompressedOutputStream( + ZOutputStream outStream, + BerSequenceGenerator sGen, + BerSequenceGenerator cGen, + BerSequenceGenerator eiGen) + { + _out = outStream; + _sGen = sGen; + _cGen = cGen; + _eiGen = eiGen; + } + + public override void WriteByte( + byte b) + { + _out.WriteByte(b); + } + + public override void Write( + byte[] bytes, + int off, + int len) + { + _out.Write(bytes, off, len); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _out.Dispose(); + + // TODO Parent context(s) should really be be closed explicitly + + _eiGen.Close(); + _cGen.Close(); + _sGen.Close(); + } + + base.Dispose(disposing); + } + } + } +} diff --git a/Crypto/src/cms/CMSContentInfoParser.cs b/Crypto/src/cms/CMSContentInfoParser.cs new file mode 100644
index 000000000..5b1606394 --- /dev/null +++ b/Crypto/src/cms/CMSContentInfoParser.cs
@@ -0,0 +1,47 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; + +namespace Org.BouncyCastle.Cms +{ + public class CmsContentInfoParser + { + protected ContentInfoParser contentInfo; + protected Stream data; + + protected CmsContentInfoParser( + Stream data) + { + if (data == null) + throw new ArgumentNullException("data"); + + this.data = data; + + try + { + Asn1StreamParser inStream = new Asn1StreamParser(data); + + this.contentInfo = new ContentInfoParser((Asn1SequenceParser)inStream.ReadObject()); + } + catch (IOException e) + { + throw new CmsException("IOException reading content.", e); + } + catch (InvalidCastException e) + { + throw new CmsException("Unexpected object reading content.", e); + } + } + + /** + * Close the underlying data stream. + * @throws IOException if the close fails. + */ + public void Close() + { + this.data.Dispose(); + } + } +} diff --git a/Crypto/src/cms/CMSEnvelopedData.cs b/Crypto/src/cms/CMSEnvelopedData.cs new file mode 100644
index 000000000..0731c307e --- /dev/null +++ b/Crypto/src/cms/CMSEnvelopedData.cs
@@ -0,0 +1,115 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Cms +{ + /** + * containing class for an CMS Enveloped Data object + */ + public class CmsEnvelopedData + { + internal RecipientInformationStore recipientInfoStore; + internal ContentInfo contentInfo; + + private AlgorithmIdentifier encAlg; + private Asn1Set unprotectedAttributes; + + public CmsEnvelopedData( + byte[] envelopedData) + : this(CmsUtilities.ReadContentInfo(envelopedData)) + { + } + + public CmsEnvelopedData( + Stream envelopedData) + : this(CmsUtilities.ReadContentInfo(envelopedData)) + { + } + + public CmsEnvelopedData( + ContentInfo contentInfo) + { + this.contentInfo = contentInfo; + + EnvelopedData envData = EnvelopedData.GetInstance(contentInfo.Content); + + // + // read the recipients + // + Asn1Set recipientInfos = envData.RecipientInfos; + + // + // read the encrypted content info + // + EncryptedContentInfo encInfo = envData.EncryptedContentInfo; + this.encAlg = encInfo.ContentEncryptionAlgorithm; + CmsReadable readable = new CmsProcessableByteArray(encInfo.EncryptedContent.GetOctets()); + CmsSecureReadable secureReadable = new CmsEnvelopedHelper.CmsEnvelopedSecureReadable( + this.encAlg, readable); + + // + // build the RecipientInformationStore + // + this.recipientInfoStore = CmsEnvelopedHelper.BuildRecipientInformationStore( + recipientInfos, secureReadable); + + this.unprotectedAttributes = envData.UnprotectedAttrs; + } + + public AlgorithmIdentifier EncryptionAlgorithmID + { + get { return encAlg; } + } + + /** + * return the object identifier for the content encryption algorithm. + */ + public string EncryptionAlgOid + { + get { return encAlg.ObjectID.Id; } + } + + /** + * return a store of the intended recipients for this message + */ + public RecipientInformationStore GetRecipientInfos() + { + return recipientInfoStore; + } + + /** + * return the ContentInfo + */ + public ContentInfo ContentInfo + { + get { return contentInfo; } + } + + /** + * return a table of the unprotected attributes indexed by + * the OID of the attribute. + */ + public Asn1.Cms.AttributeTable GetUnprotectedAttributes() + { + if (unprotectedAttributes == null) + return null; + + return new Asn1.Cms.AttributeTable(unprotectedAttributes); + } + + /** + * return the ASN.1 encoded representation of this object. + */ + public byte[] GetEncoded() + { + return contentInfo.GetEncoded(); + } + } +} diff --git a/Crypto/src/cms/CMSEnvelopedDataGenerator.cs b/Crypto/src/cms/CMSEnvelopedDataGenerator.cs new file mode 100644
index 000000000..5071af4ad --- /dev/null +++ b/Crypto/src/cms/CMSEnvelopedDataGenerator.cs
@@ -0,0 +1,178 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Date; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Cms +{ + /// <remarks> + /// General class for generating a CMS enveloped-data message. + /// + /// A simple example of usage. + /// + /// <pre> + /// CmsEnvelopedDataGenerator fact = new CmsEnvelopedDataGenerator(); + /// + /// fact.AddKeyTransRecipient(cert); + /// + /// CmsEnvelopedData data = fact.Generate(content, algorithm); + /// </pre> + /// </remarks> + public class CmsEnvelopedDataGenerator + : CmsEnvelopedGenerator + { + public CmsEnvelopedDataGenerator() + { + } + + /// <summary>Constructor allowing specific source of randomness</summary> + /// <param name="rand">Instance of <c>SecureRandom</c> to use.</param> + public CmsEnvelopedDataGenerator( + SecureRandom rand) + : base(rand) + { + } + + /// <summary> + /// Generate an enveloped object that contains a CMS Enveloped Data + /// object using the passed in key generator. + /// </summary> + private CmsEnvelopedData Generate( + CmsProcessable content, + string encryptionOid, + CipherKeyGenerator keyGen) + { + AlgorithmIdentifier encAlgId = null; + KeyParameter encKey; + Asn1OctetString encContent; + + try + { + byte[] encKeyBytes = keyGen.GenerateKey(); + encKey = ParameterUtilities.CreateKeyParameter(encryptionOid, encKeyBytes); + + Asn1Encodable asn1Params = GenerateAsn1Parameters(encryptionOid, encKeyBytes); + + ICipherParameters cipherParameters; + encAlgId = GetAlgorithmIdentifier( + encryptionOid, encKey, asn1Params, out cipherParameters); + + IBufferedCipher cipher = CipherUtilities.GetCipher(encryptionOid); + cipher.Init(true, new ParametersWithRandom(cipherParameters, rand)); + + MemoryStream bOut = new MemoryStream(); + CipherStream cOut = new CipherStream(bOut, null, cipher); + + content.Write(cOut); + + cOut.Dispose(); + + encContent = new BerOctetString(bOut.ToArray()); + } + catch (SecurityUtilityException e) + { + throw new CmsException("couldn't create cipher.", e); + } + catch (InvalidKeyException e) + { + throw new CmsException("key invalid in message.", e); + } + catch (IOException e) + { + throw new CmsException("exception decoding algorithm parameters.", e); + } + + + Asn1EncodableVector recipientInfos = new Asn1EncodableVector(); + + foreach (RecipientInfoGenerator rig in recipientInfoGenerators) + { + try + { + recipientInfos.Add(rig.Generate(encKey, rand)); + } + catch (InvalidKeyException e) + { + throw new CmsException("key inappropriate for algorithm.", e); + } + catch (GeneralSecurityException e) + { + throw new CmsException("error making encrypted content.", e); + } + } + + EncryptedContentInfo eci = new EncryptedContentInfo( + CmsObjectIdentifiers.Data, + encAlgId, + encContent); + + Asn1Set unprotectedAttrSet = null; + if (unprotectedAttributeGenerator != null) + { + Asn1.Cms.AttributeTable attrTable = unprotectedAttributeGenerator.GetAttributes(Platform.CreateHashtable()); + + unprotectedAttrSet = new BerSet(attrTable.ToAsn1EncodableVector()); + } + + ContentInfo contentInfo = new ContentInfo( + CmsObjectIdentifiers.EnvelopedData, + new EnvelopedData(null, new DerSet(recipientInfos), eci, unprotectedAttrSet)); + + return new CmsEnvelopedData(contentInfo); + } + + /// <summary>Generate an enveloped object that contains an CMS Enveloped Data object.</summary> + public CmsEnvelopedData Generate( + CmsProcessable content, + string encryptionOid) + { + try + { + CipherKeyGenerator keyGen = GeneratorUtilities.GetKeyGenerator(encryptionOid); + + keyGen.Init(new KeyGenerationParameters(rand, keyGen.DefaultStrength)); + + return Generate(content, encryptionOid, keyGen); + } + catch (SecurityUtilityException e) + { + throw new CmsException("can't find key generation algorithm.", e); + } + } + + /// <summary>Generate an enveloped object that contains an CMS Enveloped Data object.</summary> + public CmsEnvelopedData Generate( + CmsProcessable content, + string encryptionOid, + int keySize) + { + try + { + CipherKeyGenerator keyGen = GeneratorUtilities.GetKeyGenerator(encryptionOid); + + keyGen.Init(new KeyGenerationParameters(rand, keySize)); + + return Generate(content, encryptionOid, keyGen); + } + catch (SecurityUtilityException e) + { + throw new CmsException("can't find key generation algorithm.", e); + } + } + } +} diff --git a/Crypto/src/cms/CMSEnvelopedDataParser.cs b/Crypto/src/cms/CMSEnvelopedDataParser.cs new file mode 100644
index 000000000..01a949d47 --- /dev/null +++ b/Crypto/src/cms/CMSEnvelopedDataParser.cs
@@ -0,0 +1,161 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Cms +{ + /** + * Parsing class for an CMS Enveloped Data object from an input stream. + * <p> + * Note: that because we are in a streaming mode only one recipient can be tried and it is important + * that the methods on the parser are called in the appropriate order. + * </p> + * <p> + * Example of use - assuming the first recipient matches the private key we have. + * <pre> + * CmsEnvelopedDataParser ep = new CmsEnvelopedDataParser(inputStream); + * + * RecipientInformationStore recipients = ep.GetRecipientInfos(); + * + * Collection c = recipients.getRecipients(); + * Iterator it = c.iterator(); + * + * if (it.hasNext()) + * { + * RecipientInformation recipient = (RecipientInformation)it.next(); + * + * CMSTypedStream recData = recipient.getContentStream(privateKey); + * + * processDataStream(recData.getContentStream()); + * } + * </pre> + * Note: this class does not introduce buffering - if you are processing large files you should create + * the parser with: + * <pre> + * CmsEnvelopedDataParser ep = new CmsEnvelopedDataParser(new BufferedInputStream(inputStream, bufSize)); + * </pre> + * where bufSize is a suitably large buffer size. + * </p> + */ + public class CmsEnvelopedDataParser + : CmsContentInfoParser + { + internal RecipientInformationStore recipientInfoStore; + internal EnvelopedDataParser envelopedData; + + private AlgorithmIdentifier _encAlg; + private Asn1.Cms.AttributeTable _unprotectedAttributes; + private bool _attrNotRead; + + public CmsEnvelopedDataParser( + byte[] envelopedData) + : this(new MemoryStream(envelopedData, false)) + { + } + + public CmsEnvelopedDataParser( + Stream envelopedData) + : base(envelopedData) + { + this._attrNotRead = true; + this.envelopedData = new EnvelopedDataParser( + (Asn1SequenceParser)this.contentInfo.GetContent(Asn1Tags.Sequence)); + + // TODO Validate version? + //DerInteger version = this.envelopedData.Version; + + // + // read the recipients + // + Asn1Set recipientInfos = Asn1Set.GetInstance(this.envelopedData.GetRecipientInfos().ToAsn1Object()); + + // + // read the encrypted content info + // + EncryptedContentInfoParser encInfo = this.envelopedData.GetEncryptedContentInfo(); + this._encAlg = encInfo.ContentEncryptionAlgorithm; + CmsReadable readable = new CmsProcessableInputStream( + ((Asn1OctetStringParser)encInfo.GetEncryptedContent(Asn1Tags.OctetString)).GetOctetStream()); + CmsSecureReadable secureReadable = new CmsEnvelopedHelper.CmsEnvelopedSecureReadable( + this._encAlg, readable); + + // + // build the RecipientInformationStore + // + this.recipientInfoStore = CmsEnvelopedHelper.BuildRecipientInformationStore( + recipientInfos, secureReadable); + } + + public AlgorithmIdentifier EncryptionAlgorithmID + { + get { return _encAlg; } + } + + /** + * return the object identifier for the content encryption algorithm. + */ + public string EncryptionAlgOid + { + get { return _encAlg.ObjectID.Id; } + } + + /** + * return the ASN.1 encoded encryption algorithm parameters, or null if + * there aren't any. + */ + public Asn1Object EncryptionAlgParams + { + get + { + Asn1Encodable ae = _encAlg.Parameters; + + return ae == null ? null : ae.ToAsn1Object(); + } + } + + /** + * return a store of the intended recipients for this message + */ + public RecipientInformationStore GetRecipientInfos() + { + return this.recipientInfoStore; + } + + /** + * return a table of the unprotected attributes indexed by + * the OID of the attribute. + * @throws IOException + */ + public Asn1.Cms.AttributeTable GetUnprotectedAttributes() + { + if (_unprotectedAttributes == null && _attrNotRead) + { + Asn1SetParser asn1Set = this.envelopedData.GetUnprotectedAttrs(); + + _attrNotRead = false; + + if (asn1Set != null) + { + Asn1EncodableVector v = new Asn1EncodableVector(); + IAsn1Convertible o; + + while ((o = asn1Set.ReadObject()) != null) + { + Asn1SequenceParser seq = (Asn1SequenceParser)o; + + v.Add(seq.ToAsn1Object()); + } + + _unprotectedAttributes = new Asn1.Cms.AttributeTable(new DerSet(v)); + } + } + + return _unprotectedAttributes; + } + } +} diff --git a/Crypto/src/cms/CMSEnvelopedDataStreamGenerator.cs b/Crypto/src/cms/CMSEnvelopedDataStreamGenerator.cs new file mode 100644
index 000000000..820ddfe16 --- /dev/null +++ b/Crypto/src/cms/CMSEnvelopedDataStreamGenerator.cs
@@ -0,0 +1,285 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Cms +{ + /** + * General class for generating a CMS enveloped-data message stream. + * <p> + * A simple example of usage. + * <pre> + * CmsEnvelopedDataStreamGenerator edGen = new CmsEnvelopedDataStreamGenerator(); + * + * edGen.AddKeyTransRecipient(cert); + * + * MemoryStream bOut = new MemoryStream(); + * + * Stream out = edGen.Open( + * bOut, CMSEnvelopedDataGenerator.AES128_CBC);* + * out.Write(data); + * + * out.Close(); + * </pre> + * </p> + */ + public class CmsEnvelopedDataStreamGenerator + : CmsEnvelopedGenerator + { + private object _originatorInfo = null; + private object _unprotectedAttributes = null; + private int _bufferSize; + private bool _berEncodeRecipientSet; + + public CmsEnvelopedDataStreamGenerator() + { + } + + /// <summary>Constructor allowing specific source of randomness</summary> + /// <param name="rand">Instance of <c>SecureRandom</c> to use.</param> + public CmsEnvelopedDataStreamGenerator( + SecureRandom rand) + : base(rand) + { + } + + /// <summary>Set the underlying string size for encapsulated data.</summary> + /// <param name="bufferSize">Length of octet strings to buffer the data.</param> + public void SetBufferSize( + int bufferSize) + { + _bufferSize = bufferSize; + } + + /// <summary>Use a BER Set to store the recipient information.</summary> + public void SetBerEncodeRecipients( + bool berEncodeRecipientSet) + { + _berEncodeRecipientSet = berEncodeRecipientSet; + } + + private DerInteger Version + { + get + { + int version = (_originatorInfo != null || _unprotectedAttributes != null) + ? 2 + : 0; + + return new DerInteger(version); + } + } + + /// <summary> + /// Generate an enveloped object that contains an CMS Enveloped Data + /// object using the passed in key generator. + /// </summary> + private Stream Open( + Stream outStream, + string encryptionOid, + CipherKeyGenerator keyGen) + { + byte[] encKeyBytes = keyGen.GenerateKey(); + KeyParameter encKey = ParameterUtilities.CreateKeyParameter(encryptionOid, encKeyBytes); + + Asn1Encodable asn1Params = GenerateAsn1Parameters(encryptionOid, encKeyBytes); + + ICipherParameters cipherParameters; + AlgorithmIdentifier encAlgID = GetAlgorithmIdentifier( + encryptionOid, encKey, asn1Params, out cipherParameters); + + Asn1EncodableVector recipientInfos = new Asn1EncodableVector(); + + foreach (RecipientInfoGenerator rig in recipientInfoGenerators) + { + try + { + recipientInfos.Add(rig.Generate(encKey, rand)); + } + catch (InvalidKeyException e) + { + throw new CmsException("key inappropriate for algorithm.", e); + } + catch (GeneralSecurityException e) + { + throw new CmsException("error making encrypted content.", e); + } + } + + return Open(outStream, encAlgID, cipherParameters, recipientInfos); + } + + private Stream Open( + Stream outStream, + AlgorithmIdentifier encAlgID, + ICipherParameters cipherParameters, + Asn1EncodableVector recipientInfos) + { + try + { + // + // ContentInfo + // + BerSequenceGenerator cGen = new BerSequenceGenerator(outStream); + + cGen.AddObject(CmsObjectIdentifiers.EnvelopedData); + + // + // Encrypted Data + // + BerSequenceGenerator envGen = new BerSequenceGenerator( + cGen.GetRawOutputStream(), 0, true); + + envGen.AddObject(this.Version); + + Stream envRaw = envGen.GetRawOutputStream(); + Asn1Generator recipGen = _berEncodeRecipientSet + ? (Asn1Generator) new BerSetGenerator(envRaw) + : new DerSetGenerator(envRaw); + + foreach (Asn1Encodable ae in recipientInfos) + { + recipGen.AddObject(ae); + } + + recipGen.Close(); + + BerSequenceGenerator eiGen = new BerSequenceGenerator(envRaw); + eiGen.AddObject(CmsObjectIdentifiers.Data); + eiGen.AddObject(encAlgID); + + Stream octetOutputStream = CmsUtilities.CreateBerOctetOutputStream( + eiGen.GetRawOutputStream(), 0, false, _bufferSize); + + IBufferedCipher cipher = CipherUtilities.GetCipher(encAlgID.ObjectID); + cipher.Init(true, new ParametersWithRandom(cipherParameters, rand)); + CipherStream cOut = new CipherStream(octetOutputStream, null, cipher); + + return new CmsEnvelopedDataOutputStream(this, cOut, cGen, envGen, eiGen); + } + catch (SecurityUtilityException e) + { + throw new CmsException("couldn't create cipher.", e); + } + catch (InvalidKeyException e) + { + throw new CmsException("key invalid in message.", e); + } + catch (IOException e) + { + throw new CmsException("exception decoding algorithm parameters.", e); + } + } + + /** + * generate an enveloped object that contains an CMS Enveloped Data object + * @throws IOException + */ + public Stream Open( + Stream outStream, + string encryptionOid) + { + CipherKeyGenerator keyGen = GeneratorUtilities.GetKeyGenerator(encryptionOid); + + keyGen.Init(new KeyGenerationParameters(rand, keyGen.DefaultStrength)); + + return Open(outStream, encryptionOid, keyGen); + } + + /** + * generate an enveloped object that contains an CMS Enveloped Data object + * @throws IOException + */ + public Stream Open( + Stream outStream, + string encryptionOid, + int keySize) + { + CipherKeyGenerator keyGen = GeneratorUtilities.GetKeyGenerator(encryptionOid); + + keyGen.Init(new KeyGenerationParameters(rand, keySize)); + + return Open(outStream, encryptionOid, keyGen); + } + + private class CmsEnvelopedDataOutputStream + : BaseOutputStream + { + private readonly CmsEnvelopedGenerator _outer; + + private readonly CipherStream _out; + private readonly BerSequenceGenerator _cGen; + private readonly BerSequenceGenerator _envGen; + private readonly BerSequenceGenerator _eiGen; + + public CmsEnvelopedDataOutputStream( + CmsEnvelopedGenerator outer, + CipherStream outStream, + BerSequenceGenerator cGen, + BerSequenceGenerator envGen, + BerSequenceGenerator eiGen) + { + _outer = outer; + _out = outStream; + _cGen = cGen; + _envGen = envGen; + _eiGen = eiGen; + } + + public override void WriteByte( + byte b) + { + _out.WriteByte(b); + } + + public override void Write( + byte[] bytes, + int off, + int len) + { + _out.Write(bytes, off, len); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _out.Dispose(); + + // TODO Parent context(s) should really be be closed explicitly + + _eiGen.Close(); + + if (_outer.unprotectedAttributeGenerator != null) + { + Asn1.Cms.AttributeTable attrTable = _outer.unprotectedAttributeGenerator.GetAttributes(Platform.CreateHashtable()); + + Asn1Set unprotectedAttrs = new BerSet(attrTable.ToAsn1EncodableVector()); + + _envGen.AddObject(new DerTaggedObject(false, 1, unprotectedAttrs)); + } + + _envGen.Close(); + _cGen.Close(); + } + + base.Dispose(disposing); + } + } + } +} diff --git a/Crypto/src/cms/CMSEnvelopedGenerator.cs b/Crypto/src/cms/CMSEnvelopedGenerator.cs new file mode 100644
index 000000000..f92ae3824 --- /dev/null +++ b/Crypto/src/cms/CMSEnvelopedGenerator.cs
@@ -0,0 +1,331 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.Kisa; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Ntt; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Cms +{ + /** + * General class for generating a CMS enveloped-data message. + * + * A simple example of usage. + * + * <pre> + * CMSEnvelopedDataGenerator fact = new CMSEnvelopedDataGenerator(); + * + * fact.addKeyTransRecipient(cert); + * + * CMSEnvelopedData data = fact.generate(content, algorithm, "BC"); + * </pre> + */ + public class CmsEnvelopedGenerator + { + // Note: These tables are complementary: If rc2Table[i]==j, then rc2Ekb[j]==i + internal static readonly short[] rc2Table = + { + 0xbd, 0x56, 0xea, 0xf2, 0xa2, 0xf1, 0xac, 0x2a, 0xb0, 0x93, 0xd1, 0x9c, 0x1b, 0x33, 0xfd, 0xd0, + 0x30, 0x04, 0xb6, 0xdc, 0x7d, 0xdf, 0x32, 0x4b, 0xf7, 0xcb, 0x45, 0x9b, 0x31, 0xbb, 0x21, 0x5a, + 0x41, 0x9f, 0xe1, 0xd9, 0x4a, 0x4d, 0x9e, 0xda, 0xa0, 0x68, 0x2c, 0xc3, 0x27, 0x5f, 0x80, 0x36, + 0x3e, 0xee, 0xfb, 0x95, 0x1a, 0xfe, 0xce, 0xa8, 0x34, 0xa9, 0x13, 0xf0, 0xa6, 0x3f, 0xd8, 0x0c, + 0x78, 0x24, 0xaf, 0x23, 0x52, 0xc1, 0x67, 0x17, 0xf5, 0x66, 0x90, 0xe7, 0xe8, 0x07, 0xb8, 0x60, + 0x48, 0xe6, 0x1e, 0x53, 0xf3, 0x92, 0xa4, 0x72, 0x8c, 0x08, 0x15, 0x6e, 0x86, 0x00, 0x84, 0xfa, + 0xf4, 0x7f, 0x8a, 0x42, 0x19, 0xf6, 0xdb, 0xcd, 0x14, 0x8d, 0x50, 0x12, 0xba, 0x3c, 0x06, 0x4e, + 0xec, 0xb3, 0x35, 0x11, 0xa1, 0x88, 0x8e, 0x2b, 0x94, 0x99, 0xb7, 0x71, 0x74, 0xd3, 0xe4, 0xbf, + 0x3a, 0xde, 0x96, 0x0e, 0xbc, 0x0a, 0xed, 0x77, 0xfc, 0x37, 0x6b, 0x03, 0x79, 0x89, 0x62, 0xc6, + 0xd7, 0xc0, 0xd2, 0x7c, 0x6a, 0x8b, 0x22, 0xa3, 0x5b, 0x05, 0x5d, 0x02, 0x75, 0xd5, 0x61, 0xe3, + 0x18, 0x8f, 0x55, 0x51, 0xad, 0x1f, 0x0b, 0x5e, 0x85, 0xe5, 0xc2, 0x57, 0x63, 0xca, 0x3d, 0x6c, + 0xb4, 0xc5, 0xcc, 0x70, 0xb2, 0x91, 0x59, 0x0d, 0x47, 0x20, 0xc8, 0x4f, 0x58, 0xe0, 0x01, 0xe2, + 0x16, 0x38, 0xc4, 0x6f, 0x3b, 0x0f, 0x65, 0x46, 0xbe, 0x7e, 0x2d, 0x7b, 0x82, 0xf9, 0x40, 0xb5, + 0x1d, 0x73, 0xf8, 0xeb, 0x26, 0xc7, 0x87, 0x97, 0x25, 0x54, 0xb1, 0x28, 0xaa, 0x98, 0x9d, 0xa5, + 0x64, 0x6d, 0x7a, 0xd4, 0x10, 0x81, 0x44, 0xef, 0x49, 0xd6, 0xae, 0x2e, 0xdd, 0x76, 0x5c, 0x2f, + 0xa7, 0x1c, 0xc9, 0x09, 0x69, 0x9a, 0x83, 0xcf, 0x29, 0x39, 0xb9, 0xe9, 0x4c, 0xff, 0x43, 0xab + }; + +// internal static readonly short[] rc2Ekb = +// { +// 0x5d, 0xbe, 0x9b, 0x8b, 0x11, 0x99, 0x6e, 0x4d, 0x59, 0xf3, 0x85, 0xa6, 0x3f, 0xb7, 0x83, 0xc5, +// 0xe4, 0x73, 0x6b, 0x3a, 0x68, 0x5a, 0xc0, 0x47, 0xa0, 0x64, 0x34, 0x0c, 0xf1, 0xd0, 0x52, 0xa5, +// 0xb9, 0x1e, 0x96, 0x43, 0x41, 0xd8, 0xd4, 0x2c, 0xdb, 0xf8, 0x07, 0x77, 0x2a, 0xca, 0xeb, 0xef, +// 0x10, 0x1c, 0x16, 0x0d, 0x38, 0x72, 0x2f, 0x89, 0xc1, 0xf9, 0x80, 0xc4, 0x6d, 0xae, 0x30, 0x3d, +// 0xce, 0x20, 0x63, 0xfe, 0xe6, 0x1a, 0xc7, 0xb8, 0x50, 0xe8, 0x24, 0x17, 0xfc, 0x25, 0x6f, 0xbb, +// 0x6a, 0xa3, 0x44, 0x53, 0xd9, 0xa2, 0x01, 0xab, 0xbc, 0xb6, 0x1f, 0x98, 0xee, 0x9a, 0xa7, 0x2d, +// 0x4f, 0x9e, 0x8e, 0xac, 0xe0, 0xc6, 0x49, 0x46, 0x29, 0xf4, 0x94, 0x8a, 0xaf, 0xe1, 0x5b, 0xc3, +// 0xb3, 0x7b, 0x57, 0xd1, 0x7c, 0x9c, 0xed, 0x87, 0x40, 0x8c, 0xe2, 0xcb, 0x93, 0x14, 0xc9, 0x61, +// 0x2e, 0xe5, 0xcc, 0xf6, 0x5e, 0xa8, 0x5c, 0xd6, 0x75, 0x8d, 0x62, 0x95, 0x58, 0x69, 0x76, 0xa1, +// 0x4a, 0xb5, 0x55, 0x09, 0x78, 0x33, 0x82, 0xd7, 0xdd, 0x79, 0xf5, 0x1b, 0x0b, 0xde, 0x26, 0x21, +// 0x28, 0x74, 0x04, 0x97, 0x56, 0xdf, 0x3c, 0xf0, 0x37, 0x39, 0xdc, 0xff, 0x06, 0xa4, 0xea, 0x42, +// 0x08, 0xda, 0xb4, 0x71, 0xb0, 0xcf, 0x12, 0x7a, 0x4e, 0xfa, 0x6c, 0x1d, 0x84, 0x00, 0xc8, 0x7f, +// 0x91, 0x45, 0xaa, 0x2b, 0xc2, 0xb1, 0x8f, 0xd5, 0xba, 0xf2, 0xad, 0x19, 0xb2, 0x67, 0x36, 0xf7, +// 0x0f, 0x0a, 0x92, 0x7d, 0xe3, 0x9d, 0xe9, 0x90, 0x3e, 0x23, 0x27, 0x66, 0x13, 0xec, 0x81, 0x15, +// 0xbd, 0x22, 0xbf, 0x9f, 0x7e, 0xa9, 0x51, 0x4b, 0x4c, 0xfb, 0x02, 0xd3, 0x70, 0x86, 0x31, 0xe7, +// 0x3b, 0x05, 0x03, 0x54, 0x60, 0x48, 0x65, 0x18, 0xd2, 0xcd, 0x5f, 0x32, 0x88, 0x0e, 0x35, 0xfd +// }; + + + // TODO Create named constants for all of these + public static readonly string DesEde3Cbc = PkcsObjectIdentifiers.DesEde3Cbc.Id; + public static readonly string RC2Cbc = PkcsObjectIdentifiers.RC2Cbc.Id; + public const string IdeaCbc = "1.3.6.1.4.1.188.7.1.1.2"; + public const string Cast5Cbc = "1.2.840.113533.7.66.10"; + public static readonly string Aes128Cbc = NistObjectIdentifiers.IdAes128Cbc.Id; + public static readonly string Aes192Cbc = NistObjectIdentifiers.IdAes192Cbc.Id; + public static readonly string Aes256Cbc = NistObjectIdentifiers.IdAes256Cbc.Id; + public static readonly string Camellia128Cbc = NttObjectIdentifiers.IdCamellia128Cbc.Id; + public static readonly string Camellia192Cbc = NttObjectIdentifiers.IdCamellia192Cbc.Id; + public static readonly string Camellia256Cbc = NttObjectIdentifiers.IdCamellia256Cbc.Id; + public static readonly string SeedCbc = KisaObjectIdentifiers.IdSeedCbc.Id; + + public static readonly string DesEde3Wrap = PkcsObjectIdentifiers.IdAlgCms3DesWrap.Id; + public static readonly string Aes128Wrap = NistObjectIdentifiers.IdAes128Wrap.Id; + public static readonly string Aes192Wrap = NistObjectIdentifiers.IdAes192Wrap.Id; + public static readonly string Aes256Wrap = NistObjectIdentifiers.IdAes256Wrap.Id; + public static readonly string Camellia128Wrap = NttObjectIdentifiers.IdCamellia128Wrap.Id; + public static readonly string Camellia192Wrap = NttObjectIdentifiers.IdCamellia192Wrap.Id; + public static readonly string Camellia256Wrap = NttObjectIdentifiers.IdCamellia256Wrap.Id; + public static readonly string SeedWrap = KisaObjectIdentifiers.IdNpkiAppCmsSeedWrap.Id; + + public static readonly string ECDHSha1Kdf = X9ObjectIdentifiers.DHSinglePassStdDHSha1KdfScheme.Id; + public static readonly string ECMqvSha1Kdf = X9ObjectIdentifiers.MqvSinglePassSha1KdfScheme.Id; + + internal readonly IList recipientInfoGenerators = Platform.CreateArrayList(); + internal readonly SecureRandom rand; + + internal CmsAttributeTableGenerator unprotectedAttributeGenerator = null; + + public CmsEnvelopedGenerator() + : this(new SecureRandom()) + { + } + + /// <summary>Constructor allowing specific source of randomness</summary> + /// <param name="rand">Instance of <c>SecureRandom</c> to use.</param> + public CmsEnvelopedGenerator( + SecureRandom rand) + { + this.rand = rand; + } + + public CmsAttributeTableGenerator UnprotectedAttributeGenerator + { + get { return this.unprotectedAttributeGenerator; } + set { this.unprotectedAttributeGenerator = value; } + } + + /** + * add a recipient. + * + * @param cert recipient's public key certificate + * @exception ArgumentException if there is a problem with the certificate + */ + public void AddKeyTransRecipient( + X509Certificate cert) + { + KeyTransRecipientInfoGenerator ktrig = new KeyTransRecipientInfoGenerator(); + ktrig.RecipientCert = cert; + + recipientInfoGenerators.Add(ktrig); + } + + /** + * add a recipient + * + * @param key the public key used by the recipient + * @param subKeyId the identifier for the recipient's public key + * @exception ArgumentException if there is a problem with the key + */ + public void AddKeyTransRecipient( + AsymmetricKeyParameter pubKey, + byte[] subKeyId) + { + KeyTransRecipientInfoGenerator ktrig = new KeyTransRecipientInfoGenerator(); + ktrig.RecipientPublicKey = pubKey; + ktrig.SubjectKeyIdentifier = new DerOctetString(subKeyId); + + recipientInfoGenerators.Add(ktrig); + } + + /** + * add a KEK recipient. + * @param key the secret key to use for wrapping + * @param keyIdentifier the byte string that identifies the key + */ + public void AddKekRecipient( + string keyAlgorithm, // TODO Remove need for this parameter + KeyParameter key, + byte[] keyIdentifier) + { + AddKekRecipient(keyAlgorithm, key, new KekIdentifier(keyIdentifier, null, null)); + } + + /** + * add a KEK recipient. + * @param key the secret key to use for wrapping + * @param keyIdentifier the byte string that identifies the key + */ + public void AddKekRecipient( + string keyAlgorithm, // TODO Remove need for this parameter + KeyParameter key, + KekIdentifier kekIdentifier) + { + KekRecipientInfoGenerator kekrig = new KekRecipientInfoGenerator(); + kekrig.KekIdentifier = kekIdentifier; + kekrig.KeyEncryptionKeyOID = keyAlgorithm; + kekrig.KeyEncryptionKey = key; + + recipientInfoGenerators.Add(kekrig); + } + + public void AddPasswordRecipient( + CmsPbeKey pbeKey, + string kekAlgorithmOid) + { + Pbkdf2Params p = new Pbkdf2Params(pbeKey.Salt, pbeKey.IterationCount); + + PasswordRecipientInfoGenerator prig = new PasswordRecipientInfoGenerator(); + prig.KeyDerivationAlgorithm = new AlgorithmIdentifier(PkcsObjectIdentifiers.IdPbkdf2, p); + prig.KeyEncryptionKeyOID = kekAlgorithmOid; + prig.KeyEncryptionKey = pbeKey.GetEncoded(kekAlgorithmOid); + + recipientInfoGenerators.Add(prig); + } + + /** + * Add a key agreement based recipient. + * + * @param agreementAlgorithm key agreement algorithm to use. + * @param senderPrivateKey private key to initialise sender side of agreement with. + * @param senderPublicKey sender public key to include with message. + * @param recipientCert recipient's public key certificate. + * @param cekWrapAlgorithm OID for key wrapping algorithm to use. + * @exception SecurityUtilityException if the algorithm requested cannot be found + * @exception InvalidKeyException if the keys are inappropriate for the algorithm specified + */ + public void AddKeyAgreementRecipient( + string agreementAlgorithm, + AsymmetricKeyParameter senderPrivateKey, + AsymmetricKeyParameter senderPublicKey, + X509Certificate recipientCert, + string cekWrapAlgorithm) + { + IList recipientCerts = Platform.CreateArrayList(1); + recipientCerts.Add(recipientCert); + + AddKeyAgreementRecipients(agreementAlgorithm, senderPrivateKey, senderPublicKey, + recipientCerts, cekWrapAlgorithm); + } + + /** + * Add multiple key agreement based recipients (sharing a single KeyAgreeRecipientInfo structure). + * + * @param agreementAlgorithm key agreement algorithm to use. + * @param senderPrivateKey private key to initialise sender side of agreement with. + * @param senderPublicKey sender public key to include with message. + * @param recipientCerts recipients' public key certificates. + * @param cekWrapAlgorithm OID for key wrapping algorithm to use. + * @exception SecurityUtilityException if the algorithm requested cannot be found + * @exception InvalidKeyException if the keys are inappropriate for the algorithm specified + */ + public void AddKeyAgreementRecipients( + string agreementAlgorithm, + AsymmetricKeyParameter senderPrivateKey, + AsymmetricKeyParameter senderPublicKey, + ICollection recipientCerts, + string cekWrapAlgorithm) + { + if (!senderPrivateKey.IsPrivate) + throw new ArgumentException("Expected private key", "senderPrivateKey"); + if (senderPublicKey.IsPrivate) + throw new ArgumentException("Expected public key", "senderPublicKey"); + + /* TODO + * "a recipient X.509 version 3 certificate that contains a key usage extension MUST + * assert the keyAgreement bit." + */ + + KeyAgreeRecipientInfoGenerator karig = new KeyAgreeRecipientInfoGenerator(); + karig.KeyAgreementOID = new DerObjectIdentifier(agreementAlgorithm); + karig.KeyEncryptionOID = new DerObjectIdentifier(cekWrapAlgorithm); + karig.RecipientCerts = recipientCerts; + karig.SenderKeyPair = new AsymmetricCipherKeyPair(senderPublicKey, senderPrivateKey); + + recipientInfoGenerators.Add(karig); + } + + protected internal virtual AlgorithmIdentifier GetAlgorithmIdentifier( + string encryptionOid, + KeyParameter encKey, + Asn1Encodable asn1Params, + out ICipherParameters cipherParameters) + { + Asn1Object asn1Object; + if (asn1Params != null) + { + asn1Object = asn1Params.ToAsn1Object(); + cipherParameters = ParameterUtilities.GetCipherParameters( + encryptionOid, encKey, asn1Object); + } + else + { + asn1Object = DerNull.Instance; + cipherParameters = encKey; + } + + return new AlgorithmIdentifier( + new DerObjectIdentifier(encryptionOid), + asn1Object); + } + + protected internal virtual Asn1Encodable GenerateAsn1Parameters( + string encryptionOid, + byte[] encKeyBytes) + { + Asn1Encodable asn1Params = null; + + try + { + if (encryptionOid.Equals(RC2Cbc)) + { + byte[] iv = new byte[8]; + rand.NextBytes(iv); + + // TODO Is this detailed repeat of Java version really necessary? + int effKeyBits = encKeyBytes.Length * 8; + int parameterVersion; + + if (effKeyBits < 256) + { + parameterVersion = rc2Table[effKeyBits]; + } + else + { + parameterVersion = effKeyBits; + } + + asn1Params = new RC2CbcParameter(parameterVersion, iv); + } + else + { + asn1Params = ParameterUtilities.GenerateParameters(encryptionOid, rand); + } + } + catch (SecurityUtilityException) + { + // No problem... no parameters generated + } + + return asn1Params; + } + } +} diff --git a/Crypto/src/cms/CMSEnvelopedHelper.cs b/Crypto/src/cms/CMSEnvelopedHelper.cs new file mode 100644
index 000000000..fe2b14cd9 --- /dev/null +++ b/Crypto/src/cms/CMSEnvelopedHelper.cs
@@ -0,0 +1,311 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Cms +{ + class CmsEnvelopedHelper + { + internal static readonly CmsEnvelopedHelper Instance = new CmsEnvelopedHelper(); + + private static readonly IDictionary KeySizes = Platform.CreateHashtable(); + private static readonly IDictionary BaseCipherNames = Platform.CreateHashtable(); + + static CmsEnvelopedHelper() + { + KeySizes.Add(CmsEnvelopedGenerator.DesEde3Cbc, 192); + KeySizes.Add(CmsEnvelopedGenerator.Aes128Cbc, 128); + KeySizes.Add(CmsEnvelopedGenerator.Aes192Cbc, 192); + KeySizes.Add(CmsEnvelopedGenerator.Aes256Cbc, 256); + + BaseCipherNames.Add(CmsEnvelopedGenerator.DesEde3Cbc, "DESEDE"); + BaseCipherNames.Add(CmsEnvelopedGenerator.Aes128Cbc, "AES"); + BaseCipherNames.Add(CmsEnvelopedGenerator.Aes192Cbc, "AES"); + BaseCipherNames.Add(CmsEnvelopedGenerator.Aes256Cbc, "AES"); + } + + private string GetAsymmetricEncryptionAlgName( + string encryptionAlgOid) + { + if (Asn1.Pkcs.PkcsObjectIdentifiers.RsaEncryption.Id.Equals(encryptionAlgOid)) + { + return "RSA/ECB/PKCS1Padding"; + } + + return encryptionAlgOid; + } + + internal IBufferedCipher CreateAsymmetricCipher( + string encryptionOid) + { + string asymName = GetAsymmetricEncryptionAlgName(encryptionOid); + if (!asymName.Equals(encryptionOid)) + { + try + { + return CipherUtilities.GetCipher(asymName); + } + catch (SecurityUtilityException) + { + // Ignore + } + } + return CipherUtilities.GetCipher(encryptionOid); + } + + internal IWrapper CreateWrapper( + string encryptionOid) + { + try + { + return WrapperUtilities.GetWrapper(encryptionOid); + } + catch (SecurityUtilityException) + { + return WrapperUtilities.GetWrapper(GetAsymmetricEncryptionAlgName(encryptionOid)); + } + } + + internal string GetRfc3211WrapperName( + string oid) + { + if (oid == null) + throw new ArgumentNullException("oid"); + + string alg = (string) BaseCipherNames[oid]; + + if (alg == null) + throw new ArgumentException("no name for " + oid, "oid"); + + return alg + "RFC3211Wrap"; + } + + internal int GetKeySize( + string oid) + { + if (!KeySizes.Contains(oid)) + { + throw new ArgumentException("no keysize for " + oid, "oid"); + } + + return (int) KeySizes[oid]; + } + + internal static RecipientInformationStore BuildRecipientInformationStore( + Asn1Set recipientInfos, CmsSecureReadable secureReadable) + { + IList infos = Platform.CreateArrayList(); + for (int i = 0; i != recipientInfos.Count; i++) + { + RecipientInfo info = RecipientInfo.GetInstance(recipientInfos[i]); + + ReadRecipientInfo(infos, info, secureReadable); + } + return new RecipientInformationStore(infos); + } + + private static void ReadRecipientInfo( + IList infos, RecipientInfo info, CmsSecureReadable secureReadable) + { + Asn1Encodable recipInfo = info.Info; + if (recipInfo is KeyTransRecipientInfo) + { + infos.Add(new KeyTransRecipientInformation((KeyTransRecipientInfo)recipInfo, secureReadable)); + } + else if (recipInfo is KekRecipientInfo) + { + infos.Add(new KekRecipientInformation((KekRecipientInfo)recipInfo, secureReadable)); + } + else if (recipInfo is KeyAgreeRecipientInfo) + { + KeyAgreeRecipientInformation.ReadRecipientInfo(infos, (KeyAgreeRecipientInfo)recipInfo, secureReadable); + } + else if (recipInfo is PasswordRecipientInfo) + { + infos.Add(new PasswordRecipientInformation((PasswordRecipientInfo)recipInfo, secureReadable)); + } + } + + internal class CmsAuthenticatedSecureReadable : CmsSecureReadable + { + private AlgorithmIdentifier algorithm; + private IMac mac; + private CmsReadable readable; + + internal CmsAuthenticatedSecureReadable(AlgorithmIdentifier algorithm, CmsReadable readable) + { + this.algorithm = algorithm; + this.readable = readable; + } + + public AlgorithmIdentifier Algorithm + { + get { return this.algorithm; } + } + + public object CryptoObject + { + get { return this.mac; } + } + + public CmsReadable GetReadable(KeyParameter sKey) + { + string macAlg = this.algorithm.ObjectID.Id; +// Asn1Object sParams = this.algorithm.Parameters.ToAsn1Object(); + + try + { + this.mac = MacUtilities.GetMac(macAlg); + + // FIXME Support for MAC algorithm parameters similar to cipher parameters +// ASN1Object sParams = (ASN1Object)macAlg.getParameters(); +// +// if (sParams != null && !(sParams instanceof ASN1Null)) +// { +// AlgorithmParameters params = CMSEnvelopedHelper.INSTANCE.createAlgorithmParameters(macAlg.getObjectId().getId(), provider); +// +// params.init(sParams.getEncoded(), "ASN.1"); +// +// mac.init(sKey, params.getParameterSpec(IvParameterSpec.class)); +// } +// else + { + mac.Init(sKey); + } + +// Asn1Object asn1Params = asn1Enc == null ? null : asn1Enc.ToAsn1Object(); +// +// ICipherParameters cipherParameters = sKey; +// +// if (asn1Params != null && !(asn1Params is Asn1Null)) +// { +// cipherParameters = ParameterUtilities.GetCipherParameters( +// macAlg.ObjectID, cipherParameters, asn1Params); +// } +// else +// { +// string alg = macAlg.ObjectID.Id; +// if (alg.Equals(CmsEnvelopedDataGenerator.DesEde3Cbc) +// || alg.Equals(CmsEnvelopedDataGenerator.IdeaCbc) +// || alg.Equals(CmsEnvelopedDataGenerator.Cast5Cbc)) +// { +// cipherParameters = new ParametersWithIV(cipherParameters, new byte[8]); +// } +// } +// +// mac.Init(cipherParameters); + } + catch (SecurityUtilityException e) + { + throw new CmsException("couldn't create cipher.", e); + } + catch (InvalidKeyException e) + { + throw new CmsException("key invalid in message.", e); + } + catch (IOException e) + { + throw new CmsException("error decoding algorithm parameters.", e); + } + + try + { + return new CmsProcessableInputStream( + new TeeInputStream( + readable.GetInputStream(), + new MacOutputStream(this.mac))); + } + catch (IOException e) + { + throw new CmsException("error reading content.", e); + } + } + } + + internal class CmsEnvelopedSecureReadable : CmsSecureReadable + { + private AlgorithmIdentifier algorithm; + private IBufferedCipher cipher; + private CmsReadable readable; + + internal CmsEnvelopedSecureReadable(AlgorithmIdentifier algorithm, CmsReadable readable) + { + this.algorithm = algorithm; + this.readable = readable; + } + + public AlgorithmIdentifier Algorithm + { + get { return this.algorithm; } + } + + public object CryptoObject + { + get { return this.cipher; } + } + + public CmsReadable GetReadable(KeyParameter sKey) + { + try + { + this.cipher = CipherUtilities.GetCipher(this.algorithm.ObjectID); + + Asn1Encodable asn1Enc = this.algorithm.Parameters; + Asn1Object asn1Params = asn1Enc == null ? null : asn1Enc.ToAsn1Object(); + + ICipherParameters cipherParameters = sKey; + + if (asn1Params != null && !(asn1Params is Asn1Null)) + { + cipherParameters = ParameterUtilities.GetCipherParameters( + this.algorithm.ObjectID, cipherParameters, asn1Params); + } + else + { + string alg = this.algorithm.ObjectID.Id; + if (alg.Equals(CmsEnvelopedDataGenerator.DesEde3Cbc) + || alg.Equals(CmsEnvelopedDataGenerator.IdeaCbc) + || alg.Equals(CmsEnvelopedDataGenerator.Cast5Cbc)) + { + cipherParameters = new ParametersWithIV(cipherParameters, new byte[8]); + } + } + + cipher.Init(false, cipherParameters); + } + catch (SecurityUtilityException e) + { + throw new CmsException("couldn't create cipher.", e); + } + catch (InvalidKeyException e) + { + throw new CmsException("key invalid in message.", e); + } + catch (IOException e) + { + throw new CmsException("error decoding algorithm parameters.", e); + } + + try + { + return new CmsProcessableInputStream( + new CipherStream(readable.GetInputStream(), cipher, null)); + } + catch (IOException e) + { + throw new CmsException("error reading content.", e); + } + } + } + } +} \ No newline at end of file diff --git a/Crypto/src/cms/CMSException.cs b/Crypto/src/cms/CMSException.cs new file mode 100644
index 000000000..1d1cd82e3 --- /dev/null +++ b/Crypto/src/cms/CMSException.cs
@@ -0,0 +1,25 @@ +using System; + +namespace Org.BouncyCastle.Cms +{ + public class CmsException + : Exception + { + public CmsException() + { + } + + public CmsException( + string msg) + : base(msg) + { + } + + public CmsException( + string msg, + Exception e) + : base(msg, e) + { + } + } +} diff --git a/Crypto/src/cms/CMSPBEKey.cs b/Crypto/src/cms/CMSPBEKey.cs new file mode 100644
index 000000000..cb1e54c36 --- /dev/null +++ b/Crypto/src/cms/CMSPBEKey.cs
@@ -0,0 +1,109 @@ +using System; + +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; + +//import javax.crypto.interfaces.PBEKey; + +namespace Org.BouncyCastle.Cms +{ + public abstract class CmsPbeKey + // TODO Create an equivalent interface somewhere? + // : PBEKey + : ICipherParameters + { + internal readonly char[] password; + internal readonly byte[] salt; + internal readonly int iterationCount; + + [Obsolete("Use version taking 'char[]' instead")] + public CmsPbeKey( + string password, + byte[] salt, + int iterationCount) + : this(password.ToCharArray(), salt, iterationCount) + { + } + + [Obsolete("Use version taking 'char[]' instead")] + public CmsPbeKey( + string password, + AlgorithmIdentifier keyDerivationAlgorithm) + : this(password.ToCharArray(), keyDerivationAlgorithm) + { + } + + public CmsPbeKey( + char[] password, + byte[] salt, + int iterationCount) + { + this.password = (char[])password.Clone(); + this.salt = Arrays.Clone(salt); + this.iterationCount = iterationCount; + } + + public CmsPbeKey( + char[] password, + AlgorithmIdentifier keyDerivationAlgorithm) + { + if (!keyDerivationAlgorithm.ObjectID.Equals(PkcsObjectIdentifiers.IdPbkdf2)) + throw new ArgumentException("Unsupported key derivation algorithm: " + + keyDerivationAlgorithm.ObjectID); + + Pbkdf2Params kdfParams = Pbkdf2Params.GetInstance( + keyDerivationAlgorithm.Parameters.ToAsn1Object()); + + this.password = (char[])password.Clone(); + this.salt = kdfParams.GetSalt(); + this.iterationCount = kdfParams.IterationCount.IntValue; + } + + ~CmsPbeKey() + { + Array.Clear(this.password, 0, this.password.Length); + } + + [Obsolete("Will be removed")] + public string Password + { + get { return new string(password); } + } + + public byte[] Salt + { + get { return Arrays.Clone(salt); } + } + + [Obsolete("Use 'Salt' property instead")] + public byte[] GetSalt() + { + return Salt; + } + + public int IterationCount + { + get { return iterationCount; } + } + + public string Algorithm + { + get { return "PKCS5S2"; } + } + + public string Format + { + get { return "RAW"; } + } + + public byte[] GetEncoded() + { + return null; + } + + internal abstract KeyParameter GetEncoded(string algorithmOid); + } +} diff --git a/Crypto/src/cms/CMSProcessable.cs b/Crypto/src/cms/CMSProcessable.cs new file mode 100644
index 000000000..41018d12b --- /dev/null +++ b/Crypto/src/cms/CMSProcessable.cs
@@ -0,0 +1,19 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Cms +{ + public interface CmsProcessable + { + /// <summary> + /// Generic routine to copy out the data we want processed. + /// </summary> + /// <remarks> + /// This routine may be called multiple times. + /// </remarks> + void Write(Stream outStream); + + [Obsolete] + object GetContent(); + } +} diff --git a/Crypto/src/cms/CMSProcessableByteArray.cs b/Crypto/src/cms/CMSProcessableByteArray.cs new file mode 100644
index 000000000..4bee4f8bd --- /dev/null +++ b/Crypto/src/cms/CMSProcessableByteArray.cs
@@ -0,0 +1,36 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Cms +{ + /** + * a holding class for a byte array of data to be processed. + */ + public class CmsProcessableByteArray + : CmsProcessable, CmsReadable + { + private readonly byte[] bytes; + + public CmsProcessableByteArray( + byte[] bytes) + { + this.bytes = bytes; + } + + public Stream GetInputStream() + { + return new MemoryStream(bytes, false); + } + + public virtual void Write(Stream zOut) + { + zOut.Write(bytes, 0, bytes.Length); + } + + /// <returns>A clone of the byte array</returns> + public virtual object GetContent() + { + return bytes.Clone(); + } + } +} diff --git a/Crypto/src/cms/CMSProcessableFile.cs b/Crypto/src/cms/CMSProcessableFile.cs new file mode 100644
index 000000000..548dc8efb --- /dev/null +++ b/Crypto/src/cms/CMSProcessableFile.cs
@@ -0,0 +1,57 @@ +#if !PORTABLE + +using System; +using System.IO; + +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Cms +{ + /** + * a holding class for a file of data to be processed. + */ + public class CmsProcessableFile + : CmsProcessable, CmsReadable + { + private const int DefaultBufSize = 32 * 1024; + + private readonly FileInfo _file; + private readonly int _bufSize; + + public CmsProcessableFile( + FileInfo file) + : this(file, DefaultBufSize) + { + } + + public CmsProcessableFile( + FileInfo file, + int bufSize) + { + _file = file; + _bufSize = bufSize; + } + + public virtual Stream GetInputStream() + { + return new FileStream( + _file.FullName, FileMode.Open, FileAccess.Read, FileShare.Read, _bufSize); + } + + public virtual void Write( + Stream zOut) + { + Stream inStr = GetInputStream(); + Streams.PipeAll(inStr, zOut); + inStr.Close(); + } + + /// <returns>The file handle</returns> + [Obsolete] + public virtual object GetContent() + { + return _file; + } + } +} +#endif \ No newline at end of file diff --git a/Crypto/src/cms/CMSProcessableInputStream.cs b/Crypto/src/cms/CMSProcessableInputStream.cs new file mode 100644
index 000000000..d10d059c6 --- /dev/null +++ b/Crypto/src/cms/CMSProcessableInputStream.cs
@@ -0,0 +1,52 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Cms +{ + public class CmsProcessableInputStream + : CmsProcessable, CmsReadable + { + private Stream input; + private bool used = false; + + public CmsProcessableInputStream( + Stream input) + { + this.input = input; + } + + public Stream GetInputStream() + { + CheckSingleUsage(); + + return input; + } + + public void Write(Stream output) + { + CheckSingleUsage(); + + Streams.PipeAll(input, output); + input.Dispose(); + } + + [Obsolete] + public object GetContent() + { + return GetInputStream(); + } + + private void CheckSingleUsage() + { + lock (this) + { + if (used) + throw new InvalidOperationException("CmsProcessableInputStream can only be used once"); + + used = true; + } + } + } +} diff --git a/Crypto/src/cms/CMSReadable.cs b/Crypto/src/cms/CMSReadable.cs new file mode 100644
index 000000000..9507b920c --- /dev/null +++ b/Crypto/src/cms/CMSReadable.cs
@@ -0,0 +1,10 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Cms +{ + internal interface CmsReadable + { + Stream GetInputStream(); + } +} diff --git a/Crypto/src/cms/CMSSecureReadable.cs b/Crypto/src/cms/CMSSecureReadable.cs new file mode 100644
index 000000000..5ceac24fd --- /dev/null +++ b/Crypto/src/cms/CMSSecureReadable.cs
@@ -0,0 +1,14 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Cms +{ + internal interface CmsSecureReadable + { + AlgorithmIdentifier Algorithm { get; } + object CryptoObject { get; } + CmsReadable GetReadable(KeyParameter key); + } +} diff --git a/Crypto/src/cms/CMSSignedData.cs b/Crypto/src/cms/CMSSignedData.cs new file mode 100644
index 000000000..81c87a426 --- /dev/null +++ b/Crypto/src/cms/CMSSignedData.cs
@@ -0,0 +1,425 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.Cms +{ + /** + * general class for handling a pkcs7-signature message. + * + * A simple example of usage - note, in the example below the validity of + * the certificate isn't verified, just the fact that one of the certs + * matches the given signer... + * + * <pre> + * IX509Store certs = s.GetCertificates(); + * SignerInformationStore signers = s.GetSignerInfos(); + * + * foreach (SignerInformation signer in signers.GetSigners()) + * { + * ArrayList certList = new ArrayList(certs.GetMatches(signer.SignerID)); + * X509Certificate cert = (X509Certificate) certList[0]; + * + * if (signer.Verify(cert.GetPublicKey())) + * { + * verified++; + * } + * } + * </pre> + */ + public class CmsSignedData + { + private static readonly CmsSignedHelper Helper = CmsSignedHelper.Instance; + + private readonly CmsProcessable signedContent; + private SignedData signedData; + private ContentInfo contentInfo; + private SignerInformationStore signerInfoStore; + private IX509Store attrCertStore; + private IX509Store certificateStore; + private IX509Store crlStore; + private IDictionary hashes; + + private CmsSignedData( + CmsSignedData c) + { + this.signedData = c.signedData; + this.contentInfo = c.contentInfo; + this.signedContent = c.signedContent; + this.signerInfoStore = c.signerInfoStore; + } + + public CmsSignedData( + byte[] sigBlock) + : this(CmsUtilities.ReadContentInfo(new MemoryStream(sigBlock, false))) + { + } + + public CmsSignedData( + CmsProcessable signedContent, + byte[] sigBlock) + : this(signedContent, CmsUtilities.ReadContentInfo(new MemoryStream(sigBlock, false))) + { + } + + /** + * Content with detached signature, digests precomputed + * + * @param hashes a map of precomputed digests for content indexed by name of hash. + * @param sigBlock the signature object. + */ + public CmsSignedData( + IDictionary hashes, + byte[] sigBlock) + : this(hashes, CmsUtilities.ReadContentInfo(sigBlock)) + { + } + + /** + * base constructor - content with detached signature. + * + * @param signedContent the content that was signed. + * @param sigData the signature object. + */ + public CmsSignedData( + CmsProcessable signedContent, + Stream sigData) + : this(signedContent, CmsUtilities.ReadContentInfo(sigData)) + { + } + + /** + * base constructor - with encapsulated content + */ + public CmsSignedData( + Stream sigData) + : this(CmsUtilities.ReadContentInfo(sigData)) + { + } + + public CmsSignedData( + CmsProcessable signedContent, + ContentInfo sigData) + { + this.signedContent = signedContent; + this.contentInfo = sigData; + this.signedData = SignedData.GetInstance(contentInfo.Content); + } + + public CmsSignedData( + IDictionary hashes, + ContentInfo sigData) + { + this.hashes = hashes; + this.contentInfo = sigData; + this.signedData = SignedData.GetInstance(contentInfo.Content); + } + + public CmsSignedData( + ContentInfo sigData) + { + this.contentInfo = sigData; + this.signedData = SignedData.GetInstance(contentInfo.Content); + + // + // this can happen if the signed message is sent simply to send a + // certificate chain. + // + if (signedData.EncapContentInfo.Content != null) + { + this.signedContent = new CmsProcessableByteArray( + ((Asn1OctetString)(signedData.EncapContentInfo.Content)).GetOctets()); + } +// else +// { +// this.signedContent = null; +// } + } + + /// <summary>Return the version number for this object.</summary> + public int Version + { + get { return signedData.Version.Value.IntValue; } + } + + /** + * return the collection of signers that are associated with the + * signatures for the message. + */ + public SignerInformationStore GetSignerInfos() + { + if (signerInfoStore == null) + { + IList signerInfos = Platform.CreateArrayList(); + Asn1Set s = signedData.SignerInfos; + + foreach (object obj in s) + { + SignerInfo info = SignerInfo.GetInstance(obj); + DerObjectIdentifier contentType = signedData.EncapContentInfo.ContentType; + + if (hashes == null) + { + signerInfos.Add(new SignerInformation(info, contentType, signedContent, null)); + } + else + { + byte[] hash = (byte[]) hashes[info.DigestAlgorithm.ObjectID.Id]; + + signerInfos.Add(new SignerInformation(info, contentType, null, new BaseDigestCalculator(hash))); + } + } + + signerInfoStore = new SignerInformationStore(signerInfos); + } + + return signerInfoStore; + } + + /** + * return a X509Store containing the attribute certificates, if any, contained + * in this message. + * + * @param type type of store to create + * @return a store of attribute certificates + * @exception NoSuchStoreException if the store type isn't available. + * @exception CmsException if a general exception prevents creation of the X509Store + */ + public IX509Store GetAttributeCertificates( + string type) + { + if (attrCertStore == null) + { + attrCertStore = Helper.CreateAttributeStore(type, signedData.Certificates); + } + + return attrCertStore; + } + + /** + * return a X509Store containing the public key certificates, if any, contained + * in this message. + * + * @param type type of store to create + * @return a store of public key certificates + * @exception NoSuchStoreException if the store type isn't available. + * @exception CmsException if a general exception prevents creation of the X509Store + */ + public IX509Store GetCertificates( + string type) + { + if (certificateStore == null) + { + certificateStore = Helper.CreateCertificateStore(type, signedData.Certificates); + } + + return certificateStore; + } + + /** + * return a X509Store containing CRLs, if any, contained + * in this message. + * + * @param type type of store to create + * @return a store of CRLs + * @exception NoSuchStoreException if the store type isn't available. + * @exception CmsException if a general exception prevents creation of the X509Store + */ + public IX509Store GetCrls( + string type) + { + if (crlStore == null) + { + crlStore = Helper.CreateCrlStore(type, signedData.CRLs); + } + + return crlStore; + } + + [Obsolete("Use 'SignedContentType' property instead.")] + public string SignedContentTypeOid + { + get { return signedData.EncapContentInfo.ContentType.Id; } + } + + /// <summary> + /// Return the <c>DerObjectIdentifier</c> associated with the encapsulated + /// content info structure carried in the signed data. + /// </summary> + public DerObjectIdentifier SignedContentType + { + get { return signedData.EncapContentInfo.ContentType; } + } + + public CmsProcessable SignedContent + { + get { return signedContent; } + } + + /** + * return the ContentInfo + */ + public ContentInfo ContentInfo + { + get { return contentInfo; } + } + + /** + * return the ASN.1 encoded representation of this object. + */ + public byte[] GetEncoded() + { + return contentInfo.GetEncoded(); + } + + /** + * Replace the signerinformation store associated with this + * CmsSignedData object with the new one passed in. You would + * probably only want to do this if you wanted to change the unsigned + * attributes associated with a signer, or perhaps delete one. + * + * @param signedData the signed data object to be used as a base. + * @param signerInformationStore the new signer information store to use. + * @return a new signed data object. + */ + public static CmsSignedData ReplaceSigners( + CmsSignedData signedData, + SignerInformationStore signerInformationStore) + { + // + // copy + // + CmsSignedData cms = new CmsSignedData(signedData); + + // + // replace the store + // + cms.signerInfoStore = signerInformationStore; + + // + // replace the signers in the SignedData object + // + Asn1EncodableVector digestAlgs = new Asn1EncodableVector(); + Asn1EncodableVector vec = new Asn1EncodableVector(); + + foreach (SignerInformation signer in signerInformationStore.GetSigners()) + { + digestAlgs.Add(Helper.FixAlgID(signer.DigestAlgorithmID)); + vec.Add(signer.ToSignerInfo()); + } + + Asn1Set digests = new DerSet(digestAlgs); + Asn1Set signers = new DerSet(vec); + Asn1Sequence sD = (Asn1Sequence)signedData.signedData.ToAsn1Object(); + + // + // signers are the last item in the sequence. + // + vec = new Asn1EncodableVector( + sD[0], // version + digests); + + for (int i = 2; i != sD.Count - 1; i++) + { + vec.Add(sD[i]); + } + + vec.Add(signers); + + cms.signedData = SignedData.GetInstance(new BerSequence(vec)); + + // + // replace the contentInfo with the new one + // + cms.contentInfo = new ContentInfo(cms.contentInfo.ContentType, cms.signedData); + + return cms; + } + + /** + * Replace the certificate and CRL information associated with this + * CmsSignedData object with the new one passed in. + * + * @param signedData the signed data object to be used as a base. + * @param x509Certs the new certificates to be used. + * @param x509Crls the new CRLs to be used. + * @return a new signed data object. + * @exception CmsException if there is an error processing the stores + */ + public static CmsSignedData ReplaceCertificatesAndCrls( + CmsSignedData signedData, + IX509Store x509Certs, + IX509Store x509Crls, + IX509Store x509AttrCerts) + { + if (x509AttrCerts != null) + throw Platform.CreateNotImplementedException("Currently can't replace attribute certificates"); + + // + // copy + // + CmsSignedData cms = new CmsSignedData(signedData); + + // + // replace the certs and crls in the SignedData object + // + Asn1Set certs = null; + try + { + Asn1Set asn1Set = CmsUtilities.CreateBerSetFromList( + CmsUtilities.GetCertificatesFromStore(x509Certs)); + + if (asn1Set.Count != 0) + { + certs = asn1Set; + } + } + catch (X509StoreException e) + { + throw new CmsException("error getting certificates from store", e); + } + + Asn1Set crls = null; + try + { + Asn1Set asn1Set = CmsUtilities.CreateBerSetFromList( + CmsUtilities.GetCrlsFromStore(x509Crls)); + + if (asn1Set.Count != 0) + { + crls = asn1Set; + } + } + catch (X509StoreException e) + { + throw new CmsException("error getting CRLs from store", e); + } + + // + // replace the CMS structure. + // + SignedData old = signedData.signedData; + cms.signedData = new SignedData( + old.DigestAlgorithms, + old.EncapContentInfo, + certs, + crls, + old.SignerInfos); + + // + // replace the contentInfo with the new one + // + cms.contentInfo = new ContentInfo(cms.contentInfo.ContentType, cms.signedData); + + return cms; + } + } +} diff --git a/Crypto/src/cms/CMSSignedDataGenerator.cs b/Crypto/src/cms/CMSSignedDataGenerator.cs new file mode 100644
index 000000000..5b68c785d --- /dev/null +++ b/Crypto/src/cms/CMSSignedDataGenerator.cs
@@ -0,0 +1,551 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Cms +{ + /** + * general class for generating a pkcs7-signature message. + * <p> + * A simple example of usage. + * + * <pre> + * IX509Store certs... + * IX509Store crls... + * CmsSignedDataGenerator gen = new CmsSignedDataGenerator(); + * + * gen.AddSigner(privKey, cert, CmsSignedGenerator.DigestSha1); + * gen.AddCertificates(certs); + * gen.AddCrls(crls); + * + * CmsSignedData data = gen.Generate(content); + * </pre> + * </p> + */ + public class CmsSignedDataGenerator + : CmsSignedGenerator + { + private static readonly CmsSignedHelper Helper = CmsSignedHelper.Instance; + + private readonly IList signerInfs = Platform.CreateArrayList(); + + private class SignerInf + { + private readonly CmsSignedGenerator outer; + + private readonly AsymmetricKeyParameter key; + private readonly SignerIdentifier signerIdentifier; + private readonly string digestOID; + private readonly string encOID; + private readonly CmsAttributeTableGenerator sAttr; + private readonly CmsAttributeTableGenerator unsAttr; + private readonly Asn1.Cms.AttributeTable baseSignedTable; + + internal SignerInf( + CmsSignedGenerator outer, + AsymmetricKeyParameter key, + SignerIdentifier signerIdentifier, + string digestOID, + string encOID, + CmsAttributeTableGenerator sAttr, + CmsAttributeTableGenerator unsAttr, + Asn1.Cms.AttributeTable baseSignedTable) + { + this.outer = outer; + this.key = key; + this.signerIdentifier = signerIdentifier; + this.digestOID = digestOID; + this.encOID = encOID; + this.sAttr = sAttr; + this.unsAttr = unsAttr; + this.baseSignedTable = baseSignedTable; + } + + internal AlgorithmIdentifier DigestAlgorithmID + { + get { return new AlgorithmIdentifier(new DerObjectIdentifier(digestOID), DerNull.Instance); } + } + + internal CmsAttributeTableGenerator SignedAttributes + { + get { return sAttr; } + } + + internal CmsAttributeTableGenerator UnsignedAttributes + { + get { return unsAttr; } + } + + internal SignerInfo ToSignerInfo( + DerObjectIdentifier contentType, + CmsProcessable content, + SecureRandom random) + { + AlgorithmIdentifier digAlgId = DigestAlgorithmID; + string digestName = Helper.GetDigestAlgName(digestOID); + IDigest dig = Helper.GetDigestInstance(digestName); + + string signatureName = digestName + "with" + Helper.GetEncryptionAlgName(encOID); + ISigner sig = Helper.GetSignatureInstance(signatureName); + + // TODO Optimise the case where more than one signer with same digest + if (content != null) + { + content.Write(new DigOutputStream(dig)); + } + + byte[] hash = DigestUtilities.DoFinal(dig); + outer._digests.Add(digestOID, hash.Clone()); + + sig.Init(true, new ParametersWithRandom(key, random)); +#if NETCF_1_0 || NETCF_2_0 || SILVERLIGHT || PORTABLE + Stream sigStr = new SigOutputStream(sig); +#else + Stream sigStr = new BufferedStream(new SigOutputStream(sig)); +#endif + + Asn1Set signedAttr = null; + if (sAttr != null) + { + IDictionary parameters = outer.GetBaseParameters(contentType, digAlgId, hash); + +// Asn1.Cms.AttributeTable signed = sAttr.GetAttributes(Collections.unmodifiableMap(parameters)); + Asn1.Cms.AttributeTable signed = sAttr.GetAttributes(parameters); + + if (contentType == null) //counter signature + { + if (signed != null && signed[CmsAttributes.ContentType] != null) + { + IDictionary tmpSigned = signed.ToDictionary(); + tmpSigned.Remove(CmsAttributes.ContentType); + signed = new Asn1.Cms.AttributeTable(tmpSigned); + } + } + + // TODO Validate proposed signed attributes + + signedAttr = outer.GetAttributeSet(signed); + + // sig must be composed from the DER encoding. + new DerOutputStream(sigStr).WriteObject(signedAttr); + } + else if (content != null) + { + // TODO Use raw signature of the hash value instead + content.Write(sigStr); + } + + sigStr.Dispose(); + byte[] sigBytes = sig.GenerateSignature(); + + Asn1Set unsignedAttr = null; + if (unsAttr != null) + { + IDictionary baseParameters = outer.GetBaseParameters(contentType, digAlgId, hash); + baseParameters[CmsAttributeTableParameter.Signature] = sigBytes.Clone(); + +// Asn1.Cms.AttributeTable unsigned = unsAttr.GetAttributes(Collections.unmodifiableMap(baseParameters)); + Asn1.Cms.AttributeTable unsigned = unsAttr.GetAttributes(baseParameters); + + // TODO Validate proposed unsigned attributes + + unsignedAttr = outer.GetAttributeSet(unsigned); + } + + // TODO[RSAPSS] Need the ability to specify non-default parameters + Asn1Encodable sigX509Parameters = SignerUtilities.GetDefaultX509Parameters(signatureName); + AlgorithmIdentifier encAlgId = CmsSignedGenerator.GetEncAlgorithmIdentifier( + new DerObjectIdentifier(encOID), sigX509Parameters); + + return new SignerInfo(signerIdentifier, digAlgId, + signedAttr, encAlgId, new DerOctetString(sigBytes), unsignedAttr); + } + } + + public CmsSignedDataGenerator() + { + } + + /// <summary>Constructor allowing specific source of randomness</summary> + /// <param name="rand">Instance of <c>SecureRandom</c> to use.</param> + public CmsSignedDataGenerator( + SecureRandom rand) + : base(rand) + { + } + + /** + * add a signer - no attributes other than the default ones will be + * provided here. + * + * @param key signing key to use + * @param cert certificate containing corresponding public key + * @param digestOID digest algorithm OID + */ + public void AddSigner( + AsymmetricKeyParameter privateKey, + X509Certificate cert, + string digestOID) + { + AddSigner(privateKey, cert, GetEncOid(privateKey, digestOID), digestOID); + } + + /** + * add a signer, specifying the digest encryption algorithm to use - no attributes other than the default ones will be + * provided here. + * + * @param key signing key to use + * @param cert certificate containing corresponding public key + * @param encryptionOID digest encryption algorithm OID + * @param digestOID digest algorithm OID + */ + public void AddSigner( + AsymmetricKeyParameter privateKey, + X509Certificate cert, + string encryptionOID, + string digestOID) + { + doAddSigner(privateKey, GetSignerIdentifier(cert), encryptionOID, digestOID, + new DefaultSignedAttributeTableGenerator(), null, null); + } + + /** + * add a signer - no attributes other than the default ones will be + * provided here. + */ + public void AddSigner( + AsymmetricKeyParameter privateKey, + byte[] subjectKeyID, + string digestOID) + { + AddSigner(privateKey, subjectKeyID, GetEncOid(privateKey, digestOID), digestOID); + } + + /** + * add a signer, specifying the digest encryption algorithm to use - no attributes other than the default ones will be + * provided here. + */ + public void AddSigner( + AsymmetricKeyParameter privateKey, + byte[] subjectKeyID, + string encryptionOID, + string digestOID) + { + doAddSigner(privateKey, GetSignerIdentifier(subjectKeyID), encryptionOID, digestOID, + new DefaultSignedAttributeTableGenerator(), null, null); + } + + /** + * add a signer with extra signed/unsigned attributes. + * + * @param key signing key to use + * @param cert certificate containing corresponding public key + * @param digestOID digest algorithm OID + * @param signedAttr table of attributes to be included in signature + * @param unsignedAttr table of attributes to be included as unsigned + */ + public void AddSigner( + AsymmetricKeyParameter privateKey, + X509Certificate cert, + string digestOID, + Asn1.Cms.AttributeTable signedAttr, + Asn1.Cms.AttributeTable unsignedAttr) + { + AddSigner(privateKey, cert, GetEncOid(privateKey, digestOID), digestOID, + signedAttr, unsignedAttr); + } + + /** + * add a signer, specifying the digest encryption algorithm, with extra signed/unsigned attributes. + * + * @param key signing key to use + * @param cert certificate containing corresponding public key + * @param encryptionOID digest encryption algorithm OID + * @param digestOID digest algorithm OID + * @param signedAttr table of attributes to be included in signature + * @param unsignedAttr table of attributes to be included as unsigned + */ + public void AddSigner( + AsymmetricKeyParameter privateKey, + X509Certificate cert, + string encryptionOID, + string digestOID, + Asn1.Cms.AttributeTable signedAttr, + Asn1.Cms.AttributeTable unsignedAttr) + { + doAddSigner(privateKey, GetSignerIdentifier(cert), encryptionOID, digestOID, + new DefaultSignedAttributeTableGenerator(signedAttr), + new SimpleAttributeTableGenerator(unsignedAttr), + signedAttr); + } + + /** + * add a signer with extra signed/unsigned attributes. + * + * @param key signing key to use + * @param subjectKeyID subjectKeyID of corresponding public key + * @param digestOID digest algorithm OID + * @param signedAttr table of attributes to be included in signature + * @param unsignedAttr table of attributes to be included as unsigned + */ + public void AddSigner( + AsymmetricKeyParameter privateKey, + byte[] subjectKeyID, + string digestOID, + Asn1.Cms.AttributeTable signedAttr, + Asn1.Cms.AttributeTable unsignedAttr) + { + AddSigner(privateKey, subjectKeyID, GetEncOid(privateKey, digestOID), digestOID, + signedAttr, unsignedAttr); + } + + /** + * add a signer, specifying the digest encryption algorithm, with extra signed/unsigned attributes. + * + * @param key signing key to use + * @param subjectKeyID subjectKeyID of corresponding public key + * @param encryptionOID digest encryption algorithm OID + * @param digestOID digest algorithm OID + * @param signedAttr table of attributes to be included in signature + * @param unsignedAttr table of attributes to be included as unsigned + */ + public void AddSigner( + AsymmetricKeyParameter privateKey, + byte[] subjectKeyID, + string encryptionOID, + string digestOID, + Asn1.Cms.AttributeTable signedAttr, + Asn1.Cms.AttributeTable unsignedAttr) + { + doAddSigner(privateKey, GetSignerIdentifier(subjectKeyID), encryptionOID, digestOID, + new DefaultSignedAttributeTableGenerator(signedAttr), + new SimpleAttributeTableGenerator(unsignedAttr), + signedAttr); + } + + /** + * add a signer with extra signed/unsigned attributes based on generators. + */ + public void AddSigner( + AsymmetricKeyParameter privateKey, + X509Certificate cert, + string digestOID, + CmsAttributeTableGenerator signedAttrGen, + CmsAttributeTableGenerator unsignedAttrGen) + { + AddSigner(privateKey, cert, GetEncOid(privateKey, digestOID), digestOID, + signedAttrGen, unsignedAttrGen); + } + + /** + * add a signer, specifying the digest encryption algorithm, with extra signed/unsigned attributes based on generators. + */ + public void AddSigner( + AsymmetricKeyParameter privateKey, + X509Certificate cert, + string encryptionOID, + string digestOID, + CmsAttributeTableGenerator signedAttrGen, + CmsAttributeTableGenerator unsignedAttrGen) + { + doAddSigner(privateKey, GetSignerIdentifier(cert), encryptionOID, digestOID, signedAttrGen, + unsignedAttrGen, null); + } + + /** + * add a signer with extra signed/unsigned attributes based on generators. + */ + public void AddSigner( + AsymmetricKeyParameter privateKey, + byte[] subjectKeyID, + string digestOID, + CmsAttributeTableGenerator signedAttrGen, + CmsAttributeTableGenerator unsignedAttrGen) + { + AddSigner(privateKey, subjectKeyID, GetEncOid(privateKey, digestOID), digestOID, + signedAttrGen, unsignedAttrGen); + } + + /** + * add a signer, including digest encryption algorithm, with extra signed/unsigned attributes based on generators. + */ + public void AddSigner( + AsymmetricKeyParameter privateKey, + byte[] subjectKeyID, + string encryptionOID, + string digestOID, + CmsAttributeTableGenerator signedAttrGen, + CmsAttributeTableGenerator unsignedAttrGen) + { + doAddSigner(privateKey, GetSignerIdentifier(subjectKeyID), encryptionOID, digestOID, + signedAttrGen, unsignedAttrGen, null); + } + + private void doAddSigner( + AsymmetricKeyParameter privateKey, + SignerIdentifier signerIdentifier, + string encryptionOID, + string digestOID, + CmsAttributeTableGenerator signedAttrGen, + CmsAttributeTableGenerator unsignedAttrGen, + Asn1.Cms.AttributeTable baseSignedTable) + { + signerInfs.Add(new SignerInf(this, privateKey, signerIdentifier, digestOID, encryptionOID, + signedAttrGen, unsignedAttrGen, baseSignedTable)); + } + + /** + * generate a signed object that for a CMS Signed Data object + */ + public CmsSignedData Generate( + CmsProcessable content) + { + return Generate(content, false); + } + + /** + * generate a signed object that for a CMS Signed Data + * object - if encapsulate is true a copy + * of the message will be included in the signature. The content type + * is set according to the OID represented by the string signedContentType. + */ + public CmsSignedData Generate( + string signedContentType, + // FIXME Avoid accessing more than once to support CmsProcessableInputStream + CmsProcessable content, + bool encapsulate) + { + Asn1EncodableVector digestAlgs = new Asn1EncodableVector(); + Asn1EncodableVector signerInfos = new Asn1EncodableVector(); + + _digests.Clear(); // clear the current preserved digest state + + // + // add the precalculated SignerInfo objects. + // + foreach (SignerInformation signer in _signers) + { + digestAlgs.Add(Helper.FixAlgID(signer.DigestAlgorithmID)); + + // TODO Verify the content type and calculated digest match the precalculated SignerInfo + signerInfos.Add(signer.ToSignerInfo()); + } + + // + // add the SignerInfo objects + // + bool isCounterSignature = (signedContentType == null); + + DerObjectIdentifier contentTypeOid = isCounterSignature + ? null + : new DerObjectIdentifier(signedContentType); + + foreach (SignerInf signer in signerInfs) + { + try + { + digestAlgs.Add(signer.DigestAlgorithmID); + signerInfos.Add(signer.ToSignerInfo(contentTypeOid, content, rand)); + } + catch (IOException e) + { + throw new CmsException("encoding error.", e); + } + catch (InvalidKeyException e) + { + throw new CmsException("key inappropriate for signature.", e); + } + catch (SignatureException e) + { + throw new CmsException("error creating signature.", e); + } + catch (CertificateEncodingException e) + { + throw new CmsException("error creating sid.", e); + } + } + + Asn1Set certificates = null; + + if (_certs.Count != 0) + { + certificates = CmsUtilities.CreateBerSetFromList(_certs); + } + + Asn1Set certrevlist = null; + + if (_crls.Count != 0) + { + certrevlist = CmsUtilities.CreateBerSetFromList(_crls); + } + + Asn1OctetString octs = null; + if (encapsulate) + { + MemoryStream bOut = new MemoryStream(); + if (content != null) + { + try + { + content.Write(bOut); + } + catch (IOException e) + { + throw new CmsException("encapsulation error.", e); + } + } + octs = new BerOctetString(bOut.ToArray()); + } + + ContentInfo encInfo = new ContentInfo(contentTypeOid, octs); + + SignedData sd = new SignedData( + new DerSet(digestAlgs), + encInfo, + certificates, + certrevlist, + new DerSet(signerInfos)); + + ContentInfo contentInfo = new ContentInfo(CmsObjectIdentifiers.SignedData, sd); + + return new CmsSignedData(content, contentInfo); + } + + /** + * generate a signed object that for a CMS Signed Data + * object - if encapsulate is true a copy + * of the message will be included in the signature with the + * default content type "data". + */ + public CmsSignedData Generate( + CmsProcessable content, + bool encapsulate) + { + return this.Generate(Data, content, encapsulate); + } + + /** + * generate a set of one or more SignerInformation objects representing counter signatures on + * the passed in SignerInformation object. + * + * @param signer the signer to be countersigned + * @param sigProvider the provider to be used for counter signing. + * @return a store containing the signers. + */ + public SignerInformationStore GenerateCounterSigners( + SignerInformation signer) + { + return this.Generate(null, new CmsProcessableByteArray(signer.GetSignature()), false).GetSignerInfos(); + } + } +} diff --git a/Crypto/src/cms/CMSSignedDataParser.cs b/Crypto/src/cms/CMSSignedDataParser.cs new file mode 100644
index 000000000..dd794ee77 --- /dev/null +++ b/Crypto/src/cms/CMSSignedDataParser.cs
@@ -0,0 +1,455 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.Utilities.IO; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.Cms +{ + /** + * Parsing class for an CMS Signed Data object from an input stream. + * <p> + * Note: that because we are in a streaming mode only one signer can be tried and it is important + * that the methods on the parser are called in the appropriate order. + * </p> + * <p> + * A simple example of usage for an encapsulated signature. + * </p> + * <p> + * Two notes: first, in the example below the validity of + * the certificate isn't verified, just the fact that one of the certs + * matches the given signer, and, second, because we are in a streaming + * mode the order of the operations is important. + * </p> + * <pre> + * CmsSignedDataParser sp = new CmsSignedDataParser(encapSigData); + * + * sp.GetSignedContent().Drain(); + * + * IX509Store certs = sp.GetCertificates(); + * SignerInformationStore signers = sp.GetSignerInfos(); + * + * foreach (SignerInformation signer in signers.GetSigners()) + * { + * ArrayList certList = new ArrayList(certs.GetMatches(signer.SignerID)); + * X509Certificate cert = (X509Certificate) certList[0]; + * + * Console.WriteLine("verify returns: " + signer.Verify(cert)); + * } + * </pre> + * Note also: this class does not introduce buffering - if you are processing large files you should create + * the parser with: + * <pre> + * CmsSignedDataParser ep = new CmsSignedDataParser(new BufferedInputStream(encapSigData, bufSize)); + * </pre> + * where bufSize is a suitably large buffer size. + */ + public class CmsSignedDataParser + : CmsContentInfoParser + { + private static readonly CmsSignedHelper Helper = CmsSignedHelper.Instance; + + private SignedDataParser _signedData; + private DerObjectIdentifier _signedContentType; + private CmsTypedStream _signedContent; + private IDictionary _digests; + private ISet _digestOids; + + private SignerInformationStore _signerInfoStore; + private Asn1Set _certSet, _crlSet; + private bool _isCertCrlParsed; + private IX509Store _attributeStore; + private IX509Store _certificateStore; + private IX509Store _crlStore; + + public CmsSignedDataParser( + byte[] sigBlock) + : this(new MemoryStream(sigBlock, false)) + { + } + + public CmsSignedDataParser( + CmsTypedStream signedContent, + byte[] sigBlock) + : this(signedContent, new MemoryStream(sigBlock, false)) + { + } + + /** + * base constructor - with encapsulated content + */ + public CmsSignedDataParser( + Stream sigData) + : this(null, sigData) + { + } + + /** + * base constructor + * + * @param signedContent the content that was signed. + * @param sigData the signature object. + */ + public CmsSignedDataParser( + CmsTypedStream signedContent, + Stream sigData) + : base(sigData) + { + try + { + this._signedContent = signedContent; + this._signedData = SignedDataParser.GetInstance(this.contentInfo.GetContent(Asn1Tags.Sequence)); + this._digests = Platform.CreateHashtable(); + this._digestOids = new HashSet(); + + Asn1SetParser digAlgs = _signedData.GetDigestAlgorithms(); + IAsn1Convertible o; + + while ((o = digAlgs.ReadObject()) != null) + { + AlgorithmIdentifier id = AlgorithmIdentifier.GetInstance(o.ToAsn1Object()); + + try + { + string digestOid = id.ObjectID.Id; + string digestName = Helper.GetDigestAlgName(digestOid); + + if (!this._digests.Contains(digestName)) + { + this._digests[digestName] = Helper.GetDigestInstance(digestName); + this._digestOids.Add(digestOid); + } + } + catch (SecurityUtilityException) + { + // TODO Should do something other than ignore it + } + } + + // + // If the message is simply a certificate chain message GetContent() may return null. + // + ContentInfoParser cont = _signedData.GetEncapContentInfo(); + Asn1OctetStringParser octs = (Asn1OctetStringParser) + cont.GetContent(Asn1Tags.OctetString); + + if (octs != null) + { + CmsTypedStream ctStr = new CmsTypedStream( + cont.ContentType.Id, octs.GetOctetStream()); + + if (_signedContent == null) + { + this._signedContent = ctStr; + } + else + { + // + // content passed in, need to read past empty encapsulated content info object if present + // + ctStr.Drain(); + } + } + + _signedContentType = _signedContent == null + ? cont.ContentType + : new DerObjectIdentifier(_signedContent.ContentType); + } + catch (IOException e) + { + throw new CmsException("io exception: " + e.Message, e); + } + + if (_digests.Count < 1) + { + throw new CmsException("no digests could be created for message."); + } + } + + /** + * Return the version number for the SignedData object + * + * @return the version number + */ + public int Version + { + get { return _signedData.Version.Value.IntValue; } + } + + public ISet DigestOids + { + get { return new HashSet(_digestOids); } + } + + /** + * return the collection of signers that are associated with the + * signatures for the message. + * @throws CmsException + */ + public SignerInformationStore GetSignerInfos() + { + if (_signerInfoStore == null) + { + PopulateCertCrlSets(); + + IList signerInfos = Platform.CreateArrayList(); + IDictionary hashes = Platform.CreateHashtable(); + + foreach (object digestKey in _digests.Keys) + { + hashes[digestKey] = DigestUtilities.DoFinal( + (IDigest)_digests[digestKey]); + } + + try + { + Asn1SetParser s = _signedData.GetSignerInfos(); + IAsn1Convertible o; + + while ((o = s.ReadObject()) != null) + { + SignerInfo info = SignerInfo.GetInstance(o.ToAsn1Object()); + string digestName = Helper.GetDigestAlgName( + info.DigestAlgorithm.ObjectID.Id); + + byte[] hash = (byte[]) hashes[digestName]; + + signerInfos.Add(new SignerInformation(info, _signedContentType, null, new BaseDigestCalculator(hash))); + } + } + catch (IOException e) + { + throw new CmsException("io exception: " + e.Message, e); + } + + _signerInfoStore = new SignerInformationStore(signerInfos); + } + + return _signerInfoStore; + } + + /** + * return a X509Store containing the attribute certificates, if any, contained + * in this message. + * + * @param type type of store to create + * @return a store of attribute certificates + * @exception org.bouncycastle.x509.NoSuchStoreException if the store type isn't available. + * @exception CmsException if a general exception prevents creation of the X509Store + */ + public IX509Store GetAttributeCertificates( + string type) + { + if (_attributeStore == null) + { + PopulateCertCrlSets(); + + _attributeStore = Helper.CreateAttributeStore(type, _certSet); + } + + return _attributeStore; + } + + /** + * return a X509Store containing the public key certificates, if any, contained + * in this message. + * + * @param type type of store to create + * @return a store of public key certificates + * @exception NoSuchStoreException if the store type isn't available. + * @exception CmsException if a general exception prevents creation of the X509Store + */ + public IX509Store GetCertificates( + string type) + { + if (_certificateStore == null) + { + PopulateCertCrlSets(); + + _certificateStore = Helper.CreateCertificateStore(type, _certSet); + } + + return _certificateStore; + } + + /** + * return a X509Store containing CRLs, if any, contained + * in this message. + * + * @param type type of store to create + * @return a store of CRLs + * @exception NoSuchStoreException if the store type isn't available. + * @exception CmsException if a general exception prevents creation of the X509Store + */ + public IX509Store GetCrls( + string type) + { + if (_crlStore == null) + { + PopulateCertCrlSets(); + + _crlStore = Helper.CreateCrlStore(type, _crlSet); + } + + return _crlStore; + } + + private void PopulateCertCrlSets() + { + if (_isCertCrlParsed) + return; + + _isCertCrlParsed = true; + + try + { + // care! Streaming - Must process the GetCertificates() result before calling GetCrls() + _certSet = GetAsn1Set(_signedData.GetCertificates()); + _crlSet = GetAsn1Set(_signedData.GetCrls()); + } + catch (IOException e) + { + throw new CmsException("problem parsing cert/crl sets", e); + } + } + + /// <summary> + /// Return the <c>DerObjectIdentifier</c> associated with the encapsulated + /// content info structure carried in the signed data. + /// </summary> + public DerObjectIdentifier SignedContentType + { + get { return _signedContentType; } + } + + public CmsTypedStream GetSignedContent() + { + if (_signedContent == null) + { + return null; + } + + Stream digStream = _signedContent.ContentStream; + + foreach (IDigest digest in _digests.Values) + { + digStream = new DigestStream(digStream, digest, null); + } + + return new CmsTypedStream(_signedContent.ContentType, digStream); + } + + /** + * Replace the signerinformation store associated with the passed + * in message contained in the stream original with the new one passed in. + * You would probably only want to do this if you wanted to change the unsigned + * attributes associated with a signer, or perhaps delete one. + * <p> + * The output stream is returned unclosed. + * </p> + * @param original the signed data stream to be used as a base. + * @param signerInformationStore the new signer information store to use. + * @param out the stream to Write the new signed data object to. + * @return out. + */ + public static Stream ReplaceSigners( + Stream original, + SignerInformationStore signerInformationStore, + Stream outStr) + { + // NB: SecureRandom would be ignored since using existing signatures only + CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator(); + CmsSignedDataParser parser = new CmsSignedDataParser(original); + +// gen.AddDigests(parser.DigestOids); + gen.AddSigners(signerInformationStore); + + CmsTypedStream signedContent = parser.GetSignedContent(); + bool encapsulate = (signedContent != null); + Stream contentOut = gen.Open(outStr, parser.SignedContentType.Id, encapsulate); + if (encapsulate) + { + Streams.PipeAll(signedContent.ContentStream, contentOut); + } + + gen.AddAttributeCertificates(parser.GetAttributeCertificates("Collection")); + gen.AddCertificates(parser.GetCertificates("Collection")); + gen.AddCrls(parser.GetCrls("Collection")); + +// gen.AddSigners(parser.GetSignerInfos()); + + contentOut.Dispose(); + + return outStr; + } + + /** + * Replace the certificate and CRL information associated with this + * CMSSignedData object with the new one passed in. + * <p> + * The output stream is returned unclosed. + * </p> + * @param original the signed data stream to be used as a base. + * @param certsAndCrls the new certificates and CRLs to be used. + * @param out the stream to Write the new signed data object to. + * @return out. + * @exception CmsException if there is an error processing the CertStore + */ + public static Stream ReplaceCertificatesAndCrls( + Stream original, + IX509Store x509Certs, + IX509Store x509Crls, + IX509Store x509AttrCerts, + Stream outStr) + { + // NB: SecureRandom would be ignored since using existing signatures only + CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator(); + CmsSignedDataParser parser = new CmsSignedDataParser(original); + + gen.AddDigests(parser.DigestOids); + + CmsTypedStream signedContent = parser.GetSignedContent(); + bool encapsulate = (signedContent != null); + Stream contentOut = gen.Open(outStr, parser.SignedContentType.Id, encapsulate); + if (encapsulate) + { + Streams.PipeAll(signedContent.ContentStream, contentOut); + } + +// gen.AddAttributeCertificates(parser.GetAttributeCertificates("Collection")); +// gen.AddCertificates(parser.GetCertificates("Collection")); +// gen.AddCrls(parser.GetCrls("Collection")); + if (x509AttrCerts != null) + gen.AddAttributeCertificates(x509AttrCerts); + if (x509Certs != null) + gen.AddCertificates(x509Certs); + if (x509Crls != null) + gen.AddCrls(x509Crls); + + gen.AddSigners(parser.GetSignerInfos()); + + contentOut.Dispose(); + + return outStr; + } + + private static Asn1Set GetAsn1Set( + Asn1SetParser asn1SetParser) + { + return asn1SetParser == null + ? null + : Asn1Set.GetInstance(asn1SetParser.ToAsn1Object()); + } + } +} diff --git a/Crypto/src/cms/CMSSignedDataStreamGenerator.cs b/Crypto/src/cms/CMSSignedDataStreamGenerator.cs new file mode 100644
index 000000000..0bed9fcad --- /dev/null +++ b/Crypto/src/cms/CMSSignedDataStreamGenerator.cs
@@ -0,0 +1,917 @@ +using System; +using System.Collections; +using System.Diagnostics; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.Utilities.IO; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Cms +{ + /** + * General class for generating a pkcs7-signature message stream. + * <p> + * A simple example of usage. + * </p> + * <pre> + * IX509Store certs... + * CmsSignedDataStreamGenerator gen = new CmsSignedDataStreamGenerator(); + * + * gen.AddSigner(privateKey, cert, CmsSignedDataStreamGenerator.DIGEST_SHA1); + * + * gen.AddCertificates(certs); + * + * Stream sigOut = gen.Open(bOut); + * + * sigOut.Write(Encoding.UTF8.GetBytes("Hello World!")); + * + * sigOut.Close(); + * </pre> + */ + public class CmsSignedDataStreamGenerator + : CmsSignedGenerator + { + private static readonly CmsSignedHelper Helper = CmsSignedHelper.Instance; + + private readonly IList _signerInfs = Platform.CreateArrayList(); + private readonly ISet _messageDigestOids = new HashSet(); + private readonly IDictionary _messageDigests = Platform.CreateHashtable(); + private readonly IDictionary _messageHashes = Platform.CreateHashtable(); + private bool _messageDigestsLocked; + private int _bufferSize; + + private class DigestAndSignerInfoGeneratorHolder + { + internal readonly SignerInfoGenerator signerInf; + internal readonly string digestOID; + + internal DigestAndSignerInfoGeneratorHolder(SignerInfoGenerator signerInf, String digestOID) + { + this.signerInf = signerInf; + this.digestOID = digestOID; + } + + internal AlgorithmIdentifier DigestAlgorithm + { + get { return new AlgorithmIdentifier(new DerObjectIdentifier(this.digestOID), DerNull.Instance); } + } + } + + private class SignerInfoGeneratorImpl : SignerInfoGenerator + { + private readonly CmsSignedDataStreamGenerator outer; + + private readonly SignerIdentifier _signerIdentifier; + private readonly string _digestOID; + private readonly string _encOID; + private readonly CmsAttributeTableGenerator _sAttr; + private readonly CmsAttributeTableGenerator _unsAttr; + private readonly string _encName; + private readonly ISigner _sig; + + internal SignerInfoGeneratorImpl( + CmsSignedDataStreamGenerator outer, + AsymmetricKeyParameter key, + SignerIdentifier signerIdentifier, + string digestOID, + string encOID, + CmsAttributeTableGenerator sAttr, + CmsAttributeTableGenerator unsAttr) + { + this.outer = outer; + + _signerIdentifier = signerIdentifier; + _digestOID = digestOID; + _encOID = encOID; + _sAttr = sAttr; + _unsAttr = unsAttr; + _encName = Helper.GetEncryptionAlgName(_encOID); + + string digestName = Helper.GetDigestAlgName(_digestOID); + string signatureName = digestName + "with" + _encName; + + if (_sAttr != null) + { + _sig = Helper.GetSignatureInstance(signatureName); + } + else + { + // Note: Need to use raw signatures here since we have already calculated the digest + if (_encName.Equals("RSA")) + { + _sig = Helper.GetSignatureInstance("RSA"); + } + else if (_encName.Equals("DSA")) + { + _sig = Helper.GetSignatureInstance("NONEwithDSA"); + } + // TODO Add support for raw PSS +// else if (_encName.equals("RSAandMGF1")) +// { +// _sig = CMSSignedHelper.INSTANCE.getSignatureInstance("NONEWITHRSAPSS", _sigProvider); +// try +// { +// // Init the params this way to avoid having a 'raw' version of each PSS algorithm +// Signature sig2 = CMSSignedHelper.INSTANCE.getSignatureInstance(signatureName, _sigProvider); +// PSSParameterSpec spec = (PSSParameterSpec)sig2.getParameters().getParameterSpec(PSSParameterSpec.class); +// _sig.setParameter(spec); +// } +// catch (Exception e) +// { +// throw new SignatureException("algorithm: " + _encName + " could not be configured."); +// } +// } + else + { + throw new SignatureException("algorithm: " + _encName + " not supported in base signatures."); + } + } + + _sig.Init(true, new ParametersWithRandom(key, outer.rand)); + } + + public SignerInfo Generate(DerObjectIdentifier contentType, AlgorithmIdentifier digestAlgorithm, + byte[] calculatedDigest) + { + try + { + string digestName = Helper.GetDigestAlgName(_digestOID); + string signatureName = digestName + "with" + _encName; + +// AlgorithmIdentifier digAlgId = DigestAlgorithmID; +// +// byte[] hash = (byte[])outer._messageHashes[Helper.GetDigestAlgName(this._digestOID)]; +// outer._digests[_digestOID] = hash.Clone(); + + byte[] bytesToSign = calculatedDigest; + + /* RFC 3852 5.4 + * The result of the message digest calculation process depends on + * whether the signedAttrs field is present. When the field is absent, + * the result is just the message digest of the content as described + * + * above. When the field is present, however, the result is the message + * digest of the complete DER encoding of the SignedAttrs value + * contained in the signedAttrs field. + */ + Asn1Set signedAttr = null; + if (_sAttr != null) + { + IDictionary parameters = outer.GetBaseParameters(contentType, digestAlgorithm, calculatedDigest); + +// Asn1.Cms.AttributeTable signed = _sAttr.GetAttributes(Collections.unmodifiableMap(parameters)); + Asn1.Cms.AttributeTable signed = _sAttr.GetAttributes(parameters); + + if (contentType == null) //counter signature + { + if (signed != null && signed[CmsAttributes.ContentType] != null) + { + IDictionary tmpSigned = signed.ToDictionary(); + tmpSigned.Remove(CmsAttributes.ContentType); + signed = new Asn1.Cms.AttributeTable(tmpSigned); + } + } + + signedAttr = outer.GetAttributeSet(signed); + + // sig must be composed from the DER encoding. + bytesToSign = signedAttr.GetEncoded(Asn1Encodable.Der); + } + else + { + // Note: Need to use raw signatures here since we have already calculated the digest + if (_encName.Equals("RSA")) + { + DigestInfo dInfo = new DigestInfo(digestAlgorithm, calculatedDigest); + bytesToSign = dInfo.GetEncoded(Asn1Encodable.Der); + } + } + + _sig.BlockUpdate(bytesToSign, 0, bytesToSign.Length); + byte[] sigBytes = _sig.GenerateSignature(); + + Asn1Set unsignedAttr = null; + if (_unsAttr != null) + { + IDictionary parameters = outer.GetBaseParameters( + contentType, digestAlgorithm, calculatedDigest); + parameters[CmsAttributeTableParameter.Signature] = sigBytes.Clone(); + +// Asn1.Cms.AttributeTable unsigned = _unsAttr.getAttributes(Collections.unmodifiableMap(parameters)); + Asn1.Cms.AttributeTable unsigned = _unsAttr.GetAttributes(parameters); + + unsignedAttr = outer.GetAttributeSet(unsigned); + } + + // TODO[RSAPSS] Need the ability to specify non-default parameters + Asn1Encodable sigX509Parameters = SignerUtilities.GetDefaultX509Parameters(signatureName); + AlgorithmIdentifier digestEncryptionAlgorithm = CmsSignedGenerator.GetEncAlgorithmIdentifier( + new DerObjectIdentifier(_encOID), sigX509Parameters); + + return new SignerInfo(_signerIdentifier, digestAlgorithm, + signedAttr, digestEncryptionAlgorithm, new DerOctetString(sigBytes), unsignedAttr); + } + catch (IOException e) + { + throw new CmsStreamException("encoding error.", e); + } + catch (SignatureException e) + { + throw new CmsStreamException("error creating signature.", e); + } + } + } + + public CmsSignedDataStreamGenerator() + { + } + + /// <summary>Constructor allowing specific source of randomness</summary> + /// <param name="rand">Instance of <c>SecureRandom</c> to use.</param> + public CmsSignedDataStreamGenerator( + SecureRandom rand) + : base(rand) + { + } + + /** + * Set the underlying string size for encapsulated data + * + * @param bufferSize length of octet strings to buffer the data. + */ + public void SetBufferSize( + int bufferSize) + { + _bufferSize = bufferSize; + } + + public void AddDigests( + params string[] digestOids) + { + AddDigests((IEnumerable) digestOids); + } + + public void AddDigests( + IEnumerable digestOids) + { + foreach (string digestOid in digestOids) + { + ConfigureDigest(digestOid); + } + } + + /** + * add a signer - no attributes other than the default ones will be + * provided here. + * @throws NoSuchAlgorithmException + * @throws InvalidKeyException + */ + public void AddSigner( + AsymmetricKeyParameter privateKey, + X509Certificate cert, + string digestOid) + { + AddSigner(privateKey, cert, digestOid, + new DefaultSignedAttributeTableGenerator(), null); + } + + /** + * add a signer, specifying the digest encryption algorithm - no attributes other than the default ones will be + * provided here. + * @throws NoSuchProviderException + * @throws NoSuchAlgorithmException + * @throws InvalidKeyException + */ + public void AddSigner( + AsymmetricKeyParameter privateKey, + X509Certificate cert, + string encryptionOid, + string digestOid) + { + AddSigner(privateKey, cert, encryptionOid, digestOid, + new DefaultSignedAttributeTableGenerator(), + (CmsAttributeTableGenerator)null); + } + + /** + * add a signer with extra signed/unsigned attributes. + * @throws NoSuchAlgorithmException + * @throws InvalidKeyException + */ + public void AddSigner( + AsymmetricKeyParameter privateKey, + X509Certificate cert, + string digestOid, + Asn1.Cms.AttributeTable signedAttr, + Asn1.Cms.AttributeTable unsignedAttr) + { + AddSigner(privateKey, cert, digestOid, + new DefaultSignedAttributeTableGenerator(signedAttr), + new SimpleAttributeTableGenerator(unsignedAttr)); + } + + /** + * add a signer with extra signed/unsigned attributes - specifying digest + * encryption algorithm. + * @throws NoSuchProviderException + * @throws NoSuchAlgorithmException + * @throws InvalidKeyException + */ + public void AddSigner( + AsymmetricKeyParameter privateKey, + X509Certificate cert, + string encryptionOid, + string digestOid, + Asn1.Cms.AttributeTable signedAttr, + Asn1.Cms.AttributeTable unsignedAttr) + { + AddSigner(privateKey, cert, encryptionOid, digestOid, + new DefaultSignedAttributeTableGenerator(signedAttr), + new SimpleAttributeTableGenerator(unsignedAttr)); + } + + public void AddSigner( + AsymmetricKeyParameter privateKey, + X509Certificate cert, + string digestOid, + CmsAttributeTableGenerator signedAttrGenerator, + CmsAttributeTableGenerator unsignedAttrGenerator) + { + AddSigner(privateKey, cert, GetEncOid(privateKey, digestOid), digestOid, + signedAttrGenerator, unsignedAttrGenerator); + } + + public void AddSigner( + AsymmetricKeyParameter privateKey, + X509Certificate cert, + string encryptionOid, + string digestOid, + CmsAttributeTableGenerator signedAttrGenerator, + CmsAttributeTableGenerator unsignedAttrGenerator) + { + DoAddSigner(privateKey, GetSignerIdentifier(cert), encryptionOid, digestOid, + signedAttrGenerator, unsignedAttrGenerator); + } + + /** + * add a signer - no attributes other than the default ones will be + * provided here. + * @throws NoSuchAlgorithmException + * @throws InvalidKeyException + */ + public void AddSigner( + AsymmetricKeyParameter privateKey, + byte[] subjectKeyID, + string digestOid) + { + AddSigner(privateKey, subjectKeyID, digestOid, new DefaultSignedAttributeTableGenerator(), + (CmsAttributeTableGenerator)null); + } + + /** + * add a signer - no attributes other than the default ones will be + * provided here. + * @throws NoSuchProviderException + * @throws NoSuchAlgorithmException + * @throws InvalidKeyException + */ + public void AddSigner( + AsymmetricKeyParameter privateKey, + byte[] subjectKeyID, + string encryptionOid, + string digestOid) + { + AddSigner(privateKey, subjectKeyID, encryptionOid, digestOid, + new DefaultSignedAttributeTableGenerator(), + (CmsAttributeTableGenerator)null); + } + + /** + * add a signer with extra signed/unsigned attributes. + * @throws NoSuchAlgorithmException + * @throws InvalidKeyException + */ + public void AddSigner( + AsymmetricKeyParameter privateKey, + byte[] subjectKeyID, + string digestOid, + Asn1.Cms.AttributeTable signedAttr, + Asn1.Cms.AttributeTable unsignedAttr) + { + AddSigner(privateKey, subjectKeyID, digestOid, + new DefaultSignedAttributeTableGenerator(signedAttr), + new SimpleAttributeTableGenerator(unsignedAttr)); + } + + public void AddSigner( + AsymmetricKeyParameter privateKey, + byte[] subjectKeyID, + string digestOid, + CmsAttributeTableGenerator signedAttrGenerator, + CmsAttributeTableGenerator unsignedAttrGenerator) + { + AddSigner(privateKey, subjectKeyID, GetEncOid(privateKey, digestOid), + digestOid, signedAttrGenerator, unsignedAttrGenerator); + } + + public void AddSigner( + AsymmetricKeyParameter privateKey, + byte[] subjectKeyID, + string encryptionOid, + string digestOid, + CmsAttributeTableGenerator signedAttrGenerator, + CmsAttributeTableGenerator unsignedAttrGenerator) + { + DoAddSigner(privateKey, GetSignerIdentifier(subjectKeyID), encryptionOid, digestOid, + signedAttrGenerator, unsignedAttrGenerator); + } + + private void DoAddSigner( + AsymmetricKeyParameter privateKey, + SignerIdentifier signerIdentifier, + string encryptionOid, + string digestOid, + CmsAttributeTableGenerator signedAttrGenerator, + CmsAttributeTableGenerator unsignedAttrGenerator) + { + ConfigureDigest(digestOid); + + SignerInfoGeneratorImpl signerInf = new SignerInfoGeneratorImpl(this, privateKey, + signerIdentifier, digestOid, encryptionOid, signedAttrGenerator, unsignedAttrGenerator); + + _signerInfs.Add(new DigestAndSignerInfoGeneratorHolder(signerInf, digestOid)); + } + + internal override void AddSignerCallback( + SignerInformation si) + { + // FIXME If there were parameters in si.DigestAlgorithmID.Parameters, they are lost + // NB: Would need to call FixAlgID on the DigestAlgorithmID + + // For precalculated signers, just need to register the algorithm, not configure a digest + RegisterDigestOid(si.DigestAlgorithmID.ObjectID.Id); + } + + /** + * generate a signed object that for a CMS Signed Data object + */ + public Stream Open( + Stream outStream) + { + return Open(outStream, false); + } + + /** + * generate a signed object that for a CMS Signed Data + * object - if encapsulate is true a copy + * of the message will be included in the signature with the + * default content type "data". + */ + public Stream Open( + Stream outStream, + bool encapsulate) + { + return Open(outStream, Data, encapsulate); + } + + /** + * generate a signed object that for a CMS Signed Data + * object using the given provider - if encapsulate is true a copy + * of the message will be included in the signature with the + * default content type "data". If dataOutputStream is non null the data + * being signed will be written to the stream as it is processed. + * @param out stream the CMS object is to be written to. + * @param encapsulate true if data should be encapsulated. + * @param dataOutputStream output stream to copy the data being signed to. + */ + public Stream Open( + Stream outStream, + bool encapsulate, + Stream dataOutputStream) + { + return Open(outStream, Data, encapsulate, dataOutputStream); + } + + /** + * generate a signed object that for a CMS Signed Data + * object - if encapsulate is true a copy + * of the message will be included in the signature. The content type + * is set according to the OID represented by the string signedContentType. + */ + public Stream Open( + Stream outStream, + string signedContentType, + bool encapsulate) + { + return Open(outStream, signedContentType, encapsulate, null); + } + + /** + * generate a signed object that for a CMS Signed Data + * object using the given provider - if encapsulate is true a copy + * of the message will be included in the signature. The content type + * is set according to the OID represented by the string signedContentType. + * @param out stream the CMS object is to be written to. + * @param signedContentType OID for data to be signed. + * @param encapsulate true if data should be encapsulated. + * @param dataOutputStream output stream to copy the data being signed to. + */ + public Stream Open( + Stream outStream, + string signedContentType, + bool encapsulate, + Stream dataOutputStream) + { + if (outStream == null) + throw new ArgumentNullException("outStream"); + if (!outStream.CanWrite) + throw new ArgumentException("Expected writeable stream", "outStream"); + if (dataOutputStream != null && !dataOutputStream.CanWrite) + throw new ArgumentException("Expected writeable stream", "dataOutputStream"); + + _messageDigestsLocked = true; + + // + // ContentInfo + // + BerSequenceGenerator sGen = new BerSequenceGenerator(outStream); + + sGen.AddObject(CmsObjectIdentifiers.SignedData); + + // + // Signed Data + // + BerSequenceGenerator sigGen = new BerSequenceGenerator( + sGen.GetRawOutputStream(), 0, true); + + bool isCounterSignature = (signedContentType == null); + + DerObjectIdentifier contentTypeOid = isCounterSignature + ? null + : new DerObjectIdentifier(signedContentType); + + sigGen.AddObject(CalculateVersion(contentTypeOid)); + + Asn1EncodableVector digestAlgs = new Asn1EncodableVector(); + + foreach (string digestOid in _messageDigestOids) + { + digestAlgs.Add( + new AlgorithmIdentifier(new DerObjectIdentifier(digestOid), DerNull.Instance)); + } + + { + byte[] tmp = new DerSet(digestAlgs).GetEncoded(); + sigGen.GetRawOutputStream().Write(tmp, 0, tmp.Length); + } + + BerSequenceGenerator eiGen = new BerSequenceGenerator(sigGen.GetRawOutputStream()); + eiGen.AddObject(contentTypeOid); + + // If encapsulating, add the data as an octet string in the sequence + Stream encapStream = encapsulate + ? CmsUtilities.CreateBerOctetOutputStream(eiGen.GetRawOutputStream(), 0, true, _bufferSize) + : null; + + // Also send the data to 'dataOutputStream' if necessary + Stream teeStream = GetSafeTeeOutputStream(dataOutputStream, encapStream); + + // Let all the digests see the data as it is written + Stream digStream = AttachDigestsToOutputStream(_messageDigests.Values, teeStream); + + return new CmsSignedDataOutputStream(this, digStream, signedContentType, sGen, sigGen, eiGen); + } + + private void RegisterDigestOid( + string digestOid) + { + if (_messageDigestsLocked) + { + if (!_messageDigestOids.Contains(digestOid)) + throw new InvalidOperationException("Cannot register new digest OIDs after the data stream is opened"); + } + else + { + _messageDigestOids.Add(digestOid); + } + } + + private void ConfigureDigest( + string digestOid) + { + RegisterDigestOid(digestOid); + + string digestName = Helper.GetDigestAlgName(digestOid); + IDigest dig = (IDigest)_messageDigests[digestName]; + if (dig == null) + { + if (_messageDigestsLocked) + throw new InvalidOperationException("Cannot configure new digests after the data stream is opened"); + + dig = Helper.GetDigestInstance(digestName); + _messageDigests[digestName] = dig; + } + } + + // TODO Make public? + internal void Generate( + Stream outStream, + string eContentType, + bool encapsulate, + Stream dataOutputStream, + CmsProcessable content) + { + Stream signedOut = Open(outStream, eContentType, encapsulate, dataOutputStream); + if (content != null) + { + content.Write(signedOut); + } + signedOut.Dispose(); + } + + // 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 DerInteger CalculateVersion( + DerObjectIdentifier contentOid) + { + bool otherCert = false; + bool otherCrl = false; + bool attrCertV1Found = false; + bool attrCertV2Found = false; + + if (_certs != null) + { + foreach (object obj in _certs) + { + if (obj is Asn1TaggedObject) + { + Asn1TaggedObject tagged = (Asn1TaggedObject) obj; + + if (tagged.TagNo == 1) + { + attrCertV1Found = true; + } + else if (tagged.TagNo == 2) + { + attrCertV2Found = true; + } + else if (tagged.TagNo == 3) + { + otherCert = true; + break; + } + } + } + } + + if (otherCert) + { + return new DerInteger(5); + } + + if (_crls != null) + { + foreach (object obj in _crls) + { + if (obj is Asn1TaggedObject) + { + otherCrl = true; + break; + } + } + } + + if (otherCrl) + { + return new DerInteger(5); + } + + if (attrCertV2Found) + { + return new DerInteger(4); + } + + if (attrCertV1Found || !CmsObjectIdentifiers.Data.Equals(contentOid) || CheckForVersion3(_signers)) + { + return new DerInteger(3); + } + + return new DerInteger(1); + } + + private bool CheckForVersion3( + IList signerInfos) + { + foreach (SignerInformation si in signerInfos) + { + SignerInfo s = SignerInfo.GetInstance(si.ToSignerInfo()); + + if (s.Version.Value.IntValue == 3) + { + return true; + } + } + + return false; + } + + private static Stream AttachDigestsToOutputStream(ICollection digests, Stream s) + { + Stream result = s; + foreach (IDigest digest in digests) + { + result = GetSafeTeeOutputStream(result, new DigOutputStream(digest)); + } + return result; + } + + private static Stream GetSafeOutputStream(Stream s) + { + if (s == null) + return new NullOutputStream(); + return s; + } + + private static Stream GetSafeTeeOutputStream(Stream s1, Stream s2) + { + if (s1 == null) + return GetSafeOutputStream(s2); + if (s2 == null) + return GetSafeOutputStream(s1); + return new TeeOutputStream(s1, s2); + } + + private class CmsSignedDataOutputStream + : BaseOutputStream + { + private readonly CmsSignedDataStreamGenerator outer; + + private Stream _out; + private DerObjectIdentifier _contentOID; + private BerSequenceGenerator _sGen; + private BerSequenceGenerator _sigGen; + private BerSequenceGenerator _eiGen; + + public CmsSignedDataOutputStream( + CmsSignedDataStreamGenerator outer, + Stream outStream, + string contentOID, + BerSequenceGenerator sGen, + BerSequenceGenerator sigGen, + BerSequenceGenerator eiGen) + { + this.outer = outer; + + _out = outStream; + _contentOID = new DerObjectIdentifier(contentOID); + _sGen = sGen; + _sigGen = sigGen; + _eiGen = eiGen; + } + + public override void WriteByte( + byte b) + { + _out.WriteByte(b); + } + + public override void Write( + byte[] bytes, + int off, + int len) + { + _out.Write(bytes, off, len); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _out.Dispose(); + + // TODO Parent context(s) should really be be closed explicitly + + _eiGen.Close(); + + outer._digests.Clear(); // clear the current preserved digest state + + if (outer._certs.Count > 0) + { + Asn1Set certs = CmsUtilities.CreateBerSetFromList(outer._certs); + + WriteToGenerator(_sigGen, new BerTaggedObject(false, 0, certs)); + } + + if (outer._crls.Count > 0) + { + Asn1Set crls = CmsUtilities.CreateBerSetFromList(outer._crls); + + WriteToGenerator(_sigGen, new BerTaggedObject(false, 1, crls)); + } + + // + // Calculate the digest hashes + // + foreach (DictionaryEntry de in outer._messageDigests) + { + outer._messageHashes.Add(de.Key, DigestUtilities.DoFinal((IDigest)de.Value)); + } + + // TODO If the digest OIDs for precalculated signers weren't mixed in with + // the others, we could fill in outer._digests here, instead of SignerInfoGenerator.Generate + + // + // collect all the SignerInfo objects + // + Asn1EncodableVector signerInfos = new Asn1EncodableVector(); + + // + // add the generated SignerInfo objects + // + { + foreach (DigestAndSignerInfoGeneratorHolder holder in outer._signerInfs) + { + AlgorithmIdentifier digestAlgorithm = holder.DigestAlgorithm; + + byte[] calculatedDigest = (byte[])outer._messageHashes[ + Helper.GetDigestAlgName(holder.digestOID)]; + outer._digests[holder.digestOID] = calculatedDigest.Clone(); + + signerInfos.Add(holder.signerInf.Generate(_contentOID, digestAlgorithm, calculatedDigest)); + } + } + + // + // add the precalculated SignerInfo objects. + // + { + foreach (SignerInformation signer in outer._signers) + { + // TODO Verify the content type and calculated digest match the precalculated SignerInfo + // if (!signer.ContentType.Equals(_contentOID)) + // { + // // TODO The precalculated content type did not match - error? + // } + // + // byte[] calculatedDigest = (byte[])outer._digests[signer.DigestAlgOid]; + // if (calculatedDigest == null) + // { + // // TODO We can't confirm this digest because we didn't calculate it - error? + // } + // else + // { + // if (!Arrays.AreEqual(signer.GetContentDigest(), calculatedDigest)) + // { + // // TODO The precalculated digest did not match - error? + // } + // } + + signerInfos.Add(signer.ToSignerInfo()); + } + } + + WriteToGenerator(_sigGen, new DerSet(signerInfos)); + + _sigGen.Close(); + _sGen.Close(); + } + + base.Dispose(disposing); + } + + private static void WriteToGenerator( + Asn1Generator ag, + Asn1Encodable ae) + { + byte[] encoded = ae.GetEncoded(); + ag.GetRawOutputStream().Write(encoded, 0, encoded.Length); + } + } + } +} diff --git a/Crypto/src/cms/CMSSignedGenerator.cs b/Crypto/src/cms/CMSSignedGenerator.cs new file mode 100644
index 000000000..f272c830e --- /dev/null +++ b/Crypto/src/cms/CMSSignedGenerator.cs
@@ -0,0 +1,261 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.Utilities.IO; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.Cms +{ + public class CmsSignedGenerator + { + /** + * Default type for the signed data. + */ + public static readonly string Data = CmsObjectIdentifiers.Data.Id; + + public static readonly string DigestSha1 = OiwObjectIdentifiers.IdSha1.Id; + public static readonly string DigestSha224 = NistObjectIdentifiers.IdSha224.Id; + public static readonly string DigestSha256 = NistObjectIdentifiers.IdSha256.Id; + public static readonly string DigestSha384 = NistObjectIdentifiers.IdSha384.Id; + public static readonly string DigestSha512 = NistObjectIdentifiers.IdSha512.Id; + public static readonly string DigestMD5 = PkcsObjectIdentifiers.MD5.Id; + public static readonly string DigestGost3411 = CryptoProObjectIdentifiers.GostR3411.Id; + public static readonly string DigestRipeMD128 = TeleTrusTObjectIdentifiers.RipeMD128.Id; + public static readonly string DigestRipeMD160 = TeleTrusTObjectIdentifiers.RipeMD160.Id; + public static readonly string DigestRipeMD256 = TeleTrusTObjectIdentifiers.RipeMD256.Id; + + public static readonly string EncryptionRsa = PkcsObjectIdentifiers.RsaEncryption.Id; + public static readonly string EncryptionDsa = X9ObjectIdentifiers.IdDsaWithSha1.Id; + public static readonly string EncryptionECDsa = X9ObjectIdentifiers.ECDsaWithSha1.Id; + public static readonly string EncryptionRsaPss = PkcsObjectIdentifiers.IdRsassaPss.Id; + public static readonly string EncryptionGost3410 = CryptoProObjectIdentifiers.GostR3410x94.Id; + public static readonly string EncryptionECGost3410 = CryptoProObjectIdentifiers.GostR3410x2001.Id; + + private static readonly string EncryptionECDsaWithSha1 = X9ObjectIdentifiers.ECDsaWithSha1.Id; + private static readonly string EncryptionECDsaWithSha224 = X9ObjectIdentifiers.ECDsaWithSha224.Id; + private static readonly string EncryptionECDsaWithSha256 = X9ObjectIdentifiers.ECDsaWithSha256.Id; + private static readonly string EncryptionECDsaWithSha384 = X9ObjectIdentifiers.ECDsaWithSha384.Id; + private static readonly string EncryptionECDsaWithSha512 = X9ObjectIdentifiers.ECDsaWithSha512.Id; + + private static readonly ISet noParams = new HashSet(); + private static readonly IDictionary ecAlgorithms = Platform.CreateHashtable(); + + static CmsSignedGenerator() + { + noParams.Add(EncryptionDsa); +// noParams.Add(EncryptionECDsa); + noParams.Add(EncryptionECDsaWithSha1); + noParams.Add(EncryptionECDsaWithSha224); + noParams.Add(EncryptionECDsaWithSha256); + noParams.Add(EncryptionECDsaWithSha384); + noParams.Add(EncryptionECDsaWithSha512); + + ecAlgorithms.Add(DigestSha1, EncryptionECDsaWithSha1); + ecAlgorithms.Add(DigestSha224, EncryptionECDsaWithSha224); + ecAlgorithms.Add(DigestSha256, EncryptionECDsaWithSha256); + ecAlgorithms.Add(DigestSha384, EncryptionECDsaWithSha384); + ecAlgorithms.Add(DigestSha512, EncryptionECDsaWithSha512); + } + + internal IList _certs = Platform.CreateArrayList(); + internal IList _crls = Platform.CreateArrayList(); + internal IList _signers = Platform.CreateArrayList(); + internal IDictionary _digests = Platform.CreateHashtable(); + + protected readonly SecureRandom rand; + + protected CmsSignedGenerator() + : this(new SecureRandom()) + { + } + + /// <summary>Constructor allowing specific source of randomness</summary> + /// <param name="rand">Instance of <c>SecureRandom</c> to use.</param> + protected CmsSignedGenerator( + SecureRandom rand) + { + this.rand = rand; + } + + protected string GetEncOid( + AsymmetricKeyParameter key, + string digestOID) + { + string encOID = null; + + if (key is RsaKeyParameters) + { + if (!((RsaKeyParameters) key).IsPrivate) + throw new ArgumentException("Expected RSA private key"); + + encOID = EncryptionRsa; + } + else if (key is DsaPrivateKeyParameters) + { + if (!digestOID.Equals(DigestSha1)) + throw new ArgumentException("can't mix DSA with anything but SHA1"); + + encOID = EncryptionDsa; + } + else if (key is ECPrivateKeyParameters) + { + ECPrivateKeyParameters ecPrivKey = (ECPrivateKeyParameters) key; + string algName = ecPrivKey.AlgorithmName; + + if (algName == "ECGOST3410") + { + encOID = EncryptionECGost3410; + } + else + { + // TODO Should we insist on algName being one of "EC" or "ECDSA", as Java does? + encOID = (string) ecAlgorithms[digestOID]; + + if (encOID == null) + throw new ArgumentException("can't mix ECDSA with anything but SHA family digests"); + } + } + else if (key is Gost3410PrivateKeyParameters) + { + encOID = EncryptionGost3410; + } + else + { + throw new ArgumentException("Unknown algorithm in CmsSignedGenerator.GetEncOid"); + } + + return encOID; + } + + internal static AlgorithmIdentifier GetEncAlgorithmIdentifier( + DerObjectIdentifier encOid, + Asn1Encodable sigX509Parameters) + { + if (noParams.Contains(encOid.Id)) + { + return new AlgorithmIdentifier(encOid); + } + + return new AlgorithmIdentifier(encOid, sigX509Parameters); + } + + internal protected virtual IDictionary GetBaseParameters( + DerObjectIdentifier contentType, + AlgorithmIdentifier digAlgId, + byte[] hash) + { + IDictionary param = Platform.CreateHashtable(); + + if (contentType != null) + { + param[CmsAttributeTableParameter.ContentType] = contentType; + } + + param[CmsAttributeTableParameter.DigestAlgorithmIdentifier] = digAlgId; + param[CmsAttributeTableParameter.Digest] = hash.Clone(); + + return param; + } + + internal protected virtual Asn1Set GetAttributeSet( + Asn1.Cms.AttributeTable attr) + { + return attr == null + ? null + : new DerSet(attr.ToAsn1EncodableVector()); + } + + public void AddCertificates( + IX509Store certStore) + { + CollectionUtilities.AddRange(_certs, CmsUtilities.GetCertificatesFromStore(certStore)); + } + + public void AddCrls( + IX509Store crlStore) + { + CollectionUtilities.AddRange(_crls, CmsUtilities.GetCrlsFromStore(crlStore)); + } + + /** + * Add the attribute certificates contained in the passed in store to the + * generator. + * + * @param store a store of Version 2 attribute certificates + * @throws CmsException if an error occurse processing the store. + */ + public void AddAttributeCertificates( + IX509Store store) + { + try + { + foreach (IX509AttributeCertificate attrCert in store.GetMatches(null)) + { + _certs.Add(new DerTaggedObject(false, 2, + AttributeCertificate.GetInstance(Asn1Object.FromByteArray(attrCert.GetEncoded())))); + } + } + catch (Exception e) + { + throw new CmsException("error processing attribute certs", e); + } + } + + /** + * Add a store of precalculated signers to the generator. + * + * @param signerStore store of signers + */ + public void AddSigners( + SignerInformationStore signerStore) + { + foreach (SignerInformation o in signerStore.GetSigners()) + { + _signers.Add(o); + AddSignerCallback(o); + } + } + + /** + * Return a map of oids and byte arrays representing the digests calculated on the content during + * the last generate. + * + * @return a map of oids (as String objects) and byte[] representing digests. + */ + public IDictionary GetGeneratedDigests() + { + return Platform.CreateHashtable(_digests); + } + + internal virtual void AddSignerCallback( + SignerInformation si) + { + } + + internal static SignerIdentifier GetSignerIdentifier(X509Certificate cert) + { + return new SignerIdentifier(CmsUtilities.GetIssuerAndSerialNumber(cert)); + } + + internal static SignerIdentifier GetSignerIdentifier(byte[] subjectKeyIdentifier) + { + return new SignerIdentifier(new DerOctetString(subjectKeyIdentifier)); + } + } +} diff --git a/Crypto/src/cms/CMSSignedHelper.cs b/Crypto/src/cms/CMSSignedHelper.cs new file mode 100644
index 000000000..b3406fc06 --- /dev/null +++ b/Crypto/src/cms/CMSSignedHelper.cs
@@ -0,0 +1,319 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Eac; +using Org.BouncyCastle.Asn1.Iana; +using Org.BouncyCastle.Asn1.Misc; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.Cms +{ + internal class CmsSignedHelper + { + internal static readonly CmsSignedHelper Instance = new CmsSignedHelper(); + + private static readonly IDictionary encryptionAlgs = Platform.CreateHashtable(); + private static readonly IDictionary digestAlgs = Platform.CreateHashtable(); + private static readonly IDictionary digestAliases = Platform.CreateHashtable(); + + private static void AddEntries(DerObjectIdentifier oid, string digest, string encryption) + { + string alias = oid.Id; + digestAlgs.Add(alias, digest); + encryptionAlgs.Add(alias, encryption); + } + + static CmsSignedHelper() + { + AddEntries(NistObjectIdentifiers.DsaWithSha224, "SHA224", "DSA"); + AddEntries(NistObjectIdentifiers.DsaWithSha256, "SHA256", "DSA"); + AddEntries(NistObjectIdentifiers.DsaWithSha384, "SHA384", "DSA"); + AddEntries(NistObjectIdentifiers.DsaWithSha512, "SHA512", "DSA"); + AddEntries(OiwObjectIdentifiers.DsaWithSha1, "SHA1", "DSA"); + AddEntries(OiwObjectIdentifiers.MD4WithRsa, "MD4", "RSA"); + AddEntries(OiwObjectIdentifiers.MD4WithRsaEncryption, "MD4", "RSA"); + AddEntries(OiwObjectIdentifiers.MD5WithRsa, "MD5", "RSA"); + AddEntries(OiwObjectIdentifiers.Sha1WithRsa, "SHA1", "RSA"); + AddEntries(PkcsObjectIdentifiers.MD2WithRsaEncryption, "MD2", "RSA"); + AddEntries(PkcsObjectIdentifiers.MD4WithRsaEncryption, "MD4", "RSA"); + AddEntries(PkcsObjectIdentifiers.MD5WithRsaEncryption, "MD5", "RSA"); + AddEntries(PkcsObjectIdentifiers.Sha1WithRsaEncryption, "SHA1", "RSA"); + AddEntries(PkcsObjectIdentifiers.Sha224WithRsaEncryption, "SHA224", "RSA"); + AddEntries(PkcsObjectIdentifiers.Sha256WithRsaEncryption, "SHA256", "RSA"); + AddEntries(PkcsObjectIdentifiers.Sha384WithRsaEncryption, "SHA384", "RSA"); + AddEntries(PkcsObjectIdentifiers.Sha512WithRsaEncryption, "SHA512", "RSA"); + AddEntries(X9ObjectIdentifiers.ECDsaWithSha1, "SHA1", "ECDSA"); + AddEntries(X9ObjectIdentifiers.ECDsaWithSha224, "SHA224", "ECDSA"); + AddEntries(X9ObjectIdentifiers.ECDsaWithSha256, "SHA256", "ECDSA"); + AddEntries(X9ObjectIdentifiers.ECDsaWithSha384, "SHA384", "ECDSA"); + AddEntries(X9ObjectIdentifiers.ECDsaWithSha512, "SHA512", "ECDSA"); + AddEntries(X9ObjectIdentifiers.IdDsaWithSha1, "SHA1", "DSA"); + AddEntries(EacObjectIdentifiers.id_TA_ECDSA_SHA_1, "SHA1", "ECDSA"); + AddEntries(EacObjectIdentifiers.id_TA_ECDSA_SHA_224, "SHA224", "ECDSA"); + AddEntries(EacObjectIdentifiers.id_TA_ECDSA_SHA_256, "SHA256", "ECDSA"); + AddEntries(EacObjectIdentifiers.id_TA_ECDSA_SHA_384, "SHA384", "ECDSA"); + AddEntries(EacObjectIdentifiers.id_TA_ECDSA_SHA_512, "SHA512", "ECDSA"); + AddEntries(EacObjectIdentifiers.id_TA_RSA_v1_5_SHA_1, "SHA1", "RSA"); + AddEntries(EacObjectIdentifiers.id_TA_RSA_v1_5_SHA_256, "SHA256", "RSA"); + AddEntries(EacObjectIdentifiers.id_TA_RSA_PSS_SHA_1, "SHA1", "RSAandMGF1"); + AddEntries(EacObjectIdentifiers.id_TA_RSA_PSS_SHA_256, "SHA256", "RSAandMGF1"); + + encryptionAlgs.Add(X9ObjectIdentifiers.IdDsa.Id, "DSA"); + encryptionAlgs.Add(PkcsObjectIdentifiers.RsaEncryption.Id, "RSA"); + encryptionAlgs.Add(TeleTrusTObjectIdentifiers.TeleTrusTRsaSignatureAlgorithm, "RSA"); + encryptionAlgs.Add(X509ObjectIdentifiers.IdEARsa.Id, "RSA"); + encryptionAlgs.Add(CmsSignedGenerator.EncryptionRsaPss, "RSAandMGF1"); + encryptionAlgs.Add(CryptoProObjectIdentifiers.GostR3410x94.Id, "GOST3410"); + encryptionAlgs.Add(CryptoProObjectIdentifiers.GostR3410x2001.Id, "ECGOST3410"); + encryptionAlgs.Add("1.3.6.1.4.1.5849.1.6.2", "ECGOST3410"); + encryptionAlgs.Add("1.3.6.1.4.1.5849.1.1.5", "GOST3410"); + + digestAlgs.Add(PkcsObjectIdentifiers.MD2.Id, "MD2"); + digestAlgs.Add(PkcsObjectIdentifiers.MD4.Id, "MD4"); + digestAlgs.Add(PkcsObjectIdentifiers.MD5.Id, "MD5"); + digestAlgs.Add(OiwObjectIdentifiers.IdSha1.Id, "SHA1"); + digestAlgs.Add(NistObjectIdentifiers.IdSha224.Id, "SHA224"); + digestAlgs.Add(NistObjectIdentifiers.IdSha256.Id, "SHA256"); + digestAlgs.Add(NistObjectIdentifiers.IdSha384.Id, "SHA384"); + digestAlgs.Add(NistObjectIdentifiers.IdSha512.Id, "SHA512"); + digestAlgs.Add(TeleTrusTObjectIdentifiers.RipeMD128.Id, "RIPEMD128"); + digestAlgs.Add(TeleTrusTObjectIdentifiers.RipeMD160.Id, "RIPEMD160"); + digestAlgs.Add(TeleTrusTObjectIdentifiers.RipeMD256.Id, "RIPEMD256"); + digestAlgs.Add(CryptoProObjectIdentifiers.GostR3411.Id, "GOST3411"); + digestAlgs.Add("1.3.6.1.4.1.5849.1.2.1", "GOST3411"); + + digestAliases.Add("SHA1", new string[] { "SHA-1" }); + digestAliases.Add("SHA224", new string[] { "SHA-224" }); + digestAliases.Add("SHA256", new string[] { "SHA-256" }); + digestAliases.Add("SHA384", new string[] { "SHA-384" }); + digestAliases.Add("SHA512", new string[] { "SHA-512" }); + } + + /** + * Return the digest algorithm using one of the standard JCA string + * representations rather than the algorithm identifier (if possible). + */ + internal string GetDigestAlgName( + string digestAlgOid) + { + string algName = (string)digestAlgs[digestAlgOid]; + + if (algName != null) + { + return algName; + } + + return digestAlgOid; + } + + internal string[] GetDigestAliases( + string algName) + { + string[] aliases = (string[]) digestAliases[algName]; + + return aliases == null ? new String[0] : (string[]) aliases.Clone(); + } + + /** + * Return the digest encryption algorithm using one of the standard + * JCA string representations rather than the algorithm identifier (if + * possible). + */ + internal string GetEncryptionAlgName( + string encryptionAlgOid) + { + string algName = (string) encryptionAlgs[encryptionAlgOid]; + + if (algName != null) + { + return algName; + } + + return encryptionAlgOid; + } + + internal IDigest GetDigestInstance( + string algorithm) + { + try + { + return DigestUtilities.GetDigest(algorithm); + } + catch (SecurityUtilityException e) + { + // This is probably superfluous on C#, since no provider infrastructure, + // assuming DigestUtilities already knows all the aliases + foreach (string alias in GetDigestAliases(algorithm)) + { + try { return DigestUtilities.GetDigest(alias); } + catch (SecurityUtilityException) {} + } + throw e; + } + } + + internal ISigner GetSignatureInstance( + string algorithm) + { + return SignerUtilities.GetSigner(algorithm); + } + + internal IX509Store CreateAttributeStore( + string type, + Asn1Set certSet) + { + IList certs = Platform.CreateArrayList(); + + if (certSet != null) + { + foreach (Asn1Encodable ae in certSet) + { + try + { + Asn1Object obj = ae.ToAsn1Object(); + + if (obj is Asn1TaggedObject) + { + Asn1TaggedObject tagged = (Asn1TaggedObject)obj; + + if (tagged.TagNo == 2) + { + certs.Add( + new X509V2AttributeCertificate( + Asn1Sequence.GetInstance(tagged, false).GetEncoded())); + } + } + } + catch (Exception ex) + { + throw new CmsException("can't re-encode attribute certificate!", ex); + } + } + } + + try + { + return X509StoreFactory.Create( + "AttributeCertificate/" + type, + new X509CollectionStoreParameters(certs)); + } + catch (ArgumentException e) + { + throw new CmsException("can't setup the X509Store", e); + } + } + + internal IX509Store CreateCertificateStore( + string type, + Asn1Set certSet) + { + IList certs = Platform.CreateArrayList(); + + if (certSet != null) + { + AddCertsFromSet(certs, certSet); + } + + try + { + return X509StoreFactory.Create( + "Certificate/" + type, + new X509CollectionStoreParameters(certs)); + } + catch (ArgumentException e) + { + throw new CmsException("can't setup the X509Store", e); + } + } + + internal IX509Store CreateCrlStore( + string type, + Asn1Set crlSet) + { + IList crls = Platform.CreateArrayList(); + + if (crlSet != null) + { + AddCrlsFromSet(crls, crlSet); + } + + try + { + return X509StoreFactory.Create( + "CRL/" + type, + new X509CollectionStoreParameters(crls)); + } + catch (ArgumentException e) + { + throw new CmsException("can't setup the X509Store", e); + } + } + + private void AddCertsFromSet( + IList certs, + Asn1Set certSet) + { + X509CertificateParser cf = new X509CertificateParser(); + + foreach (Asn1Encodable ae in certSet) + { + try + { + Asn1Object obj = ae.ToAsn1Object(); + + if (obj is Asn1Sequence) + { + // TODO Build certificate directly from sequence? + certs.Add(cf.ReadCertificate(obj.GetEncoded())); + } + } + catch (Exception ex) + { + throw new CmsException("can't re-encode certificate!", ex); + } + } + } + + private void AddCrlsFromSet( + IList crls, + Asn1Set crlSet) + { + X509CrlParser cf = new X509CrlParser(); + + foreach (Asn1Encodable ae in crlSet) + { + try + { + // TODO Build CRL directly from ae.ToAsn1Object()? + crls.Add(cf.ReadCrl(ae.GetEncoded())); + } + catch (Exception ex) + { + throw new CmsException("can't re-encode CRL!", ex); + } + } + } + + internal AlgorithmIdentifier FixAlgID( + AlgorithmIdentifier algId) + { + if (algId.Parameters == null) + return new AlgorithmIdentifier(algId.ObjectID, DerNull.Instance); + + return algId; + } + } +} diff --git a/Crypto/src/cms/CMSStreamException.cs b/Crypto/src/cms/CMSStreamException.cs new file mode 100644
index 000000000..1626fb8b4 --- /dev/null +++ b/Crypto/src/cms/CMSStreamException.cs
@@ -0,0 +1,26 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Cms +{ + public class CmsStreamException + : IOException + { + public CmsStreamException() + { + } + + public CmsStreamException( + string name) + : base(name) + { + } + + public CmsStreamException( + string name, + Exception e) + : base(name, e) + { + } + } +} diff --git a/Crypto/src/cms/CMSTypedStream.cs b/Crypto/src/cms/CMSTypedStream.cs new file mode 100644
index 000000000..576edf234 --- /dev/null +++ b/Crypto/src/cms/CMSTypedStream.cs
@@ -0,0 +1,72 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Utilities; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Cms +{ + public class CmsTypedStream + { + private const int BufferSize = 32 * 1024; + + private readonly string _oid; + private readonly Stream _in; + + public CmsTypedStream( + Stream inStream) + : this(PkcsObjectIdentifiers.Data.Id, inStream, BufferSize) + { + } + + public CmsTypedStream( + string oid, + Stream inStream) + : this(oid, inStream, BufferSize) + { + } + + public CmsTypedStream( + string oid, + Stream inStream, + int bufSize) + { + _oid = oid; +#if NETCF_1_0 || NETCF_2_0 || SILVERLIGHT || PORTABLE + _in = new FullReaderStream(inStream); +#else + _in = new FullReaderStream(new BufferedStream(inStream, bufSize)); +#endif + } + + public string ContentType + { + get { return _oid; } + } + + public Stream ContentStream + { + get { return _in; } + } + + public void Drain() + { + Streams.Drain(_in); + _in.Dispose(); + } + + private class FullReaderStream : FilterStream + { + internal FullReaderStream(Stream input) + : base(input) + { + } + + public override int Read(byte[] buf, int off, int len) + { + return Streams.ReadFully(base.s, buf, off, len); + } + } + } +} diff --git a/Crypto/src/cms/CMSUtils.cs b/Crypto/src/cms/CMSUtils.cs new file mode 100644
index 000000000..95d710607 --- /dev/null +++ b/Crypto/src/cms/CMSUtils.cs
@@ -0,0 +1,186 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.Cms +{ + internal class CmsUtilities + { + // TODO Is there a .NET equivalent to this? +// private static readonly Runtime RUNTIME = Runtime.getRuntime(); + + internal static int MaximumMemory + { + get + { + // TODO Is there a .NET equivalent to this? + long maxMem = int.MaxValue;//RUNTIME.maxMemory(); + + if (maxMem > int.MaxValue) + { + return int.MaxValue; + } + + return (int)maxMem; + } + } + + internal static ContentInfo ReadContentInfo( + byte[] input) + { + // enforce limit checking as from a byte array + return ReadContentInfo(new Asn1InputStream(input)); + } + + internal static ContentInfo ReadContentInfo( + Stream input) + { + // enforce some limit checking + return ReadContentInfo(new Asn1InputStream(input, MaximumMemory)); + } + + private static ContentInfo ReadContentInfo( + Asn1InputStream aIn) + { + try + { + return ContentInfo.GetInstance(aIn.ReadObject()); + } + catch (IOException e) + { + throw new CmsException("IOException reading content.", e); + } + catch (InvalidCastException e) + { + throw new CmsException("Malformed content.", e); + } + catch (ArgumentException e) + { + throw new CmsException("Malformed content.", e); + } + } + + public static byte[] StreamToByteArray( + Stream inStream) + { + return Streams.ReadAll(inStream); + } + + public static byte[] StreamToByteArray( + Stream inStream, + int limit) + { + return Streams.ReadAllLimited(inStream, limit); + } + + public static IList GetCertificatesFromStore( + IX509Store certStore) + { + try + { + IList certs = Platform.CreateArrayList(); + + if (certStore != null) + { + foreach (X509Certificate c in certStore.GetMatches(null)) + { + certs.Add( + X509CertificateStructure.GetInstance( + Asn1Object.FromByteArray(c.GetEncoded()))); + } + } + + return certs; + } + catch (CertificateEncodingException e) + { + throw new CmsException("error encoding certs", e); + } + catch (Exception e) + { + throw new CmsException("error processing certs", e); + } + } + + public static IList GetCrlsFromStore( + IX509Store crlStore) + { + try + { + IList crls = Platform.CreateArrayList(); + + if (crlStore != null) + { + foreach (X509Crl c in crlStore.GetMatches(null)) + { + crls.Add( + CertificateList.GetInstance( + Asn1Object.FromByteArray(c.GetEncoded()))); + } + } + + return crls; + } + catch (CrlException e) + { + throw new CmsException("error encoding crls", e); + } + catch (Exception e) + { + throw new CmsException("error processing crls", e); + } + } + + public static Asn1Set CreateBerSetFromList( + IList berObjects) + { + Asn1EncodableVector v = new Asn1EncodableVector(); + + foreach (Asn1Encodable ae in berObjects) + { + v.Add(ae); + } + + return new BerSet(v); + } + + public static Asn1Set CreateDerSetFromList( + IList derObjects) + { + Asn1EncodableVector v = new Asn1EncodableVector(); + + foreach (Asn1Encodable ae in derObjects) + { + v.Add(ae); + } + + return new DerSet(v); + } + + internal static Stream CreateBerOctetOutputStream(Stream s, int tagNo, bool isExplicit, int bufferSize) + { + BerOctetStringGenerator octGen = new BerOctetStringGenerator(s, tagNo, isExplicit); + return octGen.GetOctetOutputStream(bufferSize); + } + + internal static TbsCertificateStructure GetTbsCertificateStructure(X509Certificate cert) + { + return TbsCertificateStructure.GetInstance(Asn1Object.FromByteArray(cert.GetTbsCertificate())); + } + + internal static IssuerAndSerialNumber GetIssuerAndSerialNumber(X509Certificate cert) + { + TbsCertificateStructure tbsCert = GetTbsCertificateStructure(cert); + return new IssuerAndSerialNumber(tbsCert.Issuer, tbsCert.SerialNumber.Value); + } + } +} diff --git a/Crypto/src/cms/CounterSignatureDigestCalculator.cs b/Crypto/src/cms/CounterSignatureDigestCalculator.cs new file mode 100644
index 000000000..6f8bf65a2 --- /dev/null +++ b/Crypto/src/cms/CounterSignatureDigestCalculator.cs
@@ -0,0 +1,28 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Cms +{ + internal class CounterSignatureDigestCalculator + : IDigestCalculator + { + private readonly string alg; + private readonly byte[] data; + + internal CounterSignatureDigestCalculator( + string alg, + byte[] data) + { + this.alg = alg; + this.data = data; + } + + public byte[] GetDigest() + { + IDigest digest = CmsSignedHelper.Instance.GetDigestInstance(alg); + return DigestUtilities.DoFinal(digest, data); + } + } +} diff --git a/Crypto/src/cms/DefaultAuthenticatedAttributeTableGenerator.cs b/Crypto/src/cms/DefaultAuthenticatedAttributeTableGenerator.cs new file mode 100644
index 000000000..d49b1d9d2 --- /dev/null +++ b/Crypto/src/cms/DefaultAuthenticatedAttributeTableGenerator.cs
@@ -0,0 +1,90 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Cms +{ + /** + * Default authenticated attributes generator. + */ + public class DefaultAuthenticatedAttributeTableGenerator + : CmsAttributeTableGenerator + { + private readonly IDictionary table; + + /** + * Initialise to use all defaults + */ + public DefaultAuthenticatedAttributeTableGenerator() + { + table = Platform.CreateHashtable(); + } + + /** + * Initialise with some extra attributes or overrides. + * + * @param attributeTable initial attribute table to use. + */ + public DefaultAuthenticatedAttributeTableGenerator( + AttributeTable attributeTable) + { + if (attributeTable != null) + { + table = attributeTable.ToDictionary(); + } + else + { + table = Platform.CreateHashtable(); + } + } + + /** + * Create a standard attribute table from the passed in parameters - this will + * normally include contentType and messageDigest. If the constructor + * using an AttributeTable was used, entries in it for contentType and + * messageDigest will override the generated ones. + * + * @param parameters source parameters for table generation. + * + * @return a filled in IDictionary of attributes. + */ + protected virtual IDictionary CreateStandardAttributeTable( + IDictionary parameters) + { + IDictionary std = Platform.CreateHashtable(table); + + if (!std.Contains(CmsAttributes.ContentType)) + { + DerObjectIdentifier contentType = (DerObjectIdentifier) + parameters[CmsAttributeTableParameter.ContentType]; + Asn1.Cms.Attribute attr = new Asn1.Cms.Attribute(CmsAttributes.ContentType, + new DerSet(contentType)); + std[attr.AttrType] = attr; + } + + if (!std.Contains(CmsAttributes.MessageDigest)) + { + byte[] messageDigest = (byte[])parameters[CmsAttributeTableParameter.Digest]; + Asn1.Cms.Attribute attr = new Asn1.Cms.Attribute(CmsAttributes.MessageDigest, + new DerSet(new DerOctetString(messageDigest))); + std[attr.AttrType] = attr; + } + + return std; + } + + /** + * @param parameters source parameters + * @return the populated attribute table + */ + public virtual AttributeTable GetAttributes( + IDictionary parameters) + { + IDictionary table = CreateStandardAttributeTable(parameters); + return new AttributeTable(table); + } + } +} diff --git a/Crypto/src/cms/DefaultSignedAttributeTableGenerator.cs b/Crypto/src/cms/DefaultSignedAttributeTableGenerator.cs new file mode 100644
index 000000000..0e93392d9 --- /dev/null +++ b/Crypto/src/cms/DefaultSignedAttributeTableGenerator.cs
@@ -0,0 +1,124 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Cms +{ + /** + * Default signed attributes generator. + */ + public class DefaultSignedAttributeTableGenerator + : CmsAttributeTableGenerator + { + private readonly IDictionary table; + + /** + * Initialise to use all defaults + */ + public DefaultSignedAttributeTableGenerator() + { + table = Platform.CreateHashtable(); + } + + /** + * Initialise with some extra attributes or overrides. + * + * @param attributeTable initial attribute table to use. + */ + public DefaultSignedAttributeTableGenerator( + AttributeTable attributeTable) + { + if (attributeTable != null) + { + table = attributeTable.ToDictionary(); + } + else + { + table = Platform.CreateHashtable(); + } + } + +#if (SILVERLIGHT || PORTABLE) + /** + * Create a standard attribute table from the passed in parameters - this will + * normally include contentType, signingTime, and messageDigest. If the constructor + * using an AttributeTable was used, entries in it for contentType, signingTime, and + * messageDigest will override the generated ones. + * + * @param parameters source parameters for table generation. + * + * @return a filled in Hashtable of attributes. + */ + protected virtual IDictionary createStandardAttributeTable( + IDictionary parameters) + { + IDictionary std = Platform.CreateHashtable(table); + DoCreateStandardAttributeTable(parameters, std); + return std; + } +#else + /** + * Create a standard attribute table from the passed in parameters - this will + * normally include contentType, signingTime, and messageDigest. If the constructor + * using an AttributeTable was used, entries in it for contentType, signingTime, and + * messageDigest will override the generated ones. + * + * @param parameters source parameters for table generation. + * + * @return a filled in Hashtable of attributes. + */ + protected virtual Hashtable createStandardAttributeTable( + IDictionary parameters) + { + Hashtable std = new Hashtable(table); + DoCreateStandardAttributeTable(parameters, std); + return std; + } +#endif + + private void DoCreateStandardAttributeTable(IDictionary parameters, IDictionary std) + { + // contentType will be absent if we're trying to generate a counter signature. + if (parameters.Contains(CmsAttributeTableParameter.ContentType)) + { + if (!std.Contains(CmsAttributes.ContentType)) + { + DerObjectIdentifier contentType = (DerObjectIdentifier) + parameters[CmsAttributeTableParameter.ContentType]; + Asn1.Cms.Attribute attr = new Asn1.Cms.Attribute(CmsAttributes.ContentType, + new DerSet(contentType)); + std[attr.AttrType] = attr; + } + } + + if (!std.Contains(CmsAttributes.SigningTime)) + { + Asn1.Cms.Attribute attr = new Asn1.Cms.Attribute(CmsAttributes.SigningTime, + new DerSet(new Time(DateTime.UtcNow))); + std[attr.AttrType] = attr; + } + + if (!std.Contains(CmsAttributes.MessageDigest)) + { + byte[] messageDigest = (byte[])parameters[CmsAttributeTableParameter.Digest]; + Asn1.Cms.Attribute attr = new Asn1.Cms.Attribute(CmsAttributes.MessageDigest, + new DerSet(new DerOctetString(messageDigest))); + std[attr.AttrType] = attr; + } + } + + /** + * @param parameters source parameters + * @return the populated attribute table + */ + public virtual AttributeTable GetAttributes( + IDictionary parameters) + { + IDictionary table = createStandardAttributeTable(parameters); + return new AttributeTable(table); + } + } +} diff --git a/Crypto/src/cms/DigOutputStream.cs b/Crypto/src/cms/DigOutputStream.cs new file mode 100644
index 000000000..103b45cac --- /dev/null +++ b/Crypto/src/cms/DigOutputStream.cs
@@ -0,0 +1,28 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Cms +{ + internal class DigOutputStream + : BaseOutputStream + { + private readonly IDigest dig; + + internal DigOutputStream(IDigest dig) + { + this.dig = dig; + } + + public override void WriteByte(byte b) + { + dig.Update(b); + } + + public override void Write(byte[] b, int off, int len) + { + dig.BlockUpdate(b, off, len); + } + } +} diff --git a/Crypto/src/cms/IDigestCalculator.cs b/Crypto/src/cms/IDigestCalculator.cs new file mode 100644
index 000000000..3661e4023 --- /dev/null +++ b/Crypto/src/cms/IDigestCalculator.cs
@@ -0,0 +1,9 @@ +using System; + +namespace Org.BouncyCastle.Cms +{ + internal interface IDigestCalculator + { + byte[] GetDigest(); + } +} diff --git a/Crypto/src/cms/KEKRecipientInfoGenerator.cs b/Crypto/src/cms/KEKRecipientInfoGenerator.cs new file mode 100644
index 000000000..a9bedade6 --- /dev/null +++ b/Crypto/src/cms/KEKRecipientInfoGenerator.cs
@@ -0,0 +1,137 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.Kisa; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Ntt; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Cms +{ + internal class KekRecipientInfoGenerator : RecipientInfoGenerator + { + private static readonly CmsEnvelopedHelper Helper = CmsEnvelopedHelper.Instance; + + private KeyParameter keyEncryptionKey; + // TODO Can get this from keyEncryptionKey? + private string keyEncryptionKeyOID; + private KekIdentifier kekIdentifier; + + // Derived + private AlgorithmIdentifier keyEncryptionAlgorithm; + + internal KekRecipientInfoGenerator() + { + } + + internal KekIdentifier KekIdentifier + { + set { this.kekIdentifier = value; } + } + + internal KeyParameter KeyEncryptionKey + { + set + { + this.keyEncryptionKey = value; + this.keyEncryptionAlgorithm = DetermineKeyEncAlg(keyEncryptionKeyOID, keyEncryptionKey); + } + } + + internal string KeyEncryptionKeyOID + { + set { this.keyEncryptionKeyOID = value; } + } + + public RecipientInfo Generate(KeyParameter contentEncryptionKey, SecureRandom random) + { + byte[] keyBytes = contentEncryptionKey.GetKey(); + + IWrapper keyWrapper = Helper.CreateWrapper(keyEncryptionAlgorithm.ObjectID.Id); + keyWrapper.Init(true, new ParametersWithRandom(keyEncryptionKey, random)); + Asn1OctetString encryptedKey = new DerOctetString( + keyWrapper.Wrap(keyBytes, 0, keyBytes.Length)); + + return new RecipientInfo(new KekRecipientInfo(kekIdentifier, keyEncryptionAlgorithm, encryptedKey)); + } + + private static AlgorithmIdentifier DetermineKeyEncAlg( + string algorithm, KeyParameter key) + { + if (algorithm.StartsWith("DES")) + { + return new AlgorithmIdentifier( + PkcsObjectIdentifiers.IdAlgCms3DesWrap, + DerNull.Instance); + } + else if (algorithm.StartsWith("RC2")) + { + return new AlgorithmIdentifier( + PkcsObjectIdentifiers.IdAlgCmsRC2Wrap, + new DerInteger(58)); + } + else if (algorithm.StartsWith("AES")) + { + int length = key.GetKey().Length * 8; + DerObjectIdentifier wrapOid; + + if (length == 128) + { + wrapOid = NistObjectIdentifiers.IdAes128Wrap; + } + else if (length == 192) + { + wrapOid = NistObjectIdentifiers.IdAes192Wrap; + } + else if (length == 256) + { + wrapOid = NistObjectIdentifiers.IdAes256Wrap; + } + else + { + throw new ArgumentException("illegal keysize in AES"); + } + + return new AlgorithmIdentifier(wrapOid); // parameters absent + } + else if (algorithm.StartsWith("SEED")) + { + // parameters absent + return new AlgorithmIdentifier(KisaObjectIdentifiers.IdNpkiAppCmsSeedWrap); + } + else if (algorithm.StartsWith("CAMELLIA")) + { + int length = key.GetKey().Length * 8; + DerObjectIdentifier wrapOid; + + if (length == 128) + { + wrapOid = NttObjectIdentifiers.IdCamellia128Wrap; + } + else if (length == 192) + { + wrapOid = NttObjectIdentifiers.IdCamellia192Wrap; + } + else if (length == 256) + { + wrapOid = NttObjectIdentifiers.IdCamellia256Wrap; + } + else + { + throw new ArgumentException("illegal keysize in Camellia"); + } + + return new AlgorithmIdentifier(wrapOid); // parameters must be absent + } + else + { + throw new ArgumentException("unknown algorithm"); + } + } + } +} diff --git a/Crypto/src/cms/KEKRecipientInformation.cs b/Crypto/src/cms/KEKRecipientInformation.cs new file mode 100644
index 000000000..f960197d6 --- /dev/null +++ b/Crypto/src/cms/KEKRecipientInformation.cs
@@ -0,0 +1,62 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Cms +{ + /** + * the RecipientInfo class for a recipient who has been sent a message + * encrypted using a secret key known to the other side. + */ + public class KekRecipientInformation + : RecipientInformation + { + private KekRecipientInfo info; + + internal KekRecipientInformation( + KekRecipientInfo info, + CmsSecureReadable secureReadable) + : base(info.KeyEncryptionAlgorithm, secureReadable) + { + this.info = info; + this.rid = new RecipientID(); + + KekIdentifier kekId = info.KekID; + + rid.KeyIdentifier = kekId.KeyIdentifier.GetOctets(); + } + + /** + * decrypt the content and return an input stream. + */ + public override CmsTypedStream GetContentStream( + ICipherParameters key) + { + try + { + byte[] encryptedKey = info.EncryptedKey.GetOctets(); + IWrapper keyWrapper = WrapperUtilities.GetWrapper(keyEncAlg.ObjectID.Id); + + keyWrapper.Init(false, key); + + KeyParameter sKey = ParameterUtilities.CreateKeyParameter( + GetContentAlgorithmName(), keyWrapper.Unwrap(encryptedKey, 0, encryptedKey.Length)); + + return GetContentFromSessionKey(sKey); + } + catch (SecurityUtilityException e) + { + throw new CmsException("couldn't create cipher.", e); + } + catch (InvalidKeyException e) + { + throw new CmsException("key invalid in message.", e); + } + } + } +} diff --git a/Crypto/src/cms/KeyAgreeRecipientInfoGenerator.cs b/Crypto/src/cms/KeyAgreeRecipientInfoGenerator.cs new file mode 100644
index 000000000..4fafb7c6e --- /dev/null +++ b/Crypto/src/cms/KeyAgreeRecipientInfoGenerator.cs
@@ -0,0 +1,171 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.Cms.Ecc; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Cms +{ + internal class KeyAgreeRecipientInfoGenerator : RecipientInfoGenerator + { + private static readonly CmsEnvelopedHelper Helper = CmsEnvelopedHelper.Instance; + + private DerObjectIdentifier keyAgreementOID; + private DerObjectIdentifier keyEncryptionOID; + private IList recipientCerts; + private AsymmetricCipherKeyPair senderKeyPair; + + internal KeyAgreeRecipientInfoGenerator() + { + } + + internal DerObjectIdentifier KeyAgreementOID + { + set { this.keyAgreementOID = value; } + } + + internal DerObjectIdentifier KeyEncryptionOID + { + set { this.keyEncryptionOID = value; } + } + + internal ICollection RecipientCerts + { + set { this.recipientCerts = Platform.CreateArrayList(value); } + } + + internal AsymmetricCipherKeyPair SenderKeyPair + { + set { this.senderKeyPair = value; } + } + + public RecipientInfo Generate(KeyParameter contentEncryptionKey, SecureRandom random) + { + byte[] keyBytes = contentEncryptionKey.GetKey(); + + AsymmetricKeyParameter senderPublicKey = senderKeyPair.Public; + ICipherParameters senderPrivateParams = senderKeyPair.Private; + + + OriginatorIdentifierOrKey originator; + try + { + originator = new OriginatorIdentifierOrKey( + CreateOriginatorPublicKey(senderPublicKey)); + } + catch (IOException e) + { + throw new InvalidKeyException("cannot extract originator public key: " + e); + } + + + Asn1OctetString ukm = null; + if (keyAgreementOID.Id.Equals(CmsEnvelopedGenerator.ECMqvSha1Kdf)) + { + try + { + IAsymmetricCipherKeyPairGenerator ephemKPG = + GeneratorUtilities.GetKeyPairGenerator(keyAgreementOID); + ephemKPG.Init( + ((ECPublicKeyParameters)senderPublicKey).CreateKeyGenerationParameters(random)); + + AsymmetricCipherKeyPair ephemKP = ephemKPG.GenerateKeyPair(); + + ukm = new DerOctetString( + new MQVuserKeyingMaterial( + CreateOriginatorPublicKey(ephemKP.Public), null)); + + senderPrivateParams = new MqvPrivateParameters( + (ECPrivateKeyParameters)senderPrivateParams, + (ECPrivateKeyParameters)ephemKP.Private, + (ECPublicKeyParameters)ephemKP.Public); + } + catch (IOException e) + { + throw new InvalidKeyException("cannot extract MQV ephemeral public key: " + e); + } + catch (SecurityUtilityException e) + { + throw new InvalidKeyException("cannot determine MQV ephemeral key pair parameters from public key: " + e); + } + } + + + DerSequence paramSeq = new DerSequence( + keyEncryptionOID, + DerNull.Instance); + AlgorithmIdentifier keyEncAlg = new AlgorithmIdentifier(keyAgreementOID, paramSeq); + + + Asn1EncodableVector recipientEncryptedKeys = new Asn1EncodableVector(); + foreach (X509Certificate recipientCert in recipientCerts) + { + TbsCertificateStructure tbsCert; + try + { + tbsCert = TbsCertificateStructure.GetInstance( + Asn1Object.FromByteArray(recipientCert.GetTbsCertificate())); + } + catch (Exception) + { + throw new ArgumentException("can't extract TBS structure from certificate"); + } + + // TODO Should there be a SubjectKeyIdentifier-based alternative? + IssuerAndSerialNumber issuerSerial = new IssuerAndSerialNumber( + tbsCert.Issuer, tbsCert.SerialNumber.Value); + KeyAgreeRecipientIdentifier karid = new KeyAgreeRecipientIdentifier(issuerSerial); + + ICipherParameters recipientPublicParams = recipientCert.GetPublicKey(); + if (keyAgreementOID.Id.Equals(CmsEnvelopedGenerator.ECMqvSha1Kdf)) + { + recipientPublicParams = new MqvPublicParameters( + (ECPublicKeyParameters)recipientPublicParams, + (ECPublicKeyParameters)recipientPublicParams); + } + + // Use key agreement to choose a wrap key for this recipient + IBasicAgreement keyAgreement = AgreementUtilities.GetBasicAgreementWithKdf( + keyAgreementOID, keyEncryptionOID.Id); + keyAgreement.Init(new ParametersWithRandom(senderPrivateParams, random)); + BigInteger agreedValue = keyAgreement.CalculateAgreement(recipientPublicParams); + + int keyEncryptionKeySize = GeneratorUtilities.GetDefaultKeySize(keyEncryptionOID) / 8; + byte[] keyEncryptionKeyBytes = X9IntegerConverter.IntegerToBytes(agreedValue, keyEncryptionKeySize); + KeyParameter keyEncryptionKey = ParameterUtilities.CreateKeyParameter( + keyEncryptionOID, keyEncryptionKeyBytes); + + // Wrap the content encryption key with the agreement key + IWrapper keyWrapper = Helper.CreateWrapper(keyEncryptionOID.Id); + keyWrapper.Init(true, new ParametersWithRandom(keyEncryptionKey, random)); + byte[] encryptedKeyBytes = keyWrapper.Wrap(keyBytes, 0, keyBytes.Length); + + Asn1OctetString encryptedKey = new DerOctetString(encryptedKeyBytes); + + recipientEncryptedKeys.Add(new RecipientEncryptedKey(karid, encryptedKey)); + } + + return new RecipientInfo(new KeyAgreeRecipientInfo(originator, ukm, keyEncAlg, + new DerSequence(recipientEncryptedKeys))); + } + + private static OriginatorPublicKey CreateOriginatorPublicKey( + AsymmetricKeyParameter publicKey) + { + SubjectPublicKeyInfo spki = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(publicKey); + return new OriginatorPublicKey( + new AlgorithmIdentifier(spki.AlgorithmID.ObjectID, DerNull.Instance), + spki.PublicKeyData.GetBytes()); + } + } +} diff --git a/Crypto/src/cms/KeyAgreeRecipientInformation.cs b/Crypto/src/cms/KeyAgreeRecipientInformation.cs new file mode 100644
index 000000000..38a94b0a4 --- /dev/null +++ b/Crypto/src/cms/KeyAgreeRecipientInformation.cs
@@ -0,0 +1,226 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.Cms.Ecc; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Utilities; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Pkcs; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Cms +{ + /** + * the RecipientInfo class for a recipient who has been sent a message + * encrypted using key agreement. + */ + public class KeyAgreeRecipientInformation + : RecipientInformation + { + private KeyAgreeRecipientInfo info; + private Asn1OctetString encryptedKey; + + internal static void ReadRecipientInfo(IList infos, KeyAgreeRecipientInfo info, + CmsSecureReadable secureReadable) + { + try + { + foreach (Asn1Encodable rek in info.RecipientEncryptedKeys) + { + RecipientEncryptedKey id = RecipientEncryptedKey.GetInstance(rek.ToAsn1Object()); + + RecipientID rid = new RecipientID(); + + Asn1.Cms.KeyAgreeRecipientIdentifier karid = id.Identifier; + + Asn1.Cms.IssuerAndSerialNumber iAndSN = karid.IssuerAndSerialNumber; + if (iAndSN != null) + { + rid.Issuer = iAndSN.Name; + rid.SerialNumber = iAndSN.SerialNumber.Value; + } + else + { + Asn1.Cms.RecipientKeyIdentifier rKeyID = karid.RKeyID; + + // Note: 'date' and 'other' fields of RecipientKeyIdentifier appear to be only informational + + rid.SubjectKeyIdentifier = rKeyID.SubjectKeyIdentifier.GetOctets(); + } + + infos.Add(new KeyAgreeRecipientInformation(info, rid, id.EncryptedKey, + secureReadable)); + } + } + catch (IOException e) + { + throw new ArgumentException("invalid rid in KeyAgreeRecipientInformation", e); + } + } + + internal KeyAgreeRecipientInformation( + KeyAgreeRecipientInfo info, + RecipientID rid, + Asn1OctetString encryptedKey, + CmsSecureReadable secureReadable) + : base(info.KeyEncryptionAlgorithm, secureReadable) + { + this.info = info; + this.rid = rid; + this.encryptedKey = encryptedKey; + } + + private AsymmetricKeyParameter GetSenderPublicKey( + AsymmetricKeyParameter receiverPrivateKey, + OriginatorIdentifierOrKey originator) + { + OriginatorPublicKey opk = originator.OriginatorPublicKey; + if (opk != null) + { + return GetPublicKeyFromOriginatorPublicKey(receiverPrivateKey, opk); + } + + OriginatorID origID = new OriginatorID(); + + Asn1.Cms.IssuerAndSerialNumber iAndSN = originator.IssuerAndSerialNumber; + if (iAndSN != null) + { + origID.Issuer = iAndSN.Name; + origID.SerialNumber = iAndSN.SerialNumber.Value; + } + else + { + SubjectKeyIdentifier ski = originator.SubjectKeyIdentifier; + + origID.SubjectKeyIdentifier = ski.GetKeyIdentifier(); + } + + return GetPublicKeyFromOriginatorID(origID); + } + + private AsymmetricKeyParameter GetPublicKeyFromOriginatorPublicKey( + AsymmetricKeyParameter receiverPrivateKey, + OriginatorPublicKey originatorPublicKey) + { + PrivateKeyInfo privInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(receiverPrivateKey); + SubjectPublicKeyInfo pubInfo = new SubjectPublicKeyInfo( + privInfo.AlgorithmID, + originatorPublicKey.PublicKey.GetBytes()); + return PublicKeyFactory.CreateKey(pubInfo); + } + + private AsymmetricKeyParameter GetPublicKeyFromOriginatorID( + OriginatorID origID) + { + // TODO Support all alternatives for OriginatorIdentifierOrKey + // see RFC 3852 6.2.2 + throw new CmsException("No support for 'originator' as IssuerAndSerialNumber or SubjectKeyIdentifier"); + } + + private KeyParameter CalculateAgreedWrapKey( + string wrapAlg, + AsymmetricKeyParameter senderPublicKey, + AsymmetricKeyParameter receiverPrivateKey) + { + DerObjectIdentifier agreeAlgID = keyEncAlg.ObjectID; + + ICipherParameters senderPublicParams = senderPublicKey; + ICipherParameters receiverPrivateParams = receiverPrivateKey; + + if (agreeAlgID.Id.Equals(CmsEnvelopedGenerator.ECMqvSha1Kdf)) + { + byte[] ukmEncoding = info.UserKeyingMaterial.GetOctets(); + MQVuserKeyingMaterial ukm = MQVuserKeyingMaterial.GetInstance( + Asn1Object.FromByteArray(ukmEncoding)); + + AsymmetricKeyParameter ephemeralKey = GetPublicKeyFromOriginatorPublicKey( + receiverPrivateKey, ukm.EphemeralPublicKey); + + senderPublicParams = new MqvPublicParameters( + (ECPublicKeyParameters)senderPublicParams, + (ECPublicKeyParameters)ephemeralKey); + receiverPrivateParams = new MqvPrivateParameters( + (ECPrivateKeyParameters)receiverPrivateParams, + (ECPrivateKeyParameters)receiverPrivateParams); + } + + IBasicAgreement agreement = AgreementUtilities.GetBasicAgreementWithKdf( + agreeAlgID, wrapAlg); + agreement.Init(receiverPrivateParams); + BigInteger agreedValue = agreement.CalculateAgreement(senderPublicParams); + + int wrapKeySize = GeneratorUtilities.GetDefaultKeySize(wrapAlg) / 8; + byte[] wrapKeyBytes = X9IntegerConverter.IntegerToBytes(agreedValue, wrapKeySize); + return ParameterUtilities.CreateKeyParameter(wrapAlg, wrapKeyBytes); + } + + private KeyParameter UnwrapSessionKey( + string wrapAlg, + KeyParameter agreedKey) + { + byte[] encKeyOctets = encryptedKey.GetOctets(); + + IWrapper keyCipher = WrapperUtilities.GetWrapper(wrapAlg); + keyCipher.Init(false, agreedKey); + byte[] sKeyBytes = keyCipher.Unwrap(encKeyOctets, 0, encKeyOctets.Length); + return ParameterUtilities.CreateKeyParameter(GetContentAlgorithmName(), sKeyBytes); + } + + internal KeyParameter GetSessionKey( + AsymmetricKeyParameter receiverPrivateKey) + { + try + { + string wrapAlg = DerObjectIdentifier.GetInstance( + Asn1Sequence.GetInstance(keyEncAlg.Parameters)[0]).Id; + + AsymmetricKeyParameter senderPublicKey = GetSenderPublicKey( + receiverPrivateKey, info.Originator); + + KeyParameter agreedWrapKey = CalculateAgreedWrapKey(wrapAlg, + senderPublicKey, receiverPrivateKey); + + return UnwrapSessionKey(wrapAlg, agreedWrapKey); + } + catch (SecurityUtilityException e) + { + throw new CmsException("couldn't create cipher.", e); + } + catch (InvalidKeyException e) + { + throw new CmsException("key invalid in message.", e); + } + catch (Exception e) + { + throw new CmsException("originator key invalid.", e); + } + } + + /** + * decrypt the content and return an input stream. + */ + public override CmsTypedStream GetContentStream( + ICipherParameters key) + { + if (!(key is AsymmetricKeyParameter)) + throw new ArgumentException("KeyAgreement requires asymmetric key", "key"); + + AsymmetricKeyParameter receiverPrivateKey = (AsymmetricKeyParameter) key; + + if (!receiverPrivateKey.IsPrivate) + throw new ArgumentException("Expected private key", "key"); + + KeyParameter sKey = GetSessionKey(receiverPrivateKey); + + return GetContentFromSessionKey(sKey); + } + } +} diff --git a/Crypto/src/cms/KeyTransRecipientInfoGenerator.cs b/Crypto/src/cms/KeyTransRecipientInfoGenerator.cs new file mode 100644
index 000000000..0992e6da6 --- /dev/null +++ b/Crypto/src/cms/KeyTransRecipientInfoGenerator.cs
@@ -0,0 +1,87 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Cms +{ + internal class KeyTransRecipientInfoGenerator : RecipientInfoGenerator + { + private static readonly CmsEnvelopedHelper Helper = CmsEnvelopedHelper.Instance; + + private TbsCertificateStructure recipientTbsCert; + private AsymmetricKeyParameter recipientPublicKey; + private Asn1OctetString subjectKeyIdentifier; + + // Derived fields + private SubjectPublicKeyInfo info; + + internal KeyTransRecipientInfoGenerator() + { + } + + internal X509Certificate RecipientCert + { + set + { + this.recipientTbsCert = CmsUtilities.GetTbsCertificateStructure(value); + this.recipientPublicKey = value.GetPublicKey(); + this.info = recipientTbsCert.SubjectPublicKeyInfo; + } + } + + internal AsymmetricKeyParameter RecipientPublicKey + { + set + { + this.recipientPublicKey = value; + + try + { + info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo( + recipientPublicKey); + } + catch (IOException) + { + throw new ArgumentException("can't extract key algorithm from this key"); + } + } + } + + internal Asn1OctetString SubjectKeyIdentifier + { + set { this.subjectKeyIdentifier = value; } + } + + public RecipientInfo Generate(KeyParameter contentEncryptionKey, SecureRandom random) + { + byte[] keyBytes = contentEncryptionKey.GetKey(); + AlgorithmIdentifier keyEncryptionAlgorithm = info.AlgorithmID; + + IWrapper keyWrapper = Helper.CreateWrapper(keyEncryptionAlgorithm.ObjectID.Id); + keyWrapper.Init(true, new ParametersWithRandom(recipientPublicKey, random)); + byte[] encryptedKeyBytes = keyWrapper.Wrap(keyBytes, 0, keyBytes.Length); + + RecipientIdentifier recipId; + if (recipientTbsCert != null) + { + IssuerAndSerialNumber issuerAndSerial = new IssuerAndSerialNumber( + recipientTbsCert.Issuer, recipientTbsCert.SerialNumber.Value); + recipId = new RecipientIdentifier(issuerAndSerial); + } + else + { + recipId = new RecipientIdentifier(subjectKeyIdentifier); + } + + return new RecipientInfo(new KeyTransRecipientInfo(recipId, keyEncryptionAlgorithm, + new DerOctetString(encryptedKeyBytes))); + } + } +} diff --git a/Crypto/src/cms/KeyTransRecipientInformation.cs b/Crypto/src/cms/KeyTransRecipientInformation.cs new file mode 100644
index 000000000..24121cb2c --- /dev/null +++ b/Crypto/src/cms/KeyTransRecipientInformation.cs
@@ -0,0 +1,113 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Asn1Pkcs = Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Cms +{ + /** + * the KeyTransRecipientInformation class for a recipient who has been sent a secret + * key encrypted using their public key that needs to be used to + * extract the message. + */ + public class KeyTransRecipientInformation + : RecipientInformation + { + private KeyTransRecipientInfo info; + + internal KeyTransRecipientInformation( + KeyTransRecipientInfo info, + CmsSecureReadable secureReadable) + : base(info.KeyEncryptionAlgorithm, secureReadable) + { + this.info = info; + this.rid = new RecipientID(); + + RecipientIdentifier r = info.RecipientIdentifier; + + try + { + if (r.IsTagged) + { + Asn1OctetString octs = Asn1OctetString.GetInstance(r.ID); + + rid.SubjectKeyIdentifier = octs.GetOctets(); + } + else + { + IssuerAndSerialNumber iAnds = IssuerAndSerialNumber.GetInstance(r.ID); + + rid.Issuer = iAnds.Name; + rid.SerialNumber = iAnds.SerialNumber.Value; + } + } + catch (IOException) + { + throw new ArgumentException("invalid rid in KeyTransRecipientInformation"); + } + } + + private string GetExchangeEncryptionAlgorithmName( + DerObjectIdentifier oid) + { + if (Asn1Pkcs.PkcsObjectIdentifiers.RsaEncryption.Equals(oid)) + { + return "RSA//PKCS1Padding"; + } + + return oid.Id; + } + + internal KeyParameter UnwrapKey(ICipherParameters key) + { + byte[] encryptedKey = info.EncryptedKey.GetOctets(); + string keyExchangeAlgorithm = GetExchangeEncryptionAlgorithmName(keyEncAlg.ObjectID); + + try + { + IWrapper keyWrapper = WrapperUtilities.GetWrapper(keyExchangeAlgorithm); + keyWrapper.Init(false, key); + + // FIXME Support for MAC algorithm parameters similar to cipher parameters + return ParameterUtilities.CreateKeyParameter( + GetContentAlgorithmName(), keyWrapper.Unwrap(encryptedKey, 0, encryptedKey.Length)); + } + catch (SecurityUtilityException e) + { + throw new CmsException("couldn't create cipher.", e); + } + catch (InvalidKeyException e) + { + throw new CmsException("key invalid in message.", e); + } +// catch (IllegalBlockSizeException e) + catch (DataLengthException e) + { + throw new CmsException("illegal blocksize in message.", e); + } +// catch (BadPaddingException e) + catch (InvalidCipherTextException e) + { + throw new CmsException("bad padding in message.", e); + } + } + + /** + * decrypt the content and return it as a byte array. + */ + public override CmsTypedStream GetContentStream( + ICipherParameters key) + { + KeyParameter sKey = UnwrapKey(key); + + return GetContentFromSessionKey(sKey); + } + } +} diff --git a/Crypto/src/cms/MacOutputStream.cs b/Crypto/src/cms/MacOutputStream.cs new file mode 100644
index 000000000..8891dbc2c --- /dev/null +++ b/Crypto/src/cms/MacOutputStream.cs
@@ -0,0 +1,28 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Cms +{ + internal class MacOutputStream + : BaseOutputStream + { + private readonly IMac mac; + + internal MacOutputStream(IMac mac) + { + this.mac = mac; + } + + public override void Write(byte[] b, int off, int len) + { + mac.BlockUpdate(b, off, len); + } + + public override void WriteByte(byte b) + { + mac.Update(b); + } + } +} diff --git a/Crypto/src/cms/NullOutputStream.cs b/Crypto/src/cms/NullOutputStream.cs new file mode 100644
index 000000000..ed937bdeb --- /dev/null +++ b/Crypto/src/cms/NullOutputStream.cs
@@ -0,0 +1,20 @@ +using System; + +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Cms +{ + internal class NullOutputStream + : BaseOutputStream + { + public override void WriteByte(byte b) + { + // do nothing + } + + public override void Write(byte[] buffer, int offset, int count) + { + // do nothing + } + } +} diff --git a/Crypto/src/cms/OriginatorId.cs b/Crypto/src/cms/OriginatorId.cs new file mode 100644
index 000000000..5a3b7374d --- /dev/null +++ b/Crypto/src/cms/OriginatorId.cs
@@ -0,0 +1,51 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.Cms +{ + /** + * a basic index for an originator. + */ + public class OriginatorID + : X509CertStoreSelector + { + public override int GetHashCode() + { + int code = Arrays.GetHashCode(this.SubjectKeyIdentifier); + + BigInteger serialNumber = this.SerialNumber; + if (serialNumber != null) + { + code ^= serialNumber.GetHashCode(); + } + + X509Name issuer = this.Issuer; + if (issuer != null) + { + code ^= issuer.GetHashCode(); + } + + return code; + } + + public override bool Equals( + object obj) + { + if (obj == this) + return false; + + OriginatorID id = obj as OriginatorID; + + if (id == null) + return false; + + return Arrays.AreEqual(SubjectKeyIdentifier, id.SubjectKeyIdentifier) + && Platform.Equals(SerialNumber, id.SerialNumber) + && IssuersMatch(Issuer, id.Issuer); + } + } +} diff --git a/Crypto/src/cms/PKCS5Scheme2PBEKey.cs b/Crypto/src/cms/PKCS5Scheme2PBEKey.cs new file mode 100644
index 000000000..08b8518a1 --- /dev/null +++ b/Crypto/src/cms/PKCS5Scheme2PBEKey.cs
@@ -0,0 +1,64 @@ +using System; + +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Cms +{ + /// <summary> + /// PKCS5 scheme-2 - password converted to bytes assuming ASCII. + /// </summary> + public class Pkcs5Scheme2PbeKey + : CmsPbeKey + { + [Obsolete("Use version taking 'char[]' instead")] + public Pkcs5Scheme2PbeKey( + string password, + byte[] salt, + int iterationCount) + : this(password.ToCharArray(), salt, iterationCount) + { + } + + [Obsolete("Use version taking 'char[]' instead")] + public Pkcs5Scheme2PbeKey( + string password, + AlgorithmIdentifier keyDerivationAlgorithm) + : this(password.ToCharArray(), keyDerivationAlgorithm) + { + } + + public Pkcs5Scheme2PbeKey( + char[] password, + byte[] salt, + int iterationCount) + : base(password, salt, iterationCount) + { + } + + public Pkcs5Scheme2PbeKey( + char[] password, + AlgorithmIdentifier keyDerivationAlgorithm) + : base(password, keyDerivationAlgorithm) + { + } + + internal override KeyParameter GetEncoded( + string algorithmOid) + { + Pkcs5S2ParametersGenerator gen = new Pkcs5S2ParametersGenerator(); + + gen.Init( + PbeParametersGenerator.Pkcs5PasswordToBytes(password), + salt, + iterationCount); + + return (KeyParameter) gen.GenerateDerivedParameters( + algorithmOid, + CmsEnvelopedHelper.Instance.GetKeySize(algorithmOid)); + } + } +} diff --git a/Crypto/src/cms/PKCS5Scheme2UTF8PBEKey.cs b/Crypto/src/cms/PKCS5Scheme2UTF8PBEKey.cs new file mode 100644
index 000000000..7aecc2978 --- /dev/null +++ b/Crypto/src/cms/PKCS5Scheme2UTF8PBEKey.cs
@@ -0,0 +1,64 @@ +using System; + +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Cms +{ + /** + * PKCS5 scheme-2 - password converted to bytes using UTF-8. + */ + public class Pkcs5Scheme2Utf8PbeKey + : CmsPbeKey + { + [Obsolete("Use version taking 'char[]' instead")] + public Pkcs5Scheme2Utf8PbeKey( + string password, + byte[] salt, + int iterationCount) + : this(password.ToCharArray(), salt, iterationCount) + { + } + + [Obsolete("Use version taking 'char[]' instead")] + public Pkcs5Scheme2Utf8PbeKey( + string password, + AlgorithmIdentifier keyDerivationAlgorithm) + : this(password.ToCharArray(), keyDerivationAlgorithm) + { + } + + public Pkcs5Scheme2Utf8PbeKey( + char[] password, + byte[] salt, + int iterationCount) + : base(password, salt, iterationCount) + { + } + + public Pkcs5Scheme2Utf8PbeKey( + char[] password, + AlgorithmIdentifier keyDerivationAlgorithm) + : base(password, keyDerivationAlgorithm) + { + } + + internal override KeyParameter GetEncoded( + string algorithmOid) + { + Pkcs5S2ParametersGenerator gen = new Pkcs5S2ParametersGenerator(); + + gen.Init( + PbeParametersGenerator.Pkcs5PasswordToUtf8Bytes(password), + salt, + iterationCount); + + return (KeyParameter) gen.GenerateDerivedParameters( + algorithmOid, + CmsEnvelopedHelper.Instance.GetKeySize(algorithmOid)); + } + } +} diff --git a/Crypto/src/cms/PasswordRecipientInfoGenerator.cs b/Crypto/src/cms/PasswordRecipientInfoGenerator.cs new file mode 100644
index 000000000..0a0b27b53 --- /dev/null +++ b/Crypto/src/cms/PasswordRecipientInfoGenerator.cs
@@ -0,0 +1,69 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Cms +{ + internal class PasswordRecipientInfoGenerator : RecipientInfoGenerator + { + private static readonly CmsEnvelopedHelper Helper = CmsEnvelopedHelper.Instance; + + private AlgorithmIdentifier keyDerivationAlgorithm; + private KeyParameter keyEncryptionKey; + // TODO Can get this from keyEncryptionKey? + private string keyEncryptionKeyOID; + + internal PasswordRecipientInfoGenerator() + { + } + + internal AlgorithmIdentifier KeyDerivationAlgorithm + { + set { this.keyDerivationAlgorithm = value; } + } + + internal KeyParameter KeyEncryptionKey + { + set { this.keyEncryptionKey = value; } + } + + internal string KeyEncryptionKeyOID + { + set { this.keyEncryptionKeyOID = value; } + } + + public RecipientInfo Generate(KeyParameter contentEncryptionKey, SecureRandom random) + { + byte[] keyBytes = contentEncryptionKey.GetKey(); + + string rfc3211WrapperName = Helper.GetRfc3211WrapperName(keyEncryptionKeyOID); + IWrapper keyWrapper = Helper.CreateWrapper(rfc3211WrapperName); + + // Note: In Java build, the IV is automatically generated in JCE layer + int ivLength = rfc3211WrapperName.StartsWith("DESEDE") ? 8 : 16; + byte[] iv = new byte[ivLength]; + random.NextBytes(iv); + + ICipherParameters parameters = new ParametersWithIV(keyEncryptionKey, iv); + keyWrapper.Init(true, new ParametersWithRandom(parameters, random)); + Asn1OctetString encryptedKey = new DerOctetString( + keyWrapper.Wrap(keyBytes, 0, keyBytes.Length)); + + DerSequence seq = new DerSequence( + new DerObjectIdentifier(keyEncryptionKeyOID), + new DerOctetString(iv)); + + AlgorithmIdentifier keyEncryptionAlgorithm = new AlgorithmIdentifier( + PkcsObjectIdentifiers.IdAlgPwriKek, seq); + + return new RecipientInfo(new PasswordRecipientInfo( + keyDerivationAlgorithm, keyEncryptionAlgorithm, encryptedKey)); + } + } +} diff --git a/Crypto/src/cms/PasswordRecipientInformation.cs b/Crypto/src/cms/PasswordRecipientInformation.cs new file mode 100644
index 000000000..f629caba6 --- /dev/null +++ b/Crypto/src/cms/PasswordRecipientInformation.cs
@@ -0,0 +1,79 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Cms +{ + /** + * the RecipientInfo class for a recipient who has been sent a message + * encrypted using a password. + */ + public class PasswordRecipientInformation + : RecipientInformation + { + private readonly PasswordRecipientInfo info; + + internal PasswordRecipientInformation( + PasswordRecipientInfo info, + CmsSecureReadable secureReadable) + : base(info.KeyEncryptionAlgorithm, secureReadable) + { + this.info = info; + this.rid = new RecipientID(); + } + + /** + * return the object identifier for the key derivation algorithm, or null + * if there is none present. + * + * @return OID for key derivation algorithm, if present. + */ + public virtual AlgorithmIdentifier KeyDerivationAlgorithm + { + get { return info.KeyDerivationAlgorithm; } + } + + /** + * decrypt the content and return an input stream. + */ + public override CmsTypedStream GetContentStream( + ICipherParameters key) + { + try + { + AlgorithmIdentifier kekAlg = AlgorithmIdentifier.GetInstance(info.KeyEncryptionAlgorithm); + Asn1Sequence kekAlgParams = (Asn1Sequence)kekAlg.Parameters; + byte[] encryptedKey = info.EncryptedKey.GetOctets(); + string kekAlgName = DerObjectIdentifier.GetInstance(kekAlgParams[0]).Id; + string cName = CmsEnvelopedHelper.Instance.GetRfc3211WrapperName(kekAlgName); + IWrapper keyWrapper = WrapperUtilities.GetWrapper(cName); + + byte[] iv = Asn1OctetString.GetInstance(kekAlgParams[1]).GetOctets(); + + ICipherParameters parameters = ((CmsPbeKey)key).GetEncoded(kekAlgName); + parameters = new ParametersWithIV(parameters, iv); + + keyWrapper.Init(false, parameters); + + KeyParameter sKey = ParameterUtilities.CreateKeyParameter( + GetContentAlgorithmName(), keyWrapper.Unwrap(encryptedKey, 0, encryptedKey.Length)); + + return GetContentFromSessionKey(sKey); + } + catch (SecurityUtilityException e) + { + throw new CmsException("couldn't create cipher.", e); + } + catch (InvalidKeyException e) + { + throw new CmsException("key invalid in message.", e); + } + } + } +} diff --git a/Crypto/src/cms/RecipientId.cs b/Crypto/src/cms/RecipientId.cs new file mode 100644
index 000000000..9b6eb093b --- /dev/null +++ b/Crypto/src/cms/RecipientId.cs
@@ -0,0 +1,58 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.Cms +{ + public class RecipientID + : X509CertStoreSelector + { + private byte[] keyIdentifier; + + public byte[] KeyIdentifier + { + get { return Arrays.Clone(keyIdentifier); } + set { keyIdentifier = Arrays.Clone(value); } + } + + public override int GetHashCode() + { + int code = Arrays.GetHashCode(keyIdentifier) + ^ Arrays.GetHashCode(this.SubjectKeyIdentifier); + + BigInteger serialNumber = this.SerialNumber; + if (serialNumber != null) + { + code ^= serialNumber.GetHashCode(); + } + + X509Name issuer = this.Issuer; + if (issuer != null) + { + code ^= issuer.GetHashCode(); + } + + return code; + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + RecipientID id = obj as RecipientID; + + if (id == null) + return false; + + return Arrays.AreEqual(keyIdentifier, id.keyIdentifier) + && Arrays.AreEqual(SubjectKeyIdentifier, id.SubjectKeyIdentifier) + && Platform.Equals(SerialNumber, id.SerialNumber) + && IssuersMatch(Issuer, id.Issuer); + } + } +} diff --git a/Crypto/src/cms/RecipientInfoGenerator.cs b/Crypto/src/cms/RecipientInfoGenerator.cs new file mode 100644
index 000000000..c41db6122 --- /dev/null +++ b/Crypto/src/cms/RecipientInfoGenerator.cs
@@ -0,0 +1,26 @@ +using System; + +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Cms +{ + interface RecipientInfoGenerator + { + /// <summary> + /// Generate a RecipientInfo object for the given key. + /// </summary> + /// <param name="contentEncryptionKey"> + /// A <see cref="KeyParameter"/> + /// </param> + /// <param name="random"> + /// A <see cref="SecureRandom"/> + /// </param> + /// <returns> + /// A <see cref="RecipientInfo"/> + /// </returns> + /// <exception cref="GeneralSecurityException"></exception> + RecipientInfo Generate(KeyParameter contentEncryptionKey, SecureRandom random); + } +} diff --git a/Crypto/src/cms/RecipientInformation.cs b/Crypto/src/cms/RecipientInformation.cs new file mode 100644
index 000000000..8b0316be4 --- /dev/null +++ b/Crypto/src/cms/RecipientInformation.cs
@@ -0,0 +1,126 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Cms +{ + public abstract class RecipientInformation + { + internal RecipientID rid = new RecipientID(); + internal AlgorithmIdentifier keyEncAlg; + internal CmsSecureReadable secureReadable; + + private byte[] resultMac; + + internal RecipientInformation( + AlgorithmIdentifier keyEncAlg, + CmsSecureReadable secureReadable) + { + this.keyEncAlg = keyEncAlg; + this.secureReadable = secureReadable; + } + + internal string GetContentAlgorithmName() + { + AlgorithmIdentifier algorithm = secureReadable.Algorithm; +// return CmsEnvelopedHelper.Instance.GetSymmetricCipherName(algorithm.ObjectID.Id); + return algorithm.ObjectID.Id; + } + + public RecipientID RecipientID + { + get { return rid; } + } + + public AlgorithmIdentifier KeyEncryptionAlgorithmID + { + get { return keyEncAlg; } + } + + /** + * return the object identifier for the key encryption algorithm. + * + * @return OID for key encryption algorithm. + */ + public string KeyEncryptionAlgOid + { + get { return keyEncAlg.ObjectID.Id; } + } + + /** + * return the ASN.1 encoded key encryption algorithm parameters, or null if + * there aren't any. + * + * @return ASN.1 encoding of key encryption algorithm parameters. + */ + public Asn1Object KeyEncryptionAlgParams + { + get + { + Asn1Encodable ae = keyEncAlg.Parameters; + + return ae == null ? null : ae.ToAsn1Object(); + } + } + + internal CmsTypedStream GetContentFromSessionKey( + KeyParameter sKey) + { + CmsReadable readable = secureReadable.GetReadable(sKey); + + try + { + return new CmsTypedStream(readable.GetInputStream()); + } + catch (IOException e) + { + throw new CmsException("error getting .", e); + } + } + + public byte[] GetContent( + ICipherParameters key) + { + try + { + return CmsUtilities.StreamToByteArray(GetContentStream(key).ContentStream); + } + catch (IOException e) + { + throw new Exception("unable to parse internal stream: " + e); + } + } + + /** + * Return the MAC calculated for the content stream. Note: this call is only meaningful once all + * the content has been read. + * + * @return byte array containing the mac. + */ + public byte[] GetMac() + { + if (resultMac == null) + { + object cryptoObject = secureReadable.CryptoObject; + if (cryptoObject is IMac) + { + resultMac = MacUtilities.DoFinal((IMac)cryptoObject); + } + } + + return Arrays.Clone(resultMac); + } + + public abstract CmsTypedStream GetContentStream(ICipherParameters key); + } +} diff --git a/Crypto/src/cms/RecipientInformationStore.cs b/Crypto/src/cms/RecipientInformationStore.cs new file mode 100644
index 000000000..33b472f9d --- /dev/null +++ b/Crypto/src/cms/RecipientInformationStore.cs
@@ -0,0 +1,86 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Cms +{ + public class RecipientInformationStore + { + private readonly IList all; //ArrayList[RecipientInformation] + private readonly IDictionary table = Platform.CreateHashtable(); // Hashtable[RecipientID, ArrayList[RecipientInformation]] + + public RecipientInformationStore( + ICollection recipientInfos) + { + foreach (RecipientInformation recipientInformation in recipientInfos) + { + RecipientID rid = recipientInformation.RecipientID; + IList list = (IList)table[rid]; + + if (list == null) + { + table[rid] = list = Platform.CreateArrayList(1); + } + + list.Add(recipientInformation); + } + + this.all = Platform.CreateArrayList(recipientInfos); + } + + public RecipientInformation this[RecipientID selector] + { + get { return GetFirstRecipient(selector); } + } + + /** + * Return the first RecipientInformation object that matches the + * passed in selector. Null if there are no matches. + * + * @param selector to identify a recipient + * @return a single RecipientInformation object. Null if none matches. + */ + public RecipientInformation GetFirstRecipient( + RecipientID selector) + { + IList list = (IList) table[selector]; + + return list == null ? null : (RecipientInformation) list[0]; + } + + /** + * Return the number of recipients in the collection. + * + * @return number of recipients identified. + */ + public int Count + { + get { return all.Count; } + } + + /** + * Return all recipients in the collection + * + * @return a collection of recipients. + */ + public ICollection GetRecipients() + { + return Platform.CreateArrayList(all); + } + + /** + * Return possible empty collection with recipients matching the passed in RecipientID + * + * @param selector a recipient id to select against. + * @return a collection of RecipientInformation objects. + */ + public ICollection GetRecipients( + RecipientID selector) + { + IList list = (IList)table[selector]; + + return list == null ? Platform.CreateArrayList() : Platform.CreateArrayList(list); + } + } +} diff --git a/Crypto/src/cms/SigOutputStream.cs b/Crypto/src/cms/SigOutputStream.cs new file mode 100644
index 000000000..a807fa7fc --- /dev/null +++ b/Crypto/src/cms/SigOutputStream.cs
@@ -0,0 +1,43 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Utilities.IO; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Cms +{ + internal class SigOutputStream + : BaseOutputStream + { + private readonly ISigner sig; + + internal SigOutputStream(ISigner sig) + { + this.sig = sig; + } + + public override void WriteByte(byte b) + { + try + { + sig.Update(b); + } + catch (SignatureException e) + { + throw new CmsStreamException("signature problem: " + e); + } + } + + public override void Write(byte[] b, int off, int len) + { + try + { + sig.BlockUpdate(b, off, len); + } + catch (SignatureException e) + { + throw new CmsStreamException("signature problem: " + e); + } + } + } +} \ No newline at end of file diff --git a/Crypto/src/cms/SignerId.cs b/Crypto/src/cms/SignerId.cs new file mode 100644
index 000000000..baac9369b --- /dev/null +++ b/Crypto/src/cms/SignerId.cs
@@ -0,0 +1,51 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.Cms +{ + /** + * a basic index for a signer. + */ + public class SignerID + : X509CertStoreSelector + { + public override int GetHashCode() + { + int code = Arrays.GetHashCode(this.SubjectKeyIdentifier); + + BigInteger serialNumber = this.SerialNumber; + if (serialNumber != null) + { + code ^= serialNumber.GetHashCode(); + } + + X509Name issuer = this.Issuer; + if (issuer != null) + { + code ^= issuer.GetHashCode(); + } + + return code; + } + + public override bool Equals( + object obj) + { + if (obj == this) + return false; + + SignerID id = obj as SignerID; + + if (id == null) + return false; + + return Arrays.AreEqual(SubjectKeyIdentifier, id.SubjectKeyIdentifier) + && Platform.Equals(SerialNumber, id.SerialNumber) + && IssuersMatch(Issuer, id.Issuer); + } + } +} diff --git a/Crypto/src/cms/SignerInfoGenerator.cs b/Crypto/src/cms/SignerInfoGenerator.cs new file mode 100644
index 000000000..f78cf2c01 --- /dev/null +++ b/Crypto/src/cms/SignerInfoGenerator.cs
@@ -0,0 +1,14 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Cms +{ + internal interface SignerInfoGenerator + { + SignerInfo Generate(DerObjectIdentifier contentType, AlgorithmIdentifier digestAlgorithm, + byte[] calculatedDigest); + } +} diff --git a/Crypto/src/cms/SignerInformation.cs b/Crypto/src/cms/SignerInformation.cs new file mode 100644
index 000000000..20af29a50 --- /dev/null +++ b/Crypto/src/cms/SignerInformation.cs
@@ -0,0 +1,758 @@ +using System; +using System.Collections; +using System.Diagnostics; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Signers; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Cms +{ + /** + * an expanded SignerInfo block from a CMS Signed message + */ + public class SignerInformation + { + private static readonly CmsSignedHelper Helper = CmsSignedHelper.Instance; + + private SignerID sid; + private SignerInfo info; + private AlgorithmIdentifier digestAlgorithm; + private AlgorithmIdentifier encryptionAlgorithm; + private readonly Asn1Set signedAttributeSet; + private readonly Asn1Set unsignedAttributeSet; + private CmsProcessable content; + private byte[] signature; + private DerObjectIdentifier contentType; + private IDigestCalculator digestCalculator; + private byte[] resultDigest; + + // Derived + private Asn1.Cms.AttributeTable signedAttributeTable; + private Asn1.Cms.AttributeTable unsignedAttributeTable; + private readonly bool isCounterSignature; + + internal SignerInformation( + SignerInfo info, + DerObjectIdentifier contentType, + CmsProcessable content, + IDigestCalculator digestCalculator) + { + this.info = info; + this.sid = new SignerID(); + this.contentType = contentType; + this.isCounterSignature = contentType == null; + + try + { + SignerIdentifier s = info.SignerID; + + if (s.IsTagged) + { + Asn1OctetString octs = Asn1OctetString.GetInstance(s.ID); + + sid.SubjectKeyIdentifier = octs.GetEncoded(); + } + else + { + Asn1.Cms.IssuerAndSerialNumber iAnds = + Asn1.Cms.IssuerAndSerialNumber.GetInstance(s.ID); + + sid.Issuer = iAnds.Name; + sid.SerialNumber = iAnds.SerialNumber.Value; + } + } + catch (IOException) + { + throw new ArgumentException("invalid sid in SignerInfo"); + } + + this.digestAlgorithm = info.DigestAlgorithm; + this.signedAttributeSet = info.AuthenticatedAttributes; + this.unsignedAttributeSet = info.UnauthenticatedAttributes; + this.encryptionAlgorithm = info.DigestEncryptionAlgorithm; + this.signature = info.EncryptedDigest.GetOctets(); + + this.content = content; + this.digestCalculator = digestCalculator; + } + + public bool IsCounterSignature + { + get { return isCounterSignature; } + } + + public DerObjectIdentifier ContentType + { + get { return contentType; } + } + + public SignerID SignerID + { + get { return sid; } + } + + /** + * return the version number for this objects underlying SignerInfo structure. + */ + public int Version + { + get { return info.Version.Value.IntValue; } + } + + public AlgorithmIdentifier DigestAlgorithmID + { + get { return digestAlgorithm; } + } + + /** + * return the object identifier for the signature. + */ + public string DigestAlgOid + { + get { return digestAlgorithm.ObjectID.Id; } + } + + /** + * return the signature parameters, or null if there aren't any. + */ + public Asn1Object DigestAlgParams + { + get + { + Asn1Encodable ae = digestAlgorithm.Parameters; + + return ae == null ? null : ae.ToAsn1Object(); + } + } + + /** + * return the content digest that was calculated during verification. + */ + public byte[] GetContentDigest() + { + if (resultDigest == null) + { + throw new InvalidOperationException("method can only be called after verify."); + } + + return (byte[])resultDigest.Clone(); + } + + public AlgorithmIdentifier EncryptionAlgorithmID + { + get { return encryptionAlgorithm; } + } + + /** + * return the object identifier for the signature. + */ + public string EncryptionAlgOid + { + get { return encryptionAlgorithm.ObjectID.Id; } + } + + /** + * return the signature/encryption algorithm parameters, or null if + * there aren't any. + */ + public Asn1Object EncryptionAlgParams + { + get + { + Asn1Encodable ae = encryptionAlgorithm.Parameters; + + return ae == null ? null : ae.ToAsn1Object(); + } + } + + /** + * return a table of the signed attributes - indexed by + * the OID of the attribute. + */ + public Asn1.Cms.AttributeTable SignedAttributes + { + get + { + if (signedAttributeSet != null && signedAttributeTable == null) + { + signedAttributeTable = new Asn1.Cms.AttributeTable(signedAttributeSet); + } + return signedAttributeTable; + } + } + + /** + * return a table of the unsigned attributes indexed by + * the OID of the attribute. + */ + public Asn1.Cms.AttributeTable UnsignedAttributes + { + get + { + if (unsignedAttributeSet != null && unsignedAttributeTable == null) + { + unsignedAttributeTable = new Asn1.Cms.AttributeTable(unsignedAttributeSet); + } + return unsignedAttributeTable; + } + } + + /** + * return the encoded signature + */ + public byte[] GetSignature() + { + return (byte[]) signature.Clone(); + } + + /** + * Return a SignerInformationStore containing the counter signatures attached to this + * signer. If no counter signatures are present an empty store is returned. + */ + public SignerInformationStore GetCounterSignatures() + { + // TODO There are several checks implied by the RFC3852 comments that are missing + + /* + The countersignature attribute MUST be an unsigned attribute; it MUST + NOT be a signed attribute, an authenticated attribute, an + unauthenticated attribute, or an unprotected attribute. + */ + Asn1.Cms.AttributeTable unsignedAttributeTable = UnsignedAttributes; + if (unsignedAttributeTable == null) + { + return new SignerInformationStore(Platform.CreateArrayList(0)); + } + + IList counterSignatures = Platform.CreateArrayList(); + + /* + The UnsignedAttributes syntax is defined as a SET OF Attributes. The + UnsignedAttributes in a signerInfo may include multiple instances of + the countersignature attribute. + */ + Asn1EncodableVector allCSAttrs = unsignedAttributeTable.GetAll(CmsAttributes.CounterSignature); + + foreach (Asn1.Cms.Attribute counterSignatureAttribute in allCSAttrs) + { + /* + A countersignature attribute can have multiple attribute values. The + syntax is defined as a SET OF AttributeValue, and there MUST be one + or more instances of AttributeValue present. + */ + Asn1Set values = counterSignatureAttribute.AttrValues; + if (values.Count < 1) + { + // TODO Throw an appropriate exception? + } + + foreach (Asn1Encodable asn1Obj in values) + { + /* + Countersignature values have the same meaning as SignerInfo values + for ordinary signatures, except that: + + 1. The signedAttributes field MUST NOT contain a content-type + attribute; there is no content type for countersignatures. + + 2. The signedAttributes field MUST contain a message-digest + attribute if it contains any other attributes. + + 3. The input to the message-digesting process is the contents + octets of the DER encoding of the signatureValue field of the + SignerInfo value with which the attribute is associated. + */ + SignerInfo si = SignerInfo.GetInstance(asn1Obj.ToAsn1Object()); + + string digestName = CmsSignedHelper.Instance.GetDigestAlgName(si.DigestAlgorithm.ObjectID.Id); + + counterSignatures.Add(new SignerInformation(si, null, null, new CounterSignatureDigestCalculator(digestName, GetSignature()))); + } + } + + return new SignerInformationStore(counterSignatures); + } + + /** + * return the DER encoding of the signed attributes. + * @throws IOException if an encoding error occurs. + */ + public byte[] GetEncodedSignedAttributes() + { + return signedAttributeSet == null + ? null + : signedAttributeSet.GetEncoded(Asn1Encodable.Der); + } + + private bool DoVerify( + AsymmetricKeyParameter key) + { + string digestName = Helper.GetDigestAlgName(this.DigestAlgOid); + IDigest digest = Helper.GetDigestInstance(digestName); + + DerObjectIdentifier sigAlgOid = this.encryptionAlgorithm.ObjectID; + Asn1Encodable sigParams = this.encryptionAlgorithm.Parameters; + ISigner sig; + + if (sigAlgOid.Equals(Asn1.Pkcs.PkcsObjectIdentifiers.IdRsassaPss)) + { + // RFC 4056 2.2 + // When the id-RSASSA-PSS algorithm identifier is used for a signature, + // the AlgorithmIdentifier parameters field MUST contain RSASSA-PSS-params. + if (sigParams == null) + throw new CmsException("RSASSA-PSS signature must specify algorithm parameters"); + + try + { + // TODO Provide abstract configuration mechanism + // (via alternate SignerUtilities.GetSigner method taking ASN.1 params) + + Asn1.Pkcs.RsassaPssParameters pss = Asn1.Pkcs.RsassaPssParameters.GetInstance( + sigParams.ToAsn1Object()); + + if (!pss.HashAlgorithm.ObjectID.Equals(this.digestAlgorithm.ObjectID)) + throw new CmsException("RSASSA-PSS signature parameters specified incorrect hash algorithm"); + if (!pss.MaskGenAlgorithm.ObjectID.Equals(Asn1.Pkcs.PkcsObjectIdentifiers.IdMgf1)) + throw new CmsException("RSASSA-PSS signature parameters specified unknown MGF"); + + IDigest pssDigest = DigestUtilities.GetDigest(pss.HashAlgorithm.ObjectID); + int saltLength = pss.SaltLength.Value.IntValue; + byte trailerField = (byte) pss.TrailerField.Value.IntValue; + + // RFC 4055 3.1 + // The value MUST be 1, which represents the trailer field with hexadecimal value 0xBC + if (trailerField != 1) + throw new CmsException("RSASSA-PSS signature parameters must have trailerField of 1"); + + sig = new PssSigner(new RsaBlindedEngine(), pssDigest, saltLength); + } + catch (Exception e) + { + throw new CmsException("failed to set RSASSA-PSS signature parameters", e); + } + } + else + { + // TODO Probably too strong a check at the moment +// if (sigParams != null) +// throw new CmsException("unrecognised signature parameters provided"); + + string signatureName = digestName + "with" + Helper.GetEncryptionAlgName(this.EncryptionAlgOid); + + sig = Helper.GetSignatureInstance(signatureName); + } + + try + { + if (digestCalculator != null) + { + resultDigest = digestCalculator.GetDigest(); + } + else + { + if (content != null) + { + content.Write(new DigOutputStream(digest)); + } + else if (signedAttributeSet == null) + { + // TODO Get rid of this exception and just treat content==null as empty not missing? + throw new CmsException("data not encapsulated in signature - use detached constructor."); + } + + resultDigest = DigestUtilities.DoFinal(digest); + } + } + catch (IOException e) + { + throw new CmsException("can't process mime object to create signature.", e); + } + + // RFC 3852 11.1 Check the content-type attribute is correct + { + Asn1Object validContentType = GetSingleValuedSignedAttribute( + CmsAttributes.ContentType, "content-type"); + if (validContentType == null) + { + if (!isCounterSignature && signedAttributeSet != null) + throw new CmsException("The content-type attribute type MUST be present whenever signed attributes are present in signed-data"); + } + else + { + if (isCounterSignature) + throw new CmsException("[For counter signatures,] the signedAttributes field MUST NOT contain a content-type attribute"); + + if (!(validContentType is DerObjectIdentifier)) + throw new CmsException("content-type attribute value not of ASN.1 type 'OBJECT IDENTIFIER'"); + + DerObjectIdentifier signedContentType = (DerObjectIdentifier)validContentType; + + if (!signedContentType.Equals(contentType)) + throw new CmsException("content-type attribute value does not match eContentType"); + } + } + + // RFC 3852 11.2 Check the message-digest attribute is correct + { + Asn1Object validMessageDigest = GetSingleValuedSignedAttribute( + CmsAttributes.MessageDigest, "message-digest"); + if (validMessageDigest == null) + { + if (signedAttributeSet != null) + throw new CmsException("the message-digest signed attribute type MUST be present when there are any signed attributes present"); + } + else + { + if (!(validMessageDigest is Asn1OctetString)) + { + throw new CmsException("message-digest attribute value not of ASN.1 type 'OCTET STRING'"); + } + + Asn1OctetString signedMessageDigest = (Asn1OctetString)validMessageDigest; + + if (!Arrays.AreEqual(resultDigest, signedMessageDigest.GetOctets())) + throw new CmsException("message-digest attribute value does not match calculated value"); + } + } + + // RFC 3852 11.4 Validate countersignature attribute(s) + { + Asn1.Cms.AttributeTable signedAttrTable = this.SignedAttributes; + if (signedAttrTable != null + && signedAttrTable.GetAll(CmsAttributes.CounterSignature).Count > 0) + { + throw new CmsException("A countersignature attribute MUST NOT be a signed attribute"); + } + + Asn1.Cms.AttributeTable unsignedAttrTable = this.UnsignedAttributes; + if (unsignedAttrTable != null) + { + foreach (Asn1.Cms.Attribute csAttr in unsignedAttrTable.GetAll(CmsAttributes.CounterSignature)) + { + if (csAttr.AttrValues.Count < 1) + throw new CmsException("A countersignature attribute MUST contain at least one AttributeValue"); + + // Note: We don't recursively validate the countersignature value + } + } + } + + try + { + sig.Init(false, key); + + if (signedAttributeSet == null) + { + if (digestCalculator != null) + { + // need to decrypt signature and check message bytes + return VerifyDigest(resultDigest, key, this.GetSignature()); + } + else if (content != null) + { + // TODO Use raw signature of the hash value instead + content.Write(new SigOutputStream(sig)); + } + } + else + { + byte[] tmp = this.GetEncodedSignedAttributes(); + sig.BlockUpdate(tmp, 0, tmp.Length); + } + + return sig.VerifySignature(this.GetSignature()); + } + catch (InvalidKeyException e) + { + throw new CmsException("key not appropriate to signature in message.", e); + } + catch (IOException e) + { + throw new CmsException("can't process mime object to create signature.", e); + } + catch (SignatureException e) + { + throw new CmsException("invalid signature format in message: " + e.Message, e); + } + } + + private bool IsNull( + Asn1Encodable o) + { + return (o is Asn1Null) || (o == null); + } + + private DigestInfo DerDecode( + byte[] encoding) + { + if (encoding[0] != (int)(Asn1Tags.Constructed | Asn1Tags.Sequence)) + { + throw new IOException("not a digest info object"); + } + + DigestInfo digInfo = DigestInfo.GetInstance(Asn1Object.FromByteArray(encoding)); + + // length check to avoid Bleichenbacher vulnerability + + if (digInfo.GetEncoded().Length != encoding.Length) + { + throw new CmsException("malformed RSA signature"); + } + + return digInfo; + } + + private bool VerifyDigest( + byte[] digest, + AsymmetricKeyParameter key, + byte[] signature) + { + string algorithm = Helper.GetEncryptionAlgName(this.EncryptionAlgOid); + + try + { + if (algorithm.Equals("RSA")) + { + IBufferedCipher c = CmsEnvelopedHelper.Instance.CreateAsymmetricCipher("RSA/ECB/PKCS1Padding"); + + c.Init(false, key); + + byte[] decrypt = c.DoFinal(signature); + + DigestInfo digInfo = DerDecode(decrypt); + + if (!digInfo.AlgorithmID.ObjectID.Equals(digestAlgorithm.ObjectID)) + { + return false; + } + + if (!IsNull(digInfo.AlgorithmID.Parameters)) + { + return false; + } + + byte[] sigHash = digInfo.GetDigest(); + + return Arrays.ConstantTimeAreEqual(digest, sigHash); + } + else if (algorithm.Equals("DSA")) + { + ISigner sig = SignerUtilities.GetSigner("NONEwithDSA"); + + sig.Init(false, key); + + sig.BlockUpdate(digest, 0, digest.Length); + + return sig.VerifySignature(signature); + } + else + { + throw new CmsException("algorithm: " + algorithm + " not supported in base signatures."); + } + } + catch (SecurityUtilityException e) + { + throw e; + } + catch (GeneralSecurityException e) + { + throw new CmsException("Exception processing signature: " + e, e); + } + catch (IOException e) + { + throw new CmsException("Exception decoding signature: " + e, e); + } + } + + /** + * verify that the given public key successfully handles and confirms the + * signature associated with this signer. + */ + public bool Verify( + AsymmetricKeyParameter pubKey) + { + if (pubKey.IsPrivate) + throw new ArgumentException("Expected public key", "pubKey"); + + // Optional, but still need to validate if present + GetSigningTime(); + + return DoVerify(pubKey); + } + + /** + * verify that the given certificate successfully handles and confirms + * the signature associated with this signer and, if a signingTime + * attribute is available, that the certificate was valid at the time the + * signature was generated. + */ + public bool Verify( + X509Certificate cert) + { + Asn1.Cms.Time signingTime = GetSigningTime(); + if (signingTime != null) + { + cert.CheckValidity(signingTime.Date); + } + + return DoVerify(cert.GetPublicKey()); + } + + /** + * Return the base ASN.1 CMS structure that this object contains. + * + * @return an object containing a CMS SignerInfo structure. + */ + public SignerInfo ToSignerInfo() + { + return info; + } + + private Asn1Object GetSingleValuedSignedAttribute( + DerObjectIdentifier attrOID, string printableName) + { + + Asn1.Cms.AttributeTable unsignedAttrTable = this.UnsignedAttributes; + if (unsignedAttrTable != null + && unsignedAttrTable.GetAll(attrOID).Count > 0) + { + throw new CmsException("The " + printableName + + " attribute MUST NOT be an unsigned attribute"); + } + + Asn1.Cms.AttributeTable signedAttrTable = this.SignedAttributes; + if (signedAttrTable == null) + { + return null; + } + + Asn1EncodableVector v = signedAttrTable.GetAll(attrOID); + switch (v.Count) + { + case 0: + return null; + case 1: + Asn1.Cms.Attribute t = (Asn1.Cms.Attribute) v[0]; + Asn1Set attrValues = t.AttrValues; + + if (attrValues.Count != 1) + throw new CmsException("A " + printableName + + " attribute MUST have a single attribute value"); + + return attrValues[0].ToAsn1Object(); + default: + throw new CmsException("The SignedAttributes in a signerInfo MUST NOT include multiple instances of the " + + printableName + " attribute"); + } + } + + private Asn1.Cms.Time GetSigningTime() + { + Asn1Object validSigningTime = GetSingleValuedSignedAttribute( + CmsAttributes.SigningTime, "signing-time"); + + if (validSigningTime == null) + return null; + + try + { + return Asn1.Cms.Time.GetInstance(validSigningTime); + } + catch (ArgumentException) + { + throw new CmsException("signing-time attribute value not a valid 'Time' structure"); + } + } + + /** + * Return a signer information object with the passed in unsigned + * attributes replacing the ones that are current associated with + * the object passed in. + * + * @param signerInformation the signerInfo to be used as the basis. + * @param unsignedAttributes the unsigned attributes to add. + * @return a copy of the original SignerInformationObject with the changed attributes. + */ + public static SignerInformation ReplaceUnsignedAttributes( + SignerInformation signerInformation, + Asn1.Cms.AttributeTable unsignedAttributes) + { + SignerInfo sInfo = signerInformation.info; + Asn1Set unsignedAttr = null; + + if (unsignedAttributes != null) + { + unsignedAttr = new DerSet(unsignedAttributes.ToAsn1EncodableVector()); + } + + return new SignerInformation( + new SignerInfo( + sInfo.SignerID, + sInfo.DigestAlgorithm, + sInfo.AuthenticatedAttributes, + sInfo.DigestEncryptionAlgorithm, + sInfo.EncryptedDigest, + unsignedAttr), + signerInformation.contentType, + signerInformation.content, + null); + } + + /** + * Return a signer information object with passed in SignerInformationStore representing counter + * signatures attached as an unsigned attribute. + * + * @param signerInformation the signerInfo to be used as the basis. + * @param counterSigners signer info objects carrying counter signature. + * @return a copy of the original SignerInformationObject with the changed attributes. + */ + public static SignerInformation AddCounterSigners( + SignerInformation signerInformation, + SignerInformationStore counterSigners) + { + // TODO Perform checks from RFC 3852 11.4 + + SignerInfo sInfo = signerInformation.info; + Asn1.Cms.AttributeTable unsignedAttr = signerInformation.UnsignedAttributes; + Asn1EncodableVector v; + + if (unsignedAttr != null) + { + v = unsignedAttr.ToAsn1EncodableVector(); + } + else + { + v = new Asn1EncodableVector(); + } + + Asn1EncodableVector sigs = new Asn1EncodableVector(); + + foreach (SignerInformation sigInf in counterSigners.GetSigners()) + { + sigs.Add(sigInf.ToSignerInfo()); + } + + v.Add(new Asn1.Cms.Attribute(CmsAttributes.CounterSignature, new DerSet(sigs))); + + return new SignerInformation( + new SignerInfo( + sInfo.SignerID, + sInfo.DigestAlgorithm, + sInfo.AuthenticatedAttributes, + sInfo.DigestEncryptionAlgorithm, + sInfo.EncryptedDigest, + new DerSet(v)), + signerInformation.contentType, + signerInformation.content, + null); + } + } +} diff --git a/Crypto/src/cms/SignerInformationStore.cs b/Crypto/src/cms/SignerInformationStore.cs new file mode 100644
index 000000000..bd613843d --- /dev/null +++ b/Crypto/src/cms/SignerInformationStore.cs
@@ -0,0 +1,74 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Cms +{ + public class SignerInformationStore + { + private readonly IList all; //ArrayList[SignerInformation] + private readonly IDictionary table = Platform.CreateHashtable(); // Hashtable[SignerID, ArrayList[SignerInformation]] + + public SignerInformationStore( + ICollection signerInfos) + { + foreach (SignerInformation signer in signerInfos) + { + SignerID sid = signer.SignerID; + IList list = (IList)table[sid]; + + if (list == null) + { + table[sid] = list = Platform.CreateArrayList(1); + } + + list.Add(signer); + } + + this.all = Platform.CreateArrayList(signerInfos); + } + + /** + * Return the first SignerInformation object that matches the + * passed in selector. Null if there are no matches. + * + * @param selector to identify a signer + * @return a single SignerInformation object. Null if none matches. + */ + public SignerInformation GetFirstSigner( + SignerID selector) + { + IList list = (IList) table[selector]; + + return list == null ? null : (SignerInformation) list[0]; + } + + /// <summary>The number of signers in the collection.</summary> + public int Count + { + get { return all.Count; } + } + + /// <returns>An ICollection of all signers in the collection</returns> + public ICollection GetSigners() + { + return Platform.CreateArrayList(all); + } + + /** + * Return possible empty collection with signers matching the passed in SignerID + * + * @param selector a signer id to select against. + * @return a collection of SignerInformation objects. + */ + public ICollection GetSigners( + SignerID selector) + { + IList list = (IList) table[selector]; + + return list == null ? Platform.CreateArrayList() : Platform.CreateArrayList(list); + } + } +} diff --git a/Crypto/src/cms/SimpleAttributeTableGenerator.cs b/Crypto/src/cms/SimpleAttributeTableGenerator.cs new file mode 100644
index 000000000..b3df21c29 --- /dev/null +++ b/Crypto/src/cms/SimpleAttributeTableGenerator.cs
@@ -0,0 +1,28 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1.Cms; + +namespace Org.BouncyCastle.Cms +{ + /** + * Basic generator that just returns a preconstructed attribute table + */ + public class SimpleAttributeTableGenerator + : CmsAttributeTableGenerator + { + private readonly AttributeTable attributes; + + public SimpleAttributeTableGenerator( + AttributeTable attributes) + { + this.attributes = attributes; + } + + public virtual AttributeTable GetAttributes( + IDictionary parameters) + { + return attributes; + } + } +} diff --git a/Crypto/src/crypto/AsymmetricCipherKeyPair.cs b/Crypto/src/crypto/AsymmetricCipherKeyPair.cs new file mode 100644
index 000000000..b00a3dc02 --- /dev/null +++ b/Crypto/src/crypto/AsymmetricCipherKeyPair.cs
@@ -0,0 +1,52 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /** + * a holding class for public/private parameter pairs. + */ + public class AsymmetricCipherKeyPair + { + private readonly AsymmetricKeyParameter publicParameter; + private readonly AsymmetricKeyParameter privateParameter; + + /** + * basic constructor. + * + * @param publicParam a public key parameters object. + * @param privateParam the corresponding private key parameters. + */ + public AsymmetricCipherKeyPair( + AsymmetricKeyParameter publicParameter, + AsymmetricKeyParameter privateParameter) + { + if (publicParameter.IsPrivate) + throw new ArgumentException("Expected a public key", "publicParameter"); + if (!privateParameter.IsPrivate) + throw new ArgumentException("Expected a private key", "privateParameter"); + + this.publicParameter = publicParameter; + this.privateParameter = privateParameter; + } + + /** + * return the public key parameters. + * + * @return the public key parameters. + */ + public AsymmetricKeyParameter Public + { + get { return publicParameter; } + } + + /** + * return the private key parameters. + * + * @return the private key parameters. + */ + public AsymmetricKeyParameter Private + { + get { return privateParameter; } + } + } +} diff --git a/Crypto/src/crypto/AsymmetricKeyParameter.cs b/Crypto/src/crypto/AsymmetricKeyParameter.cs new file mode 100644
index 000000000..7502ee305 --- /dev/null +++ b/Crypto/src/crypto/AsymmetricKeyParameter.cs
@@ -0,0 +1,47 @@ +using System; + +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Crypto +{ + public abstract class AsymmetricKeyParameter + : ICipherParameters + { + private readonly bool privateKey; + + protected AsymmetricKeyParameter( + bool privateKey) + { + this.privateKey = privateKey; + } + + public bool IsPrivate + { + get { return privateKey; } + } + + public override bool Equals( + object obj) + { + AsymmetricKeyParameter other = obj as AsymmetricKeyParameter; + + if (other == null) + { + return false; + } + + return Equals(other); + } + + protected bool Equals( + AsymmetricKeyParameter other) + { + return privateKey == other.privateKey; + } + + public override int GetHashCode() + { + return privateKey.GetHashCode(); + } + } +} diff --git a/Crypto/src/crypto/BufferedAeadBlockCipher.cs b/Crypto/src/crypto/BufferedAeadBlockCipher.cs new file mode 100644
index 000000000..87413b69d --- /dev/null +++ b/Crypto/src/crypto/BufferedAeadBlockCipher.cs
@@ -0,0 +1,259 @@ +using System; + +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto +{ + /** + * The AEAD block ciphers already handle buffering internally, so this class + * just takes care of implementing IBufferedCipher methods. + */ + public class BufferedAeadBlockCipher + : BufferedCipherBase + { + private readonly IAeadBlockCipher cipher; + + public BufferedAeadBlockCipher( + IAeadBlockCipher cipher) + { + if (cipher == null) + throw new ArgumentNullException("cipher"); + + this.cipher = cipher; + } + + public override string AlgorithmName + { + get { return cipher.AlgorithmName; } + } + + /** + * 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. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public override void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (parameters is ParametersWithRandom) + { + parameters = ((ParametersWithRandom) parameters).Parameters; + } + + cipher.Init(forEncryption, parameters); + } + + /** + * return the blocksize for the underlying cipher. + * + * @return the blocksize for the underlying cipher. + */ + public override 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 override int GetUpdateOutputSize( + int length) + { + return cipher.GetUpdateOutputSize(length); + } + + /** + * 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 override int GetOutputSize( + int length) + { + return cipher.GetOutputSize(length); + } + + /** + * 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 InvalidOperationException if the cipher isn't initialised. + */ + public override int ProcessByte( + byte input, + byte[] output, + int outOff) + { + return cipher.ProcessByte(input, output, outOff); + } + + public override byte[] ProcessByte( + byte input) + { + int outLength = GetUpdateOutputSize(1); + + byte[] outBytes = outLength > 0 ? new byte[outLength] : null; + + int pos = ProcessByte(input, outBytes, 0); + + if (outLength > 0 && pos < outLength) + { + byte[] tmp = new byte[pos]; + Array.Copy(outBytes, 0, tmp, 0, pos); + outBytes = tmp; + } + + return outBytes; + } + + public override byte[] ProcessBytes( + byte[] input, + int inOff, + int length) + { + if (input == null) + throw new ArgumentNullException("input"); + if (length < 1) + return null; + + int outLength = GetUpdateOutputSize(length); + + byte[] outBytes = outLength > 0 ? new byte[outLength] : null; + + int pos = ProcessBytes(input, inOff, length, outBytes, 0); + + if (outLength > 0 && pos < outLength) + { + byte[] tmp = new byte[pos]; + Array.Copy(outBytes, 0, tmp, 0, pos); + outBytes = tmp; + } + + return outBytes; + } + + /** + * 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 InvalidOperationException if the cipher isn't initialised. + */ + public override int ProcessBytes( + byte[] input, + int inOff, + int length, + byte[] output, + int outOff) + { + return cipher.ProcessBytes(input, inOff, length, output, outOff); + } + + public override byte[] DoFinal() + { + byte[] outBytes = EmptyBuffer; + + int length = GetOutputSize(0); + if (length > 0) + { + outBytes = new byte[length]; + + int pos = DoFinal(outBytes, 0); + if (pos < outBytes.Length) + { + byte[] tmp = new byte[pos]; + Array.Copy(outBytes, 0, tmp, 0, pos); + outBytes = tmp; + } + } + + return outBytes; + } + + public override byte[] DoFinal( + byte[] input, + int inOff, + int inLen) + { + if (input == null) + throw new ArgumentNullException("input"); + + int length = GetOutputSize(inLen); + + byte[] outBytes = EmptyBuffer; + + if (length > 0) + { + outBytes = new byte[length]; + + int pos = (inLen > 0) + ? ProcessBytes(input, inOff, inLen, outBytes, 0) + : 0; + + pos += DoFinal(outBytes, pos); + + if (pos < outBytes.Length) + { + byte[] tmp = new byte[pos]; + Array.Copy(outBytes, 0, tmp, 0, pos); + outBytes = tmp; + } + } + + return outBytes; + } + + /** + * 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 InvalidOperationException 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 override int DoFinal( + byte[] output, + int outOff) + { + return cipher.DoFinal(output, outOff); + } + + /** + * 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 override void Reset() + { + cipher.Reset(); + } + } +} diff --git a/Crypto/src/crypto/BufferedAsymmetricBlockCipher.cs b/Crypto/src/crypto/BufferedAsymmetricBlockCipher.cs new file mode 100644
index 000000000..09ec59f69 --- /dev/null +++ b/Crypto/src/crypto/BufferedAsymmetricBlockCipher.cs
@@ -0,0 +1,152 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Crypto.Engines; + +namespace 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 + : BufferedCipherBase + { + private readonly IAsymmetricBlockCipher cipher; + + private byte[] buffer; + private int bufOff; + + /** + * base constructor. + * + * @param cipher the cipher this buffering object wraps. + */ + public BufferedAsymmetricBlockCipher( + IAsymmetricBlockCipher cipher) + { + this.cipher = cipher; + } + + /** + * return the amount of data sitting in the buffer. + * + * @return the amount of data sitting in the buffer. + */ + internal int GetBufferPosition() + { + return bufOff; + } + + public override string AlgorithmName + { + get { return cipher.AlgorithmName; } + } + + public override int GetBlockSize() + { + return cipher.GetInputBlockSize(); + } + + public override int GetOutputSize( + int length) + { + return cipher.GetOutputBlockSize(); + } + + public override int GetUpdateOutputSize( + int length) + { + return 0; + } + + /** + * initialise the buffer and the underlying 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 override void Init( + bool forEncryption, + ICipherParameters parameters) + { + Reset(); + + cipher.Init(forEncryption, parameters); + + // + // we allow for an extra byte where people are using their own padding + // mechanisms on a raw cipher. + // + this.buffer = new byte[cipher.GetInputBlockSize() + (forEncryption ? 1 : 0)]; + this.bufOff = 0; + } + + public override byte[] ProcessByte( + byte input) + { + if (bufOff >= buffer.Length) + throw new DataLengthException("attempt to process message to long for cipher"); + + buffer[bufOff++] = input; + return null; + } + + public override byte[] ProcessBytes( + byte[] input, + int inOff, + int length) + { + if (length < 1) + return null; + + if (input == null) + throw new ArgumentNullException("input"); + if (bufOff + length > buffer.Length) + throw new DataLengthException("attempt to process message to long for cipher"); + + Array.Copy(input, inOff, buffer, bufOff, length); + bufOff += length; + return null; + } + + /** + * 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 override byte[] DoFinal() + { + byte[] outBytes = bufOff > 0 + ? cipher.ProcessBlock(buffer, 0, bufOff) + : EmptyBuffer; + + Reset(); + + return outBytes; + } + + public override byte[] DoFinal( + byte[] input, + int inOff, + int length) + { + ProcessBytes(input, inOff, length); + return DoFinal(); + } + + /// <summary>Reset the buffer</summary> + public override void Reset() + { + if (buffer != null) + { + Array.Clear(buffer, 0, buffer.Length); + bufOff = 0; + } + } + } +} diff --git a/Crypto/src/crypto/BufferedBlockCipher.cs b/Crypto/src/crypto/BufferedBlockCipher.cs new file mode 100644
index 000000000..72bdfed67 --- /dev/null +++ b/Crypto/src/crypto/BufferedBlockCipher.cs
@@ -0,0 +1,378 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace 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. + * </p> + */ + public class BufferedBlockCipher + : BufferedCipherBase + { + internal byte[] buf; + internal int bufOff; + internal bool forEncryption; + internal IBlockCipher cipher; + + /** + * constructor for subclasses + */ + protected BufferedBlockCipher() + { + } + + /** + * Create a buffered block cipher without padding. + * + * @param cipher the underlying block cipher this buffering object wraps. + * false otherwise. + */ + public BufferedBlockCipher( + IBlockCipher cipher) + { + if (cipher == null) + throw new ArgumentNullException("cipher"); + + this.cipher = cipher; + buf = new byte[cipher.GetBlockSize()]; + bufOff = 0; + } + + public override string AlgorithmName + { + get { return cipher.AlgorithmName; } + } + + /** + * 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. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + // Note: This doubles as the Init in the event that this cipher is being used as an IWrapper + public override void Init( + bool forEncryption, + ICipherParameters parameters) + { + this.forEncryption = forEncryption; + + if (parameters is ParametersWithRandom) + { + parameters = ((ParametersWithRandom) parameters).Parameters; + } + + Reset(); + + cipher.Init(forEncryption, parameters); + } + + /** + * return the blocksize for the underlying cipher. + * + * @return the blocksize for the underlying cipher. + */ + public override 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 override int GetUpdateOutputSize( + int length) + { + int total = length + bufOff; + int 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 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 override int GetOutputSize( + int length) + { + // Note: Can assume IsPartialBlockOkay 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 InvalidOperationException if the cipher isn't initialised. + */ + public override int ProcessByte( + byte input, + byte[] output, + int outOff) + { + buf[bufOff++] = input; + + if (bufOff == buf.Length) + { + if ((outOff + buf.Length) > output.Length) + throw new DataLengthException("output buffer too short"); + + bufOff = 0; + return cipher.ProcessBlock(buf, 0, output, outOff); + } + + return 0; + } + + public override byte[] ProcessByte( + byte input) + { + int outLength = GetUpdateOutputSize(1); + + byte[] outBytes = outLength > 0 ? new byte[outLength] : null; + + int pos = ProcessByte(input, outBytes, 0); + + if (outLength > 0 && pos < outLength) + { + byte[] tmp = new byte[pos]; + Array.Copy(outBytes, 0, tmp, 0, pos); + outBytes = tmp; + } + + return outBytes; + } + + public override byte[] ProcessBytes( + byte[] input, + int inOff, + int length) + { + if (input == null) + throw new ArgumentNullException("input"); + if (length < 1) + return null; + + int outLength = GetUpdateOutputSize(length); + + byte[] outBytes = outLength > 0 ? new byte[outLength] : null; + + int pos = ProcessBytes(input, inOff, length, outBytes, 0); + + if (outLength > 0 && pos < outLength) + { + byte[] tmp = new byte[pos]; + Array.Copy(outBytes, 0, tmp, 0, pos); + outBytes = tmp; + } + + return outBytes; + } + + /** + * 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 InvalidOperationException if the cipher isn't initialised. + */ + public override int ProcessBytes( + byte[] input, + int inOff, + int length, + byte[] output, + int outOff) + { + if (length < 1) + { + if (length < 0) + throw new ArgumentException("Can't have a negative input length!"); + + return 0; + } + + int blockSize = GetBlockSize(); + int outLength = GetUpdateOutputSize(length); + + if (outLength > 0) + { + if ((outOff + outLength) > output.Length) + { + throw new DataLengthException("output buffer too short"); + } + } + + int resultLen = 0; + int gapLen = buf.Length - bufOff; + if (length > gapLen) + { + Array.Copy(input, inOff, buf, bufOff, gapLen); + resultLen += cipher.ProcessBlock(buf, 0, output, outOff); + bufOff = 0; + length -= gapLen; + inOff += gapLen; + while (length > buf.Length) + { + resultLen += cipher.ProcessBlock(input, inOff, output, outOff + resultLen); + length -= blockSize; + inOff += blockSize; + } + } + Array.Copy(input, inOff, buf, bufOff, length); + bufOff += length; + if (bufOff == buf.Length) + { + resultLen += cipher.ProcessBlock(buf, 0, output, outOff + resultLen); + bufOff = 0; + } + return resultLen; + } + + public override byte[] DoFinal() + { + byte[] outBytes = EmptyBuffer; + + int length = GetOutputSize(0); + if (length > 0) + { + outBytes = new byte[length]; + + int pos = DoFinal(outBytes, 0); + if (pos < outBytes.Length) + { + byte[] tmp = new byte[pos]; + Array.Copy(outBytes, 0, tmp, 0, pos); + outBytes = tmp; + } + } + else + { + Reset(); + } + + return outBytes; + } + + public override byte[] DoFinal( + byte[] input, + int inOff, + int inLen) + { + if (input == null) + throw new ArgumentNullException("input"); + + int length = GetOutputSize(inLen); + + byte[] outBytes = EmptyBuffer; + + if (length > 0) + { + outBytes = new byte[length]; + + int pos = (inLen > 0) + ? ProcessBytes(input, inOff, inLen, outBytes, 0) + : 0; + + pos += DoFinal(outBytes, pos); + + if (pos < outBytes.Length) + { + byte[] tmp = new byte[pos]; + Array.Copy(outBytes, 0, tmp, 0, pos); + outBytes = tmp; + } + } + else + { + Reset(); + } + + return outBytes; + } + + /** + * 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 InvalidOperationException 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 override int DoFinal( + byte[] output, + int outOff) + { + try + { + if (bufOff != 0) + { + if (!cipher.IsPartialBlockOkay) + { + throw new DataLengthException("data not block size aligned"); + } + + if (outOff + bufOff > output.Length) + { + throw new DataLengthException("output buffer too short for DoFinal()"); + } + + // NB: Can't copy directly, or we may write too much output + cipher.ProcessBlock(buf, 0, buf, 0); + Array.Copy(buf, 0, output, outOff, bufOff); + } + + return bufOff; + } + 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 override void Reset() + { + Array.Clear(buf, 0, buf.Length); + bufOff = 0; + + cipher.Reset(); + } + } +} diff --git a/Crypto/src/crypto/BufferedCipherBase.cs b/Crypto/src/crypto/BufferedCipherBase.cs new file mode 100644
index 000000000..9d8610211 --- /dev/null +++ b/Crypto/src/crypto/BufferedCipherBase.cs
@@ -0,0 +1,113 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + public abstract class BufferedCipherBase + : IBufferedCipher + { + protected static readonly byte[] EmptyBuffer = new byte[0]; + + public abstract string AlgorithmName { get; } + + public abstract void Init(bool forEncryption, ICipherParameters parameters); + + public abstract int GetBlockSize(); + + public abstract int GetOutputSize(int inputLen); + public abstract int GetUpdateOutputSize(int inputLen); + + public abstract byte[] ProcessByte(byte input); + + public virtual int ProcessByte( + byte input, + byte[] output, + int outOff) + { + byte[] outBytes = ProcessByte(input); + if (outBytes == null) + return 0; + if (outOff + outBytes.Length > output.Length) + throw new DataLengthException("output buffer too short"); + outBytes.CopyTo(output, outOff); + return outBytes.Length; + } + + public virtual byte[] ProcessBytes( + byte[] input) + { + return ProcessBytes(input, 0, input.Length); + } + + public abstract byte[] ProcessBytes(byte[] input, int inOff, int length); + + public virtual int ProcessBytes( + byte[] input, + byte[] output, + int outOff) + { + return ProcessBytes(input, 0, input.Length, output, outOff); + } + + public virtual int ProcessBytes( + byte[] input, + int inOff, + int length, + byte[] output, + int outOff) + { + byte[] outBytes = ProcessBytes(input, inOff, length); + if (outBytes == null) + return 0; + if (outOff + outBytes.Length > output.Length) + throw new DataLengthException("output buffer too short"); + outBytes.CopyTo(output, outOff); + return outBytes.Length; + } + + public abstract byte[] DoFinal(); + + public virtual byte[] DoFinal( + byte[] input) + { + return DoFinal(input, 0, input.Length); + } + + public abstract byte[] DoFinal( + byte[] input, + int inOff, + int length); + + public virtual int DoFinal( + byte[] output, + int outOff) + { + byte[] outBytes = DoFinal(); + if (outOff + outBytes.Length > output.Length) + throw new DataLengthException("output buffer too short"); + outBytes.CopyTo(output, outOff); + return outBytes.Length; + } + + public virtual int DoFinal( + byte[] input, + byte[] output, + int outOff) + { + return DoFinal(input, 0, input.Length, output, outOff); + } + + public virtual int DoFinal( + byte[] input, + int inOff, + int length, + byte[] output, + int outOff) + { + int len = ProcessBytes(input, inOff, length, output, outOff); + len += DoFinal(output, outOff + len); + return len; + } + + public abstract void Reset(); + } +} diff --git a/Crypto/src/crypto/BufferedIesCipher.cs b/Crypto/src/crypto/BufferedIesCipher.cs new file mode 100644
index 000000000..6dab4ae33 --- /dev/null +++ b/Crypto/src/crypto/BufferedIesCipher.cs
@@ -0,0 +1,113 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto +{ + public class BufferedIesCipher + : BufferedCipherBase + { + private readonly IesEngine engine; + private bool forEncryption; + private MemoryStream buffer = new MemoryStream(); + + public BufferedIesCipher( + IesEngine engine) + { + if (engine == null) + throw new ArgumentNullException("engine"); + + this.engine = engine; + } + + public override string AlgorithmName + { + // TODO Create IESEngine.AlgorithmName + get { return "IES"; } + } + + public override void Init( + bool forEncryption, + ICipherParameters parameters) + { + this.forEncryption = forEncryption; + + // TODO + throw Platform.CreateNotImplementedException("IES"); + } + + public override int GetBlockSize() + { + return 0; + } + + public override int GetOutputSize( + int inputLen) + { + if (engine == null) + throw new InvalidOperationException("cipher not initialised"); + + int baseLen = inputLen + (int) buffer.Length; + return forEncryption + ? baseLen + 20 + : baseLen - 20; + } + + public override int GetUpdateOutputSize( + int inputLen) + { + return 0; + } + + public override byte[] ProcessByte( + byte input) + { + buffer.WriteByte(input); + return null; + } + + public override byte[] ProcessBytes( + byte[] input, + int inOff, + int length) + { + if (input == null) + throw new ArgumentNullException("input"); + if (inOff < 0) + throw new ArgumentException("inOff"); + if (length < 0) + throw new ArgumentException("length"); + if (inOff + length > input.Length) + throw new ArgumentException("invalid offset/length specified for input array"); + + buffer.Write(input, inOff, length); + return null; + } + + public override byte[] DoFinal() + { + byte[] buf = buffer.ToArray(); + + Reset(); + + return engine.ProcessBlock(buf, 0, buf.Length); + } + + public override byte[] DoFinal( + byte[] input, + int inOff, + int length) + { + ProcessBytes(input, inOff, length); + return DoFinal(); + } + + public override void Reset() + { + buffer.SetLength(0); + } + } +} diff --git a/Crypto/src/crypto/BufferedStreamCipher.cs b/Crypto/src/crypto/BufferedStreamCipher.cs new file mode 100644
index 000000000..2d4987bba --- /dev/null +++ b/Crypto/src/crypto/BufferedStreamCipher.cs
@@ -0,0 +1,131 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto +{ + public class BufferedStreamCipher + : BufferedCipherBase + { + private readonly IStreamCipher cipher; + + public BufferedStreamCipher( + IStreamCipher cipher) + { + if (cipher == null) + throw new ArgumentNullException("cipher"); + + this.cipher = cipher; + } + + public override string AlgorithmName + { + get { return cipher.AlgorithmName; } + } + + public override void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (parameters is ParametersWithRandom) + { + parameters = ((ParametersWithRandom) parameters).Parameters; + } + + cipher.Init(forEncryption, parameters); + } + + public override int GetBlockSize() + { + return 0; + } + + public override int GetOutputSize( + int inputLen) + { + return inputLen; + } + + public override int GetUpdateOutputSize( + int inputLen) + { + return inputLen; + } + + public override byte[] ProcessByte( + byte input) + { + return new byte[]{ cipher.ReturnByte(input) }; + } + + public override int ProcessByte( + byte input, + byte[] output, + int outOff) + { + if (outOff >= output.Length) + throw new DataLengthException("output buffer too short"); + + output[outOff] = cipher.ReturnByte(input); + return 1; + } + + public override byte[] ProcessBytes( + byte[] input, + int inOff, + int length) + { + if (length < 1) + return null; + + byte[] output = new byte[length]; + cipher.ProcessBytes(input, inOff, length, output, 0); + return output; + } + + public override int ProcessBytes( + byte[] input, + int inOff, + int length, + byte[] output, + int outOff) + { + if (length < 1) + return 0; + + if (length > 0) + { + cipher.ProcessBytes(input, inOff, length, output, outOff); + } + + return length; + } + + public override byte[] DoFinal() + { + Reset(); + + return EmptyBuffer; + } + + public override byte[] DoFinal( + byte[] input, + int inOff, + int length) + { + if (length < 1) + return EmptyBuffer; + + byte[] output = ProcessBytes(input, inOff, length); + + Reset(); + + return output; + } + + public override void Reset() + { + cipher.Reset(); + } + } +} diff --git a/Crypto/src/crypto/CipherKeyGenerator.cs b/Crypto/src/crypto/CipherKeyGenerator.cs new file mode 100644
index 000000000..5d00d34dd --- /dev/null +++ b/Crypto/src/crypto/CipherKeyGenerator.cs
@@ -0,0 +1,83 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto +{ + /** + * The base class for symmetric, or secret, cipher key generators. + */ + public class CipherKeyGenerator + { + protected internal SecureRandom random; + protected internal int strength; + private bool uninitialised = true; + private int defaultStrength; + + public CipherKeyGenerator() + { + } + + internal CipherKeyGenerator( + int defaultStrength) + { + if (defaultStrength < 1) + throw new ArgumentException("strength must be a positive value", "defaultStrength"); + + this.defaultStrength = defaultStrength; + } + + public int DefaultStrength + { + get { return defaultStrength; } + } + + /** + * initialise the key generator. + * + * @param param the parameters to be used for key generation + */ + public void Init( + KeyGenerationParameters parameters) + { + if (parameters == null) + throw new ArgumentNullException("parameters"); + + this.uninitialised = false; + + engineInit(parameters); + } + + protected virtual void engineInit( + KeyGenerationParameters parameters) + { + this.random = parameters.Random; + this.strength = (parameters.Strength + 7) / 8; + } + + /** + * Generate a secret key. + * + * @return a byte array containing the key value. + */ + public byte[] GenerateKey() + { + if (uninitialised) + { + if (defaultStrength < 1) + throw new InvalidOperationException("Generator has not been initialised"); + + uninitialised = false; + + engineInit(new KeyGenerationParameters(new SecureRandom(), defaultStrength)); + } + + return engineGenerateKey(); + } + + protected virtual byte[] engineGenerateKey() + { + return random.GenerateSeed(strength); + } + } +} diff --git a/Crypto/src/crypto/CryptoException.cs b/Crypto/src/crypto/CryptoException.cs new file mode 100644
index 000000000..c012063bb --- /dev/null +++ b/Crypto/src/crypto/CryptoException.cs
@@ -0,0 +1,25 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + public class CryptoException + : Exception + { + public CryptoException() + { + } + + public CryptoException( + string message) + : base(message) + { + } + + public CryptoException( + string message, + Exception exception) + : base(message, exception) + { + } + } +} diff --git a/Crypto/src/crypto/DataLengthException.cs b/Crypto/src/crypto/DataLengthException.cs new file mode 100644
index 000000000..8bd695bbc --- /dev/null +++ b/Crypto/src/crypto/DataLengthException.cs
@@ -0,0 +1,39 @@ +using System; + +namespace 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 + : CryptoException + { + /** + * 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) + : base(message) + { + } + + public DataLengthException( + string message, + Exception exception) + : base(message, exception) + { + } + } +} diff --git a/Crypto/src/crypto/IAsymmetricBlockCipher.cs b/Crypto/src/crypto/IAsymmetricBlockCipher.cs new file mode 100644
index 000000000..455cfaa69 --- /dev/null +++ b/Crypto/src/crypto/IAsymmetricBlockCipher.cs
@@ -0,0 +1,30 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /// <remarks>Base interface for a public/private key block cipher.</remarks> + public interface IAsymmetricBlockCipher + { + /// <summary>The name of the algorithm this cipher implements.</summary> + string AlgorithmName { get; } + + /// <summary>Initialise the cipher.</summary> + /// <param name="forEncryption">Initialise for encryption if true, for decryption if false.</param> + /// <param name="parameters">The key or other data required by the cipher.</param> + void Init(bool forEncryption, ICipherParameters parameters); + + /// <returns>The maximum size, in bytes, an input block may be.</returns> + int GetInputBlockSize(); + + /// <returns>The maximum size, in bytes, an output block will be.</returns> + int GetOutputBlockSize(); + + /// <summary>Process a block.</summary> + /// <param name="inBuf">The input buffer.</param> + /// <param name="inOff">The offset into <paramref>inBuf</paramref> that the input block begins.</param> + /// <param name="inLen">The length of the input block.</param> + /// <exception cref="InvalidCipherTextException">Input decrypts improperly.</exception> + /// <exception cref="DataLengthException">Input is too large for the cipher.</exception> + byte[] ProcessBlock(byte[] inBuf, int inOff, int inLen); + } +} diff --git a/Crypto/src/crypto/IAsymmetricCipherKeyPairGenerator.cs b/Crypto/src/crypto/IAsymmetricCipherKeyPairGenerator.cs new file mode 100644
index 000000000..9ec5dfada --- /dev/null +++ b/Crypto/src/crypto/IAsymmetricCipherKeyPairGenerator.cs
@@ -0,0 +1,24 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /** + * interface that a public/private key pair generator should conform to. + */ + public interface IAsymmetricCipherKeyPairGenerator + { + /** + * intialise the key pair generator. + * + * @param the parameters the key pair is to be initialised with. + */ + void Init(KeyGenerationParameters parameters); + + /** + * return an AsymmetricCipherKeyPair containing the Generated keys. + * + * @return an AsymmetricCipherKeyPair containing the Generated keys. + */ + AsymmetricCipherKeyPair GenerateKeyPair(); + } +} diff --git a/Crypto/src/crypto/IBasicAgreement.cs b/Crypto/src/crypto/IBasicAgreement.cs new file mode 100644
index 000000000..8bd363d4e --- /dev/null +++ b/Crypto/src/crypto/IBasicAgreement.cs
@@ -0,0 +1,24 @@ +using System; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto +{ + /** + * The basic interface that basic Diffie-Hellman implementations + * conforms to. + */ + public interface IBasicAgreement + { + /** + * initialise the agreement engine. + */ + void Init(ICipherParameters parameters); + + /** + * given a public key from a given party calculate the next + * message in the agreement sequence. + */ + BigInteger CalculateAgreement(ICipherParameters pubKey); + } + +} diff --git a/Crypto/src/crypto/IBlockCipher.cs b/Crypto/src/crypto/IBlockCipher.cs new file mode 100644
index 000000000..a3ad6d6e5 --- /dev/null +++ b/Crypto/src/crypto/IBlockCipher.cs
@@ -0,0 +1,36 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /// <remarks>Base interface for a symmetric key block cipher.</remarks> + public interface IBlockCipher + { + /// <summary>The name of the algorithm this cipher implements.</summary> + string AlgorithmName { get; } + + /// <summary>Initialise the cipher.</summary> + /// <param name="forEncryption">Initialise for encryption if true, for decryption if false.</param> + /// <param name="parameters">The key or other data required by the cipher.</param> + void Init(bool forEncryption, ICipherParameters parameters); + + /// <returns>The block size for this cipher, in bytes.</returns> + int GetBlockSize(); + + /// <summary>Indicates whether this cipher can handle partial blocks.</summary> + bool IsPartialBlockOkay { get; } + + /// <summary>Process a block.</summary> + /// <param name="inBuf">The input buffer.</param> + /// <param name="inOff">The offset into <paramref>inBuf</paramref> that the input block begins.</param> + /// <param name="outBuf">The output buffer.</param> + /// <param name="outOff">The offset into <paramref>outBuf</paramref> to write the output block.</param> + /// <exception cref="DataLengthException">If input block is wrong size, or outBuf too small.</exception> + /// <returns>The number of bytes processed and produced.</returns> + int ProcessBlock(byte[] inBuf, int inOff, byte[] outBuf, int outOff); + + /// <summary> + /// Reset the cipher to the same state as it was after the last init (if there was one). + /// </summary> + void Reset(); + } +} diff --git a/Crypto/src/crypto/IBufferedCipher.cs b/Crypto/src/crypto/IBufferedCipher.cs new file mode 100644
index 000000000..69dec9596 --- /dev/null +++ b/Crypto/src/crypto/IBufferedCipher.cs
@@ -0,0 +1,44 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /// <remarks>Block cipher engines are expected to conform to this interface.</remarks> + public interface IBufferedCipher + { + /// <summary>The name of the algorithm this cipher implements.</summary> + string AlgorithmName { get; } + + /// <summary>Initialise the cipher.</summary> + /// <param name="forEncryption">If true the cipher is initialised for encryption, + /// if false for decryption.</param> + /// <param name="parameters">The key and other data required by the cipher.</param> + void Init(bool forEncryption, ICipherParameters parameters); + + int GetBlockSize(); + + int GetOutputSize(int inputLen); + + int GetUpdateOutputSize(int inputLen); + + byte[] ProcessByte(byte input); + int ProcessByte(byte input, byte[] output, int outOff); + + byte[] ProcessBytes(byte[] input); + byte[] ProcessBytes(byte[] input, int inOff, int length); + int ProcessBytes(byte[] input, byte[] output, int outOff); + int ProcessBytes(byte[] input, int inOff, int length, byte[] output, int outOff); + + byte[] DoFinal(); + byte[] DoFinal(byte[] input); + byte[] DoFinal(byte[] input, int inOff, int length); + int DoFinal(byte[] output, int outOff); + int DoFinal(byte[] input, byte[] output, int outOff); + int DoFinal(byte[] input, int inOff, int length, byte[] output, int outOff); + + /// <summary> + /// Reset the cipher. After resetting the cipher is in the same state + /// as it was after the last init (if there was one). + /// </summary> + void Reset(); + } +} diff --git a/Crypto/src/crypto/ICipherParameters.cs b/Crypto/src/crypto/ICipherParameters.cs new file mode 100644
index 000000000..fff0941c7 --- /dev/null +++ b/Crypto/src/crypto/ICipherParameters.cs
@@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /** + * all parameter classes implement this. + */ + public interface ICipherParameters + { + } +} diff --git a/Crypto/src/crypto/IDSA.cs b/Crypto/src/crypto/IDSA.cs new file mode 100644
index 000000000..46056d8ca --- /dev/null +++ b/Crypto/src/crypto/IDSA.cs
@@ -0,0 +1,40 @@ +using System; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto +{ + /** + * interface for classes implementing the Digital Signature Algorithm + */ + public interface IDsa + { + string AlgorithmName { get; } + + /** + * 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. + */ + void Init(bool forSigning, ICipherParameters parameters); + + /** + * 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. + */ + 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. + */ + bool VerifySignature(byte[] message, BigInteger r, BigInteger s); + } +} diff --git a/Crypto/src/crypto/IDerivationFunction.cs b/Crypto/src/crypto/IDerivationFunction.cs new file mode 100644
index 000000000..7f289f790 --- /dev/null +++ b/Crypto/src/crypto/IDerivationFunction.cs
@@ -0,0 +1,24 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /** + * base interface for general purpose byte derivation functions. + */ + public interface IDerivationFunction + { + void Init(IDerivationParameters parameters); + + /** + * return the message digest used as the basis for the function + */ + IDigest Digest + { + get; + } + + int GenerateBytes(byte[] output, int outOff, int length); + //throws DataLengthException, ArgumentException; + } + +} diff --git a/Crypto/src/crypto/IDerivationParameters.cs b/Crypto/src/crypto/IDerivationParameters.cs new file mode 100644
index 000000000..f1c848568 --- /dev/null +++ b/Crypto/src/crypto/IDerivationParameters.cs
@@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /** + * Parameters for key/byte stream derivation classes + */ + public interface IDerivationParameters + { + } +} diff --git a/Crypto/src/crypto/IDigest.cs b/Crypto/src/crypto/IDigest.cs new file mode 100644
index 000000000..6769dcc42 --- /dev/null +++ b/Crypto/src/crypto/IDigest.cs
@@ -0,0 +1,61 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /** + * interface that a message digest conforms to. + */ + public interface IDigest + { + /** + * return the algorithm name + * + * @return the algorithm name + */ + string AlgorithmName { get; } + + /** + * 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. + */ + int GetDigestSize(); + + /** + * return the size, in bytes, of the internal buffer used by this digest. + * + * @return the size, in bytes, of the internal buffer used by this digest. + */ + int GetByteLength(); + + /** + * update the message digest with a single byte. + * + * @param inByte the input byte to be entered. + */ + void Update(byte input); + + /** + * update the message digest with a block of bytes. + * + * @param input 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. + */ + void BlockUpdate(byte[] input, int inOff, int length); + + /** + * Close the digest, producing the final digest value. The doFinal + * call leaves the digest reset. + * + * @param output the array the digest is to be copied into. + * @param outOff the offset into the out array the digest is to start at. + */ + int DoFinal(byte[] output, int outOff); + + /** + * reset the digest back to it's initial state. + */ + void Reset(); + } +} diff --git a/Crypto/src/crypto/IMac.cs b/Crypto/src/crypto/IMac.cs new file mode 100644
index 000000000..03a86e8b6 --- /dev/null +++ b/Crypto/src/crypto/IMac.cs
@@ -0,0 +1,69 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /** + * The base interface for implementations of message authentication codes (MACs). + */ + public interface IMac + { + /** + * Initialise the MAC. + * + * @param param the key and other data required by the MAC. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + void Init(ICipherParameters parameters); + + /** + * Return the name of the algorithm the MAC implements. + * + * @return the name of the algorithm the MAC implements. + */ + string AlgorithmName { get; } + + /** + * Return the block size for this MAC (in bytes). + * + * @return the block size for this MAC in bytes. + */ + int GetMacSize(); + + /** + * add a single byte to the mac for processing. + * + * @param in the byte to be processed. + * @exception InvalidOperationException if the MAC is not initialised. + */ + void Update(byte input); + + /** + * @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 InvalidOperationException if the MAC is not initialised. + * @exception DataLengthException if there isn't enough data in in. + */ + void BlockUpdate(byte[] input, int inOff, int len); + + /** + * 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. + * </p> + * @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 InvalidOperationException if the MAC is not initialised. + */ + int DoFinal(byte[] output, int outOff); + + /** + * 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). + */ + void Reset(); + } +} diff --git a/Crypto/src/crypto/ISigner.cs b/Crypto/src/crypto/ISigner.cs new file mode 100644
index 000000000..e03bbf4d3 --- /dev/null +++ b/Crypto/src/crypto/ISigner.cs
@@ -0,0 +1,50 @@ + +using System; +using System.Text; + +namespace Org.BouncyCastle.Crypto +{ + public interface ISigner + { + /** + * Return the name of the algorithm the signer implements. + * + * @return the name of the algorithm the signer implements. + */ + string AlgorithmName { get; } + + /** + * Initialise the signer for signing or verification. + * + * @param forSigning true if for signing, false otherwise + * @param param necessary parameters. + */ + void Init(bool forSigning, ICipherParameters parameters); + + /** + * update the internal digest with the byte b + */ + void Update(byte input); + + /** + * update the internal digest with the byte array in + */ + void BlockUpdate(byte[] input, int inOff, int length); + + /** + * Generate a signature for the message we've been loaded with using + * the key we were initialised with. + */ + byte[] GenerateSignature(); + /** + * return true if the internal state represents the signature described + * in the passed in array. + */ + bool VerifySignature(byte[] signature); + + /** + * reset the internal state + */ + void Reset(); + } +} diff --git a/Crypto/src/crypto/ISignerWithRecovery.cs b/Crypto/src/crypto/ISignerWithRecovery.cs new file mode 100644
index 000000000..024f5cef5 --- /dev/null +++ b/Crypto/src/crypto/ISignerWithRecovery.cs
@@ -0,0 +1,37 @@ +using System; +using System.Text; + +namespace Org.BouncyCastle.Crypto +{ + /** + * Signer with message recovery. + */ + public interface ISignerWithRecovery + : ISigner + { + /** + * Returns true if the signer has recovered the full message as + * part of signature verification. + * + * @return true if full message recovered. + */ + bool HasFullMessage(); + + /** + * Returns a reference to what message was recovered (if any). + * + * @return full/partial message, null if nothing. + */ + 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 + */ + void UpdateWithRecoveredMessage(byte[] signature); + } +} diff --git a/Crypto/src/crypto/IStreamCipher.cs b/Crypto/src/crypto/IStreamCipher.cs new file mode 100644
index 000000000..8e575a7e5 --- /dev/null +++ b/Crypto/src/crypto/IStreamCipher.cs
@@ -0,0 +1,45 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /// <summary>The interface stream ciphers conform to.</summary> + public interface IStreamCipher + { + /// <summary>The name of the algorithm this cipher implements.</summary> + string AlgorithmName { get; } + + /// <summary>Initialise the cipher.</summary> + /// <param name="forEncryption">If true the cipher is initialised for encryption, + /// if false for decryption.</param> + /// <param name="parameters">The key and other data required by the cipher.</param> + /// <exception cref="ArgumentException"> + /// If the parameters argument is inappropriate. + /// </exception> + void Init(bool forEncryption, ICipherParameters parameters); + + /// <summary>encrypt/decrypt a single byte returning the result.</summary> + /// <param name="input">the byte to be processed.</param> + /// <returns>the result of processing the input byte.</returns> + byte ReturnByte(byte input); + + /// <summary> + /// Process a block of bytes from <c>input</c> putting the result into <c>output</c>. + /// </summary> + /// <param name="input">The input byte array.</param> + /// <param name="inOff"> + /// The offset into <c>input</c> where the data to be processed starts. + /// </param> + /// <param name="length">The number of bytes to be processed.</param> + /// <param name="output">The output buffer the processed bytes go into.</param> + /// <param name="outOff"> + /// The offset into <c>output</c> the processed data starts at. + /// </param> + /// <exception cref="DataLengthException">If the output buffer is too small.</exception> + void ProcessBytes(byte[] input, int inOff, int length, byte[] output, int outOff); + + /// <summary> + /// Reset the cipher to the same state as it was after the last init (if there was one). + /// </summary> + void Reset(); + } +} diff --git a/Crypto/src/crypto/IWrapper.cs b/Crypto/src/crypto/IWrapper.cs new file mode 100644
index 000000000..58202b302 --- /dev/null +++ b/Crypto/src/crypto/IWrapper.cs
@@ -0,0 +1,18 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto +{ + public interface IWrapper + { + /// <summary>The name of the algorithm this cipher implements.</summary> + string AlgorithmName { get; } + + void Init(bool forWrapping, ICipherParameters parameters); + + byte[] Wrap(byte[] input, int inOff, int length); + + byte[] Unwrap(byte[] input, int inOff, int length); + } +} diff --git a/Crypto/src/crypto/InvalidCipherTextException.cs b/Crypto/src/crypto/InvalidCipherTextException.cs new file mode 100644
index 000000000..598ea278d --- /dev/null +++ b/Crypto/src/crypto/InvalidCipherTextException.cs
@@ -0,0 +1,37 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /** + * this exception is thrown whenever we find something we don't expect in a + * message. + */ + public class InvalidCipherTextException + : 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) + : base(message) + { + } + + public InvalidCipherTextException( + string message, + Exception exception) + : base(message, exception) + { + } + } +} diff --git a/Crypto/src/crypto/KeyGenerationParameters.cs b/Crypto/src/crypto/KeyGenerationParameters.cs new file mode 100644
index 000000000..0cb6b07c7 --- /dev/null +++ b/Crypto/src/crypto/KeyGenerationParameters.cs
@@ -0,0 +1,55 @@ +using System; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto +{ + /** + * 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) + { + if (random == null) + throw new ArgumentNullException("random"); + if (strength < 1) + throw new ArgumentException("strength must be a positive value", "strength"); + + this.random = random; + this.strength = strength; + } + + /** + * return the random source associated with this + * generator. + * + * @return the generators random source. + */ + public SecureRandom Random + { + get { 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 Strength + { + get { return strength; } + } + } +} diff --git a/Crypto/src/crypto/MaxBytesExceededException.cs b/Crypto/src/crypto/MaxBytesExceededException.cs new file mode 100644
index 000000000..9fa28abb0 --- /dev/null +++ b/Crypto/src/crypto/MaxBytesExceededException.cs
@@ -0,0 +1,29 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /// <summary> + /// This exception is thrown whenever a cipher requires a change of key, iv + /// or similar after x amount of bytes enciphered + /// </summary> + public class MaxBytesExceededException + : CryptoException + { + public MaxBytesExceededException() + { + } + + public MaxBytesExceededException( + string message) + : base(message) + { + } + + public MaxBytesExceededException( + string message, + Exception e) + : base(message, e) + { + } + } +} diff --git a/Crypto/src/crypto/PbeParametersGenerator.cs b/Crypto/src/crypto/PbeParametersGenerator.cs new file mode 100644
index 000000000..0e96abd0f --- /dev/null +++ b/Crypto/src/crypto/PbeParametersGenerator.cs
@@ -0,0 +1,190 @@ +using System; +using System.Text; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto +{ + /** + * super class for all Password Based Encyrption (Pbe) parameter generator classes. + */ + public abstract class PbeParametersGenerator + { + protected byte[] mPassword; + protected byte[] mSalt; + protected int mIterationCount; + + /** + * 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 virtual void Init( + byte[] password, + byte[] salt, + int iterationCount) + { + if (password == null) + throw new ArgumentNullException("password"); + if (salt == null) + throw new ArgumentNullException("salt"); + + this.mPassword = Arrays.Clone(password); + this.mSalt = Arrays.Clone(salt); + this.mIterationCount = iterationCount; + } + + public virtual byte[] Password + { + get { return Arrays.Clone(mPassword); } + } + + /** + * return the password byte array. + * + * @return the password byte array. + */ + [Obsolete("Use 'Password' property")] + public byte[] GetPassword() + { + return Password; + } + + public virtual byte[] Salt + { + get { return Arrays.Clone(mSalt); } + } + + /** + * return the salt byte array. + * + * @return the salt byte array. + */ + [Obsolete("Use 'Salt' property")] + public byte[] GetSalt() + { + return Salt; + } + + /** + * return the iteration count. + * + * @return the iteration count. + */ + public virtual int IterationCount + { + get { return mIterationCount; } + } + + /** + * 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. + */ + [Obsolete("Use version with 'algorithm' parameter")] + public abstract ICipherParameters GenerateDerivedParameters(int keySize); + public abstract ICipherParameters GenerateDerivedParameters(string algorithm, 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. + */ + [Obsolete("Use version with 'algorithm' parameter")] + public abstract ICipherParameters GenerateDerivedParameters(int keySize, int ivSize); + public abstract ICipherParameters GenerateDerivedParameters(string algorithm, 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 ICipherParameters 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) + { + return Strings.ToAsciiByteArray(password); + } + + [Obsolete("Use version taking 'char[]' instead")] + public static byte[] Pkcs5PasswordToBytes( + string password) + { + return Strings.ToAsciiByteArray(password); + } + + /** + * 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) + { + return Encoding.UTF8.GetBytes(password); + } + + [Obsolete("Use version taking 'char[]' instead")] + public static byte[] Pkcs5PasswordToUtf8Bytes( + string password) + { + return Encoding.UTF8.GetBytes(password); + } + + /** + * 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) + { + return Pkcs12PasswordToBytes(password, false); + } + + public static byte[] Pkcs12PasswordToBytes( + char[] password, + bool wrongPkcs12Zero) + { + if (password.Length < 1) + { + return new byte[wrongPkcs12Zero ? 2 : 0]; + } + + // +1 for extra 2 pad bytes. + byte[] bytes = new byte[(password.Length + 1) * 2]; + + Encoding.BigEndianUnicode.GetBytes(password, 0, password.Length, bytes, 0); + + return bytes; + } + } +} diff --git a/Crypto/src/crypto/StreamBlockCipher.cs b/Crypto/src/crypto/StreamBlockCipher.cs new file mode 100644
index 000000000..ef2a8b68a --- /dev/null +++ b/Crypto/src/crypto/StreamBlockCipher.cs
@@ -0,0 +1,109 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace 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 + : IStreamCipher + { + private readonly IBlockCipher cipher; + private readonly byte[] oneByte = new byte[1]; + + /** + * basic constructor. + * + * @param cipher the block cipher to be wrapped. + * @exception ArgumentException if the cipher has a block size other than + * one. + */ + public StreamBlockCipher( + IBlockCipher cipher) + { + if (cipher == null) + throw new ArgumentNullException("cipher"); + if (cipher.GetBlockSize() != 1) + throw new ArgumentException("block cipher block size != 1.", "cipher"); + + this.cipher = cipher; + } + + /** + * initialise the underlying cipher. + * + * @param forEncryption true if we are setting up for encryption, false otherwise. + * @param param the necessary parameters for the underlying cipher to be initialised. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + cipher.Init(forEncryption, parameters); + } + + /** + * return the name of the algorithm we are wrapping. + * + * @return the name of the algorithm we are wrapping. + */ + public string AlgorithmName + { + get { return cipher.AlgorithmName; } + } + + /** + * 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 input) + { + oneByte[0] = input; + + 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[] input, + int inOff, + int length, + byte[] output, + int outOff) + { + if (outOff + length > output.Length) + throw new DataLengthException("output buffer too small in ProcessBytes()"); + + for (int i = 0; i != length; i++) + { + cipher.ProcessBlock(input, inOff + i, output, 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/Crypto/src/crypto/agreement/DHAgreement.cs b/Crypto/src/crypto/agreement/DHAgreement.cs new file mode 100644
index 000000000..d214caafe --- /dev/null +++ b/Crypto/src/crypto/agreement/DHAgreement.cs
@@ -0,0 +1,93 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Agreement +{ + /** + * 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> + * <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.</p> + */ + public class DHAgreement + { + private DHPrivateKeyParameters key; + private DHParameters dhParams; + private BigInteger privateValue; + private SecureRandom random; + + public void Init( + ICipherParameters parameters) + { + AsymmetricKeyParameter kParam; + if (parameters is ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)parameters; + + this.random = rParam.Random; + kParam = (AsymmetricKeyParameter)rParam.Parameters; + } + else + { + this.random = new SecureRandom(); + kParam = (AsymmetricKeyParameter)parameters; + } + + if (!(kParam is DHPrivateKeyParameters)) + { + throw new ArgumentException("DHEngine expects DHPrivateKeyParameters"); + } + + this.key = (DHPrivateKeyParameters)kParam; + this.dhParams = key.Parameters; + } + + /** + * 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.Private).X; + + return ((DHPublicKeyParameters)dhPair.Public).Y; + } + + /** + * 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 == null) + throw new ArgumentNullException("pub"); + if (message == null) + throw new ArgumentNullException("message"); + + if (!pub.Parameters.Equals(dhParams)) + { + throw new ArgumentException("Diffie-Hellman public key has wrong parameters."); + } + + BigInteger p = dhParams.P; + + return message.ModPow(key.X, p).Multiply(pub.Y.ModPow(privateValue, p)).Mod(p); + } + } +} diff --git a/Crypto/src/crypto/agreement/DHBasicAgreement.cs b/Crypto/src/crypto/agreement/DHBasicAgreement.cs new file mode 100644
index 000000000..5a5277049 --- /dev/null +++ b/Crypto/src/crypto/agreement/DHBasicAgreement.cs
@@ -0,0 +1,60 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Agreement +{ + /** + * 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.</p> + */ + public class DHBasicAgreement + : IBasicAgreement + { + private DHPrivateKeyParameters key; + private DHParameters dhParams; + + public void Init( + ICipherParameters parameters) + { + if (parameters is ParametersWithRandom) + { + parameters = ((ParametersWithRandom) parameters).Parameters; + } + + if (!(parameters is DHPrivateKeyParameters)) + { + throw new ArgumentException("DHEngine expects DHPrivateKeyParameters"); + } + + this.key = (DHPrivateKeyParameters) parameters; + this.dhParams = key.Parameters; + } + + /** + * given a short term public key from a given party calculate the next + * message in the agreement sequence. + */ + public BigInteger CalculateAgreement( + ICipherParameters pubKey) + { + if (this.key == null) + throw new InvalidOperationException("Agreement algorithm not initialised"); + + DHPublicKeyParameters pub = (DHPublicKeyParameters)pubKey; + + if (!pub.Parameters.Equals(dhParams)) + { + throw new ArgumentException("Diffie-Hellman public key has wrong parameters."); + } + + return pub.Y.ModPow(key.X, dhParams.P); + } + } + +} diff --git a/Crypto/src/crypto/agreement/ECDHBasicAgreement.cs b/Crypto/src/crypto/agreement/ECDHBasicAgreement.cs new file mode 100644
index 000000000..f272e4969 --- /dev/null +++ b/Crypto/src/crypto/agreement/ECDHBasicAgreement.cs
@@ -0,0 +1,50 @@ +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Agreement +{ + /** + * 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 + : IBasicAgreement + { + protected internal ECPrivateKeyParameters privKey; + + public void Init( + ICipherParameters parameters) + { + if (parameters is ParametersWithRandom) + { + parameters = ((ParametersWithRandom)parameters).Parameters; + } + + this.privKey = (ECPrivateKeyParameters)parameters; + } + + public virtual BigInteger CalculateAgreement( + ICipherParameters pubKey) + { + ECPublicKeyParameters pub = (ECPublicKeyParameters) pubKey; + ECPoint P = pub.Q.Multiply(privKey.D); + + // if ( p.IsInfinity ) throw new Exception("d*Q == infinity"); + + return P.X.ToBigInteger(); + } + } + +} diff --git a/Crypto/src/crypto/agreement/ECDHCBasicAgreement.cs b/Crypto/src/crypto/agreement/ECDHCBasicAgreement.cs new file mode 100644
index 000000000..905d241fc --- /dev/null +++ b/Crypto/src/crypto/agreement/ECDHCBasicAgreement.cs
@@ -0,0 +1,58 @@ +using System; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Agreement +{ + /** + * 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!).</p> + */ + public class ECDHCBasicAgreement + : IBasicAgreement + { + private ECPrivateKeyParameters key; + + public void Init( + ICipherParameters parameters) + { + if (parameters is ParametersWithRandom) + { + parameters = ((ParametersWithRandom) parameters).Parameters; + } + + this.key = (ECPrivateKeyParameters)parameters; + } + + public BigInteger CalculateAgreement( + ICipherParameters pubKey) + { + ECPublicKeyParameters pub = (ECPublicKeyParameters) pubKey; + ECDomainParameters parameters = pub.Parameters; + ECPoint P = pub.Q.Multiply(parameters.H.Multiply(key.D)); + + // if ( p.IsInfinity ) throw new Exception("Invalid public key"); + + return P.X.ToBigInteger(); + } + } + +} diff --git a/Crypto/src/crypto/agreement/ECDHWithKdfBasicAgreement.cs b/Crypto/src/crypto/agreement/ECDHWithKdfBasicAgreement.cs new file mode 100644
index 000000000..28437a268 --- /dev/null +++ b/Crypto/src/crypto/agreement/ECDHWithKdfBasicAgreement.cs
@@ -0,0 +1,64 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto.Agreement.Kdf; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Agreement +{ + public class ECDHWithKdfBasicAgreement + : ECDHBasicAgreement + { + private readonly string algorithm; + private readonly IDerivationFunction kdf; + + public ECDHWithKdfBasicAgreement( + string algorithm, + IDerivationFunction kdf) + { + if (algorithm == null) + throw new ArgumentNullException("algorithm"); + if (kdf == null) + throw new ArgumentNullException("kdf"); + + this.algorithm = algorithm; + this.kdf = kdf; + } + + public override BigInteger CalculateAgreement( + ICipherParameters pubKey) + { + // Note that the ec.KeyAgreement class in JCE only uses kdf in one + // of the engineGenerateSecret methods. + + BigInteger result = base.CalculateAgreement(pubKey); + + int keySize = GeneratorUtilities.GetDefaultKeySize(algorithm); + + DHKdfParameters dhKdfParams = new DHKdfParameters( + new DerObjectIdentifier(algorithm), + keySize, + bigIntToBytes(result)); + + kdf.Init(dhKdfParams); + + byte[] keyBytes = new byte[keySize / 8]; + kdf.GenerateBytes(keyBytes, 0, keyBytes.Length); + + return new BigInteger(1, keyBytes); + } + + private byte[] bigIntToBytes( + BigInteger r) + { + int byteLength = X9IntegerConverter.GetByteLength(privKey.Parameters.G.X); + return X9IntegerConverter.IntegerToBytes(r, byteLength); + } + } +} diff --git a/Crypto/src/crypto/agreement/ECMqvBasicAgreement.cs b/Crypto/src/crypto/agreement/ECMqvBasicAgreement.cs new file mode 100644
index 000000000..51faa8e49 --- /dev/null +++ b/Crypto/src/crypto/agreement/ECMqvBasicAgreement.cs
@@ -0,0 +1,85 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; + +namespace Org.BouncyCastle.Crypto.Agreement +{ + public class ECMqvBasicAgreement + : IBasicAgreement + { + protected internal MqvPrivateParameters privParams; + + public void Init( + ICipherParameters parameters) + { + if (parameters is ParametersWithRandom) + { + parameters = ((ParametersWithRandom)parameters).Parameters; + } + + this.privParams = (MqvPrivateParameters)parameters; + } + + public virtual BigInteger CalculateAgreement( + ICipherParameters pubKey) + { + MqvPublicParameters pubParams = (MqvPublicParameters)pubKey; + + ECPrivateKeyParameters staticPrivateKey = privParams.StaticPrivateKey; + + ECPoint agreement = calculateMqvAgreement(staticPrivateKey.Parameters, staticPrivateKey, + privParams.EphemeralPrivateKey, privParams.EphemeralPublicKey, + pubParams.StaticPublicKey, pubParams.EphemeralPublicKey); + + return agreement.X.ToBigInteger(); + } + + // The ECMQV Primitive as described in SEC-1, 3.4 + private static ECPoint calculateMqvAgreement( + ECDomainParameters parameters, + ECPrivateKeyParameters d1U, + ECPrivateKeyParameters d2U, + ECPublicKeyParameters Q2U, + ECPublicKeyParameters Q1V, + ECPublicKeyParameters Q2V) + { + BigInteger n = parameters.N; + int e = (n.BitLength + 1) / 2; + BigInteger powE = BigInteger.One.ShiftLeft(e); + + // The Q2U public key is optional + ECPoint q; + if (Q2U == null) + { + q = parameters.G.Multiply(d2U.D); + } + else + { + q = Q2U.Q; + } + + BigInteger x = q.X.ToBigInteger(); + BigInteger xBar = x.Mod(powE); + BigInteger Q2UBar = xBar.SetBit(e); + BigInteger s = d1U.D.Multiply(Q2UBar).Mod(n).Add(d2U.D).Mod(n); + + BigInteger xPrime = Q2V.Q.X.ToBigInteger(); + BigInteger xPrimeBar = xPrime.Mod(powE); + BigInteger Q2VBar = xPrimeBar.SetBit(e); + + BigInteger hs = parameters.H.Multiply(s).Mod(n); + + //ECPoint p = Q1V.Q.Multiply(Q2VBar).Add(Q2V.Q).Multiply(hs); + ECPoint p = ECAlgorithms.SumOfTwoMultiplies( + Q1V.Q, Q2VBar.Multiply(hs).Mod(n), Q2V.Q, hs); + + if (p.IsInfinity) + throw new InvalidOperationException("Infinity is not a valid agreement value for MQV"); + + return p; + } + } +} diff --git a/Crypto/src/crypto/agreement/ECMqvWithKdfBasicAgreement.cs b/Crypto/src/crypto/agreement/ECMqvWithKdfBasicAgreement.cs new file mode 100644
index 000000000..093ce4056 --- /dev/null +++ b/Crypto/src/crypto/agreement/ECMqvWithKdfBasicAgreement.cs
@@ -0,0 +1,64 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto.Agreement.Kdf; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Agreement +{ + public class ECMqvWithKdfBasicAgreement + : ECMqvBasicAgreement + { + private readonly string algorithm; + private readonly IDerivationFunction kdf; + + public ECMqvWithKdfBasicAgreement( + string algorithm, + IDerivationFunction kdf) + { + if (algorithm == null) + throw new ArgumentNullException("algorithm"); + if (kdf == null) + throw new ArgumentNullException("kdf"); + + this.algorithm = algorithm; + this.kdf = kdf; + } + + public override BigInteger CalculateAgreement( + ICipherParameters pubKey) + { + // Note that the ec.KeyAgreement class in JCE only uses kdf in one + // of the engineGenerateSecret methods. + + BigInteger result = base.CalculateAgreement(pubKey); + + int keySize = GeneratorUtilities.GetDefaultKeySize(algorithm); + + DHKdfParameters dhKdfParams = new DHKdfParameters( + new DerObjectIdentifier(algorithm), + keySize, + bigIntToBytes(result)); + + kdf.Init(dhKdfParams); + + byte[] keyBytes = new byte[keySize / 8]; + kdf.GenerateBytes(keyBytes, 0, keyBytes.Length); + + return new BigInteger(1, keyBytes); + } + + private byte[] bigIntToBytes( + BigInteger r) + { + int byteLength = X9IntegerConverter.GetByteLength(privParams.StaticPrivateKey.Parameters.G.X); + return X9IntegerConverter.IntegerToBytes(r, byteLength); + } + } +} diff --git a/Crypto/src/crypto/agreement/kdf/DHKdfParameters.cs b/Crypto/src/crypto/agreement/kdf/DHKdfParameters.cs new file mode 100644
index 000000000..f6c9e6079 --- /dev/null +++ b/Crypto/src/crypto/agreement/kdf/DHKdfParameters.cs
@@ -0,0 +1,57 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Crypto.Agreement.Kdf +{ + public class DHKdfParameters + : IDerivationParameters + { + private readonly DerObjectIdentifier algorithm; + private readonly int keySize; + private readonly byte[] z; + private readonly 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 = algorithm; + this.keySize = keySize; + this.z = z; // TODO Clone? + this.extraInfo = extraInfo; + } + + public DerObjectIdentifier Algorithm + { + get { return algorithm; } + } + + public int KeySize + { + get { return keySize; } + } + + public byte[] GetZ() + { + // TODO Clone? + return z; + } + + public byte[] GetExtraInfo() + { + // TODO Clone? + return extraInfo; + } + } +} diff --git a/Crypto/src/crypto/agreement/kdf/DHKekGenerator.cs b/Crypto/src/crypto/agreement/kdf/DHKekGenerator.cs new file mode 100644
index 000000000..fa2921539 --- /dev/null +++ b/Crypto/src/crypto/agreement/kdf/DHKekGenerator.cs
@@ -0,0 +1,129 @@ +using System; + +using Org.BouncyCastle.Asn1; + +namespace Org.BouncyCastle.Crypto.Agreement.Kdf +{ + /** + * RFC 2631 Diffie-hellman KEK derivation function. + */ + public class DHKekGenerator + : IDerivationFunction + { + private readonly IDigest digest; + + private DerObjectIdentifier algorithm; + private int keySize; + private byte[] z; + private byte[] partyAInfo; + + public DHKekGenerator( + IDigest digest) + { + this.digest = digest; + } + + public void Init( + IDerivationParameters param) + { + DHKdfParameters parameters = (DHKdfParameters)param; + + this.algorithm = parameters.Algorithm; + this.keySize = parameters.KeySize; + this.z = parameters.GetZ(); // TODO Clone? + this.partyAInfo = parameters.GetExtraInfo(); // TODO Clone? + } + + public IDigest Digest + { + get { return digest; } + } + + public int GenerateBytes( + byte[] outBytes, + int outOff, + int len) + { + if ((outBytes.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 ArgumentException("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.BlockUpdate(z, 0, z.Length); + + // KeySpecificInfo + DerSequence keyInfo = new DerSequence( + algorithm, + new DerOctetString(integerToBytes(counter))); + + // OtherInfo + Asn1EncodableVector v1 = new Asn1EncodableVector(keyInfo); + + if (partyAInfo != null) + { + v1.Add(new DerTaggedObject(true, 0, new DerOctetString(partyAInfo))); + } + + v1.Add(new DerTaggedObject(true, 2, new DerOctetString(integerToBytes(keySize)))); + + byte[] other = new DerSequence(v1).GetDerEncoded(); + + digest.BlockUpdate(other, 0, other.Length); + + digest.DoFinal(dig, 0); + + if (len > outLen) + { + Array.Copy(dig, 0, outBytes, outOff, outLen); + outOff += outLen; + len -= outLen; + } + else + { + Array.Copy(dig, 0, outBytes, outOff, len); + } + + counter++; + } + + digest.Reset(); + + return len; + } + + private byte[] integerToBytes( + int keySize) + { + byte[] val = new byte[4]; + + val[0] = (byte)(keySize >> 24); + val[1] = (byte)(keySize >> 16); + val[2] = (byte)(keySize >> 8); + val[3] = (byte)keySize; + + return val; + } + } +} diff --git a/Crypto/src/crypto/agreement/kdf/ECDHKekGenerator.cs b/Crypto/src/crypto/agreement/kdf/ECDHKekGenerator.cs new file mode 100644
index 000000000..7d55aa485 --- /dev/null +++ b/Crypto/src/crypto/agreement/kdf/ECDHKekGenerator.cs
@@ -0,0 +1,71 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Agreement.Kdf +{ + /** + * X9.63 based key derivation function for ECDH CMS. + */ + public class ECDHKekGenerator + : IDerivationFunction + { + private readonly IDerivationFunction kdf; + + private DerObjectIdentifier algorithm; + private int keySize; + private byte[] z; + + public ECDHKekGenerator( + IDigest digest) + { + this.kdf = new Kdf2BytesGenerator(digest); + } + + public void Init( + IDerivationParameters param) + { + DHKdfParameters parameters = (DHKdfParameters)param; + + this.algorithm = parameters.Algorithm; + this.keySize = parameters.KeySize; + this.z = parameters.GetZ(); // TODO Clone? + } + + public IDigest Digest + { + get { return kdf.Digest; } + } + + public int GenerateBytes( + byte[] outBytes, + int outOff, + int len) + { + // TODO Create an ASN.1 class for this (RFC3278) + // ECC-CMS-SharedInfo + DerSequence s = new DerSequence( + new AlgorithmIdentifier(algorithm, DerNull.Instance), + new DerTaggedObject(true, 2, new DerOctetString(integerToBytes(keySize)))); + + kdf.Init(new KdfParameters(z, s.GetDerEncoded())); + + return kdf.GenerateBytes(outBytes, outOff, len); + } + + private byte[] integerToBytes(int keySize) + { + byte[] val = new byte[4]; + + val[0] = (byte)(keySize >> 24); + val[1] = (byte)(keySize >> 16); + val[2] = (byte)(keySize >> 8); + val[3] = (byte)keySize; + + return val; + } + } +} diff --git a/Crypto/src/crypto/agreement/srp/SRP6Client.cs b/Crypto/src/crypto/agreement/srp/SRP6Client.cs new file mode 100644
index 000000000..309736564 --- /dev/null +++ b/Crypto/src/crypto/agreement/srp/SRP6Client.cs
@@ -0,0 +1,93 @@ +using System; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Agreement.Srp +{ + /** + * 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 privA; + protected BigInteger pubA; + + protected BigInteger B; + + protected BigInteger x; + protected BigInteger u; + protected BigInteger S; + + protected IDigest 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 virtual void Init(BigInteger N, BigInteger g, IDigest 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 virtual BigInteger GenerateClientCredentials(byte[] salt, byte[] identity, byte[] password) + { + this.x = Srp6Utilities.CalculateX(digest, N, salt, identity, password); + this.privA = SelectPrivateValue(); + this.pubA = g.ModPow(privA, N); + + return pubA; + } + + /** + * 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 virtual BigInteger CalculateSecret(BigInteger serverB) + { + this.B = Srp6Utilities.ValidatePublicValue(N, serverB); + this.u = Srp6Utilities.CalculateU(digest, N, pubA, B); + this.S = CalculateS(); + + return S; + } + + protected virtual BigInteger SelectPrivateValue() + { + return Srp6Utilities.GeneratePrivateValue(digest, N, g, random); + } + + private BigInteger CalculateS() + { + BigInteger k = Srp6Utilities.CalculateK(digest, N, g); + BigInteger exp = u.Multiply(x).Add(privA); + BigInteger tmp = g.ModPow(x, N).Multiply(k).Mod(N); + return B.Subtract(tmp).Mod(N).ModPow(exp, N); + } + } +} diff --git a/Crypto/src/crypto/agreement/srp/SRP6Server.cs b/Crypto/src/crypto/agreement/srp/SRP6Server.cs new file mode 100644
index 000000000..35b96d488 --- /dev/null +++ b/Crypto/src/crypto/agreement/srp/SRP6Server.cs
@@ -0,0 +1,90 @@ +using System; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Agreement.Srp +{ + /** + * 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 IDigest digest; + + protected BigInteger A; + + protected BigInteger privB; + protected BigInteger pubB; + + 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 virtual void Init(BigInteger N, BigInteger g, BigInteger v, IDigest 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 virtual BigInteger GenerateServerCredentials() + { + BigInteger k = Srp6Utilities.CalculateK(digest, N, g); + this.privB = SelectPrivateValue(); + this.pubB = k.Multiply(v).Mod(N).Add(g.ModPow(privB, N)).Mod(N); + + return pubB; + } + + /** + * 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 virtual BigInteger CalculateSecret(BigInteger clientA) + { + this.A = Srp6Utilities.ValidatePublicValue(N, clientA); + this.u = Srp6Utilities.CalculateU(digest, N, A, pubB); + this.S = CalculateS(); + + return S; + } + + protected virtual BigInteger SelectPrivateValue() + { + return Srp6Utilities.GeneratePrivateValue(digest, N, g, random); + } + + private BigInteger CalculateS() + { + return v.ModPow(u, N).Multiply(A).Mod(N).ModPow(privB, N); + } + } +} diff --git a/Crypto/src/crypto/agreement/srp/SRP6Utilities.cs b/Crypto/src/crypto/agreement/srp/SRP6Utilities.cs new file mode 100644
index 000000000..4e790f572 --- /dev/null +++ b/Crypto/src/crypto/agreement/srp/SRP6Utilities.cs
@@ -0,0 +1,85 @@ +using System; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Agreement.Srp +{ + public class Srp6Utilities + { + public static BigInteger CalculateK(IDigest digest, BigInteger N, BigInteger g) + { + return HashPaddedPair(digest, N, N, g); + } + + public static BigInteger CalculateU(IDigest digest, BigInteger N, BigInteger A, BigInteger B) + { + return HashPaddedPair(digest, N, A, B); + } + + public static BigInteger CalculateX(IDigest digest, BigInteger N, byte[] salt, byte[] identity, byte[] password) + { + byte[] output = new byte[digest.GetDigestSize()]; + + digest.BlockUpdate(identity, 0, identity.Length); + digest.Update((byte)':'); + digest.BlockUpdate(password, 0, password.Length); + digest.DoFinal(output, 0); + + digest.BlockUpdate(salt, 0, salt.Length); + digest.BlockUpdate(output, 0, output.Length); + digest.DoFinal(output, 0); + + return new BigInteger(1, output); + } + + public static BigInteger GeneratePrivateValue(IDigest digest, BigInteger N, BigInteger g, SecureRandom random) + { + int minBits = System.Math.Min(256, N.BitLength / 2); + BigInteger min = BigInteger.One.ShiftLeft(minBits - 1); + BigInteger max = N.Subtract(BigInteger.One); + + return BigIntegers.CreateRandomInRange(min, max, random); + } + + public static BigInteger ValidatePublicValue(BigInteger N, BigInteger val) + { + val = val.Mod(N); + + // Check that val % N != 0 + if (val.Equals(BigInteger.Zero)) + throw new CryptoException("Invalid public value: 0"); + + return val; + } + + private static BigInteger HashPaddedPair(IDigest 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.BlockUpdate(n1_bytes, 0, n1_bytes.Length); + digest.BlockUpdate(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]; + Array.Copy(bs, 0, tmp, length - bs.Length, bs.Length); + bs = tmp; + } + return bs; + } + } +} diff --git a/Crypto/src/crypto/agreement/srp/SRP6VerifierGenerator.cs b/Crypto/src/crypto/agreement/srp/SRP6VerifierGenerator.cs new file mode 100644
index 000000000..264833b4d --- /dev/null +++ b/Crypto/src/crypto/agreement/srp/SRP6VerifierGenerator.cs
@@ -0,0 +1,49 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Agreement.Srp +{ + /** + * Generates new SRP verifier for user + */ + public class Srp6VerifierGenerator + { + protected BigInteger N; + protected BigInteger g; + protected IDigest 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 virtual void Init(BigInteger N, BigInteger g, IDigest 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 virtual BigInteger GenerateVerifier(byte[] salt, byte[] identity, byte[] password) + { + BigInteger x = Srp6Utilities.CalculateX(digest, N, salt, identity, password); + + return g.ModPow(x, N); + } + } +} + diff --git a/Crypto/src/crypto/digests/GOST3411Digest.cs b/Crypto/src/crypto/digests/GOST3411Digest.cs new file mode 100644
index 000000000..9f0bec9e6 --- /dev/null +++ b/Crypto/src/crypto/digests/GOST3411Digest.cs
@@ -0,0 +1,343 @@ +using System; + +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * implementation of GOST R 34.11-94 + */ + public class Gost3411Digest + : IDigest + { + private const 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 = MakeC(); + + private byte[] xBuf = new byte[32]; + private int xBufOff; + private ulong byteCount; + + private readonly IBlockCipher cipher = new Gost28147Engine(); + private readonly byte[] sBox; + + private static byte[][] MakeC() + { + byte[][] c = new byte[4][]; + for (int i = 0; i < 4; ++i) + { + c[i] = new byte[32]; + } + return c; + } + + /** + * 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) + { + this.sBox = t.sBox; + cipher.Init(true, new ParametersWithSBox(null, sBox)); + + Reset(); + + Array.Copy(t.H, 0, this.H, 0, t.H.Length); + Array.Copy(t.L, 0, this.L, 0, t.L.Length); + Array.Copy(t.M, 0, this.M, 0, t.M.Length); + Array.Copy(t.Sum, 0, this.Sum, 0, t.Sum.Length); + Array.Copy(t.C[1], 0, this.C[1], 0, t.C[1].Length); + Array.Copy(t.C[2], 0, this.C[2], 0, t.C[2].Length); + Array.Copy(t.C[3], 0, this.C[3], 0, t.C[3].Length); + Array.Copy(t.xBuf, 0, this.xBuf, 0, t.xBuf.Length); + + this.xBufOff = t.xBufOff; + this.byteCount = t.byteCount; + } + + public string AlgorithmName + { + get { return "Gost3411"; } + } + + public int GetDigestSize() + { + return DIGEST_LENGTH; + } + + public void Update( + byte input) + { + xBuf[xBufOff++] = input; + if (xBufOff == xBuf.Length) + { + sumByteArray(xBuf); // calc sum M + processBlock(xBuf, 0); + xBufOff = 0; + } + byteCount++; + } + + public void BlockUpdate( + byte[] input, + int inOff, + int length) + { + while ((xBufOff != 0) && (length > 0)) + { + Update(input[inOff]); + inOff++; + length--; + } + + while (length > xBuf.Length) + { + Array.Copy(input, inOff, xBuf, 0, xBuf.Length); + + sumByteArray(xBuf); // calc sum M + processBlock(xBuf, 0); + inOff += xBuf.Length; + length -= xBuf.Length; + byteCount += (uint)xBuf.Length; + } + + // load in the remainder. + while (length > 0) + { + Update(input[inOff]); + inOff++; + length--; + } + } + + // (i + 1 + 4(k - 1)) = 8i + k i = 0-3, k = 1-8 + private byte[] K = new byte[32]; + + private byte[] P(byte[] input) + { + int fourK = 0; + for(int k = 0; k < 8; k++) + { + K[fourK++] = input[k]; + K[fourK++] = input[8 + k]; + K[fourK++] = input[16 + k]; + K[fourK++] = input[24 + k]; + } + + return K; + } + + //A (x) = (x0 ^ x1) || x3 || x2 || x1 + byte[] a = new byte[8]; + private byte[] A(byte[] input) + { + for(int j=0; j<8; j++) + { + a[j]=(byte)(input[j] ^ input[j+8]); + } + + Array.Copy(input, 8, input, 0, 24); + Array.Copy(a, 0, input, 24, 8); + + return input; + } + + //Encrypt function, ECB mode + private void E(byte[] key, byte[] s, int sOff, byte[] input, int inOff) + { + cipher.Init(true, new KeyParameter(key)); + + cipher.ProcessBlock(input, inOff, s, sOff); + } + + // (in:) n16||..||n1 ==> (out:) n1^n2^n3^n4^n13^n16||n16||..||n2 + internal short[] wS = new short[16], w_S = new short[16]; + + private void fw(byte[] input) + { + cpyBytesToShort(input, wS); + w_S[15] = (short)(wS[0] ^ wS[1] ^ wS[2] ^ wS[3] ^ wS[12] ^ wS[15]); + Array.Copy(wS, 1, w_S, 0, 15); + cpyShortToBytes(w_S, input); + } + + // block processing + internal byte[] S = new byte[32], U = new byte[32], V = new byte[32], W = new byte[32]; + + private void processBlock(byte[] input, int inOff) + { + Array.Copy(input, inOff, M, 0, 32); + + //key step 1 + + // H = h3 || h2 || h1 || h0 + // S = s3 || s2 || s1 || s0 + H.CopyTo(U, 0); + M.CopyTo(V, 0); + 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); + } + Array.Copy(S, 0, H, 0, H.Length); + } + + private void finish() + { + ulong bitCount = byteCount * 8; + Pack.UInt64_To_LE(bitCount, L); + + while (xBufOff != 0) + { + Update((byte)0); + } + + processBlock(L, 0); + processBlock(Sum, 0); + } + + public int DoFinal( + byte[] output, + int outOff) + { + finish(); + + H.CopyTo(output, outOff); + + Reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables to the IV values. + */ + private static readonly 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; + + Array.Clear(H, 0, H.Length); + Array.Clear(L, 0, L.Length); + Array.Clear(M, 0, M.Length); + Array.Clear(C[1], 0, C[1].Length); // real index C = +1 because index array with 0. + Array.Clear(C[3], 0, C[3].Length); + Array.Clear(Sum, 0, Sum.Length); + Array.Clear(xBuf, 0, xBuf.Length); + + C2.CopyTo(C[2], 0); + } + + // 256 bitsblock modul -> (Sum + a mod (2^256)) + private void sumByteArray( + byte[] input) + { + int carry = 0; + + for (int i = 0; i != Sum.Length; i++) + { + int sum = (Sum[i] & 0xff) + (input[i] & 0xff) + carry; + + Sum[i] = (byte)sum; + + carry = sum >> 8; + } + } + + private static 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 static 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; + } + } +} diff --git a/Crypto/src/crypto/digests/GeneralDigest.cs b/Crypto/src/crypto/digests/GeneralDigest.cs new file mode 100644
index 000000000..77c17ed58 --- /dev/null +++ b/Crypto/src/crypto/digests/GeneralDigest.cs
@@ -0,0 +1,118 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * base implementation of MD4 family style digest as outlined in + * "Handbook of Applied Cryptography", pages 344 - 347. + */ + public abstract class GeneralDigest + : IDigest + { + private const int BYTE_LENGTH = 64; + + private byte[] xBuf; + private int xBufOff; + + private long byteCount; + + internal GeneralDigest() + { + xBuf = new byte[4]; + } + + internal GeneralDigest(GeneralDigest t) + { + xBuf = new byte[t.xBuf.Length]; + Array.Copy(t.xBuf, 0, xBuf, 0, t.xBuf.Length); + + xBufOff = t.xBufOff; + byteCount = t.byteCount; + } + + public void Update(byte input) + { + xBuf[xBufOff++] = input; + + if (xBufOff == xBuf.Length) + { + ProcessWord(xBuf, 0); + xBufOff = 0; + } + + byteCount++; + } + + public void BlockUpdate( + byte[] input, + int inOff, + int length) + { + // + // fill the current word + // + while ((xBufOff != 0) && (length > 0)) + { + Update(input[inOff]); + inOff++; + length--; + } + + // + // process whole words. + // + while (length > xBuf.Length) + { + ProcessWord(input, inOff); + + inOff += xBuf.Length; + length -= xBuf.Length; + byteCount += xBuf.Length; + } + + // + // load in the remainder. + // + while (length > 0) + { + Update(input[inOff]); + + inOff++; + length--; + } + } + + public void Finish() + { + long bitLength = (byteCount << 3); + + // + // add the pad bytes. + // + Update((byte)128); + + while (xBufOff != 0) Update((byte)0); + ProcessLength(bitLength); + ProcessBlock(); + } + + public virtual void Reset() + { + byteCount = 0; + xBufOff = 0; + Array.Clear(xBuf, 0, xBuf.Length); + } + + public int GetByteLength() + { + return BYTE_LENGTH; + } + + internal abstract void ProcessWord(byte[] input, int inOff); + internal abstract void ProcessLength(long bitLength); + internal abstract void ProcessBlock(); + public abstract string AlgorithmName { get; } + public abstract int GetDigestSize(); + public abstract int DoFinal(byte[] output, int outOff); + } +} diff --git a/Crypto/src/crypto/digests/LongDigest.cs b/Crypto/src/crypto/digests/LongDigest.cs new file mode 100644
index 000000000..702753b2b --- /dev/null +++ b/Crypto/src/crypto/digests/LongDigest.cs
@@ -0,0 +1,346 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * Base class for SHA-384 and SHA-512. + */ + public abstract class LongDigest + : IDigest + { + private int MyByteLength = 128; + + private byte[] xBuf; + private int xBufOff; + + private long byteCount1; + private long byteCount2; + + internal ulong H1, H2, H3, H4, H5, H6, H7, H8; + + private ulong[] W = new ulong[80]; + private int wOff; + + /** + * Constructor for variable length word + */ + internal LongDigest() + { + xBuf = new byte[8]; + + Reset(); + } + + /** + * Copy constructor. We are using copy constructors in place + * of the object.Clone() interface as this interface is not + * supported by J2ME. + */ + internal LongDigest( + LongDigest t) + { + xBuf = new byte[t.xBuf.Length]; + Array.Copy(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; + + Array.Copy(t.W, 0, W, 0, t.W.Length); + wOff = t.wOff; + } + + public void Update( + byte input) + { + xBuf[xBufOff++] = input; + + if (xBufOff == xBuf.Length) + { + ProcessWord(xBuf, 0); + xBufOff = 0; + } + + byteCount1++; + } + + public void BlockUpdate( + byte[] input, + int inOff, + int length) + { + // + // fill the current word + // + while ((xBufOff != 0) && (length > 0)) + { + Update(input[inOff]); + + inOff++; + length--; + } + + // + // process whole words. + // + while (length > xBuf.Length) + { + ProcessWord(input, inOff); + + inOff += xBuf.Length; + length -= xBuf.Length; + byteCount1 += xBuf.Length; + } + + // + // load in the remainder. + // + while (length > 0) + { + Update(input[inOff]); + + inOff++; + length--; + } + } + + 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 virtual void Reset() + { + byteCount1 = 0; + byteCount2 = 0; + + xBufOff = 0; + for ( int i = 0; i < xBuf.Length; i++ ) + { + xBuf[i] = 0; + } + + wOff = 0; + Array.Clear(W, 0, W.Length); + } + + internal void ProcessWord( + byte[] input, + int inOff) + { + W[wOff] = Pack.BE_To_UInt64(input, 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 += (long) ((ulong) byteCount1 >> 61); + byteCount1 &= 0x1fffffffffffffffL; + } + } + + internal void ProcessLength( + long lowW, + long hiW) + { + if (wOff > 14) + { + ProcessBlock(); + } + + W[14] = (ulong)hiW; + W[15] = (ulong)lowW; + } + + internal void ProcessBlock() + { + AdjustByteCounts(); + + // + // expand 16 word block into 80 word blocks. + // + for (int ti = 16; ti <= 79; ++ti) + { + W[ti] = Sigma1(W[ti - 2]) + W[ti - 7] + Sigma0(W[ti - 15]) + W[ti - 16]; + } + + // + // set up working variables. + // + ulong a = H1; + ulong b = H2; + ulong c = H3; + ulong d = H4; + ulong e = H5; + ulong f = H6; + ulong g = H7; + ulong 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; + Array.Clear(W, 0, 16); + } + + /* SHA-384 and SHA-512 functions (as for SHA-256 but for longs) */ + private static ulong Ch(ulong x, ulong y, ulong z) + { + return (x & y) ^ (~x & z); + } + + private static ulong Maj(ulong x, ulong y, ulong z) + { + return (x & y) ^ (x & z) ^ (y & z); + } + + private static ulong Sum0(ulong x) + { + return ((x << 36) | (x >> 28)) ^ ((x << 30) | (x >> 34)) ^ ((x << 25) | (x >> 39)); + } + + private static ulong Sum1(ulong x) + { + return ((x << 50) | (x >> 14)) ^ ((x << 46) | (x >> 18)) ^ ((x << 23) | (x >> 41)); + } + + private static ulong Sigma0(ulong x) + { + return ((x << 63) | (x >> 1)) ^ ((x << 56) | (x >> 8)) ^ (x >> 7); + } + + private static ulong Sigma1(ulong 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) + */ + internal static readonly ulong[] K = + { + 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, + 0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, + 0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, + 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694, + 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, + 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, + 0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, + 0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70, + 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df, + 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b, + 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, + 0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, + 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, + 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3, + 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec, + 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, + 0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, + 0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b, + 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c, + 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817 + }; + + public int GetByteLength() + { + return MyByteLength; + } + + public abstract string AlgorithmName { get; } + public abstract int GetDigestSize(); + public abstract int DoFinal(byte[] output, int outOff); + } +} diff --git a/Crypto/src/crypto/digests/MD2Digest.cs b/Crypto/src/crypto/digests/MD2Digest.cs new file mode 100644
index 000000000..78c696f33 --- /dev/null +++ b/Crypto/src/crypto/digests/MD2Digest.cs
@@ -0,0 +1,247 @@ +using System; +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Crypto.Digests +{ + + /** + * implementation of MD2 + * as outlined in RFC1319 by B.Kaliski from RSA Laboratories April 1992 + */ + public class MD2Digest + : IDigest + { + private const int DigestLength = 16; + private const int BYTE_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) + { + Array.Copy(t.X, 0, X, 0, t.X.Length); + xOff = t.xOff; + Array.Copy(t.M, 0, M, 0, t.M.Length); + mOff = t.mOff; + Array.Copy(t.C, 0, C, 0, t.C.Length); + COff = t.COff; + } + /** + * return the algorithm name + * + * @return the algorithm name + */ + public string AlgorithmName + { + get { return "MD2"; } + } + + public int GetDigestSize() + { + return DigestLength; + } + + public int GetByteLength() + { + return BYTE_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[] output, 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); + + Array.Copy(X, xOff, output, outOff, 16); + + Reset(); + + return DigestLength; + } + + /** + * 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 input) + { + M[mOff++] = input; + + 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 BlockUpdate(byte[] input, int inOff, int length) + { + // + // fill the current word + // + while ((mOff != 0) && (length > 0)) + { + Update(input[inOff]); + inOff++; + length--; + } + + // + // process whole words. + // + while (length > 16) + { + Array.Copy(input,inOff,M,0,16); + ProcessChecksum(M); + ProcessBlock(M); + length -= 16; + inOff += 16; + } + + // + // load in the remainder. + // + while (length > 0) + { + Update(input[inOff]); + inOff++; + length--; + } + } + + internal 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]; + } + } + internal 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 readonly 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 + }; + } + +} diff --git a/Crypto/src/crypto/digests/MD4Digest.cs b/Crypto/src/crypto/digests/MD4Digest.cs new file mode 100644
index 000000000..bc4eae0fd --- /dev/null +++ b/Crypto/src/crypto/digests/MD4Digest.cs
@@ -0,0 +1,271 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * 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 compatibility + * with legacy applications, it's not secure, don't use it for anything new!</p> + */ + public class MD4Digest + : GeneralDigest + { + private const int DigestLength = 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) : base(t) + { + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + + Array.Copy(t.X, 0, X, 0, t.X.Length); + xOff = t.xOff; + } + + public override string AlgorithmName + { + get { return "MD4"; } + } + + public override int GetDigestSize() + { + return DigestLength; + } + + internal override void ProcessWord( + byte[] input, + int inOff) + { + X[xOff++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8) + | ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24); + + if (xOff == 16) + { + ProcessBlock(); + } + } + + internal override void ProcessLength( + long bitLength) + { + if (xOff > 14) + { + ProcessBlock(); + } + + X[14] = (int)(bitLength & 0xffffffff); + X[15] = (int)((ulong) bitLength >> 32); + } + + private void UnpackWord( + int word, + byte[] outBytes, + int outOff) + { + outBytes[outOff] = (byte)word; + outBytes[outOff + 1] = (byte)((uint) word >> 8); + outBytes[outOff + 2] = (byte)((uint) word >> 16); + outBytes[outOff + 3] = (byte)((uint) word >> 24); + } + + public override int DoFinal( + byte[] output, + int outOff) + { + Finish(); + + UnpackWord(H1, output, outOff); + UnpackWord(H2, output, outOff + 4); + UnpackWord(H3, output, outOff + 8); + UnpackWord(H4, output, outOff + 12); + + Reset(); + + return DigestLength; + } + + /** + * reset the chaining variables to the IV values. + */ + public override void Reset() + { + base.Reset(); + + H1 = unchecked((int) 0x67452301); + H2 = unchecked((int) 0xefcdab89); + H3 = unchecked((int) 0x98badcfe); + H4 = unchecked((int) 0x10325476); + + xOff = 0; + + for (int i = 0; i != X.Length; i++) + { + X[i] = 0; + } + } + + // + // round 1 left rotates + // + private const int S11 = 3; + private const int S12 = 7; + private const int S13 = 11; + private const int S14 = 19; + + // + // round 2 left rotates + // + private const int S21 = 3; + private const int S22 = 5; + private const int S23 = 9; + private const int S24 = 13; + + // + // round 3 left rotates + // + private const int S31 = 3; + private const int S32 = 9; + private const int S33 = 11; + private const int S34 = 15; + + /* + * rotate int x left n bits. + */ + private int RotateLeft( + int x, + int n) + { + return (x << n) | (int) ((uint) 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; + } + + internal override 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; + } + } + } + +} diff --git a/Crypto/src/crypto/digests/MD5Digest.cs b/Crypto/src/crypto/digests/MD5Digest.cs new file mode 100644
index 000000000..50d93e4f8 --- /dev/null +++ b/Crypto/src/crypto/digests/MD5Digest.cs
@@ -0,0 +1,301 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * implementation of MD5 as outlined in "Handbook of Applied Cryptography", pages 346 - 347. + */ + public class MD5Digest + : GeneralDigest + { + private const int DigestLength = 16; + + private int H1, H2, H3, H4; // IV's + + private int[] X = new int[16]; + private int xOff; + + public MD5Digest() + { + Reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public MD5Digest(MD5Digest t) + : base(t) + { + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + + Array.Copy(t.X, 0, X, 0, t.X.Length); + xOff = t.xOff; + } + + public override string AlgorithmName + { + get { return "MD5"; } + } + + public override int GetDigestSize() + { + return DigestLength; + } + + internal override void ProcessWord( + byte[] input, + int inOff) + { + X[xOff++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8) + | ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24); + + if (xOff == 16) + { + ProcessBlock(); + } + } + + internal override void ProcessLength( + long bitLength) + { + if (xOff > 14) + { + ProcessBlock(); + } + + X[14] = (int)(bitLength & 0xffffffff); + X[15] = (int)((ulong) bitLength >> 32); + } + + private void UnpackWord( + int word, + byte[] outBytes, + int outOff) + { + outBytes[outOff] = (byte)word; + outBytes[outOff + 1] = (byte)((uint) word >> 8); + outBytes[outOff + 2] = (byte)((uint) word >> 16); + outBytes[outOff + 3] = (byte)((uint) word >> 24); + } + + public override int DoFinal( + byte[] output, + int outOff) + { + Finish(); + + UnpackWord(H1, output, outOff); + UnpackWord(H2, output, outOff + 4); + UnpackWord(H3, output, outOff + 8); + UnpackWord(H4, output, outOff + 12); + + Reset(); + + return DigestLength; + } + + /** + * reset the chaining variables to the IV values. + */ + public override void Reset() + { + base.Reset(); + + H1 = unchecked((int) 0x67452301); + H2 = unchecked((int) 0xefcdab89); + H3 = unchecked((int) 0x98badcfe); + H4 = unchecked((int) 0x10325476); + + xOff = 0; + + for (int i = 0; i != X.Length; i++) + { + X[i] = 0; + } + } + + // + // round 1 left rotates + // + private static readonly int S11 = 7; + private static readonly int S12 = 12; + private static readonly int S13 = 17; + private static readonly int S14 = 22; + + // + // round 2 left rotates + // + private static readonly int S21 = 5; + private static readonly int S22 = 9; + private static readonly int S23 = 14; + private static readonly int S24 = 20; + + // + // round 3 left rotates + // + private static readonly int S31 = 4; + private static readonly int S32 = 11; + private static readonly int S33 = 16; + private static readonly int S34 = 23; + + // + // round 4 left rotates + // + private static readonly int S41 = 6; + private static readonly int S42 = 10; + private static readonly int S43 = 15; + private static readonly int S44 = 21; + + /* + * rotate int x left n bits. + */ + private int RotateLeft( + int x, + int n) + { + return (x << n) | (int) ((uint) 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); + } + + internal override 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] + unchecked((int) 0xd76aa478)), S11) + b; + d = RotateLeft((d + F(a, b, c) + X[ 1] + unchecked((int) 0xe8c7b756)), S12) + a; + c = RotateLeft((c + F(d, a, b) + X[ 2] + unchecked((int) 0x242070db)), S13) + d; + b = RotateLeft((b + F(c, d, a) + X[ 3] + unchecked((int) 0xc1bdceee)), S14) + c; + a = RotateLeft((a + F(b, c, d) + X[ 4] + unchecked((int) 0xf57c0faf)), S11) + b; + d = RotateLeft((d + F(a, b, c) + X[ 5] + unchecked((int) 0x4787c62a)), S12) + a; + c = RotateLeft((c + F(d, a, b) + X[ 6] + unchecked((int) 0xa8304613)), S13) + d; + b = RotateLeft((b + F(c, d, a) + X[ 7] + unchecked((int) 0xfd469501)), S14) + c; + a = RotateLeft((a + F(b, c, d) + X[ 8] + unchecked((int) 0x698098d8)), S11) + b; + d = RotateLeft((d + F(a, b, c) + X[ 9] + unchecked((int) 0x8b44f7af)), S12) + a; + c = RotateLeft((c + F(d, a, b) + X[10] + unchecked((int) 0xffff5bb1)), S13) + d; + b = RotateLeft((b + F(c, d, a) + X[11] + unchecked((int) 0x895cd7be)), S14) + c; + a = RotateLeft((a + F(b, c, d) + X[12] + unchecked((int) 0x6b901122)), S11) + b; + d = RotateLeft((d + F(a, b, c) + X[13] + unchecked((int) 0xfd987193)), S12) + a; + c = RotateLeft((c + F(d, a, b) + X[14] + unchecked((int) 0xa679438e)), S13) + d; + b = RotateLeft((b + F(c, d, a) + X[15] + unchecked((int) 0x49b40821)), S14) + c; + + // + // Round 2 - G cycle, 16 times. + // + a = RotateLeft((a + G(b, c, d) + X[ 1] + unchecked((int) 0xf61e2562)), S21) + b; + d = RotateLeft((d + G(a, b, c) + X[ 6] + unchecked((int) 0xc040b340)), S22) + a; + c = RotateLeft((c + G(d, a, b) + X[11] + unchecked((int) 0x265e5a51)), S23) + d; + b = RotateLeft((b + G(c, d, a) + X[ 0] + unchecked((int) 0xe9b6c7aa)), S24) + c; + a = RotateLeft((a + G(b, c, d) + X[ 5] + unchecked((int) 0xd62f105d)), S21) + b; + d = RotateLeft((d + G(a, b, c) + X[10] + unchecked((int) 0x02441453)), S22) + a; + c = RotateLeft((c + G(d, a, b) + X[15] + unchecked((int) 0xd8a1e681)), S23) + d; + b = RotateLeft((b + G(c, d, a) + X[ 4] + unchecked((int) 0xe7d3fbc8)), S24) + c; + a = RotateLeft((a + G(b, c, d) + X[ 9] + unchecked((int) 0x21e1cde6)), S21) + b; + d = RotateLeft((d + G(a, b, c) + X[14] + unchecked((int) 0xc33707d6)), S22) + a; + c = RotateLeft((c + G(d, a, b) + X[ 3] + unchecked((int) 0xf4d50d87)), S23) + d; + b = RotateLeft((b + G(c, d, a) + X[ 8] + unchecked((int) 0x455a14ed)), S24) + c; + a = RotateLeft((a + G(b, c, d) + X[13] + unchecked((int) 0xa9e3e905)), S21) + b; + d = RotateLeft((d + G(a, b, c) + X[ 2] + unchecked((int) 0xfcefa3f8)), S22) + a; + c = RotateLeft((c + G(d, a, b) + X[ 7] + unchecked((int) 0x676f02d9)), S23) + d; + b = RotateLeft((b + G(c, d, a) + X[12] + unchecked((int) 0x8d2a4c8a)), S24) + c; + + // + // Round 3 - H cycle, 16 times. + // + a = RotateLeft((a + H(b, c, d) + X[ 5] + unchecked((int) 0xfffa3942)), S31) + b; + d = RotateLeft((d + H(a, b, c) + X[ 8] + unchecked((int) 0x8771f681)), S32) + a; + c = RotateLeft((c + H(d, a, b) + X[11] + unchecked((int) 0x6d9d6122)), S33) + d; + b = RotateLeft((b + H(c, d, a) + X[14] + unchecked((int) 0xfde5380c)), S34) + c; + a = RotateLeft((a + H(b, c, d) + X[ 1] + unchecked((int) 0xa4beea44)), S31) + b; + d = RotateLeft((d + H(a, b, c) + X[ 4] + unchecked((int) 0x4bdecfa9)), S32) + a; + c = RotateLeft((c + H(d, a, b) + X[ 7] + unchecked((int) 0xf6bb4b60)), S33) + d; + b = RotateLeft((b + H(c, d, a) + X[10] + unchecked((int) 0xbebfbc70)), S34) + c; + a = RotateLeft((a + H(b, c, d) + X[13] + unchecked((int) 0x289b7ec6)), S31) + b; + d = RotateLeft((d + H(a, b, c) + X[ 0] + unchecked((int) 0xeaa127fa)), S32) + a; + c = RotateLeft((c + H(d, a, b) + X[ 3] + unchecked((int) 0xd4ef3085)), S33) + d; + b = RotateLeft((b + H(c, d, a) + X[ 6] + unchecked((int) 0x04881d05)), S34) + c; + a = RotateLeft((a + H(b, c, d) + X[ 9] + unchecked((int) 0xd9d4d039)), S31) + b; + d = RotateLeft((d + H(a, b, c) + X[12] + unchecked((int) 0xe6db99e5)), S32) + a; + c = RotateLeft((c + H(d, a, b) + X[15] + unchecked((int) 0x1fa27cf8)), S33) + d; + b = RotateLeft((b + H(c, d, a) + X[ 2] + unchecked((int) 0xc4ac5665)), S34) + c; + + // + // Round 4 - K cycle, 16 times. + // + a = RotateLeft((a + K(b, c, d) + X[ 0] + unchecked((int) 0xf4292244)), S41) + b; + d = RotateLeft((d + K(a, b, c) + X[ 7] + unchecked((int) 0x432aff97)), S42) + a; + c = RotateLeft((c + K(d, a, b) + X[14] + unchecked((int) 0xab9423a7)), S43) + d; + b = RotateLeft((b + K(c, d, a) + X[ 5] + unchecked((int) 0xfc93a039)), S44) + c; + a = RotateLeft((a + K(b, c, d) + X[12] + unchecked((int) 0x655b59c3)), S41) + b; + d = RotateLeft((d + K(a, b, c) + X[ 3] + unchecked((int) 0x8f0ccc92)), S42) + a; + c = RotateLeft((c + K(d, a, b) + X[10] + unchecked((int) 0xffeff47d)), S43) + d; + b = RotateLeft((b + K(c, d, a) + X[ 1] + unchecked((int) 0x85845dd1)), S44) + c; + a = RotateLeft((a + K(b, c, d) + X[ 8] + unchecked((int) 0x6fa87e4f)), S41) + b; + d = RotateLeft((d + K(a, b, c) + X[15] + unchecked((int) 0xfe2ce6e0)), S42) + a; + c = RotateLeft((c + K(d, a, b) + X[ 6] + unchecked((int) 0xa3014314)), S43) + d; + b = RotateLeft((b + K(c, d, a) + X[13] + unchecked((int) 0x4e0811a1)), S44) + c; + a = RotateLeft((a + K(b, c, d) + X[ 4] + unchecked((int) 0xf7537e82)), S41) + b; + d = RotateLeft((d + K(a, b, c) + X[11] + unchecked((int) 0xbd3af235)), S42) + a; + c = RotateLeft((c + K(d, a, b) + X[ 2] + unchecked((int) 0x2ad7d2bb)), S43) + d; + b = RotateLeft((b + K(c, d, a) + X[ 9] + unchecked((int) 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; + } + } + } + +} diff --git a/Crypto/src/crypto/digests/NullDigest.cs b/Crypto/src/crypto/digests/NullDigest.cs new file mode 100644
index 000000000..e598cb145 --- /dev/null +++ b/Crypto/src/crypto/digests/NullDigest.cs
@@ -0,0 +1,49 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Digests +{ + public class NullDigest : IDigest + { + private readonly MemoryStream bOut = new MemoryStream(); + + public string AlgorithmName + { + get { return "NULL"; } + } + + public int GetByteLength() + { + // TODO Is this okay? + return 0; + } + + public int GetDigestSize() + { + return (int) bOut.Length; + } + + public void Update(byte b) + { + bOut.WriteByte(b); + } + + public void BlockUpdate(byte[] inBytes, int inOff, int len) + { + bOut.Write(inBytes, inOff, len); + } + + public int DoFinal(byte[] outBytes, int outOff) + { + byte[] res = bOut.ToArray(); + res.CopyTo(outBytes, outOff); + Reset(); + return res.Length; + } + + public void Reset() + { + bOut.SetLength(0); + } + } +} diff --git a/Crypto/src/crypto/digests/RipeMD128Digest.cs b/Crypto/src/crypto/digests/RipeMD128Digest.cs new file mode 100644
index 000000000..8977583a4 --- /dev/null +++ b/Crypto/src/crypto/digests/RipeMD128Digest.cs
@@ -0,0 +1,462 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * implementation of RipeMD128 + */ + public class RipeMD128Digest + : GeneralDigest + { + private const int DigestLength = 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) : base(t) + { + H0 = t.H0; + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + + Array.Copy(t.X, 0, X, 0, t.X.Length); + xOff = t.xOff; + } + + public override string AlgorithmName + { + get { return "RIPEMD128"; } + } + + public override int GetDigestSize() + { + return DigestLength; + } + + internal override void ProcessWord( + byte[] input, + int inOff) + { + X[xOff++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8) + | ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24); + + if (xOff == 16) + { + ProcessBlock(); + } + } + + internal override void ProcessLength( + long bitLength) + { + if (xOff > 14) + { + ProcessBlock(); + } + + X[14] = (int)(bitLength & 0xffffffff); + X[15] = (int)((ulong) bitLength >> 32); + } + + private void UnpackWord( + int word, + byte[] outBytes, + int outOff) + { + outBytes[outOff] = (byte)word; + outBytes[outOff + 1] = (byte)((uint) word >> 8); + outBytes[outOff + 2] = (byte)((uint) word >> 16); + outBytes[outOff + 3] = (byte)((uint) word >> 24); + } + + public override int DoFinal( + byte[] output, + int outOff) + { + Finish(); + + UnpackWord(H0, output, outOff); + UnpackWord(H1, output, outOff + 4); + UnpackWord(H2, output, outOff + 8); + UnpackWord(H3, output, outOff + 12); + + Reset(); + + return DigestLength; + } + + /** + * reset the chaining variables to the IV values. + */ + public override void Reset() + { + base.Reset(); + + H0 = unchecked((int) 0x67452301); + H1 = unchecked((int) 0xefcdab89); + H2 = unchecked((int) 0x98badcfe); + H3 = unchecked((int) 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) | (int) ((uint) 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 + unchecked((int) 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 + unchecked((int) 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 + unchecked((int) 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 + unchecked((int) 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 + unchecked((int) 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 + unchecked((int) 0x50a28be6), s); + } + + internal override 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; + } + } + } + +} diff --git a/Crypto/src/crypto/digests/RipeMD160Digest.cs b/Crypto/src/crypto/digests/RipeMD160Digest.cs new file mode 100644
index 000000000..8ce52ae58 --- /dev/null +++ b/Crypto/src/crypto/digests/RipeMD160Digest.cs
@@ -0,0 +1,423 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * implementation of RipeMD see, + * http://www.esat.kuleuven.ac.be/~bosselae/ripemd160.html + */ + public class RipeMD160Digest + : GeneralDigest + { + private const int DigestLength = 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) : base(t) + { + H0 = t.H0; + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + + Array.Copy(t.X, 0, X, 0, t.X.Length); + xOff = t.xOff; + } + + public override string AlgorithmName + { + get { return "RIPEMD160"; } + } + + public override int GetDigestSize() + { + return DigestLength; + } + + internal override void ProcessWord( + byte[] input, + int inOff) + { + X[xOff++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8) + | ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24); + + if (xOff == 16) + { + ProcessBlock(); + } + } + + internal override void ProcessLength( + long bitLength) + { + if (xOff > 14) + { + ProcessBlock(); + } + + X[14] = (int)(bitLength & 0xffffffff); + X[15] = (int)((ulong) bitLength >> 32); + } + + private void UnpackWord( + int word, + byte[] outBytes, + int outOff) + { + outBytes[outOff] = (byte)word; + outBytes[outOff + 1] = (byte)((uint) word >> 8); + outBytes[outOff + 2] = (byte)((uint) word >> 16); + outBytes[outOff + 3] = (byte)((uint) word >> 24); + } + + public override int DoFinal( + byte[] output, + int outOff) + { + Finish(); + + UnpackWord(H0, output, outOff); + UnpackWord(H1, output, outOff + 4); + UnpackWord(H2, output, outOff + 8); + UnpackWord(H3, output, outOff + 12); + UnpackWord(H4, output, outOff + 16); + + Reset(); + + return DigestLength; + } + + /** + * reset the chaining variables to the IV values. + */ + public override void Reset() + { + base.Reset(); + + H0 = unchecked((int) 0x67452301); + H1 = unchecked((int) 0xefcdab89); + H2 = unchecked((int) 0x98badcfe); + H3 = unchecked((int) 0x10325476); + H4 = unchecked((int) 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) | (int) ((uint) 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); + } + + internal override 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] + unchecked((int) 0x50a28be6), 8) + ee; cc = RL(cc, 10); + ee = RL(ee + F5(aa,bb,cc) + X[14] + unchecked((int) 0x50a28be6), 9) + dd; bb = RL(bb, 10); + dd = RL(dd + F5(ee,aa,bb) + X[ 7] + unchecked((int) 0x50a28be6), 9) + cc; aa = RL(aa, 10); + cc = RL(cc + F5(dd,ee,aa) + X[ 0] + unchecked((int) 0x50a28be6), 11) + bb; ee = RL(ee, 10); + bb = RL(bb + F5(cc,dd,ee) + X[ 9] + unchecked((int) 0x50a28be6), 13) + aa; dd = RL(dd, 10); + aa = RL(aa + F5(bb,cc,dd) + X[ 2] + unchecked((int) 0x50a28be6), 15) + ee; cc = RL(cc, 10); + ee = RL(ee + F5(aa,bb,cc) + X[11] + unchecked((int) 0x50a28be6), 15) + dd; bb = RL(bb, 10); + dd = RL(dd + F5(ee,aa,bb) + X[ 4] + unchecked((int) 0x50a28be6), 5) + cc; aa = RL(aa, 10); + cc = RL(cc + F5(dd,ee,aa) + X[13] + unchecked((int) 0x50a28be6), 7) + bb; ee = RL(ee, 10); + bb = RL(bb + F5(cc,dd,ee) + X[ 6] + unchecked((int) 0x50a28be6), 7) + aa; dd = RL(dd, 10); + aa = RL(aa + F5(bb,cc,dd) + X[15] + unchecked((int) 0x50a28be6), 8) + ee; cc = RL(cc, 10); + ee = RL(ee + F5(aa,bb,cc) + X[ 8] + unchecked((int) 0x50a28be6), 11) + dd; bb = RL(bb, 10); + dd = RL(dd + F5(ee,aa,bb) + X[ 1] + unchecked((int) 0x50a28be6), 14) + cc; aa = RL(aa, 10); + cc = RL(cc + F5(dd,ee,aa) + X[10] + unchecked((int) 0x50a28be6), 14) + bb; ee = RL(ee, 10); + bb = RL(bb + F5(cc,dd,ee) + X[ 3] + unchecked((int) 0x50a28be6), 12) + aa; dd = RL(dd, 10); + aa = RL(aa + F5(bb,cc,dd) + X[12] + unchecked((int) 0x50a28be6), 6) + ee; cc = RL(cc, 10); + + // + // Rounds 16-31 + // + // left + e = RL(e + F2(a,b,c) + X[ 7] + unchecked((int) 0x5a827999), 7) + d; b = RL(b, 10); + d = RL(d + F2(e,a,b) + X[ 4] + unchecked((int) 0x5a827999), 6) + c; a = RL(a, 10); + c = RL(c + F2(d,e,a) + X[13] + unchecked((int) 0x5a827999), 8) + b; e = RL(e, 10); + b = RL(b + F2(c,d,e) + X[ 1] + unchecked((int) 0x5a827999), 13) + a; d = RL(d, 10); + a = RL(a + F2(b,c,d) + X[10] + unchecked((int) 0x5a827999), 11) + e; c = RL(c, 10); + e = RL(e + F2(a,b,c) + X[ 6] + unchecked((int) 0x5a827999), 9) + d; b = RL(b, 10); + d = RL(d + F2(e,a,b) + X[15] + unchecked((int) 0x5a827999), 7) + c; a = RL(a, 10); + c = RL(c + F2(d,e,a) + X[ 3] + unchecked((int) 0x5a827999), 15) + b; e = RL(e, 10); + b = RL(b + F2(c,d,e) + X[12] + unchecked((int) 0x5a827999), 7) + a; d = RL(d, 10); + a = RL(a + F2(b,c,d) + X[ 0] + unchecked((int) 0x5a827999), 12) + e; c = RL(c, 10); + e = RL(e + F2(a,b,c) + X[ 9] + unchecked((int) 0x5a827999), 15) + d; b = RL(b, 10); + d = RL(d + F2(e,a,b) + X[ 5] + unchecked((int) 0x5a827999), 9) + c; a = RL(a, 10); + c = RL(c + F2(d,e,a) + X[ 2] + unchecked((int) 0x5a827999), 11) + b; e = RL(e, 10); + b = RL(b + F2(c,d,e) + X[14] + unchecked((int) 0x5a827999), 7) + a; d = RL(d, 10); + a = RL(a + F2(b,c,d) + X[11] + unchecked((int) 0x5a827999), 13) + e; c = RL(c, 10); + e = RL(e + F2(a,b,c) + X[ 8] + unchecked((int) 0x5a827999), 12) + d; b = RL(b, 10); + + // right + ee = RL(ee + F4(aa,bb,cc) + X[ 6] + unchecked((int) 0x5c4dd124), 9) + dd; bb = RL(bb, 10); + dd = RL(dd + F4(ee,aa,bb) + X[11] + unchecked((int) 0x5c4dd124), 13) + cc; aa = RL(aa, 10); + cc = RL(cc + F4(dd,ee,aa) + X[ 3] + unchecked((int) 0x5c4dd124), 15) + bb; ee = RL(ee, 10); + bb = RL(bb + F4(cc,dd,ee) + X[ 7] + unchecked((int) 0x5c4dd124), 7) + aa; dd = RL(dd, 10); + aa = RL(aa + F4(bb,cc,dd) + X[ 0] + unchecked((int) 0x5c4dd124), 12) + ee; cc = RL(cc, 10); + ee = RL(ee + F4(aa,bb,cc) + X[13] + unchecked((int) 0x5c4dd124), 8) + dd; bb = RL(bb, 10); + dd = RL(dd + F4(ee,aa,bb) + X[ 5] + unchecked((int) 0x5c4dd124), 9) + cc; aa = RL(aa, 10); + cc = RL(cc + F4(dd,ee,aa) + X[10] + unchecked((int) 0x5c4dd124), 11) + bb; ee = RL(ee, 10); + bb = RL(bb + F4(cc,dd,ee) + X[14] + unchecked((int) 0x5c4dd124), 7) + aa; dd = RL(dd, 10); + aa = RL(aa + F4(bb,cc,dd) + X[15] + unchecked((int) 0x5c4dd124), 7) + ee; cc = RL(cc, 10); + ee = RL(ee + F4(aa,bb,cc) + X[ 8] + unchecked((int) 0x5c4dd124), 12) + dd; bb = RL(bb, 10); + dd = RL(dd + F4(ee,aa,bb) + X[12] + unchecked((int) 0x5c4dd124), 7) + cc; aa = RL(aa, 10); + cc = RL(cc + F4(dd,ee,aa) + X[ 4] + unchecked((int) 0x5c4dd124), 6) + bb; ee = RL(ee, 10); + bb = RL(bb + F4(cc,dd,ee) + X[ 9] + unchecked((int) 0x5c4dd124), 15) + aa; dd = RL(dd, 10); + aa = RL(aa + F4(bb,cc,dd) + X[ 1] + unchecked((int) 0x5c4dd124), 13) + ee; cc = RL(cc, 10); + ee = RL(ee + F4(aa,bb,cc) + X[ 2] + unchecked((int) 0x5c4dd124), 11) + dd; bb = RL(bb, 10); + + // + // Rounds 32-47 + // + // left + d = RL(d + F3(e,a,b) + X[ 3] + unchecked((int) 0x6ed9eba1), 11) + c; a = RL(a, 10); + c = RL(c + F3(d,e,a) + X[10] + unchecked((int) 0x6ed9eba1), 13) + b; e = RL(e, 10); + b = RL(b + F3(c,d,e) + X[14] + unchecked((int) 0x6ed9eba1), 6) + a; d = RL(d, 10); + a = RL(a + F3(b,c,d) + X[ 4] + unchecked((int) 0x6ed9eba1), 7) + e; c = RL(c, 10); + e = RL(e + F3(a,b,c) + X[ 9] + unchecked((int) 0x6ed9eba1), 14) + d; b = RL(b, 10); + d = RL(d + F3(e,a,b) + X[15] + unchecked((int) 0x6ed9eba1), 9) + c; a = RL(a, 10); + c = RL(c + F3(d,e,a) + X[ 8] + unchecked((int) 0x6ed9eba1), 13) + b; e = RL(e, 10); + b = RL(b + F3(c,d,e) + X[ 1] + unchecked((int) 0x6ed9eba1), 15) + a; d = RL(d, 10); + a = RL(a + F3(b,c,d) + X[ 2] + unchecked((int) 0x6ed9eba1), 14) + e; c = RL(c, 10); + e = RL(e + F3(a,b,c) + X[ 7] + unchecked((int) 0x6ed9eba1), 8) + d; b = RL(b, 10); + d = RL(d + F3(e,a,b) + X[ 0] + unchecked((int) 0x6ed9eba1), 13) + c; a = RL(a, 10); + c = RL(c + F3(d,e,a) + X[ 6] + unchecked((int) 0x6ed9eba1), 6) + b; e = RL(e, 10); + b = RL(b + F3(c,d,e) + X[13] + unchecked((int) 0x6ed9eba1), 5) + a; d = RL(d, 10); + a = RL(a + F3(b,c,d) + X[11] + unchecked((int) 0x6ed9eba1), 12) + e; c = RL(c, 10); + e = RL(e + F3(a,b,c) + X[ 5] + unchecked((int) 0x6ed9eba1), 7) + d; b = RL(b, 10); + d = RL(d + F3(e,a,b) + X[12] + unchecked((int) 0x6ed9eba1), 5) + c; a = RL(a, 10); + + // right + dd = RL(dd + F3(ee,aa,bb) + X[15] + unchecked((int) 0x6d703ef3), 9) + cc; aa = RL(aa, 10); + cc = RL(cc + F3(dd,ee,aa) + X[ 5] + unchecked((int) 0x6d703ef3), 7) + bb; ee = RL(ee, 10); + bb = RL(bb + F3(cc,dd,ee) + X[ 1] + unchecked((int) 0x6d703ef3), 15) + aa; dd = RL(dd, 10); + aa = RL(aa + F3(bb,cc,dd) + X[ 3] + unchecked((int) 0x6d703ef3), 11) + ee; cc = RL(cc, 10); + ee = RL(ee + F3(aa,bb,cc) + X[ 7] + unchecked((int) 0x6d703ef3), 8) + dd; bb = RL(bb, 10); + dd = RL(dd + F3(ee,aa,bb) + X[14] + unchecked((int) 0x6d703ef3), 6) + cc; aa = RL(aa, 10); + cc = RL(cc + F3(dd,ee,aa) + X[ 6] + unchecked((int) 0x6d703ef3), 6) + bb; ee = RL(ee, 10); + bb = RL(bb + F3(cc,dd,ee) + X[ 9] + unchecked((int) 0x6d703ef3), 14) + aa; dd = RL(dd, 10); + aa = RL(aa + F3(bb,cc,dd) + X[11] + unchecked((int) 0x6d703ef3), 12) + ee; cc = RL(cc, 10); + ee = RL(ee + F3(aa,bb,cc) + X[ 8] + unchecked((int) 0x6d703ef3), 13) + dd; bb = RL(bb, 10); + dd = RL(dd + F3(ee,aa,bb) + X[12] + unchecked((int) 0x6d703ef3), 5) + cc; aa = RL(aa, 10); + cc = RL(cc + F3(dd,ee,aa) + X[ 2] + unchecked((int) 0x6d703ef3), 14) + bb; ee = RL(ee, 10); + bb = RL(bb + F3(cc,dd,ee) + X[10] + unchecked((int) 0x6d703ef3), 13) + aa; dd = RL(dd, 10); + aa = RL(aa + F3(bb,cc,dd) + X[ 0] + unchecked((int) 0x6d703ef3), 13) + ee; cc = RL(cc, 10); + ee = RL(ee + F3(aa,bb,cc) + X[ 4] + unchecked((int) 0x6d703ef3), 7) + dd; bb = RL(bb, 10); + dd = RL(dd + F3(ee,aa,bb) + X[13] + unchecked((int) 0x6d703ef3), 5) + cc; aa = RL(aa, 10); + + // + // Rounds 48-63 + // + // left + c = RL(c + F4(d,e,a) + X[ 1] + unchecked((int) 0x8f1bbcdc), 11) + b; e = RL(e, 10); + b = RL(b + F4(c,d,e) + X[ 9] + unchecked((int) 0x8f1bbcdc), 12) + a; d = RL(d, 10); + a = RL(a + F4(b,c,d) + X[11] + unchecked((int) 0x8f1bbcdc), 14) + e; c = RL(c, 10); + e = RL(e + F4(a,b,c) + X[10] + unchecked((int) 0x8f1bbcdc), 15) + d; b = RL(b, 10); + d = RL(d + F4(e,a,b) + X[ 0] + unchecked((int) 0x8f1bbcdc), 14) + c; a = RL(a, 10); + c = RL(c + F4(d,e,a) + X[ 8] + unchecked((int) 0x8f1bbcdc), 15) + b; e = RL(e, 10); + b = RL(b + F4(c,d,e) + X[12] + unchecked((int) 0x8f1bbcdc), 9) + a; d = RL(d, 10); + a = RL(a + F4(b,c,d) + X[ 4] + unchecked((int) 0x8f1bbcdc), 8) + e; c = RL(c, 10); + e = RL(e + F4(a,b,c) + X[13] + unchecked((int) 0x8f1bbcdc), 9) + d; b = RL(b, 10); + d = RL(d + F4(e,a,b) + X[ 3] + unchecked((int) 0x8f1bbcdc), 14) + c; a = RL(a, 10); + c = RL(c + F4(d,e,a) + X[ 7] + unchecked((int) 0x8f1bbcdc), 5) + b; e = RL(e, 10); + b = RL(b + F4(c,d,e) + X[15] + unchecked((int) 0x8f1bbcdc), 6) + a; d = RL(d, 10); + a = RL(a + F4(b,c,d) + X[14] + unchecked((int) 0x8f1bbcdc), 8) + e; c = RL(c, 10); + e = RL(e + F4(a,b,c) + X[ 5] + unchecked((int) 0x8f1bbcdc), 6) + d; b = RL(b, 10); + d = RL(d + F4(e,a,b) + X[ 6] + unchecked((int) 0x8f1bbcdc), 5) + c; a = RL(a, 10); + c = RL(c + F4(d,e,a) + X[ 2] + unchecked((int) 0x8f1bbcdc), 12) + b; e = RL(e, 10); + + // right + cc = RL(cc + F2(dd,ee,aa) + X[ 8] + unchecked((int) 0x7a6d76e9), 15) + bb; ee = RL(ee, 10); + bb = RL(bb + F2(cc,dd,ee) + X[ 6] + unchecked((int) 0x7a6d76e9), 5) + aa; dd = RL(dd, 10); + aa = RL(aa + F2(bb,cc,dd) + X[ 4] + unchecked((int) 0x7a6d76e9), 8) + ee; cc = RL(cc, 10); + ee = RL(ee + F2(aa,bb,cc) + X[ 1] + unchecked((int) 0x7a6d76e9), 11) + dd; bb = RL(bb, 10); + dd = RL(dd + F2(ee,aa,bb) + X[ 3] + unchecked((int) 0x7a6d76e9), 14) + cc; aa = RL(aa, 10); + cc = RL(cc + F2(dd,ee,aa) + X[11] + unchecked((int) 0x7a6d76e9), 14) + bb; ee = RL(ee, 10); + bb = RL(bb + F2(cc,dd,ee) + X[15] + unchecked((int) 0x7a6d76e9), 6) + aa; dd = RL(dd, 10); + aa = RL(aa + F2(bb,cc,dd) + X[ 0] + unchecked((int) 0x7a6d76e9), 14) + ee; cc = RL(cc, 10); + ee = RL(ee + F2(aa,bb,cc) + X[ 5] + unchecked((int) 0x7a6d76e9), 6) + dd; bb = RL(bb, 10); + dd = RL(dd + F2(ee,aa,bb) + X[12] + unchecked((int) 0x7a6d76e9), 9) + cc; aa = RL(aa, 10); + cc = RL(cc + F2(dd,ee,aa) + X[ 2] + unchecked((int) 0x7a6d76e9), 12) + bb; ee = RL(ee, 10); + bb = RL(bb + F2(cc,dd,ee) + X[13] + unchecked((int) 0x7a6d76e9), 9) + aa; dd = RL(dd, 10); + aa = RL(aa + F2(bb,cc,dd) + X[ 9] + unchecked((int) 0x7a6d76e9), 12) + ee; cc = RL(cc, 10); + ee = RL(ee + F2(aa,bb,cc) + X[ 7] + unchecked((int) 0x7a6d76e9), 5) + dd; bb = RL(bb, 10); + dd = RL(dd + F2(ee,aa,bb) + X[10] + unchecked((int) 0x7a6d76e9), 15) + cc; aa = RL(aa, 10); + cc = RL(cc + F2(dd,ee,aa) + X[14] + unchecked((int) 0x7a6d76e9), 8) + bb; ee = RL(ee, 10); + + // + // Rounds 64-79 + // + // left + b = RL(b + F5(c,d,e) + X[ 4] + unchecked((int) 0xa953fd4e), 9) + a; d = RL(d, 10); + a = RL(a + F5(b,c,d) + X[ 0] + unchecked((int) 0xa953fd4e), 15) + e; c = RL(c, 10); + e = RL(e + F5(a,b,c) + X[ 5] + unchecked((int) 0xa953fd4e), 5) + d; b = RL(b, 10); + d = RL(d + F5(e,a,b) + X[ 9] + unchecked((int) 0xa953fd4e), 11) + c; a = RL(a, 10); + c = RL(c + F5(d,e,a) + X[ 7] + unchecked((int) 0xa953fd4e), 6) + b; e = RL(e, 10); + b = RL(b + F5(c,d,e) + X[12] + unchecked((int) 0xa953fd4e), 8) + a; d = RL(d, 10); + a = RL(a + F5(b,c,d) + X[ 2] + unchecked((int) 0xa953fd4e), 13) + e; c = RL(c, 10); + e = RL(e + F5(a,b,c) + X[10] + unchecked((int) 0xa953fd4e), 12) + d; b = RL(b, 10); + d = RL(d + F5(e,a,b) + X[14] + unchecked((int) 0xa953fd4e), 5) + c; a = RL(a, 10); + c = RL(c + F5(d,e,a) + X[ 1] + unchecked((int) 0xa953fd4e), 12) + b; e = RL(e, 10); + b = RL(b + F5(c,d,e) + X[ 3] + unchecked((int) 0xa953fd4e), 13) + a; d = RL(d, 10); + a = RL(a + F5(b,c,d) + X[ 8] + unchecked((int) 0xa953fd4e), 14) + e; c = RL(c, 10); + e = RL(e + F5(a,b,c) + X[11] + unchecked((int) 0xa953fd4e), 11) + d; b = RL(b, 10); + d = RL(d + F5(e,a,b) + X[ 6] + unchecked((int) 0xa953fd4e), 8) + c; a = RL(a, 10); + c = RL(c + F5(d,e,a) + X[15] + unchecked((int) 0xa953fd4e), 5) + b; e = RL(e, 10); + b = RL(b + F5(c,d,e) + X[13] + unchecked((int) 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; + } + } + } + +} diff --git a/Crypto/src/crypto/digests/RipeMD256Digest.cs b/Crypto/src/crypto/digests/RipeMD256Digest.cs new file mode 100644
index 000000000..950e94f80 --- /dev/null +++ b/Crypto/src/crypto/digests/RipeMD256Digest.cs
@@ -0,0 +1,409 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /// <remarks> + /// <p>Implementation of RipeMD256.</p> + /// <p><b>Note:</b> this algorithm offers the same level of security as RipeMD128.</p> + /// </remarks> + public class RipeMD256Digest + : GeneralDigest + { + public override string AlgorithmName + { + get { return "RIPEMD256"; } + } + + public override int GetDigestSize() + { + return DigestLength; + } + + private const int DigestLength = 32; + + private int H0, H1, H2, H3, H4, H5, H6, H7; // IV's + + private int[] X = new int[16]; + private int xOff; + + /// <summary> Standard constructor</summary> + public RipeMD256Digest() + { + Reset(); + } + + /// <summary> Copy constructor. This will copy the state of the provided + /// message digest. + /// </summary> + public RipeMD256Digest(RipeMD256Digest t):base(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; + + Array.Copy(t.X, 0, X, 0, t.X.Length); + xOff = t.xOff; + } + + internal override void ProcessWord( + byte[] input, + int inOff) + { + X[xOff++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8) + | ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24); + + if (xOff == 16) + { + ProcessBlock(); + } + } + + internal override void ProcessLength( + long bitLength) + { + if (xOff > 14) + { + ProcessBlock(); + } + + X[14] = (int)(bitLength & 0xffffffff); + X[15] = (int)((ulong)bitLength >> 32); + } + + private void UnpackWord( + int word, + byte[] outBytes, + int outOff) + { + outBytes[outOff] = (byte)(uint)word; + outBytes[outOff + 1] = (byte)((uint)word >> 8); + outBytes[outOff + 2] = (byte)((uint)word >> 16); + outBytes[outOff + 3] = (byte)((uint)word >> 24); + } + + public override int DoFinal(byte[] output, int outOff) + { + Finish(); + + UnpackWord(H0, output, outOff); + UnpackWord(H1, output, outOff + 4); + UnpackWord(H2, output, outOff + 8); + UnpackWord(H3, output, outOff + 12); + UnpackWord(H4, output, outOff + 16); + UnpackWord(H5, output, outOff + 20); + UnpackWord(H6, output, outOff + 24); + UnpackWord(H7, output, outOff + 28); + + Reset(); + + return DigestLength; + } + + /// <summary> reset the chaining variables to the IV values.</summary> + public override void Reset() + { + base.Reset(); + + H0 = unchecked((int)0x67452301); + H1 = unchecked((int)0xefcdab89); + H2 = unchecked((int)0x98badcfe); + H3 = unchecked((int)0x10325476); + H4 = unchecked((int)0x76543210); + H5 = unchecked((int)0xFEDCBA98); + H6 = unchecked((int)0x89ABCDEF); + H7 = unchecked((int)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) | (int)((uint)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 + unchecked((int)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 + unchecked((int)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 + unchecked((int)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 + unchecked((int)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 + unchecked((int)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 + unchecked((int)0x50a28be6), s); + } + + internal override 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; + } + } + } +} diff --git a/Crypto/src/crypto/digests/RipeMD320Digest.cs b/Crypto/src/crypto/digests/RipeMD320Digest.cs new file mode 100644
index 000000000..25c74baef --- /dev/null +++ b/Crypto/src/crypto/digests/RipeMD320Digest.cs
@@ -0,0 +1,438 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /// <remarks> + /// <p>Implementation of RipeMD 320.</p> + /// <p><b>Note:</b> this algorithm offers the same level of security as RipeMD160.</p> + /// </remarks> + public class RipeMD320Digest + : GeneralDigest + { + public override string AlgorithmName + { + get { return "RIPEMD320"; } + } + + public override int GetDigestSize() + { + return DigestLength; + } + + private const int DigestLength = 40; + + private int H0, H1, H2, H3, H4, H5, H6, H7, H8, H9; // IV's + + private int[] X = new int[16]; + private int xOff; + + /// <summary> Standard constructor</summary> + public RipeMD320Digest() + { + Reset(); + } + + /// <summary> Copy constructor. This will copy the state of the provided + /// message digest. + /// </summary> + public RipeMD320Digest(RipeMD320Digest t) + : base(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; + + Array.Copy(t.X, 0, X, 0, t.X.Length); + xOff = t.xOff; + } + + internal override void ProcessWord( + byte[] input, + int inOff) + { + X[xOff++] = (input[inOff] & 0xff) | ((input[inOff + 1] & 0xff) << 8) + | ((input[inOff + 2] & 0xff) << 16) | ((input[inOff + 3] & 0xff) << 24); + + if (xOff == 16) + { + ProcessBlock(); + } + } + + internal override void ProcessLength( + long bitLength) + { + if (xOff > 14) + { + ProcessBlock(); + } + + X[14] = (int)(bitLength & 0xffffffff); + X[15] = (int)((ulong)bitLength >> 32); + } + + private void UnpackWord( + int word, + byte[] outBytes, + int outOff) + { + outBytes[outOff] = (byte)word; + outBytes[outOff + 1] = (byte)((uint)word >> 8); + outBytes[outOff + 2] = (byte)((uint)word >> 16); + outBytes[outOff + 3] = (byte)((uint)word >> 24); + } + + public override int DoFinal(byte[] output, int outOff) + { + Finish(); + + UnpackWord(H0, output, outOff); + UnpackWord(H1, output, outOff + 4); + UnpackWord(H2, output, outOff + 8); + UnpackWord(H3, output, outOff + 12); + UnpackWord(H4, output, outOff + 16); + UnpackWord(H5, output, outOff + 20); + UnpackWord(H6, output, outOff + 24); + UnpackWord(H7, output, outOff + 28); + UnpackWord(H8, output, outOff + 32); + UnpackWord(H9, output, outOff + 36); + + Reset(); + + return DigestLength; + } + + /// <summary> reset the chaining variables to the IV values.</summary> + public override void Reset() + { + base.Reset(); + + H0 = unchecked((int) 0x67452301); + H1 = unchecked((int) 0xefcdab89); + H2 = unchecked((int) 0x98badcfe); + H3 = unchecked((int) 0x10325476); + H4 = unchecked((int) 0xc3d2e1f0); + H5 = unchecked((int) 0x76543210); + H6 = unchecked((int) 0xFEDCBA98); + H7 = unchecked((int) 0x89ABCDEF); + H8 = unchecked((int) 0x01234567); + H9 = unchecked((int) 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) | (int)(((uint)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); + } + + internal override 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] + unchecked((int)0x50a28be6), 8) + ee; cc = RL(cc, 10); + ee = RL(ee + F5(aa, bb, cc) + X[14] + unchecked((int)0x50a28be6), 9) + dd; bb = RL(bb, 10); + dd = RL(dd + F5(ee, aa, bb) + X[7] + unchecked((int)0x50a28be6), 9) + cc; aa = RL(aa, 10); + cc = RL(cc + F5(dd, ee, aa) + X[0] + unchecked((int)0x50a28be6), 11) + bb; ee = RL(ee, 10); + bb = RL(bb + F5(cc, dd, ee) + X[9] + unchecked((int)0x50a28be6), 13) + aa; dd = RL(dd, 10); + aa = RL(aa + F5(bb, cc, dd) + X[2] + unchecked((int)0x50a28be6), 15) + ee; cc = RL(cc, 10); + ee = RL(ee + F5(aa, bb, cc) + X[11] + unchecked((int)0x50a28be6), 15) + dd; bb = RL(bb, 10); + dd = RL(dd + F5(ee, aa, bb) + X[4] + unchecked((int)0x50a28be6), 5) + cc; aa = RL(aa, 10); + cc = RL(cc + F5(dd, ee, aa) + X[13] + unchecked((int)0x50a28be6), 7) + bb; ee = RL(ee, 10); + bb = RL(bb + F5(cc, dd, ee) + X[6] + unchecked((int)0x50a28be6), 7) + aa; dd = RL(dd, 10); + aa = RL(aa + F5(bb, cc, dd) + X[15] + unchecked((int)0x50a28be6), 8) + ee; cc = RL(cc, 10); + ee = RL(ee + F5(aa, bb, cc) + X[8] + unchecked((int)0x50a28be6), 11) + dd; bb = RL(bb, 10); + dd = RL(dd + F5(ee, aa, bb) + X[1] + unchecked((int)0x50a28be6), 14) + cc; aa = RL(aa, 10); + cc = RL(cc + F5(dd, ee, aa) + X[10] + unchecked((int)0x50a28be6), 14) + bb; ee = RL(ee, 10); + bb = RL(bb + F5(cc, dd, ee) + X[3] + unchecked((int)0x50a28be6), 12) + aa; dd = RL(dd, 10); + aa = RL(aa + F5(bb, cc, dd) + X[12] + unchecked((int)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] + unchecked((int)0x5a827999), 7) + d; b = RL(b, 10); + d = RL(d + F2(e, a, b) + X[4] + unchecked((int)0x5a827999), 6) + c; a = RL(a, 10); + c = RL(c + F2(d, e, a) + X[13] + unchecked((int)0x5a827999), 8) + b; e = RL(e, 10); + b = RL(b + F2(c, d, e) + X[1] + unchecked((int)0x5a827999), 13) + a; d = RL(d, 10); + a = RL(a + F2(b, c, d) + X[10] + unchecked((int)0x5a827999), 11) + e; c = RL(c, 10); + e = RL(e + F2(a, b, c) + X[6] + unchecked((int)0x5a827999), 9) + d; b = RL(b, 10); + d = RL(d + F2(e, a, b) + X[15] + unchecked((int)0x5a827999), 7) + c; a = RL(a, 10); + c = RL(c + F2(d, e, a) + X[3] + unchecked((int)0x5a827999), 15) + b; e = RL(e, 10); + b = RL(b + F2(c, d, e) + X[12] + unchecked((int)0x5a827999), 7) + a; d = RL(d, 10); + a = RL(a + F2(b, c, d) + X[0] + unchecked((int)0x5a827999), 12) + e; c = RL(c, 10); + e = RL(e + F2(a, b, c) + X[9] + unchecked((int)0x5a827999), 15) + d; b = RL(b, 10); + d = RL(d + F2(e, a, b) + X[5] + unchecked((int)0x5a827999), 9) + c; a = RL(a, 10); + c = RL(c + F2(d, e, a) + X[2] + unchecked((int)0x5a827999), 11) + b; e = RL(e, 10); + b = RL(b + F2(c, d, e) + X[14] + unchecked((int)0x5a827999), 7) + a; d = RL(d, 10); + a = RL(a + F2(b, c, d) + X[11] + unchecked((int)0x5a827999), 13) + e; c = RL(c, 10); + e = RL(e + F2(a, b, c) + X[8] + unchecked((int)0x5a827999), 12) + d; b = RL(b, 10); + + // right + ee = RL(ee + F4(aa, bb, cc) + X[6] + unchecked((int)0x5c4dd124), 9) + dd; bb = RL(bb, 10); + dd = RL(dd + F4(ee, aa, bb) + X[11] + unchecked((int)0x5c4dd124), 13) + cc; aa = RL(aa, 10); + cc = RL(cc + F4(dd, ee, aa) + X[3] + unchecked((int)0x5c4dd124), 15) + bb; ee = RL(ee, 10); + bb = RL(bb + F4(cc, dd, ee) + X[7] + unchecked((int)0x5c4dd124), 7) + aa; dd = RL(dd, 10); + aa = RL(aa + F4(bb, cc, dd) + X[0] + unchecked((int)0x5c4dd124), 12) + ee; cc = RL(cc, 10); + ee = RL(ee + F4(aa, bb, cc) + X[13] + unchecked((int)0x5c4dd124), 8) + dd; bb = RL(bb, 10); + dd = RL(dd + F4(ee, aa, bb) + X[5] + unchecked((int)0x5c4dd124), 9) + cc; aa = RL(aa, 10); + cc = RL(cc + F4(dd, ee, aa) + X[10] + unchecked((int)0x5c4dd124), 11) + bb; ee = RL(ee, 10); + bb = RL(bb + F4(cc, dd, ee) + X[14] + unchecked((int)0x5c4dd124), 7) + aa; dd = RL(dd, 10); + aa = RL(aa + F4(bb, cc, dd) + X[15] + unchecked((int)0x5c4dd124), 7) + ee; cc = RL(cc, 10); + ee = RL(ee + F4(aa, bb, cc) + X[8] + unchecked((int)0x5c4dd124), 12) + dd; bb = RL(bb, 10); + dd = RL(dd + F4(ee, aa, bb) + X[12] + unchecked((int)0x5c4dd124), 7) + cc; aa = RL(aa, 10); + cc = RL(cc + F4(dd, ee, aa) + X[4] + unchecked((int)0x5c4dd124), 6) + bb; ee = RL(ee, 10); + bb = RL(bb + F4(cc, dd, ee) + X[9] + unchecked((int)0x5c4dd124), 15) + aa; dd = RL(dd, 10); + aa = RL(aa + F4(bb, cc, dd) + X[1] + unchecked((int)0x5c4dd124), 13) + ee; cc = RL(cc, 10); + ee = RL(ee + F4(aa, bb, cc) + X[2] + unchecked((int)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] + unchecked((int)0x6ed9eba1), 11) + c; a = RL(a, 10); + c = RL(c + F3(d, e, a) + X[10] + unchecked((int)0x6ed9eba1), 13) + b; e = RL(e, 10); + b = RL(b + F3(c, d, e) + X[14] + unchecked((int)0x6ed9eba1), 6) + a; d = RL(d, 10); + a = RL(a + F3(b, c, d) + X[4] + unchecked((int)0x6ed9eba1), 7) + e; c = RL(c, 10); + e = RL(e + F3(a, b, c) + X[9] + unchecked((int)0x6ed9eba1), 14) + d; b = RL(b, 10); + d = RL(d + F3(e, a, b) + X[15] + unchecked((int)0x6ed9eba1), 9) + c; a = RL(a, 10); + c = RL(c + F3(d, e, a) + X[8] + unchecked((int)0x6ed9eba1), 13) + b; e = RL(e, 10); + b = RL(b + F3(c, d, e) + X[1] + unchecked((int)0x6ed9eba1), 15) + a; d = RL(d, 10); + a = RL(a + F3(b, c, d) + X[2] + unchecked((int)0x6ed9eba1), 14) + e; c = RL(c, 10); + e = RL(e + F3(a, b, c) + X[7] + unchecked((int)0x6ed9eba1), 8) + d; b = RL(b, 10); + d = RL(d + F3(e, a, b) + X[0] + unchecked((int)0x6ed9eba1), 13) + c; a = RL(a, 10); + c = RL(c + F3(d, e, a) + X[6] + unchecked((int)0x6ed9eba1), 6) + b; e = RL(e, 10); + b = RL(b + F3(c, d, e) + X[13] + unchecked((int)0x6ed9eba1), 5) + a; d = RL(d, 10); + a = RL(a + F3(b, c, d) + X[11] + unchecked((int)0x6ed9eba1), 12) + e; c = RL(c, 10); + e = RL(e + F3(a, b, c) + X[5] + unchecked((int)0x6ed9eba1), 7) + d; b = RL(b, 10); + d = RL(d + F3(e, a, b) + X[12] + unchecked((int)0x6ed9eba1), 5) + c; a = RL(a, 10); + + // right + dd = RL(dd + F3(ee, aa, bb) + X[15] + unchecked((int)0x6d703ef3), 9) + cc; aa = RL(aa, 10); + cc = RL(cc + F3(dd, ee, aa) + X[5] + unchecked((int)0x6d703ef3), 7) + bb; ee = RL(ee, 10); + bb = RL(bb + F3(cc, dd, ee) + X[1] + unchecked((int)0x6d703ef3), 15) + aa; dd = RL(dd, 10); + aa = RL(aa + F3(bb, cc, dd) + X[3] + unchecked((int)0x6d703ef3), 11) + ee; cc = RL(cc, 10); + ee = RL(ee + F3(aa, bb, cc) + X[7] + unchecked((int)0x6d703ef3), 8) + dd; bb = RL(bb, 10); + dd = RL(dd + F3(ee, aa, bb) + X[14] + unchecked((int)0x6d703ef3), 6) + cc; aa = RL(aa, 10); + cc = RL(cc + F3(dd, ee, aa) + X[6] + unchecked((int)0x6d703ef3), 6) + bb; ee = RL(ee, 10); + bb = RL(bb + F3(cc, dd, ee) + X[9] + unchecked((int)0x6d703ef3), 14) + aa; dd = RL(dd, 10); + aa = RL(aa + F3(bb, cc, dd) + X[11] + unchecked((int)0x6d703ef3), 12) + ee; cc = RL(cc, 10); + ee = RL(ee + F3(aa, bb, cc) + X[8] + unchecked((int)0x6d703ef3), 13) + dd; bb = RL(bb, 10); + dd = RL(dd + F3(ee, aa, bb) + X[12] + unchecked((int)0x6d703ef3), 5) + cc; aa = RL(aa, 10); + cc = RL(cc + F3(dd, ee, aa) + X[2] + unchecked((int)0x6d703ef3), 14) + bb; ee = RL(ee, 10); + bb = RL(bb + F3(cc, dd, ee) + X[10] + unchecked((int)0x6d703ef3), 13) + aa; dd = RL(dd, 10); + aa = RL(aa + F3(bb, cc, dd) + X[0] + unchecked((int)0x6d703ef3), 13) + ee; cc = RL(cc, 10); + ee = RL(ee + F3(aa, bb, cc) + X[4] + unchecked((int)0x6d703ef3), 7) + dd; bb = RL(bb, 10); + dd = RL(dd + F3(ee, aa, bb) + X[13] + unchecked((int)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] + unchecked((int)0x8f1bbcdc), 11) + b; e = RL(e, 10); + b = RL(b + F4(c, d, e) + X[9] + unchecked((int)0x8f1bbcdc), 12) + a; d = RL(d, 10); + a = RL(a + F4(b, c, d) + X[11] + unchecked((int)0x8f1bbcdc), 14) + e; c = RL(c, 10); + e = RL(e + F4(a, b, c) + X[10] + unchecked((int)0x8f1bbcdc), 15) + d; b = RL(b, 10); + d = RL(d + F4(e, a, b) + X[0] + unchecked((int)0x8f1bbcdc), 14) + c; a = RL(a, 10); + c = RL(c + F4(d, e, a) + X[8] + unchecked((int)0x8f1bbcdc), 15) + b; e = RL(e, 10); + b = RL(b + F4(c, d, e) + X[12] + unchecked((int)0x8f1bbcdc), 9) + a; d = RL(d, 10); + a = RL(a + F4(b, c, d) + X[4] + unchecked((int)0x8f1bbcdc), 8) + e; c = RL(c, 10); + e = RL(e + F4(a, b, c) + X[13] + unchecked((int)0x8f1bbcdc), 9) + d; b = RL(b, 10); + d = RL(d + F4(e, a, b) + X[3] + unchecked((int)0x8f1bbcdc), 14) + c; a = RL(a, 10); + c = RL(c + F4(d, e, a) + X[7] + unchecked((int)0x8f1bbcdc), 5) + b; e = RL(e, 10); + b = RL(b + F4(c, d, e) + X[15] + unchecked((int)0x8f1bbcdc), 6) + a; d = RL(d, 10); + a = RL(a + F4(b, c, d) + X[14] + unchecked((int)0x8f1bbcdc), 8) + e; c = RL(c, 10); + e = RL(e + F4(a, b, c) + X[5] + unchecked((int)0x8f1bbcdc), 6) + d; b = RL(b, 10); + d = RL(d + F4(e, a, b) + X[6] + unchecked((int)0x8f1bbcdc), 5) + c; a = RL(a, 10); + c = RL(c + F4(d, e, a) + X[2] + unchecked((int)0x8f1bbcdc), 12) + b; e = RL(e, 10); + + // right + cc = RL(cc + F2(dd, ee, aa) + X[8] + unchecked((int)0x7a6d76e9), 15) + bb; ee = RL(ee, 10); + bb = RL(bb + F2(cc, dd, ee) + X[6] + unchecked((int)0x7a6d76e9), 5) + aa; dd = RL(dd, 10); + aa = RL(aa + F2(bb, cc, dd) + X[4] + unchecked((int)0x7a6d76e9), 8) + ee; cc = RL(cc, 10); + ee = RL(ee + F2(aa, bb, cc) + X[1] + unchecked((int)0x7a6d76e9), 11) + dd; bb = RL(bb, 10); + dd = RL(dd + F2(ee, aa, bb) + X[3] + unchecked((int)0x7a6d76e9), 14) + cc; aa = RL(aa, 10); + cc = RL(cc + F2(dd, ee, aa) + X[11] + unchecked((int)0x7a6d76e9), 14) + bb; ee = RL(ee, 10); + bb = RL(bb + F2(cc, dd, ee) + X[15] + unchecked((int)0x7a6d76e9), 6) + aa; dd = RL(dd, 10); + aa = RL(aa + F2(bb, cc, dd) + X[0] + unchecked((int)0x7a6d76e9), 14) + ee; cc = RL(cc, 10); + ee = RL(ee + F2(aa, bb, cc) + X[5] + unchecked((int)0x7a6d76e9), 6) + dd; bb = RL(bb, 10); + dd = RL(dd + F2(ee, aa, bb) + X[12] + unchecked((int)0x7a6d76e9), 9) + cc; aa = RL(aa, 10); + cc = RL(cc + F2(dd, ee, aa) + X[2] + unchecked((int)0x7a6d76e9), 12) + bb; ee = RL(ee, 10); + bb = RL(bb + F2(cc, dd, ee) + X[13] + unchecked((int)0x7a6d76e9), 9) + aa; dd = RL(dd, 10); + aa = RL(aa + F2(bb, cc, dd) + X[9] + unchecked((int)0x7a6d76e9), 12) + ee; cc = RL(cc, 10); + ee = RL(ee + F2(aa, bb, cc) + X[7] + unchecked((int)0x7a6d76e9), 5) + dd; bb = RL(bb, 10); + dd = RL(dd + F2(ee, aa, bb) + X[10] + unchecked((int)0x7a6d76e9), 15) + cc; aa = RL(aa, 10); + cc = RL(cc + F2(dd, ee, aa) + X[14] + unchecked((int)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] + unchecked((int)0xa953fd4e), 9) + a; d = RL(d, 10); + a = RL(a + F5(b, c, d) + X[0] + unchecked((int)0xa953fd4e), 15) + e; c = RL(c, 10); + e = RL(e + F5(a, b, c) + X[5] + unchecked((int)0xa953fd4e), 5) + d; b = RL(b, 10); + d = RL(d + F5(e, a, b) + X[9] + unchecked((int)0xa953fd4e), 11) + c; a = RL(a, 10); + c = RL(c + F5(d, e, a) + X[7] + unchecked((int)0xa953fd4e), 6) + b; e = RL(e, 10); + b = RL(b + F5(c, d, e) + X[12] + unchecked((int)0xa953fd4e), 8) + a; d = RL(d, 10); + a = RL(a + F5(b, c, d) + X[2] + unchecked((int)0xa953fd4e), 13) + e; c = RL(c, 10); + e = RL(e + F5(a, b, c) + X[10] + unchecked((int)0xa953fd4e), 12) + d; b = RL(b, 10); + d = RL(d + F5(e, a, b) + X[14] + unchecked((int)0xa953fd4e), 5) + c; a = RL(a, 10); + c = RL(c + F5(d, e, a) + X[1] + unchecked((int)0xa953fd4e), 12) + b; e = RL(e, 10); + b = RL(b + F5(c, d, e) + X[3] + unchecked((int)0xa953fd4e), 13) + a; d = RL(d, 10); + a = RL(a + F5(b, c, d) + X[8] + unchecked((int)0xa953fd4e), 14) + e; c = RL(c, 10); + e = RL(e + F5(a, b, c) + X[11] + unchecked((int)0xa953fd4e), 11) + d; b = RL(b, 10); + d = RL(d + F5(e, a, b) + X[6] + unchecked((int)0xa953fd4e), 8) + c; a = RL(a, 10); + c = RL(c + F5(d, e, a) + X[15] + unchecked((int)0xa953fd4e), 5) + b; e = RL(e, 10); + b = RL(b + F5(c, d, e) + X[13] + unchecked((int)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; + } + } + } +} diff --git a/Crypto/src/crypto/digests/Sha1Digest.cs b/Crypto/src/crypto/digests/Sha1Digest.cs new file mode 100644
index 000000000..9d8c1a4cf --- /dev/null +++ b/Crypto/src/crypto/digests/Sha1Digest.cs
@@ -0,0 +1,263 @@ +using System; + +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Digests +{ + + /** + * 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 "endienness" of the word processing! + */ + public class Sha1Digest + : GeneralDigest + { + private const int DigestLength = 20; + + private uint H1, H2, H3, H4, H5; + + private uint[] X = new uint[80]; + private int xOff; + + public Sha1Digest() + { + Reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public Sha1Digest(Sha1Digest t) + : base(t) + { + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + H5 = t.H5; + + Array.Copy(t.X, 0, X, 0, t.X.Length); + xOff = t.xOff; + } + + public override string AlgorithmName + { + get { return "SHA-1"; } + } + + public override int GetDigestSize() + { + return DigestLength; + } + + internal override void ProcessWord( + byte[] input, + int inOff) + { + X[xOff] = Pack.BE_To_UInt32(input, inOff); + + if (++xOff == 16) + { + ProcessBlock(); + } + } + + internal override void ProcessLength(long bitLength) + { + if (xOff > 14) + { + ProcessBlock(); + } + + X[14] = (uint)((ulong)bitLength >> 32); + X[15] = (uint)((ulong)bitLength); + } + + public override int DoFinal( + byte[] output, + int outOff) + { + Finish(); + + Pack.UInt32_To_BE(H1, output, outOff); + Pack.UInt32_To_BE(H2, output, outOff + 4); + Pack.UInt32_To_BE(H3, output, outOff + 8); + Pack.UInt32_To_BE(H4, output, outOff + 12); + Pack.UInt32_To_BE(H5, output, outOff + 16); + + Reset(); + + return DigestLength; + } + + /** + * reset the chaining variables + */ + public override void Reset() + { + base.Reset(); + + H1 = 0x67452301; + H2 = 0xefcdab89; + H3 = 0x98badcfe; + H4 = 0x10325476; + H5 = 0xc3d2e1f0; + + xOff = 0; + Array.Clear(X, 0, X.Length); + } + + // + // Additive constants + // + private const uint Y1 = 0x5a827999; + private const uint Y2 = 0x6ed9eba1; + private const uint Y3 = 0x8f1bbcdc; + private const uint Y4 = 0xca62c1d6; + + private static uint F(uint u, uint v, uint w) + { + return (u & v) | (~u & w); + } + + private static uint H(uint u, uint v, uint w) + { + return u ^ v ^ w; + } + + private static uint G(uint u, uint v, uint w) + { + return (u & v) | (u & w) | (v & w); + } + + internal override void ProcessBlock() + { + // + // expand 16 word block into 80 word block. + // + for (int i = 16; i < 80; i++) + { + uint t = X[i - 3] ^ X[i - 8] ^ X[i - 14] ^ X[i - 16]; + X[i] = t << 1 | t >> 31; + } + + // + // set up working variables. + // + uint A = H1; + uint B = H2; + uint C = H3; + uint D = H4; + uint 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 < 4; 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; + Array.Clear(X, 0, 16); + } + } +} diff --git a/Crypto/src/crypto/digests/Sha224Digest.cs b/Crypto/src/crypto/digests/Sha224Digest.cs new file mode 100644
index 000000000..66ecd4ecd --- /dev/null +++ b/Crypto/src/crypto/digests/Sha224Digest.cs
@@ -0,0 +1,268 @@ +using System; + +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * 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 + : GeneralDigest + { + private const int DigestLength = 28; + + private uint H1, H2, H3, H4, H5, H6, H7, H8; + + private uint[] X = new uint[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) + : base(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; + + Array.Copy(t.X, 0, X, 0, t.X.Length); + xOff = t.xOff; + } + + public override string AlgorithmName + { + get { return "SHA-224"; } + } + + public override int GetDigestSize() + { + return DigestLength; + } + + internal override void ProcessWord( + byte[] input, + int inOff) + { + X[xOff] = Pack.BE_To_UInt32(input, inOff); + + if (++xOff == 16) + { + ProcessBlock(); + } + } + + internal override void ProcessLength( + long bitLength) + { + if (xOff > 14) + { + ProcessBlock(); + } + + X[14] = (uint)((ulong)bitLength >> 32); + X[15] = (uint)((ulong)bitLength); + } + + public override int DoFinal( + byte[] output, + int outOff) + { + Finish(); + + Pack.UInt32_To_BE(H1, output, outOff); + Pack.UInt32_To_BE(H2, output, outOff + 4); + Pack.UInt32_To_BE(H3, output, outOff + 8); + Pack.UInt32_To_BE(H4, output, outOff + 12); + Pack.UInt32_To_BE(H5, output, outOff + 16); + Pack.UInt32_To_BE(H6, output, outOff + 20); + Pack.UInt32_To_BE(H7, output, outOff + 24); + + Reset(); + + return DigestLength; + } + + /** + * reset the chaining variables + */ + public override void Reset() + { + base.Reset(); + + /* SHA-224 initial hash value + */ + H1 = 0xc1059ed8; + H2 = 0x367cd507; + H3 = 0x3070dd17; + H4 = 0xf70e5939; + H5 = 0xffc00b31; + H6 = 0x68581511; + H7 = 0x64f98fa7; + H8 = 0xbefa4fa4; + + xOff = 0; + Array.Clear(X, 0, X.Length); + } + + internal override void ProcessBlock() + { + // + // expand 16 word block into 64 word blocks. + // + for (int ti = 16; ti <= 63; ti++) + { + X[ti] = Theta1(X[ti - 2]) + X[ti - 7] + Theta0(X[ti - 15]) + X[ti - 16]; + } + + // + // set up working variables. + // + uint a = H1; + uint b = H2; + uint c = H3; + uint d = H4; + uint e = H5; + uint f = H6; + uint g = H7; + uint 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; + Array.Clear(X, 0, 16); + } + + /* SHA-224 functions */ + private static uint Ch(uint x, uint y, uint z) + { + return (x & y) ^ (~x & z); + } + + private static uint Maj(uint x, uint y, uint z) + { + return (x & y) ^ (x & z) ^ (y & z); + } + + private static uint Sum0(uint x) + { + return ((x >> 2) | (x << 30)) ^ ((x >> 13) | (x << 19)) ^ ((x >> 22) | (x << 10)); + } + + private static uint Sum1(uint x) + { + return ((x >> 6) | (x << 26)) ^ ((x >> 11) | (x << 21)) ^ ((x >> 25) | (x << 7)); + } + + private static uint Theta0(uint x) + { + return ((x >> 7) | (x << 25)) ^ ((x >> 18) | (x << 14)) ^ (x >> 3); + } + + private static uint Theta1(uint 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) + */ + internal static readonly uint[] 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 + }; + } +} diff --git a/Crypto/src/crypto/digests/Sha256Digest.cs b/Crypto/src/crypto/digests/Sha256Digest.cs new file mode 100644
index 000000000..1c00ab71f --- /dev/null +++ b/Crypto/src/crypto/digests/Sha256Digest.cs
@@ -0,0 +1,309 @@ +using System; + +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * Draft FIPS 180-2 implementation of SHA-256. <b>Note:</b> As this is + * based on a draft this implementation is subject to change. + * + * <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 + : GeneralDigest + { + private const int DigestLength = 32; + + private uint H1, H2, H3, H4, H5, H6, H7, H8; + private uint[] X = new uint[64]; + private int xOff; + + public Sha256Digest() + { + initHs(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public Sha256Digest(Sha256Digest t) : base(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; + + Array.Copy(t.X, 0, X, 0, t.X.Length); + xOff = t.xOff; + } + + public override string AlgorithmName + { + get { return "SHA-256"; } + } + + public override int GetDigestSize() + { + return DigestLength; + } + + internal override void ProcessWord( + byte[] input, + int inOff) + { + X[xOff] = Pack.BE_To_UInt32(input, inOff); + + if (++xOff == 16) + { + ProcessBlock(); + } + } + + internal override void ProcessLength( + long bitLength) + { + if (xOff > 14) + { + ProcessBlock(); + } + + X[14] = (uint)((ulong)bitLength >> 32); + X[15] = (uint)((ulong)bitLength); + } + + public override int DoFinal( + byte[] output, + int outOff) + { + Finish(); + + Pack.UInt32_To_BE((uint)H1, output, outOff); + Pack.UInt32_To_BE((uint)H2, output, outOff + 4); + Pack.UInt32_To_BE((uint)H3, output, outOff + 8); + Pack.UInt32_To_BE((uint)H4, output, outOff + 12); + Pack.UInt32_To_BE((uint)H5, output, outOff + 16); + Pack.UInt32_To_BE((uint)H6, output, outOff + 20); + Pack.UInt32_To_BE((uint)H7, output, outOff + 24); + Pack.UInt32_To_BE((uint)H8, output, outOff + 28); + + Reset(); + + return DigestLength; + } + + /** + * reset the chaining variables + */ + public override void Reset() + { + base.Reset(); + + initHs(); + + xOff = 0; + Array.Clear(X, 0, X.Length); + } + + private void initHs() + { + /* 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; + } + + internal override void ProcessBlock() + { + // + // expand 16 word block into 64 word blocks. + // + for (int ti = 16; ti <= 63; ti++) + { + X[ti] = Theta1(X[ti - 2]) + X[ti - 7] + Theta0(X[ti - 15]) + X[ti - 16]; + } + + // + // set up working variables. + // + uint a = H1; + uint b = H2; + uint c = H3; + uint d = H4; + uint e = H5; + uint f = H6; + uint g = H7; + uint h = H8; + + int t = 0; + for(int i = 0; i < 8; ++i) + { + // t = 8 * i + h += Sum1Ch(e, f, g) + K[t] + X[t]; + d += h; + h += Sum0Maj(a, b, c); + ++t; + + // t = 8 * i + 1 + g += Sum1Ch(d, e, f) + K[t] + X[t]; + c += g; + g += Sum0Maj(h, a, b); + ++t; + + // t = 8 * i + 2 + f += Sum1Ch(c, d, e) + K[t] + X[t]; + b += f; + f += Sum0Maj(g, h, a); + ++t; + + // t = 8 * i + 3 + e += Sum1Ch(b, c, d) + K[t] + X[t]; + a += e; + e += Sum0Maj(f, g, h); + ++t; + + // t = 8 * i + 4 + d += Sum1Ch(a, b, c) + K[t] + X[t]; + h += d; + d += Sum0Maj(e, f, g); + ++t; + + // t = 8 * i + 5 + c += Sum1Ch(h, a, b) + K[t] + X[t]; + g += c; + c += Sum0Maj(d, e, f); + ++t; + + // t = 8 * i + 6 + b += Sum1Ch(g, h, a) + K[t] + X[t]; + f += b; + b += Sum0Maj(c, d, e); + ++t; + + // t = 8 * i + 7 + a += Sum1Ch(f, g, h) + K[t] + X[t]; + e += a; + a += Sum0Maj(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; + Array.Clear(X, 0, 16); + } + + private static uint Sum1Ch( + uint x, + uint y, + uint z) + { +// return Sum1(x) + Ch(x, y, z); + return (((x >> 6) | (x << 26)) ^ ((x >> 11) | (x << 21)) ^ ((x >> 25) | (x << 7))) + + ((x & y) ^ ((~x) & z)); + } + + private static uint Sum0Maj( + uint x, + uint y, + uint z) + { +// return Sum0(x) + Maj(x, y, z); + return (((x >> 2) | (x << 30)) ^ ((x >> 13) | (x << 19)) ^ ((x >> 22) | (x << 10))) + + ((x & y) ^ (x & z) ^ (y & z)); + } + +// /* SHA-256 functions */ +// private static uint Ch( +// uint x, +// uint y, +// uint z) +// { +// return ((x & y) ^ ((~x) & z)); +// } +// +// private static uint Maj( +// uint x, +// uint y, +// uint z) +// { +// return ((x & y) ^ (x & z) ^ (y & z)); +// } +// +// private static uint Sum0( +// uint x) +// { +// return ((x >> 2) | (x << 30)) ^ ((x >> 13) | (x << 19)) ^ ((x >> 22) | (x << 10)); +// } +// +// private static uint Sum1( +// uint x) +// { +// return ((x >> 6) | (x << 26)) ^ ((x >> 11) | (x << 21)) ^ ((x >> 25) | (x << 7)); +// } + + private static uint Theta0( + uint x) + { + return ((x >> 7) | (x << 25)) ^ ((x >> 18) | (x << 14)) ^ (x >> 3); + } + + private static uint Theta1( + uint 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) + */ + private static readonly uint[] 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 + }; + } +} diff --git a/Crypto/src/crypto/digests/Sha384Digest.cs b/Crypto/src/crypto/digests/Sha384Digest.cs new file mode 100644
index 000000000..f1372d0a9 --- /dev/null +++ b/Crypto/src/crypto/digests/Sha384Digest.cs
@@ -0,0 +1,87 @@ +using System; + +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * Draft FIPS 180-2 implementation of SHA-384. <b>Note:</b> As this is + * based on a draft this implementation is subject to change. + * + * <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 + : LongDigest + { + private const int DigestLength = 48; + + public Sha384Digest() + { + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public Sha384Digest( + Sha384Digest t) + : base(t) + { + } + + public override string AlgorithmName + { + get { return "SHA-384"; } + } + + public override int GetDigestSize() + { + return DigestLength; + } + + public override int DoFinal( + byte[] output, + int outOff) + { + Finish(); + + Pack.UInt64_To_BE(H1, output, outOff); + Pack.UInt64_To_BE(H2, output, outOff + 8); + Pack.UInt64_To_BE(H3, output, outOff + 16); + Pack.UInt64_To_BE(H4, output, outOff + 24); + Pack.UInt64_To_BE(H5, output, outOff + 32); + Pack.UInt64_To_BE(H6, output, outOff + 40); + + Reset(); + + return DigestLength; + } + + /** + * reset the chaining variables + */ + public override void Reset() + { + base.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 = 0xcbbb9d5dc1059ed8; + H2 = 0x629a292a367cd507; + H3 = 0x9159015a3070dd17; + H4 = 0x152fecd8f70e5939; + H5 = 0x67332667ffc00b31; + H6 = 0x8eb44a8768581511; + H7 = 0xdb0c2e0d64f98fa7; + H8 = 0x47b5481dbefa4fa4; + } + } +} diff --git a/Crypto/src/crypto/digests/Sha512Digest.cs b/Crypto/src/crypto/digests/Sha512Digest.cs new file mode 100644
index 000000000..ed1a50819 --- /dev/null +++ b/Crypto/src/crypto/digests/Sha512Digest.cs
@@ -0,0 +1,90 @@ +using System; + +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * Draft FIPS 180-2 implementation of SHA-512. <b>Note:</b> As this is + * based on a draft this implementation is subject to change. + * + * <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 + : LongDigest + { + private const int DigestLength = 64; + + public Sha512Digest() + { + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public Sha512Digest( + Sha512Digest t) + : base(t) + { + } + + public override string AlgorithmName + { + get { return "SHA-512"; } + } + + public override int GetDigestSize() + { + return DigestLength; + } + + public override int DoFinal( + byte[] output, + int outOff) + { + Finish(); + + Pack.UInt64_To_BE(H1, output, outOff); + Pack.UInt64_To_BE(H2, output, outOff + 8); + Pack.UInt64_To_BE(H3, output, outOff + 16); + Pack.UInt64_To_BE(H4, output, outOff + 24); + Pack.UInt64_To_BE(H5, output, outOff + 32); + Pack.UInt64_To_BE(H6, output, outOff + 40); + Pack.UInt64_To_BE(H7, output, outOff + 48); + Pack.UInt64_To_BE(H8, output, outOff + 56); + + Reset(); + + return DigestLength; + + } + + /** + * reset the chaining variables + */ + public override void Reset() + { + base.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 = 0x6a09e667f3bcc908; + H2 = 0xbb67ae8584caa73b; + H3 = 0x3c6ef372fe94f82b; + H4 = 0xa54ff53a5f1d36f1; + H5 = 0x510e527fade682d1; + H6 = 0x9b05688c2b3e6c1f; + H7 = 0x1f83d9abfb41bd6b; + H8 = 0x5be0cd19137e2179; + } + } +} diff --git a/Crypto/src/crypto/digests/ShortenedDigest.cs b/Crypto/src/crypto/digests/ShortenedDigest.cs new file mode 100644
index 000000000..9e4d99e7b --- /dev/null +++ b/Crypto/src/crypto/digests/ShortenedDigest.cs
@@ -0,0 +1,82 @@ +using System; +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * Wrapper class that reduces the output length of a particular digest to + * only the first n bytes of the digest function. + */ + public class ShortenedDigest + : IDigest + { + private IDigest baseDigest; + private int length; + + /** + * Base constructor. + * + * @param baseDigest underlying digest to use. + * @param length length in bytes of the output of doFinal. + * @exception ArgumentException if baseDigest is null, or length is greater than baseDigest.GetDigestSize(). + */ + public ShortenedDigest( + IDigest baseDigest, + int length) + { + if (baseDigest == null) + { + throw new ArgumentNullException("baseDigest"); + } + + if (length > baseDigest.GetDigestSize()) + { + throw new ArgumentException("baseDigest output not large enough to support length"); + } + + this.baseDigest = baseDigest; + this.length = length; + } + + public string AlgorithmName + { + get { return baseDigest.AlgorithmName + "(" + length * 8 + ")"; } + } + + public int GetDigestSize() + { + return length; + } + + public void Update(byte input) + { + baseDigest.Update(input); + } + + public void BlockUpdate(byte[] input, int inOff, int length) + { + baseDigest.BlockUpdate(input, inOff, length); + } + + public int DoFinal(byte[] output, int outOff) + { + byte[] tmp = new byte[baseDigest.GetDigestSize()]; + + baseDigest.DoFinal(tmp, 0); + + Array.Copy(tmp, 0, output, outOff, length); + + return length; + } + + public void Reset() + { + baseDigest.Reset(); + } + + public int GetByteLength() + { + return baseDigest.GetByteLength(); + } + } +} diff --git a/Crypto/src/crypto/digests/TigerDigest.cs b/Crypto/src/crypto/digests/TigerDigest.cs new file mode 100644
index 000000000..b8c9a7664 --- /dev/null +++ b/Crypto/src/crypto/digests/TigerDigest.cs
@@ -0,0 +1,868 @@ +using System; +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * 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 + : IDigest + { + private const int MyByteLength = 64; + + /* + * S-Boxes. + */ + private static readonly long[] t1 = { + unchecked((long) 0x02AAB17CF7E90C5EL) /* 0 */, unchecked((long) 0xAC424B03E243A8ECL) /* 1 */, + unchecked((long) 0x72CD5BE30DD5FCD3L) /* 2 */, unchecked((long) 0x6D019B93F6F97F3AL) /* 3 */, + unchecked((long) 0xCD9978FFD21F9193L) /* 4 */, unchecked((long) 0x7573A1C9708029E2L) /* 5 */, + unchecked((long) 0xB164326B922A83C3L) /* 6 */, unchecked((long) 0x46883EEE04915870L) /* 7 */, + unchecked((long) 0xEAACE3057103ECE6L) /* 8 */, unchecked((long) 0xC54169B808A3535CL) /* 9 */, + unchecked((long) 0x4CE754918DDEC47CL) /* 10 */, unchecked((long) 0x0AA2F4DFDC0DF40CL) /* 11 */, + unchecked((long) 0x10B76F18A74DBEFAL) /* 12 */, unchecked((long) 0xC6CCB6235AD1AB6AL) /* 13 */, + unchecked((long) 0x13726121572FE2FFL) /* 14 */, unchecked((long) 0x1A488C6F199D921EL) /* 15 */, + unchecked((long) 0x4BC9F9F4DA0007CAL) /* 16 */, unchecked((long) 0x26F5E6F6E85241C7L) /* 17 */, + unchecked((long) 0x859079DBEA5947B6L) /* 18 */, unchecked((long) 0x4F1885C5C99E8C92L) /* 19 */, + unchecked((long) 0xD78E761EA96F864BL) /* 20 */, unchecked((long) 0x8E36428C52B5C17DL) /* 21 */, + unchecked((long) 0x69CF6827373063C1L) /* 22 */, unchecked((long) 0xB607C93D9BB4C56EL) /* 23 */, + unchecked((long) 0x7D820E760E76B5EAL) /* 24 */, unchecked((long) 0x645C9CC6F07FDC42L) /* 25 */, + unchecked((long) 0xBF38A078243342E0L) /* 26 */, unchecked((long) 0x5F6B343C9D2E7D04L) /* 27 */, + unchecked((long) 0xF2C28AEB600B0EC6L) /* 28 */, unchecked((long) 0x6C0ED85F7254BCACL) /* 29 */, + unchecked((long) 0x71592281A4DB4FE5L) /* 30 */, unchecked((long) 0x1967FA69CE0FED9FL) /* 31 */, + unchecked((long) 0xFD5293F8B96545DBL) /* 32 */, unchecked((long) 0xC879E9D7F2A7600BL) /* 33 */, + unchecked((long) 0x860248920193194EL) /* 34 */, unchecked((long) 0xA4F9533B2D9CC0B3L) /* 35 */, + unchecked((long) 0x9053836C15957613L) /* 36 */, unchecked((long) 0xDB6DCF8AFC357BF1L) /* 37 */, + unchecked((long) 0x18BEEA7A7A370F57L) /* 38 */, unchecked((long) 0x037117CA50B99066L) /* 39 */, + unchecked((long) 0x6AB30A9774424A35L) /* 40 */, unchecked((long) 0xF4E92F02E325249BL) /* 41 */, + unchecked((long) 0x7739DB07061CCAE1L) /* 42 */, unchecked((long) 0xD8F3B49CECA42A05L) /* 43 */, + unchecked((long) 0xBD56BE3F51382F73L) /* 44 */, unchecked((long) 0x45FAED5843B0BB28L) /* 45 */, + unchecked((long) 0x1C813D5C11BF1F83L) /* 46 */, unchecked((long) 0x8AF0E4B6D75FA169L) /* 47 */, + unchecked((long) 0x33EE18A487AD9999L) /* 48 */, unchecked((long) 0x3C26E8EAB1C94410L) /* 49 */, + unchecked((long) 0xB510102BC0A822F9L) /* 50 */, unchecked((long) 0x141EEF310CE6123BL) /* 51 */, + unchecked((long) 0xFC65B90059DDB154L) /* 52 */, unchecked((long) 0xE0158640C5E0E607L) /* 53 */, + unchecked((long) 0x884E079826C3A3CFL) /* 54 */, unchecked((long) 0x930D0D9523C535FDL) /* 55 */, + unchecked((long) 0x35638D754E9A2B00L) /* 56 */, unchecked((long) 0x4085FCCF40469DD5L) /* 57 */, + unchecked((long) 0xC4B17AD28BE23A4CL) /* 58 */, unchecked((long) 0xCAB2F0FC6A3E6A2EL) /* 59 */, + unchecked((long) 0x2860971A6B943FCDL) /* 60 */, unchecked((long) 0x3DDE6EE212E30446L) /* 61 */, + unchecked((long) 0x6222F32AE01765AEL) /* 62 */, unchecked((long) 0x5D550BB5478308FEL) /* 63 */, + unchecked((long) 0xA9EFA98DA0EDA22AL) /* 64 */, unchecked((long) 0xC351A71686C40DA7L) /* 65 */, + unchecked((long) 0x1105586D9C867C84L) /* 66 */, unchecked((long) 0xDCFFEE85FDA22853L) /* 67 */, + unchecked((long) 0xCCFBD0262C5EEF76L) /* 68 */, unchecked((long) 0xBAF294CB8990D201L) /* 69 */, + unchecked((long) 0xE69464F52AFAD975L) /* 70 */, unchecked((long) 0x94B013AFDF133E14L) /* 71 */, + unchecked((long) 0x06A7D1A32823C958L) /* 72 */, unchecked((long) 0x6F95FE5130F61119L) /* 73 */, + unchecked((long) 0xD92AB34E462C06C0L) /* 74 */, unchecked((long) 0xED7BDE33887C71D2L) /* 75 */, + unchecked((long) 0x79746D6E6518393EL) /* 76 */, unchecked((long) 0x5BA419385D713329L) /* 77 */, + unchecked((long) 0x7C1BA6B948A97564L) /* 78 */, unchecked((long) 0x31987C197BFDAC67L) /* 79 */, + unchecked((long) 0xDE6C23C44B053D02L) /* 80 */, unchecked((long) 0x581C49FED002D64DL) /* 81 */, + unchecked((long) 0xDD474D6338261571L) /* 82 */, unchecked((long) 0xAA4546C3E473D062L) /* 83 */, + unchecked((long) 0x928FCE349455F860L) /* 84 */, unchecked((long) 0x48161BBACAAB94D9L) /* 85 */, + unchecked((long) 0x63912430770E6F68L) /* 86 */, unchecked((long) 0x6EC8A5E602C6641CL) /* 87 */, + unchecked((long) 0x87282515337DDD2BL) /* 88 */, unchecked((long) 0x2CDA6B42034B701BL) /* 89 */, + unchecked((long) 0xB03D37C181CB096DL) /* 90 */, unchecked((long) 0xE108438266C71C6FL) /* 91 */, + unchecked((long) 0x2B3180C7EB51B255L) /* 92 */, unchecked((long) 0xDF92B82F96C08BBCL) /* 93 */, + unchecked((long) 0x5C68C8C0A632F3BAL) /* 94 */, unchecked((long) 0x5504CC861C3D0556L) /* 95 */, + unchecked((long) 0xABBFA4E55FB26B8FL) /* 96 */, unchecked((long) 0x41848B0AB3BACEB4L) /* 97 */, + unchecked((long) 0xB334A273AA445D32L) /* 98 */, unchecked((long) 0xBCA696F0A85AD881L) /* 99 */, + unchecked((long) 0x24F6EC65B528D56CL) /* 100 */, unchecked((long) 0x0CE1512E90F4524AL) /* 101 */, + unchecked((long) 0x4E9DD79D5506D35AL) /* 102 */, unchecked((long) 0x258905FAC6CE9779L) /* 103 */, + unchecked((long) 0x2019295B3E109B33L) /* 104 */, unchecked((long) 0xF8A9478B73A054CCL) /* 105 */, + unchecked((long) 0x2924F2F934417EB0L) /* 106 */, unchecked((long) 0x3993357D536D1BC4L) /* 107 */, + unchecked((long) 0x38A81AC21DB6FF8BL) /* 108 */, unchecked((long) 0x47C4FBF17D6016BFL) /* 109 */, + unchecked((long) 0x1E0FAADD7667E3F5L) /* 110 */, unchecked((long) 0x7ABCFF62938BEB96L) /* 111 */, + unchecked((long) 0xA78DAD948FC179C9L) /* 112 */, unchecked((long) 0x8F1F98B72911E50DL) /* 113 */, + unchecked((long) 0x61E48EAE27121A91L) /* 114 */, unchecked((long) 0x4D62F7AD31859808L) /* 115 */, + unchecked((long) 0xECEBA345EF5CEAEBL) /* 116 */, unchecked((long) 0xF5CEB25EBC9684CEL) /* 117 */, + unchecked((long) 0xF633E20CB7F76221L) /* 118 */, unchecked((long) 0xA32CDF06AB8293E4L) /* 119 */, + unchecked((long) 0x985A202CA5EE2CA4L) /* 120 */, unchecked((long) 0xCF0B8447CC8A8FB1L) /* 121 */, + unchecked((long) 0x9F765244979859A3L) /* 122 */, unchecked((long) 0xA8D516B1A1240017L) /* 123 */, + unchecked((long) 0x0BD7BA3EBB5DC726L) /* 124 */, unchecked((long) 0xE54BCA55B86ADB39L) /* 125 */, + unchecked((long) 0x1D7A3AFD6C478063L) /* 126 */, unchecked((long) 0x519EC608E7669EDDL) /* 127 */, + unchecked((long) 0x0E5715A2D149AA23L) /* 128 */, unchecked((long) 0x177D4571848FF194L) /* 129 */, + unchecked((long) 0xEEB55F3241014C22L) /* 130 */, unchecked((long) 0x0F5E5CA13A6E2EC2L) /* 131 */, + unchecked((long) 0x8029927B75F5C361L) /* 132 */, unchecked((long) 0xAD139FABC3D6E436L) /* 133 */, + unchecked((long) 0x0D5DF1A94CCF402FL) /* 134 */, unchecked((long) 0x3E8BD948BEA5DFC8L) /* 135 */, + unchecked((long) 0xA5A0D357BD3FF77EL) /* 136 */, unchecked((long) 0xA2D12E251F74F645L) /* 137 */, + unchecked((long) 0x66FD9E525E81A082L) /* 138 */, unchecked((long) 0x2E0C90CE7F687A49L) /* 139 */, + unchecked((long) 0xC2E8BCBEBA973BC5L) /* 140 */, unchecked((long) 0x000001BCE509745FL) /* 141 */, + unchecked((long) 0x423777BBE6DAB3D6L) /* 142 */, unchecked((long) 0xD1661C7EAEF06EB5L) /* 143 */, + unchecked((long) 0xA1781F354DAACFD8L) /* 144 */, unchecked((long) 0x2D11284A2B16AFFCL) /* 145 */, + unchecked((long) 0xF1FC4F67FA891D1FL) /* 146 */, unchecked((long) 0x73ECC25DCB920ADAL) /* 147 */, + unchecked((long) 0xAE610C22C2A12651L) /* 148 */, unchecked((long) 0x96E0A810D356B78AL) /* 149 */, + unchecked((long) 0x5A9A381F2FE7870FL) /* 150 */, unchecked((long) 0xD5AD62EDE94E5530L) /* 151 */, + unchecked((long) 0xD225E5E8368D1427L) /* 152 */, unchecked((long) 0x65977B70C7AF4631L) /* 153 */, + unchecked((long) 0x99F889B2DE39D74FL) /* 154 */, unchecked((long) 0x233F30BF54E1D143L) /* 155 */, + unchecked((long) 0x9A9675D3D9A63C97L) /* 156 */, unchecked((long) 0x5470554FF334F9A8L) /* 157 */, + unchecked((long) 0x166ACB744A4F5688L) /* 158 */, unchecked((long) 0x70C74CAAB2E4AEADL) /* 159 */, + unchecked((long) 0xF0D091646F294D12L) /* 160 */, unchecked((long) 0x57B82A89684031D1L) /* 161 */, + unchecked((long) 0xEFD95A5A61BE0B6BL) /* 162 */, unchecked((long) 0x2FBD12E969F2F29AL) /* 163 */, + unchecked((long) 0x9BD37013FEFF9FE8L) /* 164 */, unchecked((long) 0x3F9B0404D6085A06L) /* 165 */, + unchecked((long) 0x4940C1F3166CFE15L) /* 166 */, unchecked((long) 0x09542C4DCDF3DEFBL) /* 167 */, + unchecked((long) 0xB4C5218385CD5CE3L) /* 168 */, unchecked((long) 0xC935B7DC4462A641L) /* 169 */, + unchecked((long) 0x3417F8A68ED3B63FL) /* 170 */, unchecked((long) 0xB80959295B215B40L) /* 171 */, + unchecked((long) 0xF99CDAEF3B8C8572L) /* 172 */, unchecked((long) 0x018C0614F8FCB95DL) /* 173 */, + unchecked((long) 0x1B14ACCD1A3ACDF3L) /* 174 */, unchecked((long) 0x84D471F200BB732DL) /* 175 */, + unchecked((long) 0xC1A3110E95E8DA16L) /* 176 */, unchecked((long) 0x430A7220BF1A82B8L) /* 177 */, + unchecked((long) 0xB77E090D39DF210EL) /* 178 */, unchecked((long) 0x5EF4BD9F3CD05E9DL) /* 179 */, + unchecked((long) 0x9D4FF6DA7E57A444L) /* 180 */, unchecked((long) 0xDA1D60E183D4A5F8L) /* 181 */, + unchecked((long) 0xB287C38417998E47L) /* 182 */, unchecked((long) 0xFE3EDC121BB31886L) /* 183 */, + unchecked((long) 0xC7FE3CCC980CCBEFL) /* 184 */, unchecked((long) 0xE46FB590189BFD03L) /* 185 */, + unchecked((long) 0x3732FD469A4C57DCL) /* 186 */, unchecked((long) 0x7EF700A07CF1AD65L) /* 187 */, + unchecked((long) 0x59C64468A31D8859L) /* 188 */, unchecked((long) 0x762FB0B4D45B61F6L) /* 189 */, + unchecked((long) 0x155BAED099047718L) /* 190 */, unchecked((long) 0x68755E4C3D50BAA6L) /* 191 */, + unchecked((long) 0xE9214E7F22D8B4DFL) /* 192 */, unchecked((long) 0x2ADDBF532EAC95F4L) /* 193 */, + unchecked((long) 0x32AE3909B4BD0109L) /* 194 */, unchecked((long) 0x834DF537B08E3450L) /* 195 */, + unchecked((long) 0xFA209DA84220728DL) /* 196 */, unchecked((long) 0x9E691D9B9EFE23F7L) /* 197 */, + unchecked((long) 0x0446D288C4AE8D7FL) /* 198 */, unchecked((long) 0x7B4CC524E169785BL) /* 199 */, + unchecked((long) 0x21D87F0135CA1385L) /* 200 */, unchecked((long) 0xCEBB400F137B8AA5L) /* 201 */, + unchecked((long) 0x272E2B66580796BEL) /* 202 */, unchecked((long) 0x3612264125C2B0DEL) /* 203 */, + unchecked((long) 0x057702BDAD1EFBB2L) /* 204 */, unchecked((long) 0xD4BABB8EACF84BE9L) /* 205 */, + unchecked((long) 0x91583139641BC67BL) /* 206 */, unchecked((long) 0x8BDC2DE08036E024L) /* 207 */, + unchecked((long) 0x603C8156F49F68EDL) /* 208 */, unchecked((long) 0xF7D236F7DBEF5111L) /* 209 */, + unchecked((long) 0x9727C4598AD21E80L) /* 210 */, unchecked((long) 0xA08A0896670A5FD7L) /* 211 */, + unchecked((long) 0xCB4A8F4309EBA9CBL) /* 212 */, unchecked((long) 0x81AF564B0F7036A1L) /* 213 */, + unchecked((long) 0xC0B99AA778199ABDL) /* 214 */, unchecked((long) 0x959F1EC83FC8E952L) /* 215 */, + unchecked((long) 0x8C505077794A81B9L) /* 216 */, unchecked((long) 0x3ACAAF8F056338F0L) /* 217 */, + unchecked((long) 0x07B43F50627A6778L) /* 218 */, unchecked((long) 0x4A44AB49F5ECCC77L) /* 219 */, + unchecked((long) 0x3BC3D6E4B679EE98L) /* 220 */, unchecked((long) 0x9CC0D4D1CF14108CL) /* 221 */, + unchecked((long) 0x4406C00B206BC8A0L) /* 222 */, unchecked((long) 0x82A18854C8D72D89L) /* 223 */, + unchecked((long) 0x67E366B35C3C432CL) /* 224 */, unchecked((long) 0xB923DD61102B37F2L) /* 225 */, + unchecked((long) 0x56AB2779D884271DL) /* 226 */, unchecked((long) 0xBE83E1B0FF1525AFL) /* 227 */, + unchecked((long) 0xFB7C65D4217E49A9L) /* 228 */, unchecked((long) 0x6BDBE0E76D48E7D4L) /* 229 */, + unchecked((long) 0x08DF828745D9179EL) /* 230 */, unchecked((long) 0x22EA6A9ADD53BD34L) /* 231 */, + unchecked((long) 0xE36E141C5622200AL) /* 232 */, unchecked((long) 0x7F805D1B8CB750EEL) /* 233 */, + unchecked((long) 0xAFE5C7A59F58E837L) /* 234 */, unchecked((long) 0xE27F996A4FB1C23CL) /* 235 */, + unchecked((long) 0xD3867DFB0775F0D0L) /* 236 */, unchecked((long) 0xD0E673DE6E88891AL) /* 237 */, + unchecked((long) 0x123AEB9EAFB86C25L) /* 238 */, unchecked((long) 0x30F1D5D5C145B895L) /* 239 */, + unchecked((long) 0xBB434A2DEE7269E7L) /* 240 */, unchecked((long) 0x78CB67ECF931FA38L) /* 241 */, + unchecked((long) 0xF33B0372323BBF9CL) /* 242 */, unchecked((long) 0x52D66336FB279C74L) /* 243 */, + unchecked((long) 0x505F33AC0AFB4EAAL) /* 244 */, unchecked((long) 0xE8A5CD99A2CCE187L) /* 245 */, + unchecked((long) 0x534974801E2D30BBL) /* 246 */, unchecked((long) 0x8D2D5711D5876D90L) /* 247 */, + unchecked((long) 0x1F1A412891BC038EL) /* 248 */, unchecked((long) 0xD6E2E71D82E56648L) /* 249 */, + unchecked((long) 0x74036C3A497732B7L) /* 250 */, unchecked((long) 0x89B67ED96361F5ABL) /* 251 */, + unchecked((long) 0xFFED95D8F1EA02A2L) /* 252 */, unchecked((long) 0xE72B3BD61464D43DL) /* 253 */, + unchecked((long) 0xA6300F170BDC4820L) /* 254 */, unchecked((long) 0xEBC18760ED78A77AL) /* 255 */, + }; + + private static readonly long[] t2 = { + unchecked((long) 0xE6A6BE5A05A12138L) /* 256 */, unchecked((long) 0xB5A122A5B4F87C98L) /* 257 */, + unchecked((long) 0x563C6089140B6990L) /* 258 */, unchecked((long) 0x4C46CB2E391F5DD5L) /* 259 */, + unchecked((long) 0xD932ADDBC9B79434L) /* 260 */, unchecked((long) 0x08EA70E42015AFF5L) /* 261 */, + unchecked((long) 0xD765A6673E478CF1L) /* 262 */, unchecked((long) 0xC4FB757EAB278D99L) /* 263 */, + unchecked((long) 0xDF11C6862D6E0692L) /* 264 */, unchecked((long) 0xDDEB84F10D7F3B16L) /* 265 */, + unchecked((long) 0x6F2EF604A665EA04L) /* 266 */, unchecked((long) 0x4A8E0F0FF0E0DFB3L) /* 267 */, + unchecked((long) 0xA5EDEEF83DBCBA51L) /* 268 */, unchecked((long) 0xFC4F0A2A0EA4371EL) /* 269 */, + unchecked((long) 0xE83E1DA85CB38429L) /* 270 */, unchecked((long) 0xDC8FF882BA1B1CE2L) /* 271 */, + unchecked((long) 0xCD45505E8353E80DL) /* 272 */, unchecked((long) 0x18D19A00D4DB0717L) /* 273 */, + unchecked((long) 0x34A0CFEDA5F38101L) /* 274 */, unchecked((long) 0x0BE77E518887CAF2L) /* 275 */, + unchecked((long) 0x1E341438B3C45136L) /* 276 */, unchecked((long) 0xE05797F49089CCF9L) /* 277 */, + unchecked((long) 0xFFD23F9DF2591D14L) /* 278 */, unchecked((long) 0x543DDA228595C5CDL) /* 279 */, + unchecked((long) 0x661F81FD99052A33L) /* 280 */, unchecked((long) 0x8736E641DB0F7B76L) /* 281 */, + unchecked((long) 0x15227725418E5307L) /* 282 */, unchecked((long) 0xE25F7F46162EB2FAL) /* 283 */, + unchecked((long) 0x48A8B2126C13D9FEL) /* 284 */, unchecked((long) 0xAFDC541792E76EEAL) /* 285 */, + unchecked((long) 0x03D912BFC6D1898FL) /* 286 */, unchecked((long) 0x31B1AAFA1B83F51BL) /* 287 */, + unchecked((long) 0xF1AC2796E42AB7D9L) /* 288 */, unchecked((long) 0x40A3A7D7FCD2EBACL) /* 289 */, + unchecked((long) 0x1056136D0AFBBCC5L) /* 290 */, unchecked((long) 0x7889E1DD9A6D0C85L) /* 291 */, + unchecked((long) 0xD33525782A7974AAL) /* 292 */, unchecked((long) 0xA7E25D09078AC09BL) /* 293 */, + unchecked((long) 0xBD4138B3EAC6EDD0L) /* 294 */, unchecked((long) 0x920ABFBE71EB9E70L) /* 295 */, + unchecked((long) 0xA2A5D0F54FC2625CL) /* 296 */, unchecked((long) 0xC054E36B0B1290A3L) /* 297 */, + unchecked((long) 0xF6DD59FF62FE932BL) /* 298 */, unchecked((long) 0x3537354511A8AC7DL) /* 299 */, + unchecked((long) 0xCA845E9172FADCD4L) /* 300 */, unchecked((long) 0x84F82B60329D20DCL) /* 301 */, + unchecked((long) 0x79C62CE1CD672F18L) /* 302 */, unchecked((long) 0x8B09A2ADD124642CL) /* 303 */, + unchecked((long) 0xD0C1E96A19D9E726L) /* 304 */, unchecked((long) 0x5A786A9B4BA9500CL) /* 305 */, + unchecked((long) 0x0E020336634C43F3L) /* 306 */, unchecked((long) 0xC17B474AEB66D822L) /* 307 */, + unchecked((long) 0x6A731AE3EC9BAAC2L) /* 308 */, unchecked((long) 0x8226667AE0840258L) /* 309 */, + unchecked((long) 0x67D4567691CAECA5L) /* 310 */, unchecked((long) 0x1D94155C4875ADB5L) /* 311 */, + unchecked((long) 0x6D00FD985B813FDFL) /* 312 */, unchecked((long) 0x51286EFCB774CD06L) /* 313 */, + unchecked((long) 0x5E8834471FA744AFL) /* 314 */, unchecked((long) 0xF72CA0AEE761AE2EL) /* 315 */, + unchecked((long) 0xBE40E4CDAEE8E09AL) /* 316 */, unchecked((long) 0xE9970BBB5118F665L) /* 317 */, + unchecked((long) 0x726E4BEB33DF1964L) /* 318 */, unchecked((long) 0x703B000729199762L) /* 319 */, + unchecked((long) 0x4631D816F5EF30A7L) /* 320 */, unchecked((long) 0xB880B5B51504A6BEL) /* 321 */, + unchecked((long) 0x641793C37ED84B6CL) /* 322 */, unchecked((long) 0x7B21ED77F6E97D96L) /* 323 */, + unchecked((long) 0x776306312EF96B73L) /* 324 */, unchecked((long) 0xAE528948E86FF3F4L) /* 325 */, + unchecked((long) 0x53DBD7F286A3F8F8L) /* 326 */, unchecked((long) 0x16CADCE74CFC1063L) /* 327 */, + unchecked((long) 0x005C19BDFA52C6DDL) /* 328 */, unchecked((long) 0x68868F5D64D46AD3L) /* 329 */, + unchecked((long) 0x3A9D512CCF1E186AL) /* 330 */, unchecked((long) 0x367E62C2385660AEL) /* 331 */, + unchecked((long) 0xE359E7EA77DCB1D7L) /* 332 */, unchecked((long) 0x526C0773749ABE6EL) /* 333 */, + unchecked((long) 0x735AE5F9D09F734BL) /* 334 */, unchecked((long) 0x493FC7CC8A558BA8L) /* 335 */, + unchecked((long) 0xB0B9C1533041AB45L) /* 336 */, unchecked((long) 0x321958BA470A59BDL) /* 337 */, + unchecked((long) 0x852DB00B5F46C393L) /* 338 */, unchecked((long) 0x91209B2BD336B0E5L) /* 339 */, + unchecked((long) 0x6E604F7D659EF19FL) /* 340 */, unchecked((long) 0xB99A8AE2782CCB24L) /* 341 */, + unchecked((long) 0xCCF52AB6C814C4C7L) /* 342 */, unchecked((long) 0x4727D9AFBE11727BL) /* 343 */, + unchecked((long) 0x7E950D0C0121B34DL) /* 344 */, unchecked((long) 0x756F435670AD471FL) /* 345 */, + unchecked((long) 0xF5ADD442615A6849L) /* 346 */, unchecked((long) 0x4E87E09980B9957AL) /* 347 */, + unchecked((long) 0x2ACFA1DF50AEE355L) /* 348 */, unchecked((long) 0xD898263AFD2FD556L) /* 349 */, + unchecked((long) 0xC8F4924DD80C8FD6L) /* 350 */, unchecked((long) 0xCF99CA3D754A173AL) /* 351 */, + unchecked((long) 0xFE477BACAF91BF3CL) /* 352 */, unchecked((long) 0xED5371F6D690C12DL) /* 353 */, + unchecked((long) 0x831A5C285E687094L) /* 354 */, unchecked((long) 0xC5D3C90A3708A0A4L) /* 355 */, + unchecked((long) 0x0F7F903717D06580L) /* 356 */, unchecked((long) 0x19F9BB13B8FDF27FL) /* 357 */, + unchecked((long) 0xB1BD6F1B4D502843L) /* 358 */, unchecked((long) 0x1C761BA38FFF4012L) /* 359 */, + unchecked((long) 0x0D1530C4E2E21F3BL) /* 360 */, unchecked((long) 0x8943CE69A7372C8AL) /* 361 */, + unchecked((long) 0xE5184E11FEB5CE66L) /* 362 */, unchecked((long) 0x618BDB80BD736621L) /* 363 */, + unchecked((long) 0x7D29BAD68B574D0BL) /* 364 */, unchecked((long) 0x81BB613E25E6FE5BL) /* 365 */, + unchecked((long) 0x071C9C10BC07913FL) /* 366 */, unchecked((long) 0xC7BEEB7909AC2D97L) /* 367 */, + unchecked((long) 0xC3E58D353BC5D757L) /* 368 */, unchecked((long) 0xEB017892F38F61E8L) /* 369 */, + unchecked((long) 0xD4EFFB9C9B1CC21AL) /* 370 */, unchecked((long) 0x99727D26F494F7ABL) /* 371 */, + unchecked((long) 0xA3E063A2956B3E03L) /* 372 */, unchecked((long) 0x9D4A8B9A4AA09C30L) /* 373 */, + unchecked((long) 0x3F6AB7D500090FB4L) /* 374 */, unchecked((long) 0x9CC0F2A057268AC0L) /* 375 */, + unchecked((long) 0x3DEE9D2DEDBF42D1L) /* 376 */, unchecked((long) 0x330F49C87960A972L) /* 377 */, + unchecked((long) 0xC6B2720287421B41L) /* 378 */, unchecked((long) 0x0AC59EC07C00369CL) /* 379 */, + unchecked((long) 0xEF4EAC49CB353425L) /* 380 */, unchecked((long) 0xF450244EEF0129D8L) /* 381 */, + unchecked((long) 0x8ACC46E5CAF4DEB6L) /* 382 */, unchecked((long) 0x2FFEAB63989263F7L) /* 383 */, + unchecked((long) 0x8F7CB9FE5D7A4578L) /* 384 */, unchecked((long) 0x5BD8F7644E634635L) /* 385 */, + unchecked((long) 0x427A7315BF2DC900L) /* 386 */, unchecked((long) 0x17D0C4AA2125261CL) /* 387 */, + unchecked((long) 0x3992486C93518E50L) /* 388 */, unchecked((long) 0xB4CBFEE0A2D7D4C3L) /* 389 */, + unchecked((long) 0x7C75D6202C5DDD8DL) /* 390 */, unchecked((long) 0xDBC295D8E35B6C61L) /* 391 */, + unchecked((long) 0x60B369D302032B19L) /* 392 */, unchecked((long) 0xCE42685FDCE44132L) /* 393 */, + unchecked((long) 0x06F3DDB9DDF65610L) /* 394 */, unchecked((long) 0x8EA4D21DB5E148F0L) /* 395 */, + unchecked((long) 0x20B0FCE62FCD496FL) /* 396 */, unchecked((long) 0x2C1B912358B0EE31L) /* 397 */, + unchecked((long) 0xB28317B818F5A308L) /* 398 */, unchecked((long) 0xA89C1E189CA6D2CFL) /* 399 */, + unchecked((long) 0x0C6B18576AAADBC8L) /* 400 */, unchecked((long) 0xB65DEAA91299FAE3L) /* 401 */, + unchecked((long) 0xFB2B794B7F1027E7L) /* 402 */, unchecked((long) 0x04E4317F443B5BEBL) /* 403 */, + unchecked((long) 0x4B852D325939D0A6L) /* 404 */, unchecked((long) 0xD5AE6BEEFB207FFCL) /* 405 */, + unchecked((long) 0x309682B281C7D374L) /* 406 */, unchecked((long) 0xBAE309A194C3B475L) /* 407 */, + unchecked((long) 0x8CC3F97B13B49F05L) /* 408 */, unchecked((long) 0x98A9422FF8293967L) /* 409 */, + unchecked((long) 0x244B16B01076FF7CL) /* 410 */, unchecked((long) 0xF8BF571C663D67EEL) /* 411 */, + unchecked((long) 0x1F0D6758EEE30DA1L) /* 412 */, unchecked((long) 0xC9B611D97ADEB9B7L) /* 413 */, + unchecked((long) 0xB7AFD5887B6C57A2L) /* 414 */, unchecked((long) 0x6290AE846B984FE1L) /* 415 */, + unchecked((long) 0x94DF4CDEACC1A5FDL) /* 416 */, unchecked((long) 0x058A5BD1C5483AFFL) /* 417 */, + unchecked((long) 0x63166CC142BA3C37L) /* 418 */, unchecked((long) 0x8DB8526EB2F76F40L) /* 419 */, + unchecked((long) 0xE10880036F0D6D4EL) /* 420 */, unchecked((long) 0x9E0523C9971D311DL) /* 421 */, + unchecked((long) 0x45EC2824CC7CD691L) /* 422 */, unchecked((long) 0x575B8359E62382C9L) /* 423 */, + unchecked((long) 0xFA9E400DC4889995L) /* 424 */, unchecked((long) 0xD1823ECB45721568L) /* 425 */, + unchecked((long) 0xDAFD983B8206082FL) /* 426 */, unchecked((long) 0xAA7D29082386A8CBL) /* 427 */, + unchecked((long) 0x269FCD4403B87588L) /* 428 */, unchecked((long) 0x1B91F5F728BDD1E0L) /* 429 */, + unchecked((long) 0xE4669F39040201F6L) /* 430 */, unchecked((long) 0x7A1D7C218CF04ADEL) /* 431 */, + unchecked((long) 0x65623C29D79CE5CEL) /* 432 */, unchecked((long) 0x2368449096C00BB1L) /* 433 */, + unchecked((long) 0xAB9BF1879DA503BAL) /* 434 */, unchecked((long) 0xBC23ECB1A458058EL) /* 435 */, + unchecked((long) 0x9A58DF01BB401ECCL) /* 436 */, unchecked((long) 0xA070E868A85F143DL) /* 437 */, + unchecked((long) 0x4FF188307DF2239EL) /* 438 */, unchecked((long) 0x14D565B41A641183L) /* 439 */, + unchecked((long) 0xEE13337452701602L) /* 440 */, unchecked((long) 0x950E3DCF3F285E09L) /* 441 */, + unchecked((long) 0x59930254B9C80953L) /* 442 */, unchecked((long) 0x3BF299408930DA6DL) /* 443 */, + unchecked((long) 0xA955943F53691387L) /* 444 */, unchecked((long) 0xA15EDECAA9CB8784L) /* 445 */, + unchecked((long) 0x29142127352BE9A0L) /* 446 */, unchecked((long) 0x76F0371FFF4E7AFBL) /* 447 */, + unchecked((long) 0x0239F450274F2228L) /* 448 */, unchecked((long) 0xBB073AF01D5E868BL) /* 449 */, + unchecked((long) 0xBFC80571C10E96C1L) /* 450 */, unchecked((long) 0xD267088568222E23L) /* 451 */, + unchecked((long) 0x9671A3D48E80B5B0L) /* 452 */, unchecked((long) 0x55B5D38AE193BB81L) /* 453 */, + unchecked((long) 0x693AE2D0A18B04B8L) /* 454 */, unchecked((long) 0x5C48B4ECADD5335FL) /* 455 */, + unchecked((long) 0xFD743B194916A1CAL) /* 456 */, unchecked((long) 0x2577018134BE98C4L) /* 457 */, + unchecked((long) 0xE77987E83C54A4ADL) /* 458 */, unchecked((long) 0x28E11014DA33E1B9L) /* 459 */, + unchecked((long) 0x270CC59E226AA213L) /* 460 */, unchecked((long) 0x71495F756D1A5F60L) /* 461 */, + unchecked((long) 0x9BE853FB60AFEF77L) /* 462 */, unchecked((long) 0xADC786A7F7443DBFL) /* 463 */, + unchecked((long) 0x0904456173B29A82L) /* 464 */, unchecked((long) 0x58BC7A66C232BD5EL) /* 465 */, + unchecked((long) 0xF306558C673AC8B2L) /* 466 */, unchecked((long) 0x41F639C6B6C9772AL) /* 467 */, + unchecked((long) 0x216DEFE99FDA35DAL) /* 468 */, unchecked((long) 0x11640CC71C7BE615L) /* 469 */, + unchecked((long) 0x93C43694565C5527L) /* 470 */, unchecked((long) 0xEA038E6246777839L) /* 471 */, + unchecked((long) 0xF9ABF3CE5A3E2469L) /* 472 */, unchecked((long) 0x741E768D0FD312D2L) /* 473 */, + unchecked((long) 0x0144B883CED652C6L) /* 474 */, unchecked((long) 0xC20B5A5BA33F8552L) /* 475 */, + unchecked((long) 0x1AE69633C3435A9DL) /* 476 */, unchecked((long) 0x97A28CA4088CFDECL) /* 477 */, + unchecked((long) 0x8824A43C1E96F420L) /* 478 */, unchecked((long) 0x37612FA66EEEA746L) /* 479 */, + unchecked((long) 0x6B4CB165F9CF0E5AL) /* 480 */, unchecked((long) 0x43AA1C06A0ABFB4AL) /* 481 */, + unchecked((long) 0x7F4DC26FF162796BL) /* 482 */, unchecked((long) 0x6CBACC8E54ED9B0FL) /* 483 */, + unchecked((long) 0xA6B7FFEFD2BB253EL) /* 484 */, unchecked((long) 0x2E25BC95B0A29D4FL) /* 485 */, + unchecked((long) 0x86D6A58BDEF1388CL) /* 486 */, unchecked((long) 0xDED74AC576B6F054L) /* 487 */, + unchecked((long) 0x8030BDBC2B45805DL) /* 488 */, unchecked((long) 0x3C81AF70E94D9289L) /* 489 */, + unchecked((long) 0x3EFF6DDA9E3100DBL) /* 490 */, unchecked((long) 0xB38DC39FDFCC8847L) /* 491 */, + unchecked((long) 0x123885528D17B87EL) /* 492 */, unchecked((long) 0xF2DA0ED240B1B642L) /* 493 */, + unchecked((long) 0x44CEFADCD54BF9A9L) /* 494 */, unchecked((long) 0x1312200E433C7EE6L) /* 495 */, + unchecked((long) 0x9FFCC84F3A78C748L) /* 496 */, unchecked((long) 0xF0CD1F72248576BBL) /* 497 */, + unchecked((long) 0xEC6974053638CFE4L) /* 498 */, unchecked((long) 0x2BA7B67C0CEC4E4CL) /* 499 */, + unchecked((long) 0xAC2F4DF3E5CE32EDL) /* 500 */, unchecked((long) 0xCB33D14326EA4C11L) /* 501 */, + unchecked((long) 0xA4E9044CC77E58BCL) /* 502 */, unchecked((long) 0x5F513293D934FCEFL) /* 503 */, + unchecked((long) 0x5DC9645506E55444L) /* 504 */, unchecked((long) 0x50DE418F317DE40AL) /* 505 */, + unchecked((long) 0x388CB31A69DDE259L) /* 506 */, unchecked((long) 0x2DB4A83455820A86L) /* 507 */, + unchecked((long) 0x9010A91E84711AE9L) /* 508 */, unchecked((long) 0x4DF7F0B7B1498371L) /* 509 */, + unchecked((long) 0xD62A2EABC0977179L) /* 510 */, unchecked((long) 0x22FAC097AA8D5C0EL) /* 511 */, + }; + + private static readonly long[] t3 = { + unchecked((long) 0xF49FCC2FF1DAF39BL) /* 512 */, unchecked((long) 0x487FD5C66FF29281L) /* 513 */, + unchecked((long) 0xE8A30667FCDCA83FL) /* 514 */, unchecked((long) 0x2C9B4BE3D2FCCE63L) /* 515 */, + unchecked((long) 0xDA3FF74B93FBBBC2L) /* 516 */, unchecked((long) 0x2FA165D2FE70BA66L) /* 517 */, + unchecked((long) 0xA103E279970E93D4L) /* 518 */, unchecked((long) 0xBECDEC77B0E45E71L) /* 519 */, + unchecked((long) 0xCFB41E723985E497L) /* 520 */, unchecked((long) 0xB70AAA025EF75017L) /* 521 */, + unchecked((long) 0xD42309F03840B8E0L) /* 522 */, unchecked((long) 0x8EFC1AD035898579L) /* 523 */, + unchecked((long) 0x96C6920BE2B2ABC5L) /* 524 */, unchecked((long) 0x66AF4163375A9172L) /* 525 */, + unchecked((long) 0x2174ABDCCA7127FBL) /* 526 */, unchecked((long) 0xB33CCEA64A72FF41L) /* 527 */, + unchecked((long) 0xF04A4933083066A5L) /* 528 */, unchecked((long) 0x8D970ACDD7289AF5L) /* 529 */, + unchecked((long) 0x8F96E8E031C8C25EL) /* 530 */, unchecked((long) 0xF3FEC02276875D47L) /* 531 */, + unchecked((long) 0xEC7BF310056190DDL) /* 532 */, unchecked((long) 0xF5ADB0AEBB0F1491L) /* 533 */, + unchecked((long) 0x9B50F8850FD58892L) /* 534 */, unchecked((long) 0x4975488358B74DE8L) /* 535 */, + unchecked((long) 0xA3354FF691531C61L) /* 536 */, unchecked((long) 0x0702BBE481D2C6EEL) /* 537 */, + unchecked((long) 0x89FB24057DEDED98L) /* 538 */, unchecked((long) 0xAC3075138596E902L) /* 539 */, + unchecked((long) 0x1D2D3580172772EDL) /* 540 */, unchecked((long) 0xEB738FC28E6BC30DL) /* 541 */, + unchecked((long) 0x5854EF8F63044326L) /* 542 */, unchecked((long) 0x9E5C52325ADD3BBEL) /* 543 */, + unchecked((long) 0x90AA53CF325C4623L) /* 544 */, unchecked((long) 0xC1D24D51349DD067L) /* 545 */, + unchecked((long) 0x2051CFEEA69EA624L) /* 546 */, unchecked((long) 0x13220F0A862E7E4FL) /* 547 */, + unchecked((long) 0xCE39399404E04864L) /* 548 */, unchecked((long) 0xD9C42CA47086FCB7L) /* 549 */, + unchecked((long) 0x685AD2238A03E7CCL) /* 550 */, unchecked((long) 0x066484B2AB2FF1DBL) /* 551 */, + unchecked((long) 0xFE9D5D70EFBF79ECL) /* 552 */, unchecked((long) 0x5B13B9DD9C481854L) /* 553 */, + unchecked((long) 0x15F0D475ED1509ADL) /* 554 */, unchecked((long) 0x0BEBCD060EC79851L) /* 555 */, + unchecked((long) 0xD58C6791183AB7F8L) /* 556 */, unchecked((long) 0xD1187C5052F3EEE4L) /* 557 */, + unchecked((long) 0xC95D1192E54E82FFL) /* 558 */, unchecked((long) 0x86EEA14CB9AC6CA2L) /* 559 */, + unchecked((long) 0x3485BEB153677D5DL) /* 560 */, unchecked((long) 0xDD191D781F8C492AL) /* 561 */, + unchecked((long) 0xF60866BAA784EBF9L) /* 562 */, unchecked((long) 0x518F643BA2D08C74L) /* 563 */, + unchecked((long) 0x8852E956E1087C22L) /* 564 */, unchecked((long) 0xA768CB8DC410AE8DL) /* 565 */, + unchecked((long) 0x38047726BFEC8E1AL) /* 566 */, unchecked((long) 0xA67738B4CD3B45AAL) /* 567 */, + unchecked((long) 0xAD16691CEC0DDE19L) /* 568 */, unchecked((long) 0xC6D4319380462E07L) /* 569 */, + unchecked((long) 0xC5A5876D0BA61938L) /* 570 */, unchecked((long) 0x16B9FA1FA58FD840L) /* 571 */, + unchecked((long) 0x188AB1173CA74F18L) /* 572 */, unchecked((long) 0xABDA2F98C99C021FL) /* 573 */, + unchecked((long) 0x3E0580AB134AE816L) /* 574 */, unchecked((long) 0x5F3B05B773645ABBL) /* 575 */, + unchecked((long) 0x2501A2BE5575F2F6L) /* 576 */, unchecked((long) 0x1B2F74004E7E8BA9L) /* 577 */, + unchecked((long) 0x1CD7580371E8D953L) /* 578 */, unchecked((long) 0x7F6ED89562764E30L) /* 579 */, + unchecked((long) 0xB15926FF596F003DL) /* 580 */, unchecked((long) 0x9F65293DA8C5D6B9L) /* 581 */, + unchecked((long) 0x6ECEF04DD690F84CL) /* 582 */, unchecked((long) 0x4782275FFF33AF88L) /* 583 */, + unchecked((long) 0xE41433083F820801L) /* 584 */, unchecked((long) 0xFD0DFE409A1AF9B5L) /* 585 */, + unchecked((long) 0x4325A3342CDB396BL) /* 586 */, unchecked((long) 0x8AE77E62B301B252L) /* 587 */, + unchecked((long) 0xC36F9E9F6655615AL) /* 588 */, unchecked((long) 0x85455A2D92D32C09L) /* 589 */, + unchecked((long) 0xF2C7DEA949477485L) /* 590 */, unchecked((long) 0x63CFB4C133A39EBAL) /* 591 */, + unchecked((long) 0x83B040CC6EBC5462L) /* 592 */, unchecked((long) 0x3B9454C8FDB326B0L) /* 593 */, + unchecked((long) 0x56F56A9E87FFD78CL) /* 594 */, unchecked((long) 0x2DC2940D99F42BC6L) /* 595 */, + unchecked((long) 0x98F7DF096B096E2DL) /* 596 */, unchecked((long) 0x19A6E01E3AD852BFL) /* 597 */, + unchecked((long) 0x42A99CCBDBD4B40BL) /* 598 */, unchecked((long) 0xA59998AF45E9C559L) /* 599 */, + unchecked((long) 0x366295E807D93186L) /* 600 */, unchecked((long) 0x6B48181BFAA1F773L) /* 601 */, + unchecked((long) 0x1FEC57E2157A0A1DL) /* 602 */, unchecked((long) 0x4667446AF6201AD5L) /* 603 */, + unchecked((long) 0xE615EBCACFB0F075L) /* 604 */, unchecked((long) 0xB8F31F4F68290778L) /* 605 */, + unchecked((long) 0x22713ED6CE22D11EL) /* 606 */, unchecked((long) 0x3057C1A72EC3C93BL) /* 607 */, + unchecked((long) 0xCB46ACC37C3F1F2FL) /* 608 */, unchecked((long) 0xDBB893FD02AAF50EL) /* 609 */, + unchecked((long) 0x331FD92E600B9FCFL) /* 610 */, unchecked((long) 0xA498F96148EA3AD6L) /* 611 */, + unchecked((long) 0xA8D8426E8B6A83EAL) /* 612 */, unchecked((long) 0xA089B274B7735CDCL) /* 613 */, + unchecked((long) 0x87F6B3731E524A11L) /* 614 */, unchecked((long) 0x118808E5CBC96749L) /* 615 */, + unchecked((long) 0x9906E4C7B19BD394L) /* 616 */, unchecked((long) 0xAFED7F7E9B24A20CL) /* 617 */, + unchecked((long) 0x6509EADEEB3644A7L) /* 618 */, unchecked((long) 0x6C1EF1D3E8EF0EDEL) /* 619 */, + unchecked((long) 0xB9C97D43E9798FB4L) /* 620 */, unchecked((long) 0xA2F2D784740C28A3L) /* 621 */, + unchecked((long) 0x7B8496476197566FL) /* 622 */, unchecked((long) 0x7A5BE3E6B65F069DL) /* 623 */, + unchecked((long) 0xF96330ED78BE6F10L) /* 624 */, unchecked((long) 0xEEE60DE77A076A15L) /* 625 */, + unchecked((long) 0x2B4BEE4AA08B9BD0L) /* 626 */, unchecked((long) 0x6A56A63EC7B8894EL) /* 627 */, + unchecked((long) 0x02121359BA34FEF4L) /* 628 */, unchecked((long) 0x4CBF99F8283703FCL) /* 629 */, + unchecked((long) 0x398071350CAF30C8L) /* 630 */, unchecked((long) 0xD0A77A89F017687AL) /* 631 */, + unchecked((long) 0xF1C1A9EB9E423569L) /* 632 */, unchecked((long) 0x8C7976282DEE8199L) /* 633 */, + unchecked((long) 0x5D1737A5DD1F7ABDL) /* 634 */, unchecked((long) 0x4F53433C09A9FA80L) /* 635 */, + unchecked((long) 0xFA8B0C53DF7CA1D9L) /* 636 */, unchecked((long) 0x3FD9DCBC886CCB77L) /* 637 */, + unchecked((long) 0xC040917CA91B4720L) /* 638 */, unchecked((long) 0x7DD00142F9D1DCDFL) /* 639 */, + unchecked((long) 0x8476FC1D4F387B58L) /* 640 */, unchecked((long) 0x23F8E7C5F3316503L) /* 641 */, + unchecked((long) 0x032A2244E7E37339L) /* 642 */, unchecked((long) 0x5C87A5D750F5A74BL) /* 643 */, + unchecked((long) 0x082B4CC43698992EL) /* 644 */, unchecked((long) 0xDF917BECB858F63CL) /* 645 */, + unchecked((long) 0x3270B8FC5BF86DDAL) /* 646 */, unchecked((long) 0x10AE72BB29B5DD76L) /* 647 */, + unchecked((long) 0x576AC94E7700362BL) /* 648 */, unchecked((long) 0x1AD112DAC61EFB8FL) /* 649 */, + unchecked((long) 0x691BC30EC5FAA427L) /* 650 */, unchecked((long) 0xFF246311CC327143L) /* 651 */, + unchecked((long) 0x3142368E30E53206L) /* 652 */, unchecked((long) 0x71380E31E02CA396L) /* 653 */, + unchecked((long) 0x958D5C960AAD76F1L) /* 654 */, unchecked((long) 0xF8D6F430C16DA536L) /* 655 */, + unchecked((long) 0xC8FFD13F1BE7E1D2L) /* 656 */, unchecked((long) 0x7578AE66004DDBE1L) /* 657 */, + unchecked((long) 0x05833F01067BE646L) /* 658 */, unchecked((long) 0xBB34B5AD3BFE586DL) /* 659 */, + unchecked((long) 0x095F34C9A12B97F0L) /* 660 */, unchecked((long) 0x247AB64525D60CA8L) /* 661 */, + unchecked((long) 0xDCDBC6F3017477D1L) /* 662 */, unchecked((long) 0x4A2E14D4DECAD24DL) /* 663 */, + unchecked((long) 0xBDB5E6D9BE0A1EEBL) /* 664 */, unchecked((long) 0x2A7E70F7794301ABL) /* 665 */, + unchecked((long) 0xDEF42D8A270540FDL) /* 666 */, unchecked((long) 0x01078EC0A34C22C1L) /* 667 */, + unchecked((long) 0xE5DE511AF4C16387L) /* 668 */, unchecked((long) 0x7EBB3A52BD9A330AL) /* 669 */, + unchecked((long) 0x77697857AA7D6435L) /* 670 */, unchecked((long) 0x004E831603AE4C32L) /* 671 */, + unchecked((long) 0xE7A21020AD78E312L) /* 672 */, unchecked((long) 0x9D41A70C6AB420F2L) /* 673 */, + unchecked((long) 0x28E06C18EA1141E6L) /* 674 */, unchecked((long) 0xD2B28CBD984F6B28L) /* 675 */, + unchecked((long) 0x26B75F6C446E9D83L) /* 676 */, unchecked((long) 0xBA47568C4D418D7FL) /* 677 */, + unchecked((long) 0xD80BADBFE6183D8EL) /* 678 */, unchecked((long) 0x0E206D7F5F166044L) /* 679 */, + unchecked((long) 0xE258A43911CBCA3EL) /* 680 */, unchecked((long) 0x723A1746B21DC0BCL) /* 681 */, + unchecked((long) 0xC7CAA854F5D7CDD3L) /* 682 */, unchecked((long) 0x7CAC32883D261D9CL) /* 683 */, + unchecked((long) 0x7690C26423BA942CL) /* 684 */, unchecked((long) 0x17E55524478042B8L) /* 685 */, + unchecked((long) 0xE0BE477656A2389FL) /* 686 */, unchecked((long) 0x4D289B5E67AB2DA0L) /* 687 */, + unchecked((long) 0x44862B9C8FBBFD31L) /* 688 */, unchecked((long) 0xB47CC8049D141365L) /* 689 */, + unchecked((long) 0x822C1B362B91C793L) /* 690 */, unchecked((long) 0x4EB14655FB13DFD8L) /* 691 */, + unchecked((long) 0x1ECBBA0714E2A97BL) /* 692 */, unchecked((long) 0x6143459D5CDE5F14L) /* 693 */, + unchecked((long) 0x53A8FBF1D5F0AC89L) /* 694 */, unchecked((long) 0x97EA04D81C5E5B00L) /* 695 */, + unchecked((long) 0x622181A8D4FDB3F3L) /* 696 */, unchecked((long) 0xE9BCD341572A1208L) /* 697 */, + unchecked((long) 0x1411258643CCE58AL) /* 698 */, unchecked((long) 0x9144C5FEA4C6E0A4L) /* 699 */, + unchecked((long) 0x0D33D06565CF620FL) /* 700 */, unchecked((long) 0x54A48D489F219CA1L) /* 701 */, + unchecked((long) 0xC43E5EAC6D63C821L) /* 702 */, unchecked((long) 0xA9728B3A72770DAFL) /* 703 */, + unchecked((long) 0xD7934E7B20DF87EFL) /* 704 */, unchecked((long) 0xE35503B61A3E86E5L) /* 705 */, + unchecked((long) 0xCAE321FBC819D504L) /* 706 */, unchecked((long) 0x129A50B3AC60BFA6L) /* 707 */, + unchecked((long) 0xCD5E68EA7E9FB6C3L) /* 708 */, unchecked((long) 0xB01C90199483B1C7L) /* 709 */, + unchecked((long) 0x3DE93CD5C295376CL) /* 710 */, unchecked((long) 0xAED52EDF2AB9AD13L) /* 711 */, + unchecked((long) 0x2E60F512C0A07884L) /* 712 */, unchecked((long) 0xBC3D86A3E36210C9L) /* 713 */, + unchecked((long) 0x35269D9B163951CEL) /* 714 */, unchecked((long) 0x0C7D6E2AD0CDB5FAL) /* 715 */, + unchecked((long) 0x59E86297D87F5733L) /* 716 */, unchecked((long) 0x298EF221898DB0E7L) /* 717 */, + unchecked((long) 0x55000029D1A5AA7EL) /* 718 */, unchecked((long) 0x8BC08AE1B5061B45L) /* 719 */, + unchecked((long) 0xC2C31C2B6C92703AL) /* 720 */, unchecked((long) 0x94CC596BAF25EF42L) /* 721 */, + unchecked((long) 0x0A1D73DB22540456L) /* 722 */, unchecked((long) 0x04B6A0F9D9C4179AL) /* 723 */, + unchecked((long) 0xEFFDAFA2AE3D3C60L) /* 724 */, unchecked((long) 0xF7C8075BB49496C4L) /* 725 */, + unchecked((long) 0x9CC5C7141D1CD4E3L) /* 726 */, unchecked((long) 0x78BD1638218E5534L) /* 727 */, + unchecked((long) 0xB2F11568F850246AL) /* 728 */, unchecked((long) 0xEDFABCFA9502BC29L) /* 729 */, + unchecked((long) 0x796CE5F2DA23051BL) /* 730 */, unchecked((long) 0xAAE128B0DC93537CL) /* 731 */, + unchecked((long) 0x3A493DA0EE4B29AEL) /* 732 */, unchecked((long) 0xB5DF6B2C416895D7L) /* 733 */, + unchecked((long) 0xFCABBD25122D7F37L) /* 734 */, unchecked((long) 0x70810B58105DC4B1L) /* 735 */, + unchecked((long) 0xE10FDD37F7882A90L) /* 736 */, unchecked((long) 0x524DCAB5518A3F5CL) /* 737 */, + unchecked((long) 0x3C9E85878451255BL) /* 738 */, unchecked((long) 0x4029828119BD34E2L) /* 739 */, + unchecked((long) 0x74A05B6F5D3CECCBL) /* 740 */, unchecked((long) 0xB610021542E13ECAL) /* 741 */, + unchecked((long) 0x0FF979D12F59E2ACL) /* 742 */, unchecked((long) 0x6037DA27E4F9CC50L) /* 743 */, + unchecked((long) 0x5E92975A0DF1847DL) /* 744 */, unchecked((long) 0xD66DE190D3E623FEL) /* 745 */, + unchecked((long) 0x5032D6B87B568048L) /* 746 */, unchecked((long) 0x9A36B7CE8235216EL) /* 747 */, + unchecked((long) 0x80272A7A24F64B4AL) /* 748 */, unchecked((long) 0x93EFED8B8C6916F7L) /* 749 */, + unchecked((long) 0x37DDBFF44CCE1555L) /* 750 */, unchecked((long) 0x4B95DB5D4B99BD25L) /* 751 */, + unchecked((long) 0x92D3FDA169812FC0L) /* 752 */, unchecked((long) 0xFB1A4A9A90660BB6L) /* 753 */, + unchecked((long) 0x730C196946A4B9B2L) /* 754 */, unchecked((long) 0x81E289AA7F49DA68L) /* 755 */, + unchecked((long) 0x64669A0F83B1A05FL) /* 756 */, unchecked((long) 0x27B3FF7D9644F48BL) /* 757 */, + unchecked((long) 0xCC6B615C8DB675B3L) /* 758 */, unchecked((long) 0x674F20B9BCEBBE95L) /* 759 */, + unchecked((long) 0x6F31238275655982L) /* 760 */, unchecked((long) 0x5AE488713E45CF05L) /* 761 */, + unchecked((long) 0xBF619F9954C21157L) /* 762 */, unchecked((long) 0xEABAC46040A8EAE9L) /* 763 */, + unchecked((long) 0x454C6FE9F2C0C1CDL) /* 764 */, unchecked((long) 0x419CF6496412691CL) /* 765 */, + unchecked((long) 0xD3DC3BEF265B0F70L) /* 766 */, unchecked((long) 0x6D0E60F5C3578A9EL) /* 767 */, + }; + + private static readonly long[] t4 = { + unchecked((long) 0x5B0E608526323C55L) /* 768 */, unchecked((long) 0x1A46C1A9FA1B59F5L) /* 769 */, + unchecked((long) 0xA9E245A17C4C8FFAL) /* 770 */, unchecked((long) 0x65CA5159DB2955D7L) /* 771 */, + unchecked((long) 0x05DB0A76CE35AFC2L) /* 772 */, unchecked((long) 0x81EAC77EA9113D45L) /* 773 */, + unchecked((long) 0x528EF88AB6AC0A0DL) /* 774 */, unchecked((long) 0xA09EA253597BE3FFL) /* 775 */, + unchecked((long) 0x430DDFB3AC48CD56L) /* 776 */, unchecked((long) 0xC4B3A67AF45CE46FL) /* 777 */, + unchecked((long) 0x4ECECFD8FBE2D05EL) /* 778 */, unchecked((long) 0x3EF56F10B39935F0L) /* 779 */, + unchecked((long) 0x0B22D6829CD619C6L) /* 780 */, unchecked((long) 0x17FD460A74DF2069L) /* 781 */, + unchecked((long) 0x6CF8CC8E8510ED40L) /* 782 */, unchecked((long) 0xD6C824BF3A6ECAA7L) /* 783 */, + unchecked((long) 0x61243D581A817049L) /* 784 */, unchecked((long) 0x048BACB6BBC163A2L) /* 785 */, + unchecked((long) 0xD9A38AC27D44CC32L) /* 786 */, unchecked((long) 0x7FDDFF5BAAF410ABL) /* 787 */, + unchecked((long) 0xAD6D495AA804824BL) /* 788 */, unchecked((long) 0xE1A6A74F2D8C9F94L) /* 789 */, + unchecked((long) 0xD4F7851235DEE8E3L) /* 790 */, unchecked((long) 0xFD4B7F886540D893L) /* 791 */, + unchecked((long) 0x247C20042AA4BFDAL) /* 792 */, unchecked((long) 0x096EA1C517D1327CL) /* 793 */, + unchecked((long) 0xD56966B4361A6685L) /* 794 */, unchecked((long) 0x277DA5C31221057DL) /* 795 */, + unchecked((long) 0x94D59893A43ACFF7L) /* 796 */, unchecked((long) 0x64F0C51CCDC02281L) /* 797 */, + unchecked((long) 0x3D33BCC4FF6189DBL) /* 798 */, unchecked((long) 0xE005CB184CE66AF1L) /* 799 */, + unchecked((long) 0xFF5CCD1D1DB99BEAL) /* 800 */, unchecked((long) 0xB0B854A7FE42980FL) /* 801 */, + unchecked((long) 0x7BD46A6A718D4B9FL) /* 802 */, unchecked((long) 0xD10FA8CC22A5FD8CL) /* 803 */, + unchecked((long) 0xD31484952BE4BD31L) /* 804 */, unchecked((long) 0xC7FA975FCB243847L) /* 805 */, + unchecked((long) 0x4886ED1E5846C407L) /* 806 */, unchecked((long) 0x28CDDB791EB70B04L) /* 807 */, + unchecked((long) 0xC2B00BE2F573417FL) /* 808 */, unchecked((long) 0x5C9590452180F877L) /* 809 */, + unchecked((long) 0x7A6BDDFFF370EB00L) /* 810 */, unchecked((long) 0xCE509E38D6D9D6A4L) /* 811 */, + unchecked((long) 0xEBEB0F00647FA702L) /* 812 */, unchecked((long) 0x1DCC06CF76606F06L) /* 813 */, + unchecked((long) 0xE4D9F28BA286FF0AL) /* 814 */, unchecked((long) 0xD85A305DC918C262L) /* 815 */, + unchecked((long) 0x475B1D8732225F54L) /* 816 */, unchecked((long) 0x2D4FB51668CCB5FEL) /* 817 */, + unchecked((long) 0xA679B9D9D72BBA20L) /* 818 */, unchecked((long) 0x53841C0D912D43A5L) /* 819 */, + unchecked((long) 0x3B7EAA48BF12A4E8L) /* 820 */, unchecked((long) 0x781E0E47F22F1DDFL) /* 821 */, + unchecked((long) 0xEFF20CE60AB50973L) /* 822 */, unchecked((long) 0x20D261D19DFFB742L) /* 823 */, + unchecked((long) 0x16A12B03062A2E39L) /* 824 */, unchecked((long) 0x1960EB2239650495L) /* 825 */, + unchecked((long) 0x251C16FED50EB8B8L) /* 826 */, unchecked((long) 0x9AC0C330F826016EL) /* 827 */, + unchecked((long) 0xED152665953E7671L) /* 828 */, unchecked((long) 0x02D63194A6369570L) /* 829 */, + unchecked((long) 0x5074F08394B1C987L) /* 830 */, unchecked((long) 0x70BA598C90B25CE1L) /* 831 */, + unchecked((long) 0x794A15810B9742F6L) /* 832 */, unchecked((long) 0x0D5925E9FCAF8C6CL) /* 833 */, + unchecked((long) 0x3067716CD868744EL) /* 834 */, unchecked((long) 0x910AB077E8D7731BL) /* 835 */, + unchecked((long) 0x6A61BBDB5AC42F61L) /* 836 */, unchecked((long) 0x93513EFBF0851567L) /* 837 */, + unchecked((long) 0xF494724B9E83E9D5L) /* 838 */, unchecked((long) 0xE887E1985C09648DL) /* 839 */, + unchecked((long) 0x34B1D3C675370CFDL) /* 840 */, unchecked((long) 0xDC35E433BC0D255DL) /* 841 */, + unchecked((long) 0xD0AAB84234131BE0L) /* 842 */, unchecked((long) 0x08042A50B48B7EAFL) /* 843 */, + unchecked((long) 0x9997C4EE44A3AB35L) /* 844 */, unchecked((long) 0x829A7B49201799D0L) /* 845 */, + unchecked((long) 0x263B8307B7C54441L) /* 846 */, unchecked((long) 0x752F95F4FD6A6CA6L) /* 847 */, + unchecked((long) 0x927217402C08C6E5L) /* 848 */, unchecked((long) 0x2A8AB754A795D9EEL) /* 849 */, + unchecked((long) 0xA442F7552F72943DL) /* 850 */, unchecked((long) 0x2C31334E19781208L) /* 851 */, + unchecked((long) 0x4FA98D7CEAEE6291L) /* 852 */, unchecked((long) 0x55C3862F665DB309L) /* 853 */, + unchecked((long) 0xBD0610175D53B1F3L) /* 854 */, unchecked((long) 0x46FE6CB840413F27L) /* 855 */, + unchecked((long) 0x3FE03792DF0CFA59L) /* 856 */, unchecked((long) 0xCFE700372EB85E8FL) /* 857 */, + unchecked((long) 0xA7BE29E7ADBCE118L) /* 858 */, unchecked((long) 0xE544EE5CDE8431DDL) /* 859 */, + unchecked((long) 0x8A781B1B41F1873EL) /* 860 */, unchecked((long) 0xA5C94C78A0D2F0E7L) /* 861 */, + unchecked((long) 0x39412E2877B60728L) /* 862 */, unchecked((long) 0xA1265EF3AFC9A62CL) /* 863 */, + unchecked((long) 0xBCC2770C6A2506C5L) /* 864 */, unchecked((long) 0x3AB66DD5DCE1CE12L) /* 865 */, + unchecked((long) 0xE65499D04A675B37L) /* 866 */, unchecked((long) 0x7D8F523481BFD216L) /* 867 */, + unchecked((long) 0x0F6F64FCEC15F389L) /* 868 */, unchecked((long) 0x74EFBE618B5B13C8L) /* 869 */, + unchecked((long) 0xACDC82B714273E1DL) /* 870 */, unchecked((long) 0xDD40BFE003199D17L) /* 871 */, + unchecked((long) 0x37E99257E7E061F8L) /* 872 */, unchecked((long) 0xFA52626904775AAAL) /* 873 */, + unchecked((long) 0x8BBBF63A463D56F9L) /* 874 */, unchecked((long) 0xF0013F1543A26E64L) /* 875 */, + unchecked((long) 0xA8307E9F879EC898L) /* 876 */, unchecked((long) 0xCC4C27A4150177CCL) /* 877 */, + unchecked((long) 0x1B432F2CCA1D3348L) /* 878 */, unchecked((long) 0xDE1D1F8F9F6FA013L) /* 879 */, + unchecked((long) 0x606602A047A7DDD6L) /* 880 */, unchecked((long) 0xD237AB64CC1CB2C7L) /* 881 */, + unchecked((long) 0x9B938E7225FCD1D3L) /* 882 */, unchecked((long) 0xEC4E03708E0FF476L) /* 883 */, + unchecked((long) 0xFEB2FBDA3D03C12DL) /* 884 */, unchecked((long) 0xAE0BCED2EE43889AL) /* 885 */, + unchecked((long) 0x22CB8923EBFB4F43L) /* 886 */, unchecked((long) 0x69360D013CF7396DL) /* 887 */, + unchecked((long) 0x855E3602D2D4E022L) /* 888 */, unchecked((long) 0x073805BAD01F784CL) /* 889 */, + unchecked((long) 0x33E17A133852F546L) /* 890 */, unchecked((long) 0xDF4874058AC7B638L) /* 891 */, + unchecked((long) 0xBA92B29C678AA14AL) /* 892 */, unchecked((long) 0x0CE89FC76CFAADCDL) /* 893 */, + unchecked((long) 0x5F9D4E0908339E34L) /* 894 */, unchecked((long) 0xF1AFE9291F5923B9L) /* 895 */, + unchecked((long) 0x6E3480F60F4A265FL) /* 896 */, unchecked((long) 0xEEBF3A2AB29B841CL) /* 897 */, + unchecked((long) 0xE21938A88F91B4ADL) /* 898 */, unchecked((long) 0x57DFEFF845C6D3C3L) /* 899 */, + unchecked((long) 0x2F006B0BF62CAAF2L) /* 900 */, unchecked((long) 0x62F479EF6F75EE78L) /* 901 */, + unchecked((long) 0x11A55AD41C8916A9L) /* 902 */, unchecked((long) 0xF229D29084FED453L) /* 903 */, + unchecked((long) 0x42F1C27B16B000E6L) /* 904 */, unchecked((long) 0x2B1F76749823C074L) /* 905 */, + unchecked((long) 0x4B76ECA3C2745360L) /* 906 */, unchecked((long) 0x8C98F463B91691BDL) /* 907 */, + unchecked((long) 0x14BCC93CF1ADE66AL) /* 908 */, unchecked((long) 0x8885213E6D458397L) /* 909 */, + unchecked((long) 0x8E177DF0274D4711L) /* 910 */, unchecked((long) 0xB49B73B5503F2951L) /* 911 */, + unchecked((long) 0x10168168C3F96B6BL) /* 912 */, unchecked((long) 0x0E3D963B63CAB0AEL) /* 913 */, + unchecked((long) 0x8DFC4B5655A1DB14L) /* 914 */, unchecked((long) 0xF789F1356E14DE5CL) /* 915 */, + unchecked((long) 0x683E68AF4E51DAC1L) /* 916 */, unchecked((long) 0xC9A84F9D8D4B0FD9L) /* 917 */, + unchecked((long) 0x3691E03F52A0F9D1L) /* 918 */, unchecked((long) 0x5ED86E46E1878E80L) /* 919 */, + unchecked((long) 0x3C711A0E99D07150L) /* 920 */, unchecked((long) 0x5A0865B20C4E9310L) /* 921 */, + unchecked((long) 0x56FBFC1FE4F0682EL) /* 922 */, unchecked((long) 0xEA8D5DE3105EDF9BL) /* 923 */, + unchecked((long) 0x71ABFDB12379187AL) /* 924 */, unchecked((long) 0x2EB99DE1BEE77B9CL) /* 925 */, + unchecked((long) 0x21ECC0EA33CF4523L) /* 926 */, unchecked((long) 0x59A4D7521805C7A1L) /* 927 */, + unchecked((long) 0x3896F5EB56AE7C72L) /* 928 */, unchecked((long) 0xAA638F3DB18F75DCL) /* 929 */, + unchecked((long) 0x9F39358DABE9808EL) /* 930 */, unchecked((long) 0xB7DEFA91C00B72ACL) /* 931 */, + unchecked((long) 0x6B5541FD62492D92L) /* 932 */, unchecked((long) 0x6DC6DEE8F92E4D5BL) /* 933 */, + unchecked((long) 0x353F57ABC4BEEA7EL) /* 934 */, unchecked((long) 0x735769D6DA5690CEL) /* 935 */, + unchecked((long) 0x0A234AA642391484L) /* 936 */, unchecked((long) 0xF6F9508028F80D9DL) /* 937 */, + unchecked((long) 0xB8E319A27AB3F215L) /* 938 */, unchecked((long) 0x31AD9C1151341A4DL) /* 939 */, + unchecked((long) 0x773C22A57BEF5805L) /* 940 */, unchecked((long) 0x45C7561A07968633L) /* 941 */, + unchecked((long) 0xF913DA9E249DBE36L) /* 942 */, unchecked((long) 0xDA652D9B78A64C68L) /* 943 */, + unchecked((long) 0x4C27A97F3BC334EFL) /* 944 */, unchecked((long) 0x76621220E66B17F4L) /* 945 */, + unchecked((long) 0x967743899ACD7D0BL) /* 946 */, unchecked((long) 0xF3EE5BCAE0ED6782L) /* 947 */, + unchecked((long) 0x409F753600C879FCL) /* 948 */, unchecked((long) 0x06D09A39B5926DB6L) /* 949 */, + unchecked((long) 0x6F83AEB0317AC588L) /* 950 */, unchecked((long) 0x01E6CA4A86381F21L) /* 951 */, + unchecked((long) 0x66FF3462D19F3025L) /* 952 */, unchecked((long) 0x72207C24DDFD3BFBL) /* 953 */, + unchecked((long) 0x4AF6B6D3E2ECE2EBL) /* 954 */, unchecked((long) 0x9C994DBEC7EA08DEL) /* 955 */, + unchecked((long) 0x49ACE597B09A8BC4L) /* 956 */, unchecked((long) 0xB38C4766CF0797BAL) /* 957 */, + unchecked((long) 0x131B9373C57C2A75L) /* 958 */, unchecked((long) 0xB1822CCE61931E58L) /* 959 */, + unchecked((long) 0x9D7555B909BA1C0CL) /* 960 */, unchecked((long) 0x127FAFDD937D11D2L) /* 961 */, + unchecked((long) 0x29DA3BADC66D92E4L) /* 962 */, unchecked((long) 0xA2C1D57154C2ECBCL) /* 963 */, + unchecked((long) 0x58C5134D82F6FE24L) /* 964 */, unchecked((long) 0x1C3AE3515B62274FL) /* 965 */, + unchecked((long) 0xE907C82E01CB8126L) /* 966 */, unchecked((long) 0xF8ED091913E37FCBL) /* 967 */, + unchecked((long) 0x3249D8F9C80046C9L) /* 968 */, unchecked((long) 0x80CF9BEDE388FB63L) /* 969 */, + unchecked((long) 0x1881539A116CF19EL) /* 970 */, unchecked((long) 0x5103F3F76BD52457L) /* 971 */, + unchecked((long) 0x15B7E6F5AE47F7A8L) /* 972 */, unchecked((long) 0xDBD7C6DED47E9CCFL) /* 973 */, + unchecked((long) 0x44E55C410228BB1AL) /* 974 */, unchecked((long) 0xB647D4255EDB4E99L) /* 975 */, + unchecked((long) 0x5D11882BB8AAFC30L) /* 976 */, unchecked((long) 0xF5098BBB29D3212AL) /* 977 */, + unchecked((long) 0x8FB5EA14E90296B3L) /* 978 */, unchecked((long) 0x677B942157DD025AL) /* 979 */, + unchecked((long) 0xFB58E7C0A390ACB5L) /* 980 */, unchecked((long) 0x89D3674C83BD4A01L) /* 981 */, + unchecked((long) 0x9E2DA4DF4BF3B93BL) /* 982 */, unchecked((long) 0xFCC41E328CAB4829L) /* 983 */, + unchecked((long) 0x03F38C96BA582C52L) /* 984 */, unchecked((long) 0xCAD1BDBD7FD85DB2L) /* 985 */, + unchecked((long) 0xBBB442C16082AE83L) /* 986 */, unchecked((long) 0xB95FE86BA5DA9AB0L) /* 987 */, + unchecked((long) 0xB22E04673771A93FL) /* 988 */, unchecked((long) 0x845358C9493152D8L) /* 989 */, + unchecked((long) 0xBE2A488697B4541EL) /* 990 */, unchecked((long) 0x95A2DC2DD38E6966L) /* 991 */, + unchecked((long) 0xC02C11AC923C852BL) /* 992 */, unchecked((long) 0x2388B1990DF2A87BL) /* 993 */, + unchecked((long) 0x7C8008FA1B4F37BEL) /* 994 */, unchecked((long) 0x1F70D0C84D54E503L) /* 995 */, + unchecked((long) 0x5490ADEC7ECE57D4L) /* 996 */, unchecked((long) 0x002B3C27D9063A3AL) /* 997 */, + unchecked((long) 0x7EAEA3848030A2BFL) /* 998 */, unchecked((long) 0xC602326DED2003C0L) /* 999 */, + unchecked((long) 0x83A7287D69A94086L) /* 1000 */, unchecked((long) 0xC57A5FCB30F57A8AL) /* 1001 */, + unchecked((long) 0xB56844E479EBE779L) /* 1002 */, unchecked((long) 0xA373B40F05DCBCE9L) /* 1003 */, + unchecked((long) 0xD71A786E88570EE2L) /* 1004 */, unchecked((long) 0x879CBACDBDE8F6A0L) /* 1005 */, + unchecked((long) 0x976AD1BCC164A32FL) /* 1006 */, unchecked((long) 0xAB21E25E9666D78BL) /* 1007 */, + unchecked((long) 0x901063AAE5E5C33CL) /* 1008 */, unchecked((long) 0x9818B34448698D90L) /* 1009 */, + unchecked((long) 0xE36487AE3E1E8ABBL) /* 1010 */, unchecked((long) 0xAFBDF931893BDCB4L) /* 1011 */, + unchecked((long) 0x6345A0DC5FBBD519L) /* 1012 */, unchecked((long) 0x8628FE269B9465CAL) /* 1013 */, + unchecked((long) 0x1E5D01603F9C51ECL) /* 1014 */, unchecked((long) 0x4DE44006A15049B7L) /* 1015 */, + unchecked((long) 0xBF6C70E5F776CBB1L) /* 1016 */, unchecked((long) 0x411218F2EF552BEDL) /* 1017 */, + unchecked((long) 0xCB0C0708705A36A3L) /* 1018 */, unchecked((long) 0xE74D14754F986044L) /* 1019 */, + unchecked((long) 0xCD56D9430EA8280EL) /* 1020 */, unchecked((long) 0xC12591D7535F5065L) /* 1021 */, + unchecked((long) 0xC83223F1720AEF96L) /* 1022 */, unchecked((long) 0xC3A0396F7363A51FL) /* 1023 */ + }; + + private const int DigestLength = 24; + + // + // registers + // + private long a, b, c; + private long byteCount; + + // + // buffers + // + private byte[] Buffer = new byte[8]; + private int bOff; + + private long[] x = new long[8]; + private int xOff; + + /** + * Standard constructor + */ + public TigerDigest() + { + Reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public TigerDigest(TigerDigest t) + { + a = t.a; + b = t.b; + c = t.c; + + Array.Copy(t.x, 0, x, 0, t.x.Length); + xOff = t.xOff; + + Array.Copy(t.Buffer, 0, Buffer, 0, t.Buffer.Length); + bOff = t.bOff; + + byteCount = t.byteCount; + } + + public string AlgorithmName + { + get { return "Tiger"; } + } + + public int GetDigestSize() + { + return DigestLength; + } + + public int GetByteLength() + { + return MyByteLength; + } + + 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) + | ((uint)(b[off + 0] & 0xff)); + + if (xOff == x.Length) + { + ProcessBlock(); + } + + bOff = 0; + } + + public void Update( + byte input) + { + Buffer[bOff++] = input; + + if (bOff == Buffer.Length) + { + ProcessWord(Buffer, 0); + } + + byteCount++; + } + + public void BlockUpdate( + byte[] input, + int inOff, + int length) + { + // + // fill the current word + // + while ((bOff != 0) && (length > 0)) + { + Update(input[inOff]); + + inOff++; + length--; + } + + // + // process whole words. + // + while (length > 8) + { + ProcessWord(input, inOff); + + inOff += 8; + length -= 8; + byteCount += 8; + } + + // + // load in the remainder. + // + while (length > 0) + { + Update(input[inOff]); + + inOff++; + length--; + } + } + + 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] ^ unchecked ((long) 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] ^ (long) ((ulong) (~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] ^ (long) ((ulong) (~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; + } + } + + private void UnpackWord( + long r, + byte[] output, + int outOff) + { + output[outOff + 7] = (byte)(r >> 56); + output[outOff + 6] = (byte)(r >> 48); + output[outOff + 5] = (byte)(r >> 40); + output[outOff + 4] = (byte)(r >> 32); + output[outOff + 3] = (byte)(r >> 24); + output[outOff + 2] = (byte)(r >> 16); + output[outOff + 1] = (byte)(r >> 8); + output[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[] output, + int outOff) + { + Finish(); + + UnpackWord(a, output, outOff); + UnpackWord(b, output, outOff + 8); + UnpackWord(c, output, outOff + 16); + + Reset(); + + return DigestLength; + } + + /** + * reset the chaining variables + */ + public void Reset() + { + a = unchecked((long) 0x0123456789ABCDEFL); + b = unchecked((long) 0xFEDCBA9876543210L); + c = unchecked((long) 0xF096A5B4C3B2E187L); + + xOff = 0; + for (int i = 0; i != x.Length; i++) + { + x[i] = 0; + } + + bOff = 0; + for (int i = 0; i != Buffer.Length; i++) + { + Buffer[i] = 0; + } + + byteCount = 0; + } + } +} diff --git a/Crypto/src/crypto/digests/WhirlpoolDigest.cs b/Crypto/src/crypto/digests/WhirlpoolDigest.cs new file mode 100644
index 000000000..df83f4508 --- /dev/null +++ b/Crypto/src/crypto/digests/WhirlpoolDigest.cs
@@ -0,0 +1,397 @@ +using System; + +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * Implementation of WhirlpoolDigest, based on Java source published by Barreto + * and Rijmen. + * + */ + public sealed class WhirlpoolDigest : IDigest + { + private const int BYTE_LENGTH = 64; + + private const int DIGEST_LENGTH_BYTES = 512 / 8; + private const int ROUNDS = 10; + private const int REDUCTION_POLYNOMIAL = 0x011d; // 2^8 + 2^4 + 2^3 + 2 + 1; + + private static readonly 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 readonly long[] C0 = new long[256]; + private static readonly long[] C1 = new long[256]; + private static readonly long[] C2 = new long[256]; + private static readonly long[] C3 = new long[256]; + private static readonly long[] C4 = new long[256]; + private static readonly long[] C5 = new long[256]; + private static readonly long[] C6 = new long[256]; + private static readonly long[] C7 = new long[256]; + + private readonly long[] _rc = new long[ROUNDS + 1]; + + /* + * 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 readonly short[] EIGHT = new short[BITCOUNT_ARRAY_SIZE]; + + static WhirlpoolDigest() + { + EIGHT[BITCOUNT_ARRAY_SIZE - 1] = 8; + + 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); + } + } + + public WhirlpoolDigest() + { + _rc[0] = 0L; + for (int r = 1; r <= ROUNDS; r++) + { + int i = 8 * (r - 1); + _rc[r] = (long)((ulong)C0[i] & 0xff00000000000000L) ^ + (C1[i + 1] & (long) 0x00ff000000000000L) ^ + (C2[i + 2] & (long) 0x0000ff0000000000L) ^ + (C3[i + 3] & (long) 0x000000ff00000000L) ^ + (C4[i + 4] & (long) 0x00000000ff000000L) ^ + (C5[i + 5] & (long) 0x0000000000ff0000L) ^ + (C6[i + 6] & (long) 0x000000000000ff00L) ^ + (C7[i + 7] & (long) 0x00000000000000ffL); + } + } + + private static 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 static 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 const int BITCOUNT_ARRAY_SIZE = 32; + private byte[] _buffer = new byte[64]; + private int _bufferPos; + 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) + { + Array.Copy(originalDigest._rc, 0, _rc, 0, _rc.Length); + + Array.Copy(originalDigest._buffer, 0, _buffer, 0, _buffer.Length); + + this._bufferPos = originalDigest._bufferPos; + Array.Copy(originalDigest._bitCount, 0, _bitCount, 0, _bitCount.Length); + + // -- internal hash state -- + Array.Copy(originalDigest._hash, 0, _hash, 0, _hash.Length); + Array.Copy(originalDigest._K, 0, _K, 0, _K.Length); + Array.Copy(originalDigest._L, 0, _L, 0, _L.Length); + Array.Copy(originalDigest._block, 0, _block, 0, _block.Length); + Array.Copy(originalDigest._state, 0, _state, 0, _state.Length); + } + + public string AlgorithmName + { + get { return "Whirlpool"; } + } + + public int GetDigestSize() + { + return DIGEST_LENGTH_BYTES; + } + + public int DoFinal(byte[] output, int outOff) + { + // sets output[outOff] .. output[outOff+DIGEST_LENGTH_BYTES] + finish(); + + for (int i = 0; i < 8; i++) + { + convertLongToByteArray(_hash[i], output, outOff + (i * 8)); + } + + Reset(); + + return GetDigestSize(); + } + + /** + * Reset the chaining variables + */ + public void Reset() + { + // set variables to null, blank, whatever + _bufferPos = 0; + Array.Clear(_bitCount, 0, _bitCount.Length); + Array.Clear(_buffer, 0, _buffer.Length); + Array.Clear(_hash, 0, _hash.Length); + Array.Clear(_K, 0, _K.Length); + Array.Clear(_L, 0, _L.Length); + Array.Clear(_block, 0, _block.Length); + Array.Clear(_state, 0, _state.Length); + } + + // this takes a buffer of information and fills the block + private void processFilledBuffer() + { + // copies into the block... + for (int i = 0; i < _state.Length; i++) + { + _block[i] = bytesToLongFromBuffer(_buffer, i * 8); + } + processBlock(); + _bufferPos = 0; + Array.Clear(_buffer, 0, _buffer.Length); + } + + private static 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 static void convertLongToByteArray(long inputLong, byte[] outputArray, int offSet) + { + for (int i = 0; i < 8; i++) + { + outputArray[offSet + i] = (byte)((inputLong >> (56 - (i * 8))) & 0xff); + } + } + + private 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]; + } + + Array.Copy(_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 + Array.Copy(_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 input) + { + _buffer[_bufferPos] = input; + + //Console.WriteLine("adding to buffer = "+_buffer[_bufferPos]); + + ++_bufferPos; + + if (_bufferPos == _buffer.Length) + { + processFilledBuffer(); + } + + increment(); + } + + 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 BlockUpdate(byte[] input, int inOff, int length) + { + while (length > 0) + { + Update(input[inOff]); + ++inOff; + --length; + } + + } + + 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(); + } + + /* + * 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.... + Array.Copy(bitLength, 0, _buffer, 32, bitLength.Length); + + processFilledBuffer(); + } + + 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; + } + } +} diff --git a/Crypto/src/crypto/encodings/ISO9796d1Encoding.cs b/Crypto/src/crypto/encodings/ISO9796d1Encoding.cs new file mode 100644
index 000000000..30e988356 --- /dev/null +++ b/Crypto/src/crypto/encodings/ISO9796d1Encoding.cs
@@ -0,0 +1,273 @@ +using System; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Encodings +{ + /** + * 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 + : IAsymmetricBlockCipher + { + private static readonly BigInteger Sixteen = BigInteger.ValueOf(16); + private static readonly BigInteger Six = BigInteger.ValueOf(6); + + private static readonly byte[] shadows = { 0xe, 0x3, 0x5, 0x8, 0x9, 0x4, 0x2, 0xf, + 0x0, 0xd, 0xb, 0x6, 0x7, 0xa, 0xc, 0x1 }; + private static readonly byte[] inverse = { 0x8, 0xf, 0x6, 0x1, 0x5, 0x2, 0xb, 0xc, + 0x3, 0x4, 0xd, 0xa, 0xe, 0x9, 0x0, 0x7 }; + + private readonly IAsymmetricBlockCipher engine; + private bool forEncryption; + private int bitSize; + private int padBits = 0; + private BigInteger modulus; + + public ISO9796d1Encoding( + IAsymmetricBlockCipher cipher) + { + this.engine = cipher; + } + + public string AlgorithmName + { + get { return engine.AlgorithmName + "/ISO9796-1Padding"; } + } + + public IAsymmetricBlockCipher GetUnderlyingCipher() + { + return engine; + } + + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + RsaKeyParameters kParam; + if (parameters is ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)parameters; + kParam = (RsaKeyParameters)rParam.Parameters; + } + else + { + kParam = (RsaKeyParameters)parameters; + } + + engine.Init(forEncryption, parameters); + + modulus = kParam.Modulus; + 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 ArgumentException("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[] input, + int inOff, + int length) + { + if (forEncryption) + { + return EncodeBlock(input, inOff, length); + } + else + { + return DecodeBlock(input, inOff, length); + } + } + + private byte[] EncodeBlock( + byte[] input, + int inOff, + int inLen) + { + 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) + { + Array.Copy(input, inOff + inLen - (t - i), + block, block.Length - t, t - i); + } + else + { + Array.Copy(input, 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[(uint) (val & 0xff) >> 4] << 4) + | shadows[val & 0x0f]); + block[i + 1] = val; + } + + block[block.Length - 2 * z] ^= (byte) 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] &= (byte) ((ushort) 0xff >> maxBit); + block[0] |= (byte) ((ushort) 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[] input, + int inOff, + int inLen) + { + byte[] block = engine.ProcessBlock(input, 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 + { + iR = modulus.Subtract(iS); + + if (!iR.Mod(Sixteen).Equals(Six)) + throw new InvalidCipherTextException("resulting integer iS or (modulus - iS) is not congruent to 6 mod 16"); + } + + block = iR.ToByteArrayUnsigned(); + + if ((block[block.Length - 1] & 0x0f) != 0x6) + throw new InvalidCipherTextException("invalid forcing byte in block"); + + block[block.Length - 1] = + (byte)(((ushort)(block[block.Length - 1] & 0xff) >> 4) + | ((inverse[(block[block.Length - 2] & 0xff) >> 4]) << 4)); + + block[0] = (byte)((shadows[(uint) (block[1] & 0xff) >> 4] << 4) + | shadows[block[1] & 0x0f]); + + bool boundaryFound = false; + int boundary = 0; + + for (int i = block.Length - 1; i >= block.Length - 2 * t; i -= 2) + { + int val = ((shadows[(uint) (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; + } + } +} diff --git a/Crypto/src/crypto/encodings/OaepEncoding.cs b/Crypto/src/crypto/encodings/OaepEncoding.cs new file mode 100644
index 000000000..81561e7f5 --- /dev/null +++ b/Crypto/src/crypto/encodings/OaepEncoding.cs
@@ -0,0 +1,345 @@ +using System; + +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Encodings +{ + /** + * Optimal Asymmetric Encryption Padding (OAEP) - see PKCS 1 V 2. + */ + public class OaepEncoding + : IAsymmetricBlockCipher + { + private byte[] defHash; + private IDigest hash; + private IDigest mgf1Hash; + + private IAsymmetricBlockCipher engine; + private SecureRandom random; + private bool forEncryption; + + public OaepEncoding( + IAsymmetricBlockCipher cipher) + : this(cipher, new Sha1Digest(), null) + { + } + + public OaepEncoding( + IAsymmetricBlockCipher cipher, + IDigest hash) + : this(cipher, hash, null) + { + } + + public OaepEncoding( + IAsymmetricBlockCipher cipher, + IDigest hash, + byte[] encodingParams) + : this(cipher, hash, hash, encodingParams) + { + } + + public OaepEncoding( + IAsymmetricBlockCipher cipher, + IDigest hash, + IDigest mgf1Hash, + byte[] encodingParams) + { + this.engine = cipher; + this.hash = hash; + this.mgf1Hash = mgf1Hash; + this.defHash = new byte[hash.GetDigestSize()]; + + if (encodingParams != null) + { + hash.BlockUpdate(encodingParams, 0, encodingParams.Length); + } + + hash.DoFinal(defHash, 0); + } + + public IAsymmetricBlockCipher GetUnderlyingCipher() + { + return engine; + } + + public string AlgorithmName + { + get { return engine.AlgorithmName + "/OAEPPadding"; } + } + + public void Init( + bool forEncryption, + ICipherParameters param) + { + if (param is ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)param; + this.random = rParam.Random; + } + 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[] inBytes, + int inOff, + int inLen) + { + if (forEncryption) + { + return encodeBlock(inBytes, inOff, inLen); + } + else + { + return decodeBlock(inBytes, inOff, inLen); + } + } + + private byte[] encodeBlock( + byte[] inBytes, + int inOff, + int inLen) + { + byte[] block = new byte[GetInputBlockSize() + 1 + 2 * defHash.Length]; + + // + // copy in the message + // + Array.Copy(inBytes, 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. + // + Array.Copy(defHash, 0, block, defHash.Length, defHash.Length); + + // + // generate the seed. + // + byte[] seed = random.GenerateSeed(defHash.Length); + + // + // 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 + // + Array.Copy(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. + */ + private byte[] decodeBlock( + byte[] inBytes, + int inOff, + int inLen) + { + byte[] data = engine.ProcessBlock(inBytes, 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()]; + + Array.Copy(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. + // + for (int i = 0; i != defHash.Length; i++) + { + if (defHash[i] != block[defHash.Length + i]) + { + 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]; + + Array.Copy(block, start, output, 0, output.Length); + + return output; + } + + /** + * int to octet string. + */ + private void ItoOSP( + int i, + byte[] sp) + { + sp[0] = (byte)((uint)i >> 24); + sp[1] = (byte)((uint)i >> 16); + sp[2] = (byte)((uint)i >> 8); + sp[3] = (byte)((uint)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; + + hash.Reset(); + + do + { + ItoOSP(counter, C); + + mgf1Hash.BlockUpdate(Z, zOff, zLen); + mgf1Hash.BlockUpdate(C, 0, C.Length); + mgf1Hash.DoFinal(hashBuf, 0); + + Array.Copy(hashBuf, 0, mask, counter * hashBuf.Length, hashBuf.Length); + } + while (++counter < (length / hashBuf.Length)); + + if ((counter * hashBuf.Length) < length) + { + ItoOSP(counter, C); + + mgf1Hash.BlockUpdate(Z, zOff, zLen); + mgf1Hash.BlockUpdate(C, 0, C.Length); + mgf1Hash.DoFinal(hashBuf, 0); + + Array.Copy(hashBuf, 0, mask, counter * hashBuf.Length, mask.Length - (counter * hashBuf.Length)); + } + + return mask; + } + } +} + diff --git a/Crypto/src/crypto/encodings/Pkcs1Encoding.cs b/Crypto/src/crypto/encodings/Pkcs1Encoding.cs new file mode 100644
index 000000000..d2225a7d4 --- /dev/null +++ b/Crypto/src/crypto/encodings/Pkcs1Encoding.cs
@@ -0,0 +1,232 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Encodings +{ + /** + * 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 + : IAsymmetricBlockCipher + { + /** + * 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. + */ + public const string StrictLengthEnabledProperty = "Org.BouncyCastle.Pkcs1.Strict"; + + private const int HeaderLength = 10; + + /** + * The same effect can be achieved by setting the static property directly + * <p> + * The static property is checked during construction of the encoding object, it is set to + * true by default. + * </p> + */ + public static bool StrictLengthEnabled + { + get { return strictLengthEnabled[0]; } + set { strictLengthEnabled[0] = value; } + } + + private static readonly bool[] strictLengthEnabled; + + static Pkcs1Encoding() + { + string strictProperty = Platform.GetEnvironmentVariable(StrictLengthEnabledProperty); + + strictLengthEnabled = new bool[]{ strictProperty == null || strictProperty.Equals("true")}; + } + + + private SecureRandom random; + private IAsymmetricBlockCipher engine; + private bool forEncryption; + private bool forPrivateKey; + private bool useStrictLength; + + /** + * Basic constructor. + * @param cipher + */ + public Pkcs1Encoding( + IAsymmetricBlockCipher cipher) + { + this.engine = cipher; + this.useStrictLength = StrictLengthEnabled; + } + + public IAsymmetricBlockCipher GetUnderlyingCipher() + { + return engine; + } + + public string AlgorithmName + { + get { return engine.AlgorithmName + "/PKCS1Padding"; } + } + + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + AsymmetricKeyParameter kParam; + if (parameters is ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)parameters; + + this.random = rParam.Random; + kParam = (AsymmetricKeyParameter)rParam.Parameters; + } + else + { + this.random = new SecureRandom(); + kParam = (AsymmetricKeyParameter)parameters; + } + + engine.Init(forEncryption, parameters); + + this.forPrivateKey = kParam.IsPrivate; + this.forEncryption = forEncryption; + } + + public int GetInputBlockSize() + { + int baseBlockSize = engine.GetInputBlockSize(); + + return forEncryption + ? baseBlockSize - HeaderLength + : baseBlockSize; + } + + public int GetOutputBlockSize() + { + int baseBlockSize = engine.GetOutputBlockSize(); + + return forEncryption + ? baseBlockSize + : baseBlockSize - HeaderLength; + } + + public byte[] ProcessBlock( + byte[] input, + int inOff, + int length) + { + return forEncryption + ? EncodeBlock(input, inOff, length) + : DecodeBlock(input, inOff, length); + } + + private byte[] EncodeBlock( + byte[] input, + int inOff, + int inLen) + { + if (inLen > GetInputBlockSize()) + throw new ArgumentException("input data too large", "inLen"); + + 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 + Array.Copy(input, 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[] input, + int inOff, + int inLen) + { + byte[] block = engine.ProcessBlock(input, inOff, inLen); + + if (block.Length < GetOutputBlockSize()) + { + throw new InvalidCipherTextException("block truncated"); + } + + byte type = block[0]; + + if (type != 1 && type != 2) + { + 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 < HeaderLength) + { + throw new InvalidCipherTextException("no data in block"); + } + + byte[] result = new byte[block.Length - start]; + + Array.Copy(block, start, result, 0, result.Length); + + return result; + } + } + +} diff --git a/Crypto/src/crypto/engines/AesEngine.cs b/Crypto/src/crypto/engines/AesEngine.cs new file mode 100644
index 000000000..4211a9559 --- /dev/null +++ b/Crypto/src/crypto/engines/AesEngine.cs
@@ -0,0 +1,525 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * 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> + * <p> + * This file contains the middle performance version with 2Kbytes of static tables for round precomputation. + * </p> + */ + public class AesEngine + : IBlockCipher + { + // The S box + private static readonly byte[] S = + { + 99, 124, 119, 123, 242, 107, 111, 197, + 48, 1, 103, 43, 254, 215, 171, 118, + 202, 130, 201, 125, 250, 89, 71, 240, + 173, 212, 162, 175, 156, 164, 114, 192, + 183, 253, 147, 38, 54, 63, 247, 204, + 52, 165, 229, 241, 113, 216, 49, 21, + 4, 199, 35, 195, 24, 150, 5, 154, + 7, 18, 128, 226, 235, 39, 178, 117, + 9, 131, 44, 26, 27, 110, 90, 160, + 82, 59, 214, 179, 41, 227, 47, 132, + 83, 209, 0, 237, 32, 252, 177, 91, + 106, 203, 190, 57, 74, 76, 88, 207, + 208, 239, 170, 251, 67, 77, 51, 133, + 69, 249, 2, 127, 80, 60, 159, 168, + 81, 163, 64, 143, 146, 157, 56, 245, + 188, 182, 218, 33, 16, 255, 243, 210, + 205, 12, 19, 236, 95, 151, 68, 23, + 196, 167, 126, 61, 100, 93, 25, 115, + 96, 129, 79, 220, 34, 42, 144, 136, + 70, 238, 184, 20, 222, 94, 11, 219, + 224, 50, 58, 10, 73, 6, 36, 92, + 194, 211, 172, 98, 145, 149, 228, 121, + 231, 200, 55, 109, 141, 213, 78, 169, + 108, 86, 244, 234, 101, 122, 174, 8, + 186, 120, 37, 46, 28, 166, 180, 198, + 232, 221, 116, 31, 75, 189, 139, 138, + 112, 62, 181, 102, 72, 3, 246, 14, + 97, 53, 87, 185, 134, 193, 29, 158, + 225, 248, 152, 17, 105, 217, 142, 148, + 155, 30, 135, 233, 206, 85, 40, 223, + 140, 161, 137, 13, 191, 230, 66, 104, + 65, 153, 45, 15, 176, 84, 187, 22, + }; + + // The inverse S-box + private static readonly byte[] Si = + { + 82, 9, 106, 213, 48, 54, 165, 56, + 191, 64, 163, 158, 129, 243, 215, 251, + 124, 227, 57, 130, 155, 47, 255, 135, + 52, 142, 67, 68, 196, 222, 233, 203, + 84, 123, 148, 50, 166, 194, 35, 61, + 238, 76, 149, 11, 66, 250, 195, 78, + 8, 46, 161, 102, 40, 217, 36, 178, + 118, 91, 162, 73, 109, 139, 209, 37, + 114, 248, 246, 100, 134, 104, 152, 22, + 212, 164, 92, 204, 93, 101, 182, 146, + 108, 112, 72, 80, 253, 237, 185, 218, + 94, 21, 70, 87, 167, 141, 157, 132, + 144, 216, 171, 0, 140, 188, 211, 10, + 247, 228, 88, 5, 184, 179, 69, 6, + 208, 44, 30, 143, 202, 63, 15, 2, + 193, 175, 189, 3, 1, 19, 138, 107, + 58, 145, 17, 65, 79, 103, 220, 234, + 151, 242, 207, 206, 240, 180, 230, 115, + 150, 172, 116, 34, 231, 173, 53, 133, + 226, 249, 55, 232, 28, 117, 223, 110, + 71, 241, 26, 113, 29, 41, 197, 137, + 111, 183, 98, 14, 170, 24, 190, 27, + 252, 86, 62, 75, 198, 210, 121, 32, + 154, 219, 192, 254, 120, 205, 90, 244, + 31, 221, 168, 51, 136, 7, 199, 49, + 177, 18, 16, 89, 39, 128, 236, 95, + 96, 81, 127, 169, 25, 181, 74, 13, + 45, 229, 122, 159, 147, 201, 156, 239, + 160, 224, 59, 77, 174, 42, 245, 176, + 200, 235, 187, 60, 131, 83, 153, 97, + 23, 43, 4, 126, 186, 119, 214, 38, + 225, 105, 20, 99, 85, 33, 12, 125, + }; + + // vector used in calculating key schedule (powers of x in GF(256)) + private static readonly byte[] 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 readonly uint[] 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 readonly uint[] 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 uint Shift( + uint r, + int shift) + { + return (r >> shift) | (r << (32 - shift)); + } + + /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */ + + private const uint m1 = 0x80808080; + private const uint m2 = 0x7f7f7f7f; + private const uint m3 = 0x0000001b; + + private uint FFmulX( + uint 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 uint Inv_Mcol( + uint x) + { + uint f2 = FFmulX(x); + uint f4 = FFmulX(f2); + uint f8 = FFmulX(f4); + uint f9 = x ^ f8; + + return f2 ^ f4 ^ f8 ^ Shift(f2 ^ f9, 8) ^ Shift(f4 ^ f9, 16) ^ Shift(f9, 24); + } + + private uint SubWord( + uint x) + { + return (uint)S[x&255] + | (((uint)S[(x>>8)&255]) << 8) + | (((uint)S[(x>>16)&255]) << 16) + | (((uint)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 uint[,] GenerateWorkingKey( + byte[] key, + bool forEncryption) + { + int KC = key.Length / 4; // key length in words + int t; + + if ((KC != 4) && (KC != 6) && (KC != 8)) + throw new ArgumentException("Key length not 128/192/256 bits."); + + ROUNDS = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes + uint[,] W = new uint[ROUNDS+1, 4]; // 4 words in a block + + // + // copy the key into the round key array + // + + t = 0; + for (int i = 0; i < key.Length; t++) + { + W[t >> 2, t & 3] = Pack.LE_To_UInt32(key, i); + i+=4; + } + + // + // while not enough round key material calculated + // calculate new values + // + int k = (ROUNDS + 1) << 2; + for (int i = KC; (i < k); i++) + { + uint 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 (int i = 0; i < 4; i++) + { + W[j, i] = Inv_Mcol(W[j, i]); + } + } + } + + return W; + } + + private int ROUNDS; + private uint[,] WorkingKey; + private uint C0, C1, C2, C3; + private bool forEncryption; + + private const 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 parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + KeyParameter keyParameter = parameters as KeyParameter; + + if (keyParameter == null) + throw new ArgumentException("invalid parameter passed to AES init - " + parameters.GetType().Name); + + WorkingKey = GenerateWorkingKey(keyParameter.GetKey(), forEncryption); + + this.forEncryption = forEncryption; + } + + public string AlgorithmName + { + get { return "AES"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return BLOCK_SIZE; + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (WorkingKey == null) + { + throw new InvalidOperationException("AES engine not initialised"); + } + + if ((inOff + (32 / 2)) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + (32 / 2)) > output.Length) + { + throw new DataLengthException("output buffer too short"); + } + + UnPackBlock(input, inOff); + + if (forEncryption) + { + EncryptBlock(WorkingKey); + } + else + { + DecryptBlock(WorkingKey); + } + + PackBlock(output, outOff); + + return BLOCK_SIZE; + } + + public void Reset() + { + } + + private void UnPackBlock( + byte[] bytes, + int off) + { + C0 = Pack.LE_To_UInt32(bytes, off); + C1 = Pack.LE_To_UInt32(bytes, off + 4); + C2 = Pack.LE_To_UInt32(bytes, off + 8); + C3 = Pack.LE_To_UInt32(bytes, off + 12); + } + + private void PackBlock( + byte[] bytes, + int off) + { + Pack.UInt32_To_LE(C0, bytes, off); + Pack.UInt32_To_LE(C1, bytes, off + 4); + Pack.UInt32_To_LE(C2, bytes, off + 8); + Pack.UInt32_To_LE(C3, bytes, off + 12); + } + + private void EncryptBlock( + uint[,] KW) + { + uint 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 = 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 = (uint)S[r0&255] ^ (((uint)S[(r1>>8)&255])<<8) ^ (((uint)S[(r2>>16)&255])<<16) ^ (((uint)S[(r3>>24)&255])<<24) ^ KW[r,0]; + C1 = (uint)S[r1&255] ^ (((uint)S[(r2>>8)&255])<<8) ^ (((uint)S[(r3>>16)&255])<<16) ^ (((uint)S[(r0>>24)&255])<<24) ^ KW[r,1]; + C2 = (uint)S[r2&255] ^ (((uint)S[(r3>>8)&255])<<8) ^ (((uint)S[(r0>>16)&255])<<16) ^ (((uint)S[(r1>>24)&255])<<24) ^ KW[r,2]; + C3 = (uint)S[r3&255] ^ (((uint)S[(r0>>8)&255])<<8) ^ (((uint)S[(r1>>16)&255])<<16) ^ (((uint)S[(r2>>24)&255])<<24) ^ KW[r,3]; + } + + private void DecryptBlock( + uint[,] KW) + { + int r; + uint 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 = 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 = (uint)Si[r0&255] ^ (((uint)Si[(r3>>8)&255])<<8) ^ (((uint)Si[(r2>>16)&255])<<16) ^ (((uint)Si[(r1>>24)&255])<<24) ^ KW[0,0]; + C1 = (uint)Si[r1&255] ^ (((uint)Si[(r0>>8)&255])<<8) ^ (((uint)Si[(r3>>16)&255])<<16) ^ (((uint)Si[(r2>>24)&255])<<24) ^ KW[0,1]; + C2 = (uint)Si[r2&255] ^ (((uint)Si[(r1>>8)&255])<<8) ^ (((uint)Si[(r0>>16)&255])<<16) ^ (((uint)Si[(r3>>24)&255])<<24) ^ KW[0,2]; + C3 = (uint)Si[r3&255] ^ (((uint)Si[(r2>>8)&255])<<8) ^ (((uint)Si[(r1>>16)&255])<<16) ^ (((uint)Si[(r0>>24)&255])<<24) ^ KW[0,3]; + } + } +} diff --git a/Crypto/src/crypto/engines/AesFastEngine.cs b/Crypto/src/crypto/engines/AesFastEngine.cs new file mode 100644
index 000000000..603b5ce4d --- /dev/null +++ b/Crypto/src/crypto/engines/AesFastEngine.cs
@@ -0,0 +1,853 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * 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> + * <p> + * This file contains the fast version with 8Kbytes of static tables for round precomputation + * </p> + */ + public class AesFastEngine + : IBlockCipher + { + // The S box + private static readonly byte[] S = + { + 99, 124, 119, 123, 242, 107, 111, 197, + 48, 1, 103, 43, 254, 215, 171, 118, + 202, 130, 201, 125, 250, 89, 71, 240, + 173, 212, 162, 175, 156, 164, 114, 192, + 183, 253, 147, 38, 54, 63, 247, 204, + 52, 165, 229, 241, 113, 216, 49, 21, + 4, 199, 35, 195, 24, 150, 5, 154, + 7, 18, 128, 226, 235, 39, 178, 117, + 9, 131, 44, 26, 27, 110, 90, 160, + 82, 59, 214, 179, 41, 227, 47, 132, + 83, 209, 0, 237, 32, 252, 177, 91, + 106, 203, 190, 57, 74, 76, 88, 207, + 208, 239, 170, 251, 67, 77, 51, 133, + 69, 249, 2, 127, 80, 60, 159, 168, + 81, 163, 64, 143, 146, 157, 56, 245, + 188, 182, 218, 33, 16, 255, 243, 210, + 205, 12, 19, 236, 95, 151, 68, 23, + 196, 167, 126, 61, 100, 93, 25, 115, + 96, 129, 79, 220, 34, 42, 144, 136, + 70, 238, 184, 20, 222, 94, 11, 219, + 224, 50, 58, 10, 73, 6, 36, 92, + 194, 211, 172, 98, 145, 149, 228, 121, + 231, 200, 55, 109, 141, 213, 78, 169, + 108, 86, 244, 234, 101, 122, 174, 8, + 186, 120, 37, 46, 28, 166, 180, 198, + 232, 221, 116, 31, 75, 189, 139, 138, + 112, 62, 181, 102, 72, 3, 246, 14, + 97, 53, 87, 185, 134, 193, 29, 158, + 225, 248, 152, 17, 105, 217, 142, 148, + 155, 30, 135, 233, 206, 85, 40, 223, + 140, 161, 137, 13, 191, 230, 66, 104, + 65, 153, 45, 15, 176, 84, 187, 22, + }; + + // The inverse S-box + private static readonly byte[] Si = + { + 82, 9, 106, 213, 48, 54, 165, 56, + 191, 64, 163, 158, 129, 243, 215, 251, + 124, 227, 57, 130, 155, 47, 255, 135, + 52, 142, 67, 68, 196, 222, 233, 203, + 84, 123, 148, 50, 166, 194, 35, 61, + 238, 76, 149, 11, 66, 250, 195, 78, + 8, 46, 161, 102, 40, 217, 36, 178, + 118, 91, 162, 73, 109, 139, 209, 37, + 114, 248, 246, 100, 134, 104, 152, 22, + 212, 164, 92, 204, 93, 101, 182, 146, + 108, 112, 72, 80, 253, 237, 185, 218, + 94, 21, 70, 87, 167, 141, 157, 132, + 144, 216, 171, 0, 140, 188, 211, 10, + 247, 228, 88, 5, 184, 179, 69, 6, + 208, 44, 30, 143, 202, 63, 15, 2, + 193, 175, 189, 3, 1, 19, 138, 107, + 58, 145, 17, 65, 79, 103, 220, 234, + 151, 242, 207, 206, 240, 180, 230, 115, + 150, 172, 116, 34, 231, 173, 53, 133, + 226, 249, 55, 232, 28, 117, 223, 110, + 71, 241, 26, 113, 29, 41, 197, 137, + 111, 183, 98, 14, 170, 24, 190, 27, + 252, 86, 62, 75, 198, 210, 121, 32, + 154, 219, 192, 254, 120, 205, 90, 244, + 31, 221, 168, 51, 136, 7, 199, 49, + 177, 18, 16, 89, 39, 128, 236, 95, + 96, 81, 127, 169, 25, 181, 74, 13, + 45, 229, 122, 159, 147, 201, 156, 239, + 160, 224, 59, 77, 174, 42, 245, 176, + 200, 235, 187, 60, 131, 83, 153, 97, + 23, 43, 4, 126, 186, 119, 214, 38, + 225, 105, 20, 99, 85, 33, 12, 125, + }; + + // vector used in calculating key schedule (powers of x in GF(256)) + private static readonly byte[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 uint Shift( + uint r, + int shift) + { + return (r >> shift) | (r << (32 - shift)); + } + + /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */ + + private const uint m1 = 0x80808080; + private const uint m2 = 0x7f7f7f7f; + private const uint m3 = 0x0000001b; + + private uint FFmulX( + uint 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 uint Inv_Mcol( + uint x) { + uint f2 = FFmulX(x); + uint f4 = FFmulX(f2); + uint f8 = FFmulX(f4); + uint f9 = x ^ f8; + + return f2 ^ f4 ^ f8 ^ Shift(f2 ^ f9, 8) ^ Shift(f4 ^ f9, 16) ^ Shift(f9, 24); + } + + private uint SubWord( + uint x) + { + return (uint)S[x&255] + | (((uint)S[(x>>8)&255]) << 8) + | (((uint)S[(x>>16)&255]) << 16) + | (((uint)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 uint[,] GenerateWorkingKey( + byte[] key, + bool forEncryption) + { + int KC = key.Length / 4; // key length in words + + if (((KC != 4) && (KC != 6) && (KC != 8)) || ((KC * 4) != key.Length)) + throw new ArgumentException("Key length not 128/192/256 bits."); + + ROUNDS = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes + uint[,] W = new uint[ROUNDS+1,4]; // 4 words in a block + + // + // copy the key into the round key array + // + + int t = 0; + for (int i = 0; i < key.Length; t++) + { + W[t >> 2,t & 3] = Pack.LE_To_UInt32(key, i); + i+=4; + } + + // + // while not enough round key material calculated + // calculate new values + // + int k = (ROUNDS + 1) << 2; + for (int i = KC; (i < k); i++) + { + uint 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 (int i = 0; i < 4; i++) + { + W[j,i] = Inv_Mcol(W[j,i]); + } + } + } + + return W; + } + + private int ROUNDS; + private uint[,] WorkingKey; + private uint C0, C1, C2, C3; + private bool forEncryption; + + private const 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 parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + throw new ArgumentException("invalid parameter passed to AES init - " + parameters.GetType().ToString()); + + WorkingKey = GenerateWorkingKey(((KeyParameter)parameters).GetKey(), forEncryption); + this.forEncryption = forEncryption; + } + + public string AlgorithmName + { + get { return "AES"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return BLOCK_SIZE; + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (WorkingKey == null) + { + throw new InvalidOperationException("AES engine not initialised"); + } + + if ((inOff + (32 / 2)) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + (32 / 2)) > output.Length) + { + throw new DataLengthException("output buffer too short"); + } + + UnPackBlock(input, inOff); + + if (forEncryption) + { + EncryptBlock(WorkingKey); + } + else + { + DecryptBlock(WorkingKey); + } + + PackBlock(output, outOff); + + return BLOCK_SIZE; + } + + public void Reset() + { + } + + private void UnPackBlock( + byte[] bytes, + int off) + { + C0 = Pack.LE_To_UInt32(bytes, off); + C1 = Pack.LE_To_UInt32(bytes, off + 4); + C2 = Pack.LE_To_UInt32(bytes, off + 8); + C3 = Pack.LE_To_UInt32(bytes, off + 12); + } + + private void PackBlock( + byte[] bytes, + int off) + { + Pack.UInt32_To_LE(C0, bytes, off); + Pack.UInt32_To_LE(C1, bytes, off + 4); + Pack.UInt32_To_LE(C2, bytes, off + 8); + Pack.UInt32_To_LE(C3, bytes, off + 12); + } + + private void EncryptBlock( + uint[,] KW) + { + int r; + uint 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 = T0[C0&255] ^ T1[(C1>>8)&255] ^ T2[(C2>>16)&255] ^ T3[C3>>24] ^ KW[r,0]; + r1 = T0[C1&255] ^ T1[(C2>>8)&255] ^ T2[(C3>>16)&255] ^ T3[C0>>24] ^ KW[r,1]; + r2 = T0[C2&255] ^ T1[(C3>>8)&255] ^ T2[(C0>>16)&255] ^ T3[C1>>24] ^ KW[r,2]; + r3 = T0[C3&255] ^ T1[(C0>>8)&255] ^ T2[(C1>>16)&255] ^ T3[C2>>24] ^ KW[r++,3]; + C0 = T0[r0&255] ^ T1[(r1>>8)&255] ^ T2[(r2>>16)&255] ^ T3[r3>>24] ^ KW[r,0]; + C1 = T0[r1&255] ^ T1[(r2>>8)&255] ^ T2[(r3>>16)&255] ^ T3[r0>>24] ^ KW[r,1]; + C2 = T0[r2&255] ^ T1[(r3>>8)&255] ^ T2[(r0>>16)&255] ^ T3[r1>>24] ^ KW[r,2]; + C3 = T0[r3&255] ^ T1[(r0>>8)&255] ^ T2[(r1>>16)&255] ^ T3[r2>>24] ^ KW[r++,3]; + } + + r0 = T0[C0&255] ^ T1[(C1>>8)&255] ^ T2[(C2>>16)&255] ^ T3[C3>>24] ^ KW[r,0]; + r1 = T0[C1&255] ^ T1[(C2>>8)&255] ^ T2[(C3>>16)&255] ^ T3[C0>>24] ^ KW[r,1]; + r2 = T0[C2&255] ^ T1[(C3>>8)&255] ^ T2[(C0>>16)&255] ^ T3[C1>>24] ^ KW[r,2]; + r3 = T0[C3&255] ^ T1[(C0>>8)&255] ^ T2[(C1>>16)&255] ^ T3[C2>>24] ^ 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 = (uint)S[r0&255] ^ (((uint)S[(r1>>8)&255])<<8) ^ (((uint)S[(r2>>16)&255])<<16) ^ (((uint)S[r3>>24])<<24) ^ KW[r,0]; + C1 = (uint)S[r1&255] ^ (((uint)S[(r2>>8)&255])<<8) ^ (((uint)S[(r3>>16)&255])<<16) ^ (((uint)S[r0>>24])<<24) ^ KW[r,1]; + C2 = (uint)S[r2&255] ^ (((uint)S[(r3>>8)&255])<<8) ^ (((uint)S[(r0>>16)&255])<<16) ^ (((uint)S[r1>>24])<<24) ^ KW[r,2]; + C3 = (uint)S[r3&255] ^ (((uint)S[(r0>>8)&255])<<8) ^ (((uint)S[(r1>>16)&255])<<16) ^ (((uint)S[r2>>24])<<24) ^ KW[r,3]; + } + + private void DecryptBlock( + uint[,] KW) + { + int r; + uint 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 = Tinv0[C0&255] ^ Tinv1[(C3>>8)&255] ^ Tinv2[(C2>>16)&255] ^ Tinv3[C1>>24] ^ KW[r,0]; + r1 = Tinv0[C1&255] ^ Tinv1[(C0>>8)&255] ^ Tinv2[(C3>>16)&255] ^ Tinv3[C2>>24] ^ KW[r,1]; + r2 = Tinv0[C2&255] ^ Tinv1[(C1>>8)&255] ^ Tinv2[(C0>>16)&255] ^ Tinv3[C3>>24] ^ KW[r,2]; + r3 = Tinv0[C3&255] ^ Tinv1[(C2>>8)&255] ^ Tinv2[(C1>>16)&255] ^ Tinv3[C0>>24] ^ KW[r--,3]; + C0 = Tinv0[r0&255] ^ Tinv1[(r3>>8)&255] ^ Tinv2[(r2>>16)&255] ^ Tinv3[r1>>24] ^ KW[r,0]; + C1 = Tinv0[r1&255] ^ Tinv1[(r0>>8)&255] ^ Tinv2[(r3>>16)&255] ^ Tinv3[r2>>24] ^ KW[r,1]; + C2 = Tinv0[r2&255] ^ Tinv1[(r1>>8)&255] ^ Tinv2[(r0>>16)&255] ^ Tinv3[r3>>24] ^ KW[r,2]; + C3 = Tinv0[r3&255] ^ Tinv1[(r2>>8)&255] ^ Tinv2[(r1>>16)&255] ^ Tinv3[r0>>24] ^ KW[r--,3]; + } + + r0 = Tinv0[C0&255] ^ Tinv1[(C3>>8)&255] ^ Tinv2[(C2>>16)&255] ^ Tinv3[C1>>24] ^ KW[r,0]; + r1 = Tinv0[C1&255] ^ Tinv1[(C0>>8)&255] ^ Tinv2[(C3>>16)&255] ^ Tinv3[C2>>24] ^ KW[r,1]; + r2 = Tinv0[C2&255] ^ Tinv1[(C1>>8)&255] ^ Tinv2[(C0>>16)&255] ^ Tinv3[C3>>24] ^ KW[r,2]; + r3 = Tinv0[C3&255] ^ Tinv1[(C2>>8)&255] ^ Tinv2[(C1>>16)&255] ^ Tinv3[C0>>24] ^ 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 = (uint)Si[r0&255] ^ (((uint)Si[(r3>>8)&255])<<8) ^ (((uint)Si[(r2>>16)&255])<<16) ^ (((uint)Si[r1>>24])<<24) ^ KW[0,0]; + C1 = (uint)Si[r1&255] ^ (((uint)Si[(r0>>8)&255])<<8) ^ (((uint)Si[(r3>>16)&255])<<16) ^ (((uint)Si[r2>>24])<<24) ^ KW[0,1]; + C2 = (uint)Si[r2&255] ^ (((uint)Si[(r1>>8)&255])<<8) ^ (((uint)Si[(r0>>16)&255])<<16) ^ (((uint)Si[r3>>24])<<24) ^ KW[0,2]; + C3 = (uint)Si[r3&255] ^ (((uint)Si[(r2>>8)&255])<<8) ^ (((uint)Si[(r1>>16)&255])<<16) ^ (((uint)Si[r0>>24])<<24) ^ KW[0,3]; + } + } +} diff --git a/Crypto/src/crypto/engines/AesLightEngine.cs b/Crypto/src/crypto/engines/AesLightEngine.cs new file mode 100644
index 000000000..2c495578d --- /dev/null +++ b/Crypto/src/crypto/engines/AesLightEngine.cs
@@ -0,0 +1,419 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * 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> + * <p> + * This file contains the slowest performance version with no static tables + * for round precomputation, but it has the smallest foot print. + * </p> + */ + public class AesLightEngine + : IBlockCipher + { + // The S box + private static readonly byte[] S = + { + 99, 124, 119, 123, 242, 107, 111, 197, + 48, 1, 103, 43, 254, 215, 171, 118, + 202, 130, 201, 125, 250, 89, 71, 240, + 173, 212, 162, 175, 156, 164, 114, 192, + 183, 253, 147, 38, 54, 63, 247, 204, + 52, 165, 229, 241, 113, 216, 49, 21, + 4, 199, 35, 195, 24, 150, 5, 154, + 7, 18, 128, 226, 235, 39, 178, 117, + 9, 131, 44, 26, 27, 110, 90, 160, + 82, 59, 214, 179, 41, 227, 47, 132, + 83, 209, 0, 237, 32, 252, 177, 91, + 106, 203, 190, 57, 74, 76, 88, 207, + 208, 239, 170, 251, 67, 77, 51, 133, + 69, 249, 2, 127, 80, 60, 159, 168, + 81, 163, 64, 143, 146, 157, 56, 245, + 188, 182, 218, 33, 16, 255, 243, 210, + 205, 12, 19, 236, 95, 151, 68, 23, + 196, 167, 126, 61, 100, 93, 25, 115, + 96, 129, 79, 220, 34, 42, 144, 136, + 70, 238, 184, 20, 222, 94, 11, 219, + 224, 50, 58, 10, 73, 6, 36, 92, + 194, 211, 172, 98, 145, 149, 228, 121, + 231, 200, 55, 109, 141, 213, 78, 169, + 108, 86, 244, 234, 101, 122, 174, 8, + 186, 120, 37, 46, 28, 166, 180, 198, + 232, 221, 116, 31, 75, 189, 139, 138, + 112, 62, 181, 102, 72, 3, 246, 14, + 97, 53, 87, 185, 134, 193, 29, 158, + 225, 248, 152, 17, 105, 217, 142, 148, + 155, 30, 135, 233, 206, 85, 40, 223, + 140, 161, 137, 13, 191, 230, 66, 104, + 65, 153, 45, 15, 176, 84, 187, 22, + }; + + // The inverse S-box + private static readonly byte[] Si = + { + 82, 9, 106, 213, 48, 54, 165, 56, + 191, 64, 163, 158, 129, 243, 215, 251, + 124, 227, 57, 130, 155, 47, 255, 135, + 52, 142, 67, 68, 196, 222, 233, 203, + 84, 123, 148, 50, 166, 194, 35, 61, + 238, 76, 149, 11, 66, 250, 195, 78, + 8, 46, 161, 102, 40, 217, 36, 178, + 118, 91, 162, 73, 109, 139, 209, 37, + 114, 248, 246, 100, 134, 104, 152, 22, + 212, 164, 92, 204, 93, 101, 182, 146, + 108, 112, 72, 80, 253, 237, 185, 218, + 94, 21, 70, 87, 167, 141, 157, 132, + 144, 216, 171, 0, 140, 188, 211, 10, + 247, 228, 88, 5, 184, 179, 69, 6, + 208, 44, 30, 143, 202, 63, 15, 2, + 193, 175, 189, 3, 1, 19, 138, 107, + 58, 145, 17, 65, 79, 103, 220, 234, + 151, 242, 207, 206, 240, 180, 230, 115, + 150, 172, 116, 34, 231, 173, 53, 133, + 226, 249, 55, 232, 28, 117, 223, 110, + 71, 241, 26, 113, 29, 41, 197, 137, + 111, 183, 98, 14, 170, 24, 190, 27, + 252, 86, 62, 75, 198, 210, 121, 32, + 154, 219, 192, 254, 120, 205, 90, 244, + 31, 221, 168, 51, 136, 7, 199, 49, + 177, 18, 16, 89, 39, 128, 236, 95, + 96, 81, 127, 169, 25, 181, 74, 13, + 45, 229, 122, 159, 147, 201, 156, 239, + 160, 224, 59, 77, 174, 42, 245, 176, + 200, 235, 187, 60, 131, 83, 153, 97, + 23, 43, 4, 126, 186, 119, 214, 38, + 225, 105, 20, 99, 85, 33, 12, 125, + }; + + // vector used in calculating key schedule (powers of x in GF(256)) + private static readonly byte[] 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 uint Shift( + uint r, + int shift) + { + return (r >> shift) | (r << (32 - shift)); + } + + /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */ + + private const uint m1 = 0x80808080; + private const uint m2 = 0x7f7f7f7f; + private const uint m3 = 0x0000001b; + + private uint FFmulX( + uint 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 uint Mcol( + uint x) + { + uint f2 = FFmulX(x); + return f2 ^ Shift(x ^ f2, 8) ^ Shift(x, 16) ^ Shift(x, 24); + } + + private uint Inv_Mcol( + uint x) + { + uint f2 = FFmulX(x); + uint f4 = FFmulX(f2); + uint f8 = FFmulX(f4); + uint f9 = x ^ f8; + + return f2 ^ f4 ^ f8 ^ Shift(f2 ^ f9, 8) ^ Shift(f4 ^ f9, 16) ^ Shift(f9, 24); + } + + private uint SubWord( + uint x) + { + return (uint)S[x&255] + | (((uint)S[(x>>8)&255]) << 8) + | (((uint)S[(x>>16)&255]) << 16) + | (((uint)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 uint[,] GenerateWorkingKey( + byte[] key, + bool forEncryption) + { + int KC = key.Length / 4; // key length in words + int t; + + if ((KC != 4) && (KC != 6) && (KC != 8)) + throw new ArgumentException("Key length not 128/192/256 bits."); + + ROUNDS = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes + uint[,] W = new uint[ROUNDS+1,4]; // 4 words in a block + + // + // copy the key into the round key array + // + + t = 0; + for (int i = 0; i < key.Length; t++) + { + W[t >> 2, t & 3] = Pack.LE_To_UInt32(key, i); + i+=4; + } + + // + // while not enough round key material calculated + // calculate new values + // + int k = (ROUNDS + 1) << 2; + for (int i = KC; (i < k); i++) + { + uint 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 (int i = 0; i < 4; i++) + { + W[j,i] = Inv_Mcol(W[j,i]); + } + } + } + + return W; + } + + private int ROUNDS; + private uint[,] WorkingKey; + private uint C0, C1, C2, C3; + private bool forEncryption; + + private const 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 parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + throw new ArgumentException("invalid parameter passed to AES init - " + parameters.GetType().ToString()); + + WorkingKey = GenerateWorkingKey(((KeyParameter)parameters).GetKey(), forEncryption); + this.forEncryption = forEncryption; + } + + public string AlgorithmName + { + get { return "AES"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return BLOCK_SIZE; + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (WorkingKey == null) + { + throw new InvalidOperationException("AES engine not initialised"); + } + + if ((inOff + (32 / 2)) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + (32 / 2)) > output.Length) + { + throw new DataLengthException("output buffer too short"); + } + + if (forEncryption) + { + UnPackBlock(input, inOff); + EncryptBlock(WorkingKey); + PackBlock(output, outOff); + } + else + { + UnPackBlock(input, inOff); + DecryptBlock(WorkingKey); + PackBlock(output, outOff); + } + + return BLOCK_SIZE; + } + + public void Reset() + { + } + + private void UnPackBlock( + byte[] bytes, + int off) + { + C0 = Pack.LE_To_UInt32(bytes, off); + C1 = Pack.LE_To_UInt32(bytes, off + 4); + C2 = Pack.LE_To_UInt32(bytes, off + 8); + C3 = Pack.LE_To_UInt32(bytes, off + 12); + } + + private void PackBlock( + byte[] bytes, + int off) + { + Pack.UInt32_To_LE(C0, bytes, off); + Pack.UInt32_To_LE(C1, bytes, off + 4); + Pack.UInt32_To_LE(C2, bytes, off + 8); + Pack.UInt32_To_LE(C3, bytes, off + 12); + } + + private void EncryptBlock( + uint[,] KW) + { + int r; + uint 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((uint)S[C0&255] ^ (((uint)S[(C1>>8)&255])<<8) ^ (((uint)S[(C2>>16)&255])<<16) ^ (((uint)S[(C3>>24)&255])<<24)) ^ KW[r,0]; + r1 = Mcol((uint)S[C1&255] ^ (((uint)S[(C2>>8)&255])<<8) ^ (((uint)S[(C3>>16)&255])<<16) ^ (((uint)S[(C0>>24)&255])<<24)) ^ KW[r,1]; + r2 = Mcol((uint)S[C2&255] ^ (((uint)S[(C3>>8)&255])<<8) ^ (((uint)S[(C0>>16)&255])<<16) ^ (((uint)S[(C1>>24)&255])<<24)) ^ KW[r,2]; + r3 = Mcol((uint)S[C3&255] ^ (((uint)S[(C0>>8)&255])<<8) ^ (((uint)S[(C1>>16)&255])<<16) ^ (((uint)S[(C2>>24)&255])<<24)) ^ KW[r++,3]; + C0 = Mcol((uint)S[r0&255] ^ (((uint)S[(r1>>8)&255])<<8) ^ (((uint)S[(r2>>16)&255])<<16) ^ (((uint)S[(r3>>24)&255])<<24)) ^ KW[r,0]; + C1 = Mcol((uint)S[r1&255] ^ (((uint)S[(r2>>8)&255])<<8) ^ (((uint)S[(r3>>16)&255])<<16) ^ (((uint)S[(r0>>24)&255])<<24)) ^ KW[r,1]; + C2 = Mcol((uint)S[r2&255] ^ (((uint)S[(r3>>8)&255])<<8) ^ (((uint)S[(r0>>16)&255])<<16) ^ (((uint)S[(r1>>24)&255])<<24)) ^ KW[r,2]; + C3 = Mcol((uint)S[r3&255] ^ (((uint)S[(r0>>8)&255])<<8) ^ (((uint)S[(r1>>16)&255])<<16) ^ (((uint)S[(r2>>24)&255])<<24)) ^ KW[r++,3]; + } + + r0 = Mcol((uint)S[C0&255] ^ (((uint)S[(C1>>8)&255])<<8) ^ (((uint)S[(C2>>16)&255])<<16) ^ (((uint)S[(C3>>24)&255])<<24)) ^ KW[r,0]; + r1 = Mcol((uint)S[C1&255] ^ (((uint)S[(C2>>8)&255])<<8) ^ (((uint)S[(C3>>16)&255])<<16) ^ (((uint)S[(C0>>24)&255])<<24)) ^ KW[r,1]; + r2 = Mcol((uint)S[C2&255] ^ (((uint)S[(C3>>8)&255])<<8) ^ (((uint)S[(C0>>16)&255])<<16) ^ (((uint)S[(C1>>24)&255])<<24)) ^ KW[r,2]; + r3 = Mcol((uint)S[C3&255] ^ (((uint)S[(C0>>8)&255])<<8) ^ (((uint)S[(C1>>16)&255])<<16) ^ (((uint)S[(C2>>24)&255])<<24)) ^ KW[r++,3]; + + // the final round is a simple function of S + + C0 = (uint)S[r0&255] ^ (((uint)S[(r1>>8)&255])<<8) ^ (((uint)S[(r2>>16)&255])<<16) ^ (((uint)S[(r3>>24)&255])<<24) ^ KW[r,0]; + C1 = (uint)S[r1&255] ^ (((uint)S[(r2>>8)&255])<<8) ^ (((uint)S[(r3>>16)&255])<<16) ^ (((uint)S[(r0>>24)&255])<<24) ^ KW[r,1]; + C2 = (uint)S[r2&255] ^ (((uint)S[(r3>>8)&255])<<8) ^ (((uint)S[(r0>>16)&255])<<16) ^ (((uint)S[(r1>>24)&255])<<24) ^ KW[r,2]; + C3 = (uint)S[r3&255] ^ (((uint)S[(r0>>8)&255])<<8) ^ (((uint)S[(r1>>16)&255])<<16) ^ (((uint)S[(r2>>24)&255])<<24) ^ KW[r,3]; + } + + private void DecryptBlock( + uint[,] KW) + { + int r; + uint 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((uint)Si[C0&255] ^ (((uint)Si[(C3>>8)&255])<<8) ^ (((uint)Si[(C2>>16)&255])<<16) ^ ((uint)Si[(C1>>24)&255]<<24)) ^ KW[r,0]; + r1 = Inv_Mcol((uint)Si[C1&255] ^ (((uint)Si[(C0>>8)&255])<<8) ^ (((uint)Si[(C3>>16)&255])<<16) ^ ((uint)Si[(C2>>24)&255]<<24)) ^ KW[r,1]; + r2 = Inv_Mcol((uint)Si[C2&255] ^ (((uint)Si[(C1>>8)&255])<<8) ^ (((uint)Si[(C0>>16)&255])<<16) ^ ((uint)Si[(C3>>24)&255]<<24)) ^ KW[r,2]; + r3 = Inv_Mcol((uint)Si[C3&255] ^ (((uint)Si[(C2>>8)&255])<<8) ^ (((uint)Si[(C1>>16)&255])<<16) ^ ((uint)Si[(C0>>24)&255]<<24)) ^ KW[r--,3]; + C0 = Inv_Mcol((uint)Si[r0&255] ^ (((uint)Si[(r3>>8)&255])<<8) ^ (((uint)Si[(r2>>16)&255])<<16) ^ ((uint)Si[(r1>>24)&255]<<24)) ^ KW[r,0]; + C1 = Inv_Mcol((uint)Si[r1&255] ^ (((uint)Si[(r0>>8)&255])<<8) ^ (((uint)Si[(r3>>16)&255])<<16) ^ ((uint)Si[(r2>>24)&255]<<24)) ^ KW[r,1]; + C2 = Inv_Mcol((uint)Si[r2&255] ^ (((uint)Si[(r1>>8)&255])<<8) ^ (((uint)Si[(r0>>16)&255])<<16) ^ ((uint)Si[(r3>>24)&255]<<24)) ^ KW[r,2]; + C3 = Inv_Mcol((uint)Si[r3&255] ^ (((uint)Si[(r2>>8)&255])<<8) ^ (((uint)Si[(r1>>16)&255])<<16) ^ ((uint)Si[(r0>>24)&255]<<24)) ^ KW[r--,3]; + } + + r0 = Inv_Mcol((uint)Si[C0&255] ^ (((uint)Si[(C3>>8)&255])<<8) ^ (((uint)Si[(C2>>16)&255])<<16) ^ ((uint)Si[(C1>>24)&255]<<24)) ^ KW[r,0]; + r1 = Inv_Mcol((uint)Si[C1&255] ^ (((uint)Si[(C0>>8)&255])<<8) ^ (((uint)Si[(C3>>16)&255])<<16) ^ ((uint)Si[(C2>>24)&255]<<24)) ^ KW[r,1]; + r2 = Inv_Mcol((uint)Si[C2&255] ^ (((uint)Si[(C1>>8)&255])<<8) ^ (((uint)Si[(C0>>16)&255])<<16) ^ ((uint)Si[(C3>>24)&255]<<24)) ^ KW[r,2]; + r3 = Inv_Mcol((uint)Si[C3&255] ^ (((uint)Si[(C2>>8)&255])<<8) ^ (((uint)Si[(C1>>16)&255])<<16) ^ ((uint)Si[(C0>>24)&255]<<24)) ^ KW[r,3]; + + // the final round's table is a simple function of Si + + C0 = (uint)Si[r0&255] ^ (((uint)Si[(r3>>8)&255])<<8) ^ (((uint)Si[(r2>>16)&255])<<16) ^ (((uint)Si[(r1>>24)&255])<<24) ^ KW[0,0]; + C1 = (uint)Si[r1&255] ^ (((uint)Si[(r0>>8)&255])<<8) ^ (((uint)Si[(r3>>16)&255])<<16) ^ (((uint)Si[(r2>>24)&255])<<24) ^ KW[0,1]; + C2 = (uint)Si[r2&255] ^ (((uint)Si[(r1>>8)&255])<<8) ^ (((uint)Si[(r0>>16)&255])<<16) ^ (((uint)Si[(r3>>24)&255])<<24) ^ KW[0,2]; + C3 = (uint)Si[r3&255] ^ (((uint)Si[(r2>>8)&255])<<8) ^ (((uint)Si[(r1>>16)&255])<<16) ^ (((uint)Si[(r0>>24)&255])<<24) ^ KW[0,3]; + } + } +} diff --git a/Crypto/src/crypto/engines/AesWrapEngine.cs b/Crypto/src/crypto/engines/AesWrapEngine.cs new file mode 100644
index 000000000..1ce01542b --- /dev/null +++ b/Crypto/src/crypto/engines/AesWrapEngine.cs
@@ -0,0 +1,16 @@ +namespace Org.BouncyCastle.Crypto.Engines +{ + /// <remarks> + /// 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>. + /// </remarks> + public class AesWrapEngine + : Rfc3394WrapEngine + { + public AesWrapEngine() + : base(new AesEngine()) + { + } + } +} diff --git a/Crypto/src/crypto/engines/BlowfishEngine.cs b/Crypto/src/crypto/engines/BlowfishEngine.cs new file mode 100644
index 000000000..8f80f712e --- /dev/null +++ b/Crypto/src/crypto/engines/BlowfishEngine.cs
@@ -0,0 +1,561 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * 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 sealed class BlowfishEngine + : IBlockCipher + { + private readonly static uint[] 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 readonly int ROUNDS = 16; + private const int BLOCK_SIZE = 8; // bytes = 64 bits + private static readonly int SBOX_SK = 256; + private static readonly int P_SZ = ROUNDS+2; + + private readonly uint[] S0, S1, S2, S3; // the s-boxes + private readonly uint[] P; // the p-array + + private bool encrypting; + + private byte[] workingKey; + + public BlowfishEngine() + { + S0 = new uint[SBOX_SK]; + S1 = new uint[SBOX_SK]; + S2 = new uint[SBOX_SK]; + S3 = new uint[SBOX_SK]; + P = new uint[P_SZ]; + } + + /** + * initialise a Blowfish cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + throw new ArgumentException("invalid parameter passed to Blowfish init - " + parameters.GetType().ToString()); + + this.encrypting = forEncryption; + this.workingKey = ((KeyParameter)parameters).GetKey(); + SetKey(this.workingKey); + } + + public string AlgorithmName + { + get { return "Blowfish"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (workingKey == null) + { + throw new InvalidOperationException("Blowfish not initialised"); + } + + if ((inOff + BLOCK_SIZE) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + BLOCK_SIZE) > output.Length) + { + throw new DataLengthException("output buffer too short"); + } + + if (encrypting) + { + EncryptBlock(input, inOff, output, outOff); + } + else + { + DecryptBlock(input, inOff, output, outOff); + } + + return BLOCK_SIZE; + } + + public void Reset() + { + } + + public int GetBlockSize() + { + return BLOCK_SIZE; + } + + //================================== + // Private Implementation + //================================== + + private uint F(uint 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( + uint xl, + uint xr, + uint[] 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...) + */ + Array.Copy(KS0, 0, S0, 0, SBOX_SK); + Array.Copy(KS1, 0, S1, 0, SBOX_SK); + Array.Copy(KS2, 0, S2, 0, SBOX_SK); + Array.Copy(KS3, 0, S3, 0, SBOX_SK); + + Array.Copy(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 + uint data = 0x0000000; + for (int j=0; j < 4; j++) + { + // create a 32 bit block + data = (data << 8) | (uint)key[keyIndex++]; + + // 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) + { + uint xl = Pack.BE_To_UInt32(src, srcIndex); + uint xr = Pack.BE_To_UInt32(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]; + + Pack.UInt32_To_BE(xr, dst, dstIndex); + Pack.UInt32_To_BE(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) + { + uint xl = Pack.BE_To_UInt32(src, srcIndex); + uint xr = Pack.BE_To_UInt32(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]; + + Pack.UInt32_To_BE(xr, dst, dstIndex); + Pack.UInt32_To_BE(xl, dst, dstIndex + 4); + } + } +} diff --git a/Crypto/src/crypto/engines/CamelliaEngine.cs b/Crypto/src/crypto/engines/CamelliaEngine.cs new file mode 100644
index 000000000..8f4a442e9 --- /dev/null +++ b/Crypto/src/crypto/engines/CamelliaEngine.cs
@@ -0,0 +1,669 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * Camellia - based on RFC 3713. + */ + public class CamelliaEngine + : IBlockCipher + { + private bool initialised = false; + private bool _keyIs128; + + private const int BLOCK_SIZE = 16; + + private uint[] subkey = new uint[24 * 4]; + private uint[] kw = new uint[4 * 2]; // for whitening + private uint[] ke = new uint[6 * 2]; // for FL and FL^(-1) + private uint[] state = new uint[4]; // for encryption and decryption + + private static readonly uint[] SIGMA = new uint[]{ + 0xa09e667f, 0x3bcc908b, + 0xb67ae858, 0x4caa73b2, + 0xc6ef372f, 0xe94f82be, + 0x54ff53a5, 0xf1d36f1c, + 0x10e527fa, 0xde682d1d, + 0xb05688c2, 0xb3e6c1fd + }; + + /* + * + * S-box data + * + */ + private static readonly uint[] SBOX1_1110 = new uint[]{ + 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 readonly uint[] SBOX4_4404 = new uint[]{ + 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 readonly uint[] SBOX2_0222 = new uint[]{ + 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 readonly uint[] SBOX3_3033 = new uint[]{ + 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 uint rightRotate(uint x, int s) + { + return ((x >> s) + (x << (32 - s))); + } + + private static uint leftRotate(uint x, int s) + { + return (x << s) + (x >> (32 - s)); + } + + private static void roldq(int rot, uint[] ki, int ioff, uint[] 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, uint[] ki, int ioff, uint[] 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, uint[] ki, int ioff, uint[] 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, uint[] ki, int ioff, uint[] 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 static uint bytes2uint(byte[] src, int offset) + { + uint word = 0; + for (int i = 0; i < 4; i++) + { + word = (word << 8) + (uint)src[i + offset]; + } + return word; + } + + private static void uint2bytes(uint word, byte[] dst, int offset) + { + for (int i = 0; i < 4; i++) + { + dst[(3 - i) + offset] = (byte)word; + word >>= 8; + } + } + + private static void camelliaF2(uint[] s, uint[] skey, int keyoff) + { + uint t1, t2, u, v; + + t1 = s[0] ^ skey[0 + keyoff]; + u = SBOX4_4404[(byte)t1]; + u ^= SBOX3_3033[(byte)(t1 >> 8)]; + u ^= SBOX2_0222[(byte)(t1 >> 16)]; + u ^= SBOX1_1110[(byte)(t1 >> 24)]; + t2 = s[1] ^ skey[1 + keyoff]; + v = SBOX1_1110[(byte)t2]; + v ^= SBOX4_4404[(byte)(t2 >> 8)]; + v ^= SBOX3_3033[(byte)(t2 >> 16)]; + v ^= SBOX2_0222[(byte)(t2 >> 24)]; + + s[2] ^= u ^ v; + s[3] ^= u ^ v ^ rightRotate(u, 8); + + t1 = s[2] ^ skey[2 + keyoff]; + u = SBOX4_4404[(byte)t1]; + u ^= SBOX3_3033[(byte)(t1 >> 8)]; + u ^= SBOX2_0222[(byte)(t1 >> 16)]; + u ^= SBOX1_1110[(byte)(t1 >> 24)]; + t2 = s[3] ^ skey[3 + keyoff]; + v = SBOX1_1110[(byte)t2]; + v ^= SBOX4_4404[(byte)(t2 >> 8)]; + v ^= SBOX3_3033[(byte)(t2 >> 16)]; + v ^= SBOX2_0222[(byte)(t2 >> 24)]; + + s[0] ^= u ^ v; + s[1] ^= u ^ v ^ rightRotate(u, 8); + } + + private static void camelliaFLs(uint[] s, uint[] 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(bool forEncryption, byte[] key) + { + uint[] k = new uint[8]; + uint[] ka = new uint[4]; + uint[] kb = new uint[4]; + uint[] t = new uint[4]; + + switch (key.Length) + { + case 16: + _keyIs128 = true; + k[0] = bytes2uint(key, 0); + k[1] = bytes2uint(key, 4); + k[2] = bytes2uint(key, 8); + k[3] = bytes2uint(key, 12); + k[4] = k[5] = k[6] = k[7] = 0; + break; + case 24: + k[0] = bytes2uint(key, 0); + k[1] = bytes2uint(key, 4); + k[2] = bytes2uint(key, 8); + k[3] = bytes2uint(key, 12); + k[4] = bytes2uint(key, 16); + k[5] = bytes2uint(key, 20); + k[6] = ~k[4]; + k[7] = ~k[5]; + _keyIs128 = false; + break; + case 32: + k[0] = bytes2uint(key, 0); + k[1] = bytes2uint(key, 4); + k[2] = bytes2uint(key, 8); + k[3] = bytes2uint(key, 12); + k[4] = bytes2uint(key, 16); + k[5] = bytes2uint(key, 20); + k[6] = bytes2uint(key, 24); + k[7] = bytes2uint(key, 28); + _keyIs128 = false; + break; + default: + throw new ArgumentException("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[] input, int inOff, byte[] output, int outOff) + { + for (int i = 0; i < 4; i++) + { + state[i] = bytes2uint(input, 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]; + + uint2bytes(state[2], output, outOff); + uint2bytes(state[3], output, outOff + 4); + uint2bytes(state[0], output, outOff + 8); + uint2bytes(state[1], output, outOff + 12); + + return BLOCK_SIZE; + } + + private int processBlock192or256(byte[] input, int inOff, byte[] output, int outOff) + { + for (int i = 0; i < 4; i++) + { + state[i] = bytes2uint(input, 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]; + + uint2bytes(state[2], output, outOff); + uint2bytes(state[3], output, outOff + 4); + uint2bytes(state[0], output, outOff + 8); + uint2bytes(state[1], output, outOff + 12); + return BLOCK_SIZE; + } + + public CamelliaEngine() + { + } + + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + throw new ArgumentException("only simple KeyParameter expected."); + + setKey(forEncryption, ((KeyParameter)parameters).GetKey()); + + initialised = true; + } + + public string AlgorithmName + { + get { return "Camellia"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return BLOCK_SIZE; + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (!initialised) + throw new InvalidOperationException("Camellia engine not initialised"); + if ((inOff + BLOCK_SIZE) > input.Length) + throw new DataLengthException("input buffer too short"); + if ((outOff + BLOCK_SIZE) > output.Length) + throw new DataLengthException("output buffer too short"); + + if (_keyIs128) + { + return processBlock128(input, inOff, output, outOff); + } + else + { + return processBlock192or256(input, inOff, output, outOff); + } + } + + public void Reset() + { + // nothing + } + } +} diff --git a/Crypto/src/crypto/engines/CamelliaLightEngine.cs b/Crypto/src/crypto/engines/CamelliaLightEngine.cs new file mode 100644
index 000000000..a301eb55e --- /dev/null +++ b/Crypto/src/crypto/engines/CamelliaLightEngine.cs
@@ -0,0 +1,581 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * Camellia - based on RFC 3713, smaller implementation, about half the size of CamelliaEngine. + */ + public class CamelliaLightEngine + : IBlockCipher + { + private const int BLOCK_SIZE = 16; +// private const int MASK8 = 0xff; + private bool initialised; + private bool _keyis128; + + private uint[] subkey = new uint[24 * 4]; + private uint[] kw = new uint[4 * 2]; // for whitening + private uint[] ke = new uint[6 * 2]; // for FL and FL^(-1) + private uint[] state = new uint[4]; // for encryption and decryption + + private static readonly uint[] SIGMA = { + 0xa09e667f, 0x3bcc908b, + 0xb67ae858, 0x4caa73b2, + 0xc6ef372f, 0xe94f82be, + 0x54ff53a5, 0xf1d36f1c, + 0x10e527fa, 0xde682d1d, + 0xb05688c2, 0xb3e6c1fd + }; + + /* + * + * S-box data + * + */ + private static readonly 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 uint rightRotate(uint x, int s) + { + return ((x >> s) + (x << (32 - s))); + } + + private static uint leftRotate(uint x, int s) + { + return (x << s) + (x >> (32 - s)); + } + + private static void roldq(int rot, uint[] ki, int ioff, uint[] 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, uint[] ki, int ioff, uint[] 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, uint[] ki, int ioff, uint[] 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, uint[] ki, int ioff, uint[] 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 static uint bytes2uint(byte[] src, int offset) + { + uint word = 0; + for (int i = 0; i < 4; i++) + { + word = (word << 8) + (uint)src[i + offset]; + } + return word; + } + + private static void uint2bytes(uint 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)(((uint)v << rot) | ((uint)v >> (8 - rot))); + } + + private uint sbox2(int x) + { + return (uint)lRot8(SBOX1[x], 1); + } + + private uint sbox3(int x) + { + return (uint)lRot8(SBOX1[x], 7); + } + + private uint sbox4(int x) + { + return (uint)SBOX1[lRot8((byte)x, 1)]; + } + + private void camelliaF2(uint[] s, uint[] skey, int keyoff) + { + uint t1, t2, u, v; + + t1 = s[0] ^ skey[0 + keyoff]; + u = sbox4((byte)t1); + u |= (sbox3((byte)(t1 >> 8)) << 8); + u |= (sbox2((byte)(t1 >> 16)) << 16); + u |= ((uint)(SBOX1[(byte)(t1 >> 24)]) << 24); + + t2 = s[1] ^ skey[1 + keyoff]; + v = (uint)SBOX1[(byte)t2]; + v |= (sbox4((byte)(t2 >> 8)) << 8); + v |= (sbox3((byte)(t2 >> 16)) << 16); + v |= (sbox2((byte)(t2 >> 24)) << 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((byte)t1); + u |= sbox3((byte)(t1 >> 8)) << 8; + u |= sbox2((byte)(t1 >> 16)) << 16; + u |= ((uint)SBOX1[(byte)(t1 >> 24)]) << 24; + + t2 = s[3] ^ skey[3 + keyoff]; + v = (uint)SBOX1[(byte)t2]; + v |= sbox4((byte)(t2 >> 8)) << 8; + v |= sbox3((byte)(t2 >> 16)) << 16; + v |= sbox2((byte)(t2 >> 24)) << 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(uint[] s, uint[] 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(bool forEncryption, byte[] key) + { + uint[] k = new uint[8]; + uint[] ka = new uint[4]; + uint[] kb = new uint[4]; + uint[] t = new uint[4]; + + switch (key.Length) + { + case 16: + _keyis128 = true; + k[0] = bytes2uint(key, 0); + k[1] = bytes2uint(key, 4); + k[2] = bytes2uint(key, 8); + k[3] = bytes2uint(key, 12); + k[4] = k[5] = k[6] = k[7] = 0; + break; + case 24: + k[0] = bytes2uint(key, 0); + k[1] = bytes2uint(key, 4); + k[2] = bytes2uint(key, 8); + k[3] = bytes2uint(key, 12); + k[4] = bytes2uint(key, 16); + k[5] = bytes2uint(key, 20); + k[6] = ~k[4]; + k[7] = ~k[5]; + _keyis128 = false; + break; + case 32: + k[0] = bytes2uint(key, 0); + k[1] = bytes2uint(key, 4); + k[2] = bytes2uint(key, 8); + k[3] = bytes2uint(key, 12); + k[4] = bytes2uint(key, 16); + k[5] = bytes2uint(key, 20); + k[6] = bytes2uint(key, 24); + k[7] = bytes2uint(key, 28); + _keyis128 = false; + break; + default: + throw new ArgumentException("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[] input, int inOff, byte[] output, int outOff) + { + for (int i = 0; i < 4; i++) + { + state[i] = bytes2uint(input, 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]; + + uint2bytes(state[2], output, outOff); + uint2bytes(state[3], output, outOff + 4); + uint2bytes(state[0], output, outOff + 8); + uint2bytes(state[1], output, outOff + 12); + + return BLOCK_SIZE; + } + + private int processBlock192or256(byte[] input, int inOff, byte[] output, int outOff) + { + for (int i = 0; i < 4; i++) + { + state[i] = bytes2uint(input, 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]; + + uint2bytes(state[2], output, outOff); + uint2bytes(state[3], output, outOff + 4); + uint2bytes(state[0], output, outOff + 8); + uint2bytes(state[1], output, outOff + 12); + return BLOCK_SIZE; + } + + public CamelliaLightEngine() + { + initialised = false; + } + + public string AlgorithmName + { + get { return "Camellia"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return BLOCK_SIZE; + } + + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + throw new ArgumentException("only simple KeyParameter expected."); + + setKey(forEncryption, ((KeyParameter)parameters).GetKey()); + + initialised = true; + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (!initialised) + throw new InvalidOperationException("Camellia engine not initialised"); + if ((inOff + BLOCK_SIZE) > input.Length) + throw new DataLengthException("input buffer too short"); + if ((outOff + BLOCK_SIZE) > output.Length) + throw new DataLengthException("output buffer too short"); + + if (_keyis128) + { + return processBlock128(input, inOff, output, outOff); + } + else + { + return processBlock192or256(input, inOff, output, outOff); + } + } + + public void Reset() + { + } + } +} diff --git a/Crypto/src/crypto/engines/CamelliaWrapEngine.cs b/Crypto/src/crypto/engines/CamelliaWrapEngine.cs new file mode 100644
index 000000000..49dc833e6 --- /dev/null +++ b/Crypto/src/crypto/engines/CamelliaWrapEngine.cs
@@ -0,0 +1,16 @@ +namespace Org.BouncyCastle.Crypto.Engines +{ + /// <remarks> + /// 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>. + /// </remarks> + public class CamelliaWrapEngine + : Rfc3394WrapEngine + { + public CamelliaWrapEngine() + : base(new CamelliaEngine()) + { + } + } +} diff --git a/Crypto/src/crypto/engines/Cast5Engine.cs b/Crypto/src/crypto/engines/Cast5Engine.cs new file mode 100644
index 000000000..4c3f84a55 --- /dev/null +++ b/Crypto/src/crypto/engines/Cast5Engine.cs
@@ -0,0 +1,802 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * 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 + : IBlockCipher + { + internal static readonly uint[] 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 + //==================================== + + internal static readonly int MAX_ROUNDS = 16; + internal static readonly int RED_ROUNDS = 12; + + private const int BLOCK_SIZE = 8; // bytes = 64 bits + + private int[] _Kr = new int[17]; // the rotating round key + private uint[] _Km = new uint[17]; // the masking round key + + private bool _encrypting; + + private byte[] _workingKey; + private int _rounds = MAX_ROUNDS; + + public Cast5Engine() + { + } + + /** + * initialise a CAST cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + throw new ArgumentException("Invalid parameter passed to "+ AlgorithmName +" init - " + parameters.GetType().ToString()); + + _encrypting = forEncryption; + _workingKey = ((KeyParameter)parameters).GetKey(); + SetKey(_workingKey); + } + + public virtual string AlgorithmName + { + get { return "CAST5"; } + } + + public virtual bool IsPartialBlockOkay + { + get { return false; } + } + + public virtual int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + int blockSize = GetBlockSize(); + if (_workingKey == null) + throw new InvalidOperationException(AlgorithmName + " not initialised"); + if ((inOff + blockSize) > input.Length) + throw new DataLengthException("Input buffer too short"); + if ((outOff + blockSize) > output.Length) + throw new DataLengthException("Output buffer too short"); + + if (_encrypting) + { + return EncryptBlock(input, inOff, output, outOff); + } + else + { + return DecryptBlock(input, inOff, output, outOff); + } + } + + public virtual void Reset() + { + } + + public virtual int GetBlockSize() + { + return BLOCK_SIZE; + } + + //================================== + // Private Implementation + //================================== + + /* + * Creates the subkeys using the same nomenclature + * as described in RFC2144. + * + * See section 2.4 + */ + internal virtual 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]; + + uint z03, z47, z8B, zCF; + uint x03, x47, x8B, xCF; + + /* copy the key into x */ + for (int i=0; i< key.Length; i++) + { + x[i] = (int)(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]=(int)((S5[z[0x8]]^S6[z[0x9]]^S7[z[0x7]]^S8[z[0x6]] ^ S5[z[0x2]])&0x1f); + _Kr[ 2]=(int)((S5[z[0xA]]^S6[z[0xB]]^S7[z[0x5]]^S8[z[0x4]] ^ S6[z[0x6]])&0x1f); + _Kr[ 3]=(int)((S5[z[0xC]]^S6[z[0xD]]^S7[z[0x3]]^S8[z[0x2]] ^ S7[z[0x9]])&0x1f); + _Kr[ 4]=(int)((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]=(int)((S5[x[0x3]]^S6[x[0x2]]^S7[x[0xC]]^S8[x[0xD]]^S5[x[0x8]])&0x1f); + _Kr[ 6]=(int)((S5[x[0x1]]^S6[x[0x0]]^S7[x[0xE]]^S8[x[0xF]]^S6[x[0xD]])&0x1f); + _Kr[ 7]=(int)((S5[x[0x7]]^S6[x[0x6]]^S7[x[0x8]]^S8[x[0x9]]^S7[x[0x3]])&0x1f); + _Kr[ 8]=(int)((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]=(int)((S5[z[0x3]]^S6[z[0x2]]^S7[z[0xC]]^S8[z[0xD]]^S5[z[0x9]])&0x1f); + _Kr[10]=(int)((S5[z[0x1]]^S6[z[0x0]]^S7[z[0xE]]^S8[z[0xF]]^S6[z[0xc]])&0x1f); + _Kr[11]=(int)((S5[z[0x7]]^S6[z[0x6]]^S7[z[0x8]]^S8[z[0x9]]^S7[z[0x2]])&0x1f); + _Kr[12]=(int)((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]=(int)((S5[x[0x8]]^S6[x[0x9]]^S7[x[0x7]]^S8[x[0x6]]^S5[x[0x3]])&0x1f); + _Kr[14]=(int)((S5[x[0xA]]^S6[x[0xB]]^S7[x[0x5]]^S8[x[0x4]]^S6[x[0x7]])&0x1f); + _Kr[15]=(int)((S5[x[0xC]]^S6[x[0xD]]^S7[x[0x3]]^S8[x[0x2]]^S7[x[0x8]])&0x1f); + _Kr[16]=(int)((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 + */ + internal virtual int EncryptBlock( + byte[] src, + int srcIndex, + byte[] dst, + int dstIndex) + { + // 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 + + uint L0 = Pack.BE_To_UInt32(src, srcIndex); + uint R0 = Pack.BE_To_UInt32(src, srcIndex + 4); + + uint[] result = new uint[2]; + CAST_Encipher(L0, R0, result); + + // now stuff them into the destination block + Pack.UInt32_To_BE(result[0], dst, dstIndex); + Pack.UInt32_To_BE(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 + */ + internal virtual int DecryptBlock( + byte[] src, + int srcIndex, + byte[] dst, + int dstIndex) + { + // 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 + uint L16 = Pack.BE_To_UInt32(src, srcIndex); + uint R16 = Pack.BE_To_UInt32(src, srcIndex + 4); + + uint[] result = new uint[2]; + CAST_Decipher(L16, R16, result); + + // now stuff them into the destination block + Pack.UInt32_To_BE(result[0], dst, dstIndex); + Pack.UInt32_To_BE(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 + * + */ + internal static uint F1(uint D, uint Kmi, int Kri) + { + uint 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 + * + */ + internal static uint F2(uint D, uint Kmi, int Kri) + { + uint 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 + * + */ + internal static uint F3(uint D, uint Kmi, int Kri) + { + uint 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 + */ + internal void CAST_Encipher(uint L0, uint R0, uint[] result) + { + uint Lp = L0; // the previous value, equiv to L[i-1] + uint Rp = R0; // equivalent to R[i-1] + + /* + * numbering consistent with paper to make + * checking and validating easier + */ + uint 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; + } + + internal void CAST_Decipher(uint L16, uint R16, uint[] result) + { + uint Lp = L16; // the previous value, equiv to L[i-1] + uint Rp = R16; // equivalent to R[i-1] + + /* + * numbering consistent with paper to make + * checking and validating easier + */ + uint 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; + } + + internal static void Bits32ToInts(uint inData, int[] b, int offset) + { + b[offset + 3] = (int) (inData & 0xff); + b[offset + 2] = (int) ((inData >> 8) & 0xff); + b[offset + 1] = (int) ((inData >> 16) & 0xff); + b[offset] = (int) ((inData >> 24) & 0xff); + } + + internal static uint IntsTo32bits(int[] b, int i) + { + return (uint)(((b[i] & 0xff) << 24) | + ((b[i+1] & 0xff) << 16) | + ((b[i+2] & 0xff) << 8) | + ((b[i+3] & 0xff))); + } + } +} diff --git a/Crypto/src/crypto/engines/Cast6Engine.cs b/Crypto/src/crypto/engines/Cast6Engine.cs new file mode 100644
index 000000000..c5c419b78 --- /dev/null +++ b/Crypto/src/crypto/engines/Cast6Engine.cs
@@ -0,0 +1,279 @@ +using System; + +using Org.BouncyCastle.Crypto.Utilities; + +namespace 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 sealed class Cast6Engine + : Cast5Engine + { + //==================================== + // Useful constants + //==================================== + private const int ROUNDS = 12; + private const int BLOCK_SIZE = 16; // bytes = 128 bits + + /* + * Put the round and mask keys into an array. + * Kr0[i] => _Kr[i*4 + 0] + */ + private int []_Kr = new int[ROUNDS*4]; // the rotating round key(s) + private uint []_Km = new uint[ROUNDS*4]; // the masking round key(s) + + /* + * Key setup + */ + private int []_Tr = new int[24 * 8]; + private uint []_Tm = new uint[24 * 8]; + private uint[] _workingKey = new uint[8]; + + public Cast6Engine() + { + } + + public override string AlgorithmName + { + get { return "CAST6"; } + } + + public override void Reset() + { + } + + public override int GetBlockSize() + { + return BLOCK_SIZE; + } + + //================================== + // Private Implementation + //================================== + /* + * Creates the subkeys using the same nomenclature + * as described in RFC2612. + * + * See section 2.4 + */ + internal override void SetKey( + byte[] key) + { + uint Cm = 0x5a827999; + uint 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 += Mm; //mod 2^32; + _Tr[i*8 + j] = Cr; + Cr = (Cr + Mr) & 0x1f; // mod 32 + } + } + + byte[] tmpKey = new byte[64]; + key.CopyTo(tmpKey, 0); + + // now create ABCDEFGH + for (int i = 0; i < 8; i++) + { + _workingKey[i] = Pack.BE_To_UInt32(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] = (int)(_workingKey[0] & 0x1f); + _Kr[i*4 + 1] = (int)(_workingKey[2] & 0x1f); + _Kr[i*4 + 2] = (int)(_workingKey[4] & 0x1f); + _Kr[i*4 + 3] = (int)(_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 + */ + internal override int EncryptBlock( + byte[] src, + int srcIndex, + byte[] dst, + int dstIndex) + { + // process the input block + // batch the units up into 4x32 bit chunks and go for it + uint A = Pack.BE_To_UInt32(src, srcIndex); + uint B = Pack.BE_To_UInt32(src, srcIndex + 4); + uint C = Pack.BE_To_UInt32(src, srcIndex + 8); + uint D = Pack.BE_To_UInt32(src, srcIndex + 12); + uint[] result = new uint[4]; + CAST_Encipher(A, B, C, D, result); + // now stuff them into the destination block + Pack.UInt32_To_BE(result[0], dst, dstIndex); + Pack.UInt32_To_BE(result[1], dst, dstIndex + 4); + Pack.UInt32_To_BE(result[2], dst, dstIndex + 8); + Pack.UInt32_To_BE(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 + */ + internal override int DecryptBlock( + byte[] src, + int srcIndex, + byte[] dst, + int dstIndex) + { + // process the input block + // batch the units up into 4x32 bit chunks and go for it + uint A = Pack.BE_To_UInt32(src, srcIndex); + uint B = Pack.BE_To_UInt32(src, srcIndex + 4); + uint C = Pack.BE_To_UInt32(src, srcIndex + 8); + uint D = Pack.BE_To_UInt32(src, srcIndex + 12); + uint[] result = new uint[4]; + CAST_Decipher(A, B, C, D, result); + // now stuff them into the destination block + Pack.UInt32_To_BE(result[0], dst, dstIndex); + Pack.UInt32_To_BE(result[1], dst, dstIndex + 4); + Pack.UInt32_To_BE(result[2], dst, dstIndex + 8); + Pack.UInt32_To_BE(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 + */ + private void CAST_Encipher( + uint A, + uint B, + uint C, + uint D, + uint[] result) + { + for (int i = 0; i < 6; i++) + { + int 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++) + { + int 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 + */ + private void CAST_Decipher( + uint A, + uint B, + uint C, + uint D, + uint[] result) + { + for (int i = 0; i < 6; i++) + { + int 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++) + { + int 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/Crypto/src/crypto/engines/DesEdeEngine.cs b/Crypto/src/crypto/engines/DesEdeEngine.cs new file mode 100644
index 000000000..b319888e3 --- /dev/null +++ b/Crypto/src/crypto/engines/DesEdeEngine.cs
@@ -0,0 +1,100 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /// <remarks>A class that provides a basic DESede (or Triple DES) engine.</remarks> + public class DesEdeEngine + : DesEngine + { + private int[] workingKey1, workingKey2, workingKey3; + private bool forEncryption; + + /** + * initialise a DESede cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public override void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + { + throw new ArgumentException("invalid parameter passed to DESede init - " + parameters.GetType().ToString()); + } + + byte[] keyMaster = ((KeyParameter)parameters).GetKey(); + + this.forEncryption = forEncryption; + + byte[] key1 = new byte[8]; + Array.Copy(keyMaster, 0, key1, 0, key1.Length); + workingKey1 = GenerateWorkingKey(forEncryption, key1); + + byte[] key2 = new byte[8]; + Array.Copy(keyMaster, 8, key2, 0, key2.Length); + workingKey2 = GenerateWorkingKey(!forEncryption, key2); + + if (keyMaster.Length == 24) + { + byte[] key3 = new byte[8]; + Array.Copy(keyMaster, 16, key3, 0, key3.Length); + workingKey3 = GenerateWorkingKey(forEncryption, key3); + } + else // 16 byte key + { + workingKey3 = workingKey1; + } + } + + public override string AlgorithmName + { + get { return "DESede"; } + } + + public override int GetBlockSize() + { + return BLOCK_SIZE; + } + + public override int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (workingKey1 == null) + throw new InvalidOperationException("DESede engine not initialised"); + if ((inOff + BLOCK_SIZE) > input.Length) + throw new DataLengthException("input buffer too short"); + if ((outOff + BLOCK_SIZE) > output.Length) + throw new DataLengthException("output buffer too short"); + + byte[] temp = new byte[BLOCK_SIZE]; + + if (forEncryption) + { + DesFunc(workingKey1, input, inOff, temp, 0); + DesFunc(workingKey2, temp, 0, temp, 0); + DesFunc(workingKey3, temp, 0, output, outOff); + } + else + { + DesFunc(workingKey3, input, inOff, temp, 0); + DesFunc(workingKey2, temp, 0, temp, 0); + DesFunc(workingKey1, temp, 0, output, outOff); + } + + return BLOCK_SIZE; + } + + public override void Reset() + { + } + } +} diff --git a/Crypto/src/crypto/engines/DesEdeWrapEngine.cs b/Crypto/src/crypto/engines/DesEdeWrapEngine.cs new file mode 100644
index 000000000..fdc71687f --- /dev/null +++ b/Crypto/src/crypto/engines/DesEdeWrapEngine.cs
@@ -0,0 +1,322 @@ +using System; + +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * 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> + * <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.</li> + * </ul> + * </p> + */ + public class DesEdeWrapEngine + : IWrapper + { + /** Field engine */ + private CbcBlockCipher engine; + /** Field param */ + private KeyParameter param; + /** Field paramPlusIV */ + private ParametersWithIV paramPlusIV; + /** Field iv */ + private byte[] iv; + /** Field forWrapping */ + private bool forWrapping; + /** Field IV2 */ + private static readonly byte[] IV2 = { (byte) 0x4a, (byte) 0xdd, (byte) 0xa2, + (byte) 0x2c, (byte) 0x79, (byte) 0xe8, + (byte) 0x21, (byte) 0x05 }; + + // + // checksum digest + // + private readonly IDigest sha1 = new Sha1Digest(); + private readonly byte[] digest = new byte[20]; + + /** + * Method init + * + * @param forWrapping + * @param param + */ + public void Init( + bool forWrapping, + ICipherParameters parameters) + { + this.forWrapping = forWrapping; + this.engine = new CbcBlockCipher(new DesEdeEngine()); + + SecureRandom sr; + if (parameters is ParametersWithRandom) + { + ParametersWithRandom pr = (ParametersWithRandom) parameters; + parameters = pr.Parameters; + sr = pr.Random; + } + else + { + sr = new SecureRandom(); + } + + if (parameters is KeyParameter) + { + this.param = (KeyParameter) parameters; + 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 (parameters is ParametersWithIV) + { + if (!forWrapping) + throw new ArgumentException("You should not supply an IV for unwrapping"); + + this.paramPlusIV = (ParametersWithIV) parameters; + this.iv = this.paramPlusIV.GetIV(); + this.param = (KeyParameter) this.paramPlusIV.Parameters; + + if (this.iv.Length != 8) + throw new ArgumentException("IV is not 8 octets", "parameters"); + } + } + + /** + * Method GetAlgorithmName + * + * @return + */ + public string AlgorithmName + { + get { return "DESede"; } + } + + /** + * Method wrap + * + * @param in + * @param inOff + * @param inLen + * @return + */ + public byte[] Wrap( + byte[] input, + int inOff, + int length) + { + if (!forWrapping) + { + throw new InvalidOperationException("Not initialized for wrapping"); + } + + byte[] keyToBeWrapped = new byte[length]; + Array.Copy(input, inOff, keyToBeWrapped, 0, 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]; + Array.Copy(keyToBeWrapped, 0, WKCKS, 0, keyToBeWrapped.Length); + Array.Copy(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 InvalidOperationException("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]; + Array.Copy(this.iv, 0, TEMP2, 0, this.iv.Length); + Array.Copy(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 + * @throws InvalidCipherTextException + */ + public byte[] Unwrap( + byte[] input, + int inOff, + int length) + { + if (forWrapping) + { + throw new InvalidOperationException("Not set for unwrapping"); + } + if (input == null) + { + throw new InvalidCipherTextException("Null pointer as ciphertext"); + } + + int blockSize = engine.GetBlockSize(); + + if (length % 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[length]; + + for (int currentBytePos = 0; currentBytePos != TEMP3.Length; currentBytePos += blockSize) + { + engine.ProcessBlock(input, 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]; + Array.Copy(TEMP2, 0, this.iv, 0, 8); + Array.Copy(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]; + Array.Copy(WKCKS, 0, result, 0, WKCKS.Length - 8); + Array.Copy(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 + * @throws Exception + * @see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum + */ + private byte[] CalculateCmsKeyChecksum( + byte[] key) + { + sha1.BlockUpdate(key, 0, key.Length); + sha1.DoFinal(digest, 0); + + byte[] result = new byte[8]; + Array.Copy(digest, 0, result, 0, 8); + return result; + } + + /** + * @param key + * @param checksum + * @return + * @see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum + */ + private bool 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/Crypto/src/crypto/engines/DesEngine.cs b/Crypto/src/crypto/engines/DesEngine.cs new file mode 100644
index 000000000..067cf45e3 --- /dev/null +++ b/Crypto/src/crypto/engines/DesEngine.cs
@@ -0,0 +1,475 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /// <remarks>A class that provides a basic DES engine.</remarks> + public class DesEngine + : IBlockCipher + { + internal const int BLOCK_SIZE = 8; + + private int[] workingKey; + + public virtual int[] GetWorkingKey() + { + return workingKey; + } + + /** + * initialise a DES cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public virtual void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + throw new ArgumentException("invalid parameter passed to DES init - " + parameters.GetType().ToString()); + + workingKey = GenerateWorkingKey(forEncryption, ((KeyParameter)parameters).GetKey()); + } + + public virtual string AlgorithmName + { + get { return "DES"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public virtual int GetBlockSize() + { + return BLOCK_SIZE; + } + + public virtual int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (workingKey == null) + throw new InvalidOperationException("DES engine not initialised"); + if ((inOff + BLOCK_SIZE) > input.Length) + throw new DataLengthException("input buffer too short"); + if ((outOff + BLOCK_SIZE) > output.Length) + throw new DataLengthException("output buffer too short"); + + DesFunc(workingKey, input, inOff, output, outOff); + + return BLOCK_SIZE; + } + + public virtual 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 readonly 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 readonly short[] bytebit = + { + 128, 64, 32, 16, 8, 4, 2, 1 + }; + + private static readonly 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 readonly 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 readonly byte[] totrot = + { + 1, 2, 4, 6, 8, 10, 12, 14, + 15, 17, 19, 21, 23, 25, 27, 28 + }; + + private static readonly 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 and Phil Karn. + * (whoever, and wherever they are!). + */ + protected static int[] GenerateWorkingKey( + bool encrypting, + byte[] key) + { + int[] newKey = new int[32]; + bool[] pc1m = new bool[56]; + bool[] pcr = new bool[56]; + + for (int j = 0; j < 56; j++ ) + { + int l = pc1[j]; + + pc1m[j] = ((key[(uint) 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] = (int) ( (uint) ((i1 & 0x00fc0000) << 6) | + (uint) ((i1 & 0x00000fc0) << 10) | + ((uint) (i2 & 0x00fc0000) >> 10) | + ((uint) (i2 & 0x00000fc0) >> 6)); + + newKey[i + 1] = (int) ( (uint) ((i1 & 0x0003f000) << 12) | + (uint) ((i1 & 0x0000003f) << 16) | + ((uint) (i2 & 0x0003f000) >> 4) | + (uint) (i2 & 0x0000003f)); + } + + return newKey; + } + + /** + * the DES engine. + */ + internal static void DesFunc( + int[] wKey, + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + uint left = Pack.BE_To_UInt32(input, inOff); + uint right = Pack.BE_To_UInt32(input, inOff + 4); + uint work; + + 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); + work = (left ^ right) & 0xaaaaaaaa; + left ^= work; + right ^= work; + left = (left << 1) | (left >> 31); + + for (int round = 0; round < 8; round++) + { + uint fval; + + work = (right << 28) | (right >> 4); + work ^= (uint)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 ^ (uint)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 ^= (uint)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 ^ (uint)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); + + Pack.UInt32_To_BE(right, outBytes, outOff); + Pack.UInt32_To_BE(left, outBytes, outOff + 4); + } + } +} diff --git a/Crypto/src/crypto/engines/ElGamalEngine.cs b/Crypto/src/crypto/engines/ElGamalEngine.cs new file mode 100644
index 000000000..3d256a087 --- /dev/null +++ b/Crypto/src/crypto/engines/ElGamalEngine.cs
@@ -0,0 +1,178 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * this does your basic ElGamal algorithm. + */ + public class ElGamalEngine + : IAsymmetricBlockCipher + { + private ElGamalKeyParameters key; + private SecureRandom random; + private bool forEncryption; + private int bitSize; + + public string AlgorithmName + { + get { return "ElGamal"; } + } + + /** + * initialise the ElGamal engine. + * + * @param forEncryption true if we are encrypting, false otherwise. + * @param param the necessary ElGamal key parameters. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (parameters is ParametersWithRandom) + { + ParametersWithRandom p = (ParametersWithRandom) parameters; + + this.key = (ElGamalKeyParameters) p.Parameters; + this.random = p.Random; + } + else + { + this.key = (ElGamalKeyParameters) parameters; + this.random = new SecureRandom(); + } + + this.forEncryption = forEncryption; + this.bitSize = key.Parameters.P.BitLength; + + if (forEncryption) + { + if (!(key is ElGamalPublicKeyParameters)) + { + throw new ArgumentException("ElGamalPublicKeyParameters are required for encryption."); + } + } + else + { + if (!(key is ElGamalPrivateKeyParameters)) + { + throw new ArgumentException("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 length 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[] input, + int inOff, + int length) + { + if (key == null) + throw new InvalidOperationException("ElGamal engine not initialised"); + + int maxLength = forEncryption + ? (bitSize - 1 + 7) / 8 + : GetInputBlockSize(); + + if (length > maxLength) + throw new DataLengthException("input too large for ElGamal cipher.\n"); + + BigInteger p = key.Parameters.P; + + byte[] output; + if (key is ElGamalPrivateKeyParameters) // decryption + { + int halfLength = length / 2; + BigInteger gamma = new BigInteger(1, input, inOff, halfLength); + BigInteger phi = new BigInteger(1, input, inOff + halfLength, halfLength); + + 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(BigInteger.One).Subtract(priv.X), p).Multiply(phi).Mod(p); + + output = m.ToByteArrayUnsigned(); + } + else // encryption + { + BigInteger tmp = new BigInteger(1, input, inOff, length); + + if (tmp.BitLength >= p.BitLength) + throw new DataLengthException("input too large for ElGamal cipher.\n"); + + + ElGamalPublicKeyParameters pub = (ElGamalPublicKeyParameters) key; + + BigInteger pSub2 = p.Subtract(BigInteger.Two); + + // TODO In theory, a series of 'k', 'g.ModPow(k, p)' and 'y.ModPow(k, p)' can be pre-calculated + BigInteger k; + do + { + k = new BigInteger(p.BitLength, random); + } + while (k.SignValue == 0 || k.CompareTo(pSub2) > 0); + + BigInteger g = key.Parameters.G; + BigInteger gamma = g.ModPow(k, p); + BigInteger phi = tmp.Multiply(pub.Y.ModPow(k, p)).Mod(p); + + output = new byte[this.GetOutputBlockSize()]; + + // TODO Add methods to allow writing BigInteger to existing byte array? + byte[] out1 = gamma.ToByteArrayUnsigned(); + byte[] out2 = phi.ToByteArrayUnsigned(); + out1.CopyTo(output, output.Length / 2 - out1.Length); + out2.CopyTo(output, output.Length - out2.Length); + } + + return output; + } + } +} diff --git a/Crypto/src/crypto/engines/GOST28147Engine.cs b/Crypto/src/crypto/engines/GOST28147Engine.cs new file mode 100644
index 000000000..17593d2c0 --- /dev/null +++ b/Crypto/src/crypto/engines/GOST28147Engine.cs
@@ -0,0 +1,377 @@ +using System; +using System.Collections; +using System.Globalization; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * implementation of GOST 28147-89 + */ + public class Gost28147Engine + : IBlockCipher + { + private const int BlockSize = 8; + private int[] workingKey = null; + private bool 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 readonly 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 readonly 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 readonly 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 readonly 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 readonly 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 readonly 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 readonly 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 readonly 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 readonly IDictionary sBoxes = Platform.CreateHashtable(); + + static Gost28147Engine() + { + 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.Add(sBoxName.ToUpperInvariant(), sBox); + } + + /** + * standard constructor. + */ + public Gost28147Engine() + { + } + + /** + * initialise an Gost28147 cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (parameters is ParametersWithSBox) + { + ParametersWithSBox param = (ParametersWithSBox)parameters; + + // + // Set the S-Box + // + byte[] sBox = param.GetSBox(); + if (sBox.Length != Sbox_Default.Length) + throw new ArgumentException("invalid S-box passed to GOST28147 init"); + + this.S = Arrays.Clone(sBox); + + // + // set key if there is one + // + if (param.Parameters != null) + { + workingKey = generateWorkingKey(forEncryption, + ((KeyParameter)param.Parameters).GetKey()); + } + } + else if (parameters is KeyParameter) + { + workingKey = generateWorkingKey(forEncryption, + ((KeyParameter)parameters).GetKey()); + } + else if (parameters != null) + { + throw new ArgumentException("invalid parameter passed to Gost28147 init - " + parameters.GetType().Name); + } + } + + public string AlgorithmName + { + get { return "Gost28147"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return BlockSize; + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (workingKey == null) + { + throw new InvalidOperationException("Gost28147 engine not initialised"); + } + + if ((inOff + BlockSize) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + BlockSize) > output.Length) + { + throw new DataLengthException("output buffer too short"); + } + + Gost28147Func(workingKey, input, inOff, output, outOff); + + return BlockSize; + } + + public void Reset() + { + } + + private int[] generateWorkingKey( + bool forEncryption, + byte[] userKey) + { + this.forEncryption = forEncryption; + + if (userKey.Length != 32) + { + throw new ArgumentException("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 + int omLeft = om << 11; + int omRight = (int)(((uint) om) >> (32 - 11)); // Note: Casts required to get unsigned bit rotation + + return omLeft | omRight; + } + + private void Gost28147Func( + int[] workingKey, + byte[] inBytes, + int inOff, + byte[] outBytes, + int outOff) + { + int N1, N2, tmp; //tmp -> for saving N1 + N1 = bytesToint(inBytes, inOff); + N2 = bytesToint(inBytes, inOff + 4); + + if (this.forEncryption) + { + for(int k = 0; k < 3; k++) // 1-24 steps + { + for(int j = 0; j < 8; j++) + { + tmp = N1; + int step = Gost28147_mainStep(N1, workingKey[j]); + N1 = N2 ^ step; // 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, outBytes, outOff); + intTobytes(N2, outBytes, outOff + 4); + } + + //array of bytes to type int + private static int bytesToint( + byte[] inBytes, + int inOff) + { + return (int)((inBytes[inOff + 3] << 24) & 0xff000000) + ((inBytes[inOff + 2] << 16) & 0xff0000) + + ((inBytes[inOff + 1] << 8) & 0xff00) + (inBytes[inOff] & 0xff); + } + + //int to array of bytes + private static void intTobytes( + int num, + byte[] outBytes, + int outOff) + { + outBytes[outOff + 3] = (byte)(num >> 24); + outBytes[outOff + 2] = (byte)(num >> 16); + outBytes[outOff + 1] = (byte)(num >> 8); + outBytes[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[sBoxName.ToUpperInvariant()]; + + if (sBox == null) + { + throw new ArgumentException("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/Crypto/src/crypto/engines/HC128Engine.cs b/Crypto/src/crypto/engines/HC128Engine.cs new file mode 100644
index 000000000..a2d099f87 --- /dev/null +++ b/Crypto/src/crypto/engines/HC128Engine.cs
@@ -0,0 +1,235 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * 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 + : IStreamCipher + { + private uint[] p = new uint[512]; + private uint[] q = new uint[512]; + private uint cnt = 0; + + private static uint F1(uint x) + { + return RotateRight(x, 7) ^ RotateRight(x, 18) ^ (x >> 3); + } + + private static uint F2(uint x) + { + return RotateRight(x, 17) ^ RotateRight(x, 19) ^ (x >> 10); + } + + private uint G1(uint x, uint y, uint z) + { + return (RotateRight(x, 10) ^ RotateRight(z, 23)) + RotateRight(y, 8); + } + + private uint G2(uint x, uint y, uint z) + { + return (RotateLeft(x, 10) ^ RotateLeft(z, 23)) + RotateLeft(y, 8); + } + + private static uint RotateLeft(uint x, int bits) + { + return (x << bits) | (x >> -bits); + } + + private static uint RotateRight(uint x, int bits) + { + return (x >> bits) | (x << -bits); + } + + private uint H1(uint x) + { + return q[x & 0xFF] + q[((x >> 16) & 0xFF) + 256]; + } + + private uint H2(uint x) + { + return p[x & 0xFF] + p[((x >> 16) & 0xFF) + 256]; + } + + private static uint Mod1024(uint x) + { + return x & 0x3FF; + } + + private static uint Mod512(uint x) + { + return x & 0x1FF; + } + + private static uint Dim(uint x, uint y) + { + return Mod512(x - y); + } + + private uint Step() + { + uint j = Mod512(cnt); + uint 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 bool initialised; + + private void Init() + { + if (key.Length != 16) + throw new ArgumentException("The key must be 128 bits long"); + + cnt = 0; + + uint[] w = new uint[1280]; + + for (int i = 0; i < 16; i++) + { + w[i >> 2] |= ((uint)key[i] << (8 * (i & 0x3))); + } + Array.Copy(w, 0, w, 4, 4); + + for (int i = 0; i < iv.Length && i < 16; i++) + { + w[(i >> 2) + 8] |= ((uint)iv[i] << (8 * (i & 0x3))); + } + Array.Copy(w, 8, w, 12, 4); + + for (uint i = 16; i < 1280; i++) + { + w[i] = F2(w[i - 2]) + w[i - 7] + F1(w[i - 15]) + w[i - 16] + i; + } + + Array.Copy(w, 256, p, 0, 512); + Array.Copy(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 AlgorithmName + { + get { 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 ArgumentException if the params argument is + * inappropriate (ie. the key is not 128 bit long). + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + ICipherParameters keyParam = parameters; + + if (parameters is ParametersWithIV) + { + iv = ((ParametersWithIV)parameters).GetIV(); + keyParam = ((ParametersWithIV)parameters).Parameters; + } + else + { + iv = new byte[0]; + } + + if (keyParam is KeyParameter) + { + key = ((KeyParameter)keyParam).GetKey(); + Init(); + } + else + { + throw new ArgumentException( + "Invalid parameter passed to HC128 init - " + parameters.GetType().Name, + "parameters"); + } + + initialised = true; + } + + private byte[] buf = new byte[4]; + private int idx = 0; + + private byte GetByte() + { + if (idx == 0) + { + Pack.UInt32_To_LE(Step(), buf); + } + byte ret = buf[idx]; + idx = idx + 1 & 0x3; + return ret; + } + + public void ProcessBytes( + byte[] input, + int inOff, + int len, + byte[] output, + int outOff) + { + if (!initialised) + throw new InvalidOperationException(AlgorithmName + " not initialised"); + if ((inOff + len) > input.Length) + throw new DataLengthException("input buffer too short"); + if ((outOff + len) > output.Length) + throw new DataLengthException("output buffer too short"); + + for (int i = 0; i < len; i++) + { + output[outOff + i] = (byte)(input[inOff + i] ^ GetByte()); + } + } + + public void Reset() + { + idx = 0; + Init(); + } + + public byte ReturnByte(byte input) + { + return (byte)(input ^ GetByte()); + } + } +} diff --git a/Crypto/src/crypto/engines/HC256Engine.cs b/Crypto/src/crypto/engines/HC256Engine.cs new file mode 100644
index 000000000..da717dab7 --- /dev/null +++ b/Crypto/src/crypto/engines/HC256Engine.cs
@@ -0,0 +1,224 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * 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 + : IStreamCipher + { + private uint[] p = new uint[1024]; + private uint[] q = new uint[1024]; + private uint cnt = 0; + + private uint Step() + { + uint j = cnt & 0x3FF; + uint ret; + if (cnt < 1024) + { + uint x = p[(j - 3 & 0x3FF)]; + uint 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 + { + uint x = q[(j - 3 & 0x3FF)]; + uint 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 bool initialised; + + private void Init() + { + if (key.Length != 32 && key.Length != 16) + throw new ArgumentException("The key must be 128/256 bits long"); + + if (iv.Length < 16) + throw new ArgumentException("The IV must be at least 128 bits long"); + + if (key.Length != 32) + { + byte[] k = new byte[32]; + + Array.Copy(key, 0, k, 0, key.Length); + Array.Copy(key, 0, k, 16, key.Length); + + key = k; + } + + if (iv.Length < 32) + { + byte[] newIV = new byte[32]; + + Array.Copy(iv, 0, newIV, 0, iv.Length); + Array.Copy(iv, 0, newIV, iv.Length, newIV.Length - iv.Length); + + iv = newIV; + } + + cnt = 0; + + uint[] w = new uint[2560]; + + for (int i = 0; i < 32; i++) + { + w[i >> 2] |= ((uint)key[i] << (8 * (i & 0x3))); + } + + for (int i = 0; i < 32; i++) + { + w[(i >> 2) + 8] |= ((uint)iv[i] << (8 * (i & 0x3))); + } + + for (uint i = 16; i < 2560; i++) + { + uint x = w[i - 2]; + uint 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; + } + + Array.Copy(w, 512, p, 0, 1024); + Array.Copy(w, 1536, q, 0, 1024); + + for (int i = 0; i < 4096; i++) + { + Step(); + } + + cnt = 0; + } + + public string AlgorithmName + { + get { 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 ArgumentException if the params argument is + * inappropriate (ie. the key is not 256 bit long). + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + ICipherParameters keyParam = parameters; + + if (parameters is ParametersWithIV) + { + iv = ((ParametersWithIV)parameters).GetIV(); + keyParam = ((ParametersWithIV)parameters).Parameters; + } + else + { + iv = new byte[0]; + } + + if (keyParam is KeyParameter) + { + key = ((KeyParameter)keyParam).GetKey(); + Init(); + } + else + { + throw new ArgumentException( + "Invalid parameter passed to HC256 init - " + parameters.GetType().Name, + "parameters"); + } + + initialised = true; + } + + private byte[] buf = new byte[4]; + private int idx = 0; + + private byte GetByte() + { + if (idx == 0) + { + Pack.UInt32_To_LE(Step(), buf); + } + byte ret = buf[idx]; + idx = idx + 1 & 0x3; + return ret; + } + + public void ProcessBytes( + byte[] input, + int inOff, + int len, + byte[] output, + int outOff) + { + if (!initialised) + throw new InvalidOperationException(AlgorithmName + " not initialised"); + if ((inOff + len) > input.Length) + throw new DataLengthException("input buffer too short"); + if ((outOff + len) > output.Length) + throw new DataLengthException("output buffer too short"); + + for (int i = 0; i < len; i++) + { + output[outOff + i] = (byte)(input[inOff + i] ^ GetByte()); + } + } + + public void Reset() + { + idx = 0; + Init(); + } + + public byte ReturnByte(byte input) + { + return (byte)(input ^ GetByte()); + } + + private static uint RotateRight(uint x, int bits) + { + return (x >> bits) | (x << -bits); + } + } +} diff --git a/Crypto/src/crypto/engines/ISAACEngine.cs b/Crypto/src/crypto/engines/ISAACEngine.cs new file mode 100644
index 000000000..1120a4104 --- /dev/null +++ b/Crypto/src/crypto/engines/ISAACEngine.cs
@@ -0,0 +1,252 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * Implementation of Bob Jenkin's ISAAC (Indirection Shift Accumulate Add and Count). + * see: http://www.burtleburtle.net/bob/rand/isaacafa.html + */ + public class IsaacEngine + : IStreamCipher + { + // Constants + private static readonly int sizeL = 8, + stateArraySize = sizeL<<5; // 256 + + // Cipher's internal state + private uint[] engineState = null, // mm + results = null; // randrsl + private uint 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 bool 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 ArgumentException if the params argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + throw new ArgumentException( + "invalid parameter passed to ISAAC Init - " + parameters.GetType().Name, + "parameters"); + + /* + * ISAAC encryption and decryption is completely + * symmetrical, so the 'forEncryption' is + * irrelevant. + */ + KeyParameter p = (KeyParameter) parameters; + setKey(p.GetKey()); + } + + public byte ReturnByte( + byte input) + { + if (index == 0) + { + isaac(); + keyStream = intToByteLittle(results); + } + + byte output = (byte)(keyStream[index]^input); + index = (index + 1) & 1023; + + return output; + } + + public void ProcessBytes( + byte[] input, + int inOff, + int len, + byte[] output, + int outOff) + { + if (!initialised) + throw new InvalidOperationException(AlgorithmName + " not initialised"); + if ((inOff + len) > input.Length) + throw new DataLengthException("input buffer too short"); + if ((outOff + len) > output.Length) + throw new DataLengthException("output buffer too short"); + + for (int i = 0; i < len; i++) + { + if (index == 0) + { + isaac(); + keyStream = intToByteLittle(results); + } + output[i+outOff] = (byte)(keyStream[index]^input[i+inOff]); + index = (index + 1) & 1023; + } + } + + public string AlgorithmName + { + get { return "ISAAC"; } + } + + public void Reset() + { + setKey(workingKey); + } + + // Private implementation + private void setKey( + byte[] keyBytes) + { + workingKey = keyBytes; + + if (engineState == null) + { + engineState = new uint[stateArraySize]; + } + + if (results == null) + { + results = new uint[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)]; + Array.Copy(keyBytes, 0, t, 0, keyBytes.Length); + for (i = 0; i < t.Length; i+=4) + { + results[i>>2] = byteToIntLittle(t, i); + } + + // It has begun? + uint[] abcdefgh = new uint[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() + { + uint x, y; + + b += ++c; + for (int 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[(int)((uint)x >> 2) & 0xFF] + a + b; + results[i] = b = engineState[(int)((uint)y >> 10) & 0xFF] + x; + } + } + + private void mix(uint[] 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]; + 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]; + } + + private uint byteToIntLittle( + byte[] x, + int offset) + { + uint result = (byte) x[offset + 3]; + result = (result << 8) | x[offset + 2]; + result = (result << 8) | x[offset + 1]; + result = (result << 8) | x[offset + 0]; + return result; + } + + private byte[] intToByteLittle( + uint x) + { + byte[] output = new byte[4]; + output[3] = (byte)x; + output[2] = (byte)(x >> 8); + output[1] = (byte)(x >> 16); + output[0] = (byte)(x >> 24); + return output; + } + + private byte[] intToByteLittle( + uint[] x) + { + byte[] output = new byte[4*x.Length]; + for (int i = 0, j = 0; i < x.Length; i++,j+=4) + { + Array.Copy(intToByteLittle(x[i]), 0, output, j, 4); + } + return output; + } + } +} diff --git a/Crypto/src/crypto/engines/IdeaEngine.cs b/Crypto/src/crypto/engines/IdeaEngine.cs new file mode 100644
index 000000000..f763c5939 --- /dev/null +++ b/Crypto/src/crypto/engines/IdeaEngine.cs
@@ -0,0 +1,341 @@ +#if INCLUDE_IDEA + +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * 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> + * <p> + * It can be found at ftp://ftp.funet.fi/pub/crypt/cryptography/symmetric/idea/ + * </p> + * <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> + * <p> + * Note 2: Due to the requests concerning the above, this algorithm is now only + * included in the extended assembly. It is not included in the default distributions. + * </p> + */ + public class IdeaEngine + : IBlockCipher + { + private const int BLOCK_SIZE = 8; + private int[] workingKey; + /** + * standard constructor. + */ + public IdeaEngine() + { + } + /** + * initialise an IDEA cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + throw new ArgumentException("invalid parameter passed to IDEA init - " + parameters.GetType().ToString()); + + workingKey = GenerateWorkingKey(forEncryption, + ((KeyParameter)parameters).GetKey()); + } + + public string AlgorithmName + { + get { return "IDEA"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return BLOCK_SIZE; + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (workingKey == null) + { + throw new InvalidOperationException("IDEA engine not initialised"); + } + if ((inOff + BLOCK_SIZE) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + if ((outOff + BLOCK_SIZE) > output.Length) + { + throw new DataLengthException("output buffer too short"); + } + IdeaFunc(workingKey, input, inOff, output, outOff); + return BLOCK_SIZE; + } + public void Reset() + { + } + private static readonly int MASK = 0xffff; + private static readonly int BASE = 0x10001; + private int BytesToWord( + byte[] input, + int inOff) + { + return ((input[inOff] << 8) & 0xff00) + (input[inOff + 1] & 0xff); + } + private void WordToBytes( + int word, + byte[] outBytes, + int outOff) + { + outBytes[outOff] = (byte)((uint) word >> 8); + outBytes[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 = (int) ((uint) p >> 16); + x = y - x + ((y < x) ? 1 : 0); + } + return x & MASK; + } + private void IdeaFunc( + int[] workingKey, + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + int x0, x1, x2, x3, t0, t1; + int keyOff = 0; + x0 = BytesToWord(input, inOff); + x1 = BytesToWord(input, inOff + 2); + x2 = BytesToWord(input, inOff + 4); + x3 = BytesToWord(input, 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++]), outBytes, outOff); + WordToBytes(x2 + workingKey[keyOff++], outBytes, outOff + 2); /* NB: Order */ + WordToBytes(x1 + workingKey[keyOff++], outBytes, outOff + 4); + WordToBytes(Mul(x3, workingKey[keyOff]), outBytes, 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]; + Array.Copy(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) + * </p> + */ + 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 + * </p> + */ + 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( + bool forEncryption, + byte[] userKey) + { + if (forEncryption) + { + return ExpandKey(userKey); + } + else + { + return InvertKey(ExpandKey(userKey)); + } + } + } +} + +#endif diff --git a/Crypto/src/crypto/engines/IesEngine.cs b/Crypto/src/crypto/engines/IesEngine.cs new file mode 100644
index 000000000..c49b2a9ee --- /dev/null +++ b/Crypto/src/crypto/engines/IesEngine.cs
@@ -0,0 +1,236 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * support class for constructing intergrated encryption ciphers + * for doing basic message exchanges on top of key agreement ciphers + */ + public class IesEngine + { + private readonly IBasicAgreement agree; + private readonly IDerivationFunction kdf; + private readonly IMac mac; + private readonly BufferedBlockCipher cipher; + private readonly byte[] macBuf; + + private bool forEncryption; + private ICipherParameters privParam, pubParam; + private IesParameters param; + + /** + * 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( + IBasicAgreement agree, + IDerivationFunction kdf, + IMac 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( + IBasicAgreement agree, + IDerivationFunction kdf, + IMac 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( + bool forEncryption, + ICipherParameters privParameters, + ICipherParameters pubParameters, + ICipherParameters iesParameters) + { + this.forEncryption = forEncryption; + this.privParam = privParameters; + this.pubParam = pubParameters; + this.param = (IesParameters)iesParameters; + } + + private byte[] DecryptBlock( + byte[] in_enc, + int inOff, + int inLen, + byte[] z) + { + byte[] M = null; + KeyParameter macKey = null; + KdfParameters kParam = new KdfParameters(z, param.GetDerivationV()); + int macKeySize = param.MacKeySize; + + kdf.Init(kParam); + + inLen -= mac.GetMacSize(); + + if (cipher == null) // stream mode + { + byte[] Buffer = GenerateKdfBytes(kParam, inLen + (macKeySize / 8)); + + M = new byte[inLen]; + + for (int i = 0; i != inLen; i++) + { + M[i] = (byte)(in_enc[inOff + i] ^ Buffer[i]); + } + + macKey = new KeyParameter(Buffer, inLen, (macKeySize / 8)); + } + else + { + int cipherKeySize = ((IesWithCipherParameters)param).CipherKeySize; + byte[] Buffer = GenerateKdfBytes(kParam, (cipherKeySize / 8) + (macKeySize / 8)); + + cipher.Init(false, new KeyParameter(Buffer, 0, (cipherKeySize / 8))); + + M = cipher.DoFinal(in_enc, inOff, inLen); + + macKey = new KeyParameter(Buffer, (cipherKeySize / 8), (macKeySize / 8)); + } + + byte[] macIV = param.GetEncodingV(); + + mac.Init(macKey); + mac.BlockUpdate(in_enc, inOff, inLen); + mac.BlockUpdate(macIV, 0, macIV.Length); + mac.DoFinal(macBuf, 0); + + inOff += inLen; + + for (int t = 0; t < macBuf.Length; t++) + { + if (macBuf[t] != in_enc[inOff + t]) + { + throw (new InvalidCipherTextException("IMac codes failed to equal.")); + } + } + + return M; + } + + private byte[] EncryptBlock( + byte[] input, + int inOff, + int inLen, + byte[] z) + { + byte[] C = null; + KeyParameter macKey = null; + KdfParameters kParam = new KdfParameters(z, param.GetDerivationV()); + int c_text_length = 0; + int macKeySize = param.MacKeySize; + + if (cipher == null) // stream mode + { + byte[] Buffer = GenerateKdfBytes(kParam, inLen + (macKeySize / 8)); + + C = new byte[inLen + mac.GetMacSize()]; + c_text_length = inLen; + + for (int i = 0; i != inLen; i++) + { + C[i] = (byte)(input[inOff + i] ^ Buffer[i]); + } + + macKey = new KeyParameter(Buffer, inLen, (macKeySize / 8)); + } + else + { + int cipherKeySize = ((IesWithCipherParameters)param).CipherKeySize; + byte[] Buffer = GenerateKdfBytes(kParam, (cipherKeySize / 8) + (macKeySize / 8)); + + cipher.Init(true, new KeyParameter(Buffer, 0, (cipherKeySize / 8))); + + c_text_length = cipher.GetOutputSize(inLen); + byte[] tmp = new byte[c_text_length]; + + int len = cipher.ProcessBytes(input, inOff, inLen, tmp, 0); + len += cipher.DoFinal(tmp, len); + + C = new byte[len + mac.GetMacSize()]; + c_text_length = len; + + Array.Copy(tmp, 0, C, 0, len); + + macKey = new KeyParameter(Buffer, (cipherKeySize / 8), (macKeySize / 8)); + } + + byte[] macIV = param.GetEncodingV(); + + mac.Init(macKey); + mac.BlockUpdate(C, 0, c_text_length); + mac.BlockUpdate(macIV, 0, macIV.Length); + // + // return the message and it's MAC + // + mac.DoFinal(C, c_text_length); + return C; + } + + private byte[] GenerateKdfBytes( + KdfParameters kParam, + int length) + { + byte[] buf = new byte[length]; + + kdf.Init(kParam); + + kdf.GenerateBytes(buf, 0, buf.Length); + + return buf; + } + + public byte[] ProcessBlock( + byte[] input, + int inOff, + int inLen) + { + agree.Init(privParam); + + BigInteger z = agree.CalculateAgreement(pubParam); + + // TODO Is a fixed length result expected? + byte[] zBytes = z.ToByteArrayUnsigned(); + + return forEncryption + ? EncryptBlock(input, inOff, inLen, zBytes) + : DecryptBlock(input, inOff, inLen, zBytes); + } + } + +} diff --git a/Crypto/src/crypto/engines/NaccacheSternEngine.cs b/Crypto/src/crypto/engines/NaccacheSternEngine.cs new file mode 100644
index 000000000..9a0d1e0fe --- /dev/null +++ b/Crypto/src/crypto/engines/NaccacheSternEngine.cs
@@ -0,0 +1,432 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * NaccacheStern Engine. For details on this cipher, please see + * http://www.gemplus.com/smart/rd/publications/pdf/NS98pkcs.pdf + */ + public class NaccacheSternEngine + : IAsymmetricBlockCipher + { + private bool forEncryption; + + private NaccacheSternKeyParameters key; + + private IList[] lookup = null; + + private bool debug = false; + + public string AlgorithmName + { + get { return "NaccacheStern"; } + } + + /** + * Initializes this algorithm. Must be called before all other Functions. + * + * @see org.bouncycastle.crypto.AsymmetricBlockCipher#init(bool, + * org.bouncycastle.crypto.CipherParameters) + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + this.forEncryption = forEncryption; + + if (parameters is ParametersWithRandom) + { + parameters = ((ParametersWithRandom) parameters).Parameters; + } + + key = (NaccacheSternKeyParameters)parameters; + + // construct lookup table for faster decryption if necessary + if (!this.forEncryption) + { + if (debug) + { + System.Diagnostics.Debug.WriteLine("Constructing lookup Array"); + } + NaccacheSternPrivateKeyParameters priv = (NaccacheSternPrivateKeyParameters)key; + IList primes = priv.SmallPrimesList; + lookup = new IList[primes.Count]; + for (int i = 0; i < primes.Count; i++) + { + BigInteger actualPrime = (BigInteger) primes[i]; + int actualPrimeValue = actualPrime.IntValue; + + lookup[i] = Platform.CreateArrayList(actualPrimeValue); + lookup[i].Add(BigInteger.One); + + if (debug) + { + System.Diagnostics.Debug.WriteLine("Constructing lookup ArrayList for " + actualPrimeValue); + } + + BigInteger accJ = BigInteger.Zero; + + for (int j = 1; j < actualPrimeValue; j++) + { +// BigInteger bigJ = BigInteger.ValueOf(j); +// accJ = priv.PhiN.Multiply(bigJ); + accJ = accJ.Add(priv.PhiN); + BigInteger comp = accJ.Divide(actualPrime); + lookup[i].Add(priv.G.ModPow(comp, priv.Modulus)); + } + } + } + } + + public bool Debug + { + set { this.debug = value; } + } + + /** + * 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.LowerSigmaBound + 7) / 8 - 1; + } + else + { + // We pad to modulus-size bytes for easier decryption. +// return key.Modulus.ToByteArray().Length; + return key.Modulus.BitLength / 8 + 1; + } + } + + /** + * 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.Modulus.ToByteArray().Length; + return key.Modulus.BitLength / 8 + 1; + } + else + { + // decrypted Data has upper limit lowerSigmaBound + return (key.LowerSigmaBound + 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[] inBytes, + int inOff, + int length) + { + if (key == null) + throw new InvalidOperationException("NaccacheStern engine not initialised"); + if (length > (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 (length < GetInputBlockSize()) + { + throw new InvalidCipherTextException("BlockLength does not match modulus for Naccache-Stern cipher.\n"); + } + } + + // transform input into BigInteger + BigInteger input = new BigInteger(1, inBytes, inOff, length); + + if (debug) + { + System.Diagnostics.Debug.WriteLine("input as BigInteger: " + input); + } + + byte[] output; + if (forEncryption) + { + output = Encrypt(input); + } + else + { + IList plain = Platform.CreateArrayList(); + NaccacheSternPrivateKeyParameters priv = (NaccacheSternPrivateKeyParameters)key; + IList primes = priv.SmallPrimesList; + // Get Chinese Remainders of CipherText + for (int i = 0; i < primes.Count; i++) + { + BigInteger exp = input.ModPow(priv.PhiN.Divide((BigInteger)primes[i]), priv.Modulus); + IList al = lookup[i]; + if (lookup[i].Count != ((BigInteger)primes[i]).IntValue) + { + if (debug) + { + System.Diagnostics.Debug.WriteLine("Prime is " + primes[i] + ", lookup table has size " + al.Count); + } + throw new InvalidCipherTextException("Error in lookup Array for " + + ((BigInteger)primes[i]).IntValue + + ": Size mismatch. Expected ArrayList with length " + + ((BigInteger)primes[i]).IntValue + " but found ArrayList of length " + + lookup[i].Count); + } + int lookedup = al.IndexOf(exp); + + if (lookedup == -1) + { + if (debug) + { + System.Diagnostics.Debug.WriteLine("Actual prime is " + primes[i]); + System.Diagnostics.Debug.WriteLine("Decrypted value is " + exp); + + System.Diagnostics.Debug.WriteLine("LookupList for " + primes[i] + " with size " + lookup[i].Count + + " is: "); + for (int j = 0; j < lookup[i].Count; j++) + { + System.Diagnostics.Debug.WriteLine(lookup[i][j]); + } + } + throw new InvalidCipherTextException("Lookup failed"); + } + plain.Add(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) { + // Console.WriteLine("Decryption is " + test); + // } + // if ((key.G.ModPow(test, key.Modulus)).Equals(input)) { + // output = test.ToByteArray(); + // } else { + // if(debug){ + // Console.WriteLine("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.Modulus.ToByteArray(); +// Array.Clear(output, 0, output.Length); + byte[] output = new byte[key.Modulus.BitLength / 8 + 1]; + + byte[] tmp = key.G.ModPow(plain, key.Modulus).ToByteArray(); + Array.Copy(tmp, 0, output, output.Length - tmp.Length, tmp.Length); + if (debug) + { + System.Diagnostics.Debug.WriteLine("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) + { + // 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.Modulus); + if (debug) + { + System.Diagnostics.Debug.WriteLine("c(m1) as BigInteger:....... " + m1Crypt); + System.Diagnostics.Debug.WriteLine("c(m2) as BigInteger:....... " + m2Crypt); + System.Diagnostics.Debug.WriteLine("c(m1)*c(m2)%n = c(m1+m2)%n: " + m1m2Crypt); + } + + //byte[] output = key.Modulus.ToByteArray(); + //Array.Clear(output, 0, output.Length); + byte[] output = new byte[key.Modulus.BitLength / 8 + 1]; + + byte[] m1m2CryptBytes = m1m2Crypt.ToByteArray(); + Array.Copy(m1m2CryptBytes, 0, output, + output.Length - m1m2CryptBytes.Length, m1m2CryptBytes.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) + { + if (debug) + { + System.Diagnostics.Debug.WriteLine(""); + } + if (data.Length > GetInputBlockSize()) + { + int inBlocksize = GetInputBlockSize(); + int outBlocksize = GetOutputBlockSize(); + if (debug) + { + System.Diagnostics.Debug.WriteLine("Input blocksize is: " + inBlocksize + " bytes"); + System.Diagnostics.Debug.WriteLine("Output blocksize is: " + outBlocksize + " bytes"); + System.Diagnostics.Debug.WriteLine("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.Diagnostics.Debug.WriteLine("new datapos is " + datapos); + } + if (tmp != null) + { + tmp.CopyTo(retval, retpos); + retpos += tmp.Length; + } + else + { + if (debug) + { + System.Diagnostics.Debug.WriteLine("cipher returned null"); + } + throw new InvalidCipherTextException("cipher returned null"); + } + } + byte[] ret = new byte[retpos]; + Array.Copy(retval, 0, ret, 0, retpos); + if (debug) + { + System.Diagnostics.Debug.WriteLine("returning " + ret.Length + " bytes"); + } + return ret; + } + else + { + if (debug) + { + System.Diagnostics.Debug.WriteLine("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(IList congruences, IList primes) + { + BigInteger retval = BigInteger.Zero; + BigInteger all = BigInteger.One; + for (int i = 0; i < primes.Count; i++) + { + all = all.Multiply((BigInteger)primes[i]); + } + for (int i = 0; i < primes.Count; i++) + { + BigInteger a = (BigInteger)primes[i]; + BigInteger b = all.Divide(a); + BigInteger b2 = b.ModInverse(a); + BigInteger tmp = b.Multiply(b2); + tmp = tmp.Multiply((BigInteger)congruences[i]); + retval = retval.Add(tmp); + } + + return retval.Mod(all); + } + } +} diff --git a/Crypto/src/crypto/engines/NoekeonEngine.cs b/Crypto/src/crypto/engines/NoekeonEngine.cs new file mode 100644
index 000000000..b73e696a9 --- /dev/null +++ b/Crypto/src/crypto/engines/NoekeonEngine.cs
@@ -0,0 +1,240 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * A Noekeon engine, using direct-key mode. + */ + public class NoekeonEngine + : IBlockCipher + { + private const int GenericSize = 16; // Block and key size, as well as the amount of rounds. + + private static readonly uint[] nullVector = + { + 0x00, 0x00, 0x00, 0x00 // Used in decryption + }; + + private static readonly uint[] roundConstants = + { + 0x80, 0x1b, 0x36, 0x6c, + 0xd8, 0xab, 0x4d, 0x9a, + 0x2f, 0x5e, 0xbc, 0x63, + 0xc6, 0x97, 0x35, 0x6a, + 0xd4 + }; + + private uint[] state = new uint[4], // a + subKeys = new uint[4], // k + decryptKeys = new uint[4]; + + private bool _initialised, _forEncryption; + + /** + * Create an instance of the Noekeon encryption algorithm + * and set some defaults + */ + public NoekeonEngine() + { + _initialised = false; + } + + public string AlgorithmName + { + get { return "Noekeon"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + 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 ArgumentException if the params argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + throw new ArgumentException("Invalid parameters passed to Noekeon init - " + parameters.GetType().Name, "parameters"); + + _forEncryption = forEncryption; + _initialised = true; + + KeyParameter p = (KeyParameter) parameters; + + setKey(p.GetKey()); + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (!_initialised) + throw new InvalidOperationException(AlgorithmName + " not initialised"); + if ((inOff + GenericSize) > input.Length) + throw new DataLengthException("input buffer too short"); + if ((outOff + GenericSize) > output.Length) + throw new DataLengthException("output buffer too short"); + + return _forEncryption + ? encryptBlock(input, inOff, output, outOff) + : decryptBlock(input, inOff, output, outOff); + } + + public void Reset() + { + // TODO This should do something in case the encryption is aborted + } + + /** + * Re-key the cipher. + * + * @param key the key to be used + */ + private void setKey(byte[] key) + { + subKeys[0] = Pack.BE_To_UInt32(key, 0); + subKeys[1] = Pack.BE_To_UInt32(key, 4); + subKeys[2] = Pack.BE_To_UInt32(key, 8); + subKeys[3] = Pack.BE_To_UInt32(key, 12); + } + + private int encryptBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + state[0] = Pack.BE_To_UInt32(input, inOff); + state[1] = Pack.BE_To_UInt32(input, inOff+4); + state[2] = Pack.BE_To_UInt32(input, inOff+8); + state[3] = Pack.BE_To_UInt32(input, 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); + + Pack.UInt32_To_BE(state[0], output, outOff); + Pack.UInt32_To_BE(state[1], output, outOff+4); + Pack.UInt32_To_BE(state[2], output, outOff+8); + Pack.UInt32_To_BE(state[3], output, outOff+12); + + return GenericSize; + } + + private int decryptBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + state[0] = Pack.BE_To_UInt32(input, inOff); + state[1] = Pack.BE_To_UInt32(input, inOff+4); + state[2] = Pack.BE_To_UInt32(input, inOff+8); + state[3] = Pack.BE_To_UInt32(input, inOff+12); + + Array.Copy(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]; + + Pack.UInt32_To_BE(state[0], output, outOff); + Pack.UInt32_To_BE(state[1], output, outOff+4); + Pack.UInt32_To_BE(state[2], output, outOff+8); + Pack.UInt32_To_BE(state[3], output, outOff+12); + + return GenericSize; + } + + private void gamma(uint[] a) + { + a[1] ^= ~a[3] & ~a[2]; + a[0] ^= a[2] & a[1]; + + uint 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(uint[] a, uint[] k) + { + uint 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(uint[] a) + { + a[1] = rotl(a[1], 1); + a[2] = rotl(a[2], 5); + a[3] = rotl(a[3], 2); + } + + private void pi2(uint[] a) + { + a[1] = rotl(a[1], 31); + a[2] = rotl(a[2], 27); + a[3] = rotl(a[3], 30); + } + + // Helpers + + private uint rotl(uint x, int y) + { + return (x << y) | (x >> (32-y)); + } + } +} diff --git a/Crypto/src/crypto/engines/NullEngine.cs b/Crypto/src/crypto/engines/NullEngine.cs new file mode 100644
index 000000000..407b8ccc6 --- /dev/null +++ b/Crypto/src/crypto/engines/NullEngine.cs
@@ -0,0 +1,70 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * The no-op engine that just copies bytes through, irrespective of whether encrypting and decrypting. + * Provided for the sake of completeness. + */ + public class NullEngine + : IBlockCipher + { + private bool initialised; + private const int BlockSize = 1; + + public NullEngine() + { + } + + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + // we don't mind any parameters that may come in + initialised = true; + } + + public string AlgorithmName + { + get { return "Null"; } + } + + public bool IsPartialBlockOkay + { + get { return true; } + } + + public int GetBlockSize() + { + return BlockSize; + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (!initialised) + throw new InvalidOperationException("Null engine not initialised"); + if ((inOff + BlockSize) > input.Length) + throw new DataLengthException("input buffer too short"); + if ((outOff + BlockSize) > output.Length) + throw new DataLengthException("output buffer too short"); + + for (int i = 0; i < BlockSize; ++i) + { + output[outOff + i] = input[inOff + i]; + } + + return BlockSize; + } + + public void Reset() + { + // nothing needs to be done + } + } +} diff --git a/Crypto/src/crypto/engines/RC2Engine.cs b/Crypto/src/crypto/engines/RC2Engine.cs new file mode 100644
index 000000000..aaf8c714c --- /dev/null +++ b/Crypto/src/crypto/engines/RC2Engine.cs
@@ -0,0 +1,312 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * an implementation of RC2 as described in RFC 2268 + * "A Description of the RC2(r) Encryption Algorithm" R. Rivest. + */ + public class RC2Engine + : IBlockCipher + { + // + // the values we use for key expansion (based on the digits of PI) + // + private static readonly 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 const int BLOCK_SIZE = 8; + + private int[] workingKey; + private bool 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 forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + this.encrypting = forEncryption; + + if (parameters is RC2Parameters) + { + RC2Parameters param = (RC2Parameters) parameters; + + workingKey = GenerateWorkingKey(param.GetKey(), param.EffectiveKeyBits); + } + else if (parameters is KeyParameter) + { + KeyParameter param = (KeyParameter) parameters; + byte[] key = param.GetKey(); + + workingKey = GenerateWorkingKey(key, key.Length * 8); + } + else + { + throw new ArgumentException("invalid parameter passed to RC2 init - " + parameters.GetType().Name); + } + } + + public void Reset() + { + } + + public string AlgorithmName + { + get { return "RC2"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return BLOCK_SIZE; + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (workingKey == null) + throw new InvalidOperationException("RC2 engine not initialised"); + if ((inOff + BLOCK_SIZE) > input.Length) + throw new DataLengthException("input buffer too short"); + if ((outOff + BLOCK_SIZE) > output.Length) + throw new DataLengthException("output buffer too short"); + + if (encrypting) + { + EncryptBlock(input, inOff, output, outOff); + } + else + { + DecryptBlock(input, inOff, output, 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[] input, + int inOff, + byte[] outBytes, + int outOff) + { + int x76, x54, x32, x10; + + x76 = ((input[inOff + 7] & 0xff) << 8) + (input[inOff + 6] & 0xff); + x54 = ((input[inOff + 5] & 0xff) << 8) + (input[inOff + 4] & 0xff); + x32 = ((input[inOff + 3] & 0xff) << 8) + (input[inOff + 2] & 0xff); + x10 = ((input[inOff + 1] & 0xff) << 8) + (input[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); + } + + outBytes[outOff + 0] = (byte)x10; + outBytes[outOff + 1] = (byte)(x10 >> 8); + outBytes[outOff + 2] = (byte)x32; + outBytes[outOff + 3] = (byte)(x32 >> 8); + outBytes[outOff + 4] = (byte)x54; + outBytes[outOff + 5] = (byte)(x54 >> 8); + outBytes[outOff + 6] = (byte)x76; + outBytes[outOff + 7] = (byte)(x76 >> 8); + } + + private void DecryptBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + int x76, x54, x32, x10; + + x76 = ((input[inOff + 7] & 0xff) << 8) + (input[inOff + 6] & 0xff); + x54 = ((input[inOff + 5] & 0xff) << 8) + (input[inOff + 4] & 0xff); + x32 = ((input[inOff + 3] & 0xff) << 8) + (input[inOff + 2] & 0xff); + x10 = ((input[inOff + 1] & 0xff) << 8) + (input[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 ]); + } + + outBytes[outOff + 0] = (byte)x10; + outBytes[outOff + 1] = (byte)(x10 >> 8); + outBytes[outOff + 2] = (byte)x32; + outBytes[outOff + 3] = (byte)(x32 >> 8); + outBytes[outOff + 4] = (byte)x54; + outBytes[outOff + 5] = (byte)(x54 >> 8); + outBytes[outOff + 6] = (byte)x76; + outBytes[outOff + 7] = (byte)(x76 >> 8); + } + } + +} diff --git a/Crypto/src/crypto/engines/RC2WrapEngine.cs b/Crypto/src/crypto/engines/RC2WrapEngine.cs new file mode 100644
index 000000000..238c9f76a --- /dev/null +++ b/Crypto/src/crypto/engines/RC2WrapEngine.cs
@@ -0,0 +1,370 @@ +using System; + +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * Wrap keys according to RFC 3217 - RC2 mechanism + */ + public class RC2WrapEngine + : IWrapper + { + /** Field engine */ + private CbcBlockCipher engine; + + /** Field param */ + private ICipherParameters parameters; + + /** Field paramPlusIV */ + private ParametersWithIV paramPlusIV; + + /** Field iv */ + private byte[] iv; + + /** Field forWrapping */ + private bool forWrapping; + + private SecureRandom sr; + + /** Field IV2 */ + private static readonly byte[] IV2 = + { + (byte) 0x4a, (byte) 0xdd, (byte) 0xa2, + (byte) 0x2c, (byte) 0x79, (byte) 0xe8, + (byte) 0x21, (byte) 0x05 + }; + + // + // checksum digest + // + IDigest sha1 = new Sha1Digest(); + byte[] digest = new byte[20]; + + /** + * Method init + * + * @param forWrapping + * @param param + */ + public void Init( + bool forWrapping, + ICipherParameters parameters) + { + this.forWrapping = forWrapping; + this.engine = new CbcBlockCipher(new RC2Engine()); + + if (parameters is ParametersWithRandom) + { + ParametersWithRandom pWithR = (ParametersWithRandom)parameters; + sr = pWithR.Random; + parameters = pWithR.Parameters; + } + else + { + sr = new SecureRandom(); + } + + if (parameters is ParametersWithIV) + { + if (!forWrapping) + throw new ArgumentException("You should not supply an IV for unwrapping"); + + this.paramPlusIV = (ParametersWithIV)parameters; + this.iv = this.paramPlusIV.GetIV(); + this.parameters = this.paramPlusIV.Parameters; + + if (this.iv.Length != 8) + throw new ArgumentException("IV is not 8 octets"); + } + else + { + this.parameters = parameters; + + 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.parameters, this.iv); + } + } + } + + /** + * Method GetAlgorithmName + * + * @return + */ + public string AlgorithmName + { + get { return "RC2"; } + } + + /** + * Method wrap + * + * @param in + * @param inOff + * @param inLen + * @return + */ + public byte[] Wrap( + byte[] input, + int inOff, + int length) + { + if (!forWrapping) + { + throw new InvalidOperationException("Not initialized for wrapping"); + } + + int len = length + 1; + if ((len % 8) != 0) + { + len += 8 - (len % 8); + } + + byte [] keyToBeWrapped = new byte[len]; + + keyToBeWrapped[0] = (byte)length; + Array.Copy(input, inOff, keyToBeWrapped, 1, length); + + byte[] pad = new byte[keyToBeWrapped.Length - length - 1]; + + if (pad.Length > 0) + { + sr.NextBytes(pad); + Array.Copy(pad, 0, keyToBeWrapped, length + 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]; + + Array.Copy(keyToBeWrapped, 0, WKCKS, 0, keyToBeWrapped.Length); + Array.Copy(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]; + + Array.Copy(WKCKS, 0, TEMP1, 0, WKCKS.Length); + + int noOfBlocks = WKCKS.Length / engine.GetBlockSize(); + int extraBytes = WKCKS.Length % engine.GetBlockSize(); + + if (extraBytes != 0) + { + throw new InvalidOperationException("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]; + + Array.Copy(this.iv, 0, TEMP2, 0, this.iv.Length); + Array.Copy(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.parameters, 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 + * @throws InvalidCipherTextException + */ + public byte[] Unwrap( + byte[] input, + int inOff, + int length) + { + if (forWrapping) + { + throw new InvalidOperationException("Not set for unwrapping"); + } + + if (input == null) + { + throw new InvalidCipherTextException("Null pointer as ciphertext"); + } + + if (length % 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.parameters, IV2); + + this.engine.Init(false, param2); + + byte [] TEMP3 = new byte[length]; + + Array.Copy(input, inOff, TEMP3, 0, length); + + 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]; + + Array.Copy(TEMP2, 0, this.iv, 0, 8); + Array.Copy(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.parameters, this.iv); + + this.engine.Init(false, this.paramPlusIV); + + byte[] LCEKPADICV = new byte[TEMP1.Length]; + + Array.Copy(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]; + + Array.Copy(LCEKPADICV, 0, result, 0, LCEKPADICV.Length - 8); + Array.Copy(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]]; + Array.Copy(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 Exception + * @see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum + */ + private byte[] CalculateCmsKeyChecksum( + byte[] key) + { + sha1.BlockUpdate(key, 0, key.Length); + sha1.DoFinal(digest, 0); + + byte[] result = new byte[8]; + Array.Copy(digest, 0, result, 0, 8); + return result; + } + + /** + * @param key + * @param checksum + * @return + * @see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum + */ + private bool CheckCmsKeyChecksum( + byte[] key, + byte[] checksum) + { + return Arrays.ConstantTimeAreEqual(CalculateCmsKeyChecksum(key), checksum); + } + } +} diff --git a/Crypto/src/crypto/engines/RC4Engine.cs b/Crypto/src/crypto/engines/RC4Engine.cs new file mode 100644
index 000000000..c65468d93 --- /dev/null +++ b/Crypto/src/crypto/engines/RC4Engine.cs
@@ -0,0 +1,147 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + public class RC4Engine + : IStreamCipher + { + private readonly static int STATE_LENGTH = 256; + + /* + * variables to hold the state of the RC4 engine + * during encryption and decryption + */ + + private byte[] engineState; + private int x; + private int y; + private byte[] workingKey; + + /** + * initialise a RC4 cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (parameters is KeyParameter) + { + /* + * RC4 encryption and decryption is completely + * symmetrical, so the 'forEncryption' is + * irrelevant. + */ + workingKey = ((KeyParameter)parameters).GetKey(); + SetKey(workingKey); + + return; + } + + throw new ArgumentException("invalid parameter passed to RC4 init - " + parameters.GetType().ToString()); + } + + public string AlgorithmName + { + get { return "RC4"; } + } + + public byte ReturnByte( + byte input) + { + x = (x + 1) & 0xff; + y = (engineState[x] + y) & 0xff; + + // swap + byte tmp = engineState[x]; + engineState[x] = engineState[y]; + engineState[y] = tmp; + + // xor + return (byte)(input ^ engineState[(engineState[x] + engineState[y]) & 0xff]); + } + + public void ProcessBytes( + byte[] input, + int inOff, + int length, + byte[] output, + int outOff + ) + { + if ((inOff + length) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + length) > output.Length) + { + throw new DataLengthException("output buffer too short"); + } + + for (int i = 0; i < length ; i++) + { + x = (x + 1) & 0xff; + y = (engineState[x] + y) & 0xff; + + // swap + byte tmp = engineState[x]; + engineState[x] = engineState[y]; + engineState[y] = tmp; + + // xor + output[i+outOff] = (byte)(input[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/Crypto/src/crypto/engines/RC532Engine.cs b/Crypto/src/crypto/engines/RC532Engine.cs new file mode 100644
index 000000000..1661707ef --- /dev/null +++ b/Crypto/src/crypto/engines/RC532Engine.cs
@@ -0,0 +1,294 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * 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> + */ + public class RC532Engine + : IBlockCipher + { + /* + * 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 readonly int P32 = unchecked((int) 0xb7e15163); + private static readonly int Q32 = unchecked((int) 0x9e3779b9); + + private bool forEncryption; + + /** + * Create an instance of the RC5 encryption algorithm + * and set some defaults + */ + public RC532Engine() + { + _noRounds = 12; // the default +// _S = null; + } + + public string AlgorithmName + { + get { return "RC5-32"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return 2 * 4; + } + + /** + * initialise a RC5-32 cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (typeof(RC5Parameters).IsInstanceOfType(parameters)) + { + RC5Parameters p = (RC5Parameters)parameters; + + _noRounds = p.Rounds; + + SetKey(p.GetKey()); + } + else if (typeof(KeyParameter).IsInstanceOfType(parameters)) + { + KeyParameter p = (KeyParameter)parameters; + + SetKey(p.GetKey()); + } + else + { + throw new ArgumentException("invalid parameter passed to RC532 init - " + parameters.GetType().ToString()); + } + + this.forEncryption = forEncryption; + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + return (forEncryption) + ? EncryptBlock(input, inOff, output, outOff) + : DecryptBlock(input, inOff, output, outOff); + } + + public void Reset() + { + } + + /** + * Re-key the cipher. + * + * @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 ii = 0, jj = 0; + + for (int k = 0; k < iter; k++) + { + A = _S[ii] = RotateLeft(_S[ii] + A + B, 3); + B = L[jj] = RotateLeft( L[jj] + A + B, A+B); + ii = (ii+1) % _S.Length; + jj = (jj+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. + * + * @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[] input, + int inOff, + byte[] outBytes, + int outOff) + { + int A = BytesToWord(input, inOff) + _S[0]; + int B = BytesToWord(input, 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, outBytes, outOff); + WordToBytes(B, outBytes, outOff + 4); + + return 2 * 4; + } + + private int DecryptBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + int A = BytesToWord(input, inOff); + int B = BytesToWord(input, 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], outBytes, outOff); + WordToBytes(B - _S[1], outBytes, 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. + * + * @param x word to rotate + * @param y number of bits to rotate % 32 + */ + private int RotateLeft(int x, int y) { + return ((int) ( (uint) (x << (y & (32-1))) | + ((uint) 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. + * + * @param x word to rotate + * @param y number of bits to rotate % 32 + */ + private int RotateRight(int x, int y) { + return ((int) ( ((uint) x >> (y & (32-1))) | + (uint) (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/Crypto/src/crypto/engines/RC564Engine.cs b/Crypto/src/crypto/engines/RC564Engine.cs new file mode 100644
index 000000000..5c69d40ff --- /dev/null +++ b/Crypto/src/crypto/engines/RC564Engine.cs
@@ -0,0 +1,295 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * 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> + */ + public class RC564Engine + : IBlockCipher + { + private static readonly int wordSize = 64; + private static readonly 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 readonly long P64 = unchecked( (long) 0xb7e151628aed2a6bL); + private static readonly long Q64 = unchecked( (long) 0x9e3779b97f4a7c15L); + + private bool forEncryption; + + /** + * Create an instance of the RC5 encryption algorithm + * and set some defaults + */ + public RC564Engine() + { + _noRounds = 12; +// _S = null; + } + + public string AlgorithmName + { + get { return "RC5-64"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return 2 * bytesPerWord; + } + + /** + * initialise a RC5-64 cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(typeof(RC5Parameters).IsInstanceOfType(parameters))) + { + throw new ArgumentException("invalid parameter passed to RC564 init - " + parameters.GetType().ToString()); + } + + RC5Parameters p = (RC5Parameters)parameters; + + this.forEncryption = forEncryption; + + _noRounds = p.Rounds; + + SetKey(p.GetKey()); + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + return (forEncryption) ? EncryptBlock(input, inOff, output, outOff) + : DecryptBlock(input, inOff, output, outOff); + } + + public void Reset() + { + } + + /** + * Re-key the cipher. + * + * @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 ii = 0, jj = 0; + + for (int k = 0; k < iter; k++) + { + A = _S[ii] = RotateLeft(_S[ii] + A + B, 3); + B = L[jj] = RotateLeft( L[jj] + A + B, A+B); + ii = (ii+1) % _S.Length; + jj = (jj+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. + * + * @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[] input, + int inOff, + byte[] outBytes, + int outOff) + { + long A = BytesToWord(input, inOff) + _S[0]; + long B = BytesToWord(input, 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, outBytes, outOff); + WordToBytes(B, outBytes, outOff + bytesPerWord); + + return 2 * bytesPerWord; + } + + private int DecryptBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + long A = BytesToWord(input, inOff); + long B = BytesToWord(input, 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], outBytes, outOff); + WordToBytes(B - _S[1], outBytes, 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. + * + * @param x word to rotate + * @param y number of bits to rotate % wordSize + */ + private long RotateLeft(long x, long y) { + return ((long) ( (ulong) (x << (int) (y & (wordSize-1))) | + ((ulong) x >> (int) (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. + * + * @param x word to rotate + * @param y number of bits to rotate % wordSize + */ + private long RotateRight(long x, long y) { + return ((long) ( ((ulong) x >> (int) (y & (wordSize-1))) | + (ulong) (x << (int) (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 = (long) ((ulong) word >> 8); + } + } + } + +} diff --git a/Crypto/src/crypto/engines/RC6Engine.cs b/Crypto/src/crypto/engines/RC6Engine.cs new file mode 100644
index 000000000..d72cc2f7b --- /dev/null +++ b/Crypto/src/crypto/engines/RC6Engine.cs
@@ -0,0 +1,362 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * An RC6 engine. + */ + public class RC6Engine + : IBlockCipher + { + private static readonly int wordSize = 32; + private static readonly int bytesPerWord = wordSize / 8; + + /* + * the number of rounds to perform + */ + private static readonly 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 readonly int P32 = unchecked((int) 0xb7e15163); + private static readonly int Q32 = unchecked((int) 0x9e3779b9); + + private static readonly int LGW = 5; // log2(32) + + private bool forEncryption; + + /** + * Create an instance of the RC6 encryption algorithm + * and set some defaults + */ + public RC6Engine() + { +// _S = null; + } + + public string AlgorithmName + { + get { return "RC6"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return 4 * bytesPerWord; + } + + /** + * initialise a RC5-32 cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + throw new ArgumentException("invalid parameter passed to RC6 init - " + parameters.GetType().ToString()); + + this.forEncryption = forEncryption; + + KeyParameter p = (KeyParameter)parameters; + SetKey(p.GetKey()); + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + int blockSize = GetBlockSize(); + if (_S == null) + throw new InvalidOperationException("RC6 engine not initialised"); + if ((inOff + blockSize) > input.Length) + throw new DataLengthException("input buffer too short"); + if ((outOff + blockSize) > output.Length) + throw new DataLengthException("output buffer too short"); + + return (forEncryption) + ? EncryptBlock(input, inOff, output, outOff) + : DecryptBlock(input, inOff, output, outOff); + } + + public void Reset() + { + } + + /** + * Re-key the cipher. + * + * @param inKey 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 ii = 0, jj = 0; + + for (int k = 0; k < iter; k++) + { + A = _S[ii] = RotateLeft(_S[ii] + A + B, 3); + B = L[jj] = RotateLeft( L[jj] + A + B, A+B); + ii = (ii+1) % _S.Length; + jj = (jj+1) % L.Length; + } + } + + private int EncryptBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + // load A,B,C and D registers from in. + int A = BytesToWord(input, inOff); + int B = BytesToWord(input, inOff + bytesPerWord); + int C = BytesToWord(input, inOff + bytesPerWord*2); + int D = BytesToWord(input, 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, outBytes, outOff); + WordToBytes(B, outBytes, outOff + bytesPerWord); + WordToBytes(C, outBytes, outOff + bytesPerWord*2); + WordToBytes(D, outBytes, outOff + bytesPerWord*3); + + return 4 * bytesPerWord; + } + + private int DecryptBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + // load A,B,C and D registers from out. + int A = BytesToWord(input, inOff); + int B = BytesToWord(input, inOff + bytesPerWord); + int C = BytesToWord(input, inOff + bytesPerWord*2); + int D = BytesToWord(input, 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, outBytes, outOff); + WordToBytes(B, outBytes, outOff + bytesPerWord); + WordToBytes(C, outBytes, outOff + bytesPerWord*2); + WordToBytes(D, outBytes, 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 a power of 2. + * + * @param x word to rotate + * @param y number of bits to rotate % wordSize + */ + private int RotateLeft(int x, int y) + { + return ((int)((uint)(x << (y & (wordSize-1))) + | ((uint) 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. + * + * @param x word to rotate + * @param y number of bits to rotate % wordSize + */ + private int RotateRight(int x, int y) + { + return ((int)(((uint) x >> (y & (wordSize-1))) + | (uint)(x << (wordSize - (y & (wordSize-1)))))); + } + + 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 = (int) ((uint) word >> 8); + } + } + } + +} diff --git a/Crypto/src/crypto/engines/RFC3211WrapEngine.cs b/Crypto/src/crypto/engines/RFC3211WrapEngine.cs new file mode 100644
index 000000000..e520075f9 --- /dev/null +++ b/Crypto/src/crypto/engines/RFC3211WrapEngine.cs
@@ -0,0 +1,168 @@ +using System; + +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * an implementation of the RFC 3211 Key Wrap + * Specification. + */ + public class Rfc3211WrapEngine + : IWrapper + { + private CbcBlockCipher engine; + private ParametersWithIV param; + private bool forWrapping; + private SecureRandom rand; + + public Rfc3211WrapEngine( + IBlockCipher engine) + { + this.engine = new CbcBlockCipher(engine); + } + + public void Init( + bool forWrapping, + ICipherParameters param) + { + this.forWrapping = forWrapping; + + if (param is ParametersWithRandom) + { + ParametersWithRandom p = (ParametersWithRandom) param; + + this.rand = p.Random; + this.param = (ParametersWithIV) p.Parameters; + } + else + { + if (forWrapping) + { + rand = new SecureRandom(); + } + + this.param = (ParametersWithIV) param; + } + } + + public string AlgorithmName + { + get { return engine.GetUnderlyingCipher().AlgorithmName + "/RFC3211Wrap"; } + } + + public byte[] Wrap( + byte[] inBytes, + int inOff, + int inLen) + { + if (!forWrapping) + { + throw new InvalidOperationException("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)~inBytes[inOff]; + cekBlock[2] = (byte)~inBytes[inOff + 1]; + cekBlock[3] = (byte)~inBytes[inOff + 2]; + + Array.Copy(inBytes, inOff, cekBlock, 4, inLen); + + rand.NextBytes(cekBlock, inLen + 4, cekBlock.Length - inLen - 4); + + 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[] inBytes, + int inOff, + int inLen) + { + if (forWrapping) + { + throw new InvalidOperationException("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]; + + Array.Copy(inBytes, inOff, cekBlock, 0, inLen); + Array.Copy(inBytes, inOff, iv, 0, iv.Length); + + engine.Init(false, new ParametersWithIV(param.Parameters, iv)); + + for (int i = blockSize; i < cekBlock.Length; i += blockSize) + { + engine.ProcessBlock(cekBlock, i, cekBlock, i); + } + + Array.Copy(cekBlock, cekBlock.Length - iv.Length, iv, 0, iv.Length); + + engine.Init(false, new ParametersWithIV(param.Parameters, 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]; + + Array.Copy(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/Crypto/src/crypto/engines/RFC3394WrapEngine.cs b/Crypto/src/crypto/engines/RFC3394WrapEngine.cs new file mode 100644
index 000000000..7596e7218 --- /dev/null +++ b/Crypto/src/crypto/engines/RFC3394WrapEngine.cs
@@ -0,0 +1,178 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /// <remarks> + /// 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>. + /// </remarks> + public class Rfc3394WrapEngine + : IWrapper + { + private readonly IBlockCipher engine; + + private KeyParameter param; + private bool forWrapping; + + private byte[] iv = + { + 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6 + }; + + public Rfc3394WrapEngine( + IBlockCipher engine) + { + this.engine = engine; + } + + public void Init( + bool forWrapping, + ICipherParameters parameters) + { + this.forWrapping = forWrapping; + + if (parameters is ParametersWithRandom) + { + parameters = ((ParametersWithRandom) parameters).Parameters; + } + + if (parameters is KeyParameter) + { + this.param = (KeyParameter) parameters; + } + else if (parameters is ParametersWithIV) + { + ParametersWithIV pIV = (ParametersWithIV) parameters; + byte[] iv = pIV.GetIV(); + + if (iv.Length != 8) + throw new ArgumentException("IV length not equal to 8", "parameters"); + + this.iv = iv; + this.param = (KeyParameter) pIV.Parameters; + } + else + { + // TODO Throw an exception for bad parameters? + } + } + + public string AlgorithmName + { + get { return engine.AlgorithmName; } + } + + public byte[] Wrap( + byte[] input, + int inOff, + int inLen) + { + if (!forWrapping) + { + throw new InvalidOperationException("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]; + + Array.Copy(iv, 0, block, 0, iv.Length); + Array.Copy(input, 0, block, iv.Length, inLen); + + engine.Init(true, param); + + for (int j = 0; j != 6; j++) + { + for (int i = 1; i <= n; i++) + { + Array.Copy(block, 0, buf, 0, iv.Length); + Array.Copy(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 = (int) ((uint)t >> 8); + } + + Array.Copy(buf, 0, block, 0, 8); + Array.Copy(buf, 8, block, 8 * i, 8); + } + } + + return block; + } + + public byte[] Unwrap( + byte[] input, + int inOff, + int inLen) + { + if (forWrapping) + { + throw new InvalidOperationException("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]; + + Array.Copy(input, 0, a, 0, iv.Length); + Array.Copy(input, 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--) + { + Array.Copy(a, 0, buf, 0, iv.Length); + Array.Copy(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 = (int) ((uint)t >> 8); + } + + engine.ProcessBlock(buf, 0, buf, 0); + Array.Copy(buf, 0, a, 0, 8); + Array.Copy(buf, 8, block, 8 * (i - 1), 8); + } + } + + if (!Arrays.ConstantTimeAreEqual(a, iv)) + throw new InvalidCipherTextException("checksum failed"); + + return block; + } + } +} diff --git a/Crypto/src/crypto/engines/RSABlindedEngine.cs b/Crypto/src/crypto/engines/RSABlindedEngine.cs new file mode 100644
index 000000000..cdf69ddda --- /dev/null +++ b/Crypto/src/crypto/engines/RSABlindedEngine.cs
@@ -0,0 +1,124 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * this does your basic RSA algorithm with blinding + */ + public class RsaBlindedEngine + : IAsymmetricBlockCipher + { + private readonly RsaCoreEngine core = new RsaCoreEngine(); + private RsaKeyParameters key; + private SecureRandom random; + + public string AlgorithmName + { + get { return "RSA"; } + } + + /** + * initialise the RSA engine. + * + * @param forEncryption true if we are encrypting, false otherwise. + * @param param the necessary RSA key parameters. + */ + public void Init( + bool forEncryption, + ICipherParameters param) + { + core.Init(forEncryption, param); + + if (param is ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)param; + + key = (RsaKeyParameters)rParam.Parameters; + random = rParam.Random; + } + 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 inBuf 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[] inBuf, + int inOff, + int inLen) + { + if (key == null) + throw new InvalidOperationException("RSA engine not initialised"); + + BigInteger input = core.ConvertInput(inBuf, inOff, inLen); + + BigInteger result; + if (key is RsaPrivateCrtKeyParameters) + { + RsaPrivateCrtKeyParameters k = (RsaPrivateCrtKeyParameters)key; + BigInteger e = k.PublicExponent; + if (e != null) // can't do blinding without a public exponent + { + BigInteger m = k.Modulus; + BigInteger r = BigIntegers.CreateRandomInRange( + BigInteger.One, m.Subtract(BigInteger.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/Crypto/src/crypto/engines/RSABlindingEngine.cs b/Crypto/src/crypto/engines/RSABlindingEngine.cs new file mode 100644
index 000000000..76b57a3f7 --- /dev/null +++ b/Crypto/src/crypto/engines/RSABlindingEngine.cs
@@ -0,0 +1,139 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * 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 + : IAsymmetricBlockCipher + { + private readonly RsaCoreEngine core = new RsaCoreEngine(); + + private RsaKeyParameters key; + private BigInteger blindingFactor; + + private bool forEncryption; + + public string AlgorithmName + { + get { return "RSA"; } + } + + /** + * Initialise the blinding engine. + * + * @param forEncryption true if we are encrypting (blinding), false otherwise. + * @param param the necessary RSA key parameters. + */ + public void Init( + bool forEncryption, + ICipherParameters param) + { + RsaBlindingParameters p; + + if (param is ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)param; + + p = (RsaBlindingParameters)rParam.Parameters; + } + else + { + p = (RsaBlindingParameters)param; + } + + core.Init(forEncryption, p.PublicKey); + + this.forEncryption = forEncryption; + this.key = p.PublicKey; + this.blindingFactor = p.BlindingFactor; + } + + /** + * 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[] inBuf, + int inOff, + int inLen) + { + BigInteger msg = core.ConvertInput(inBuf, 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.Exponent, key.Modulus)); + blindMsg = blindMsg.Mod(key.Modulus); + + return blindMsg; + } + + /* + * Unblind the message blinded with the blind factor. + */ + private BigInteger UnblindMessage( + BigInteger blindedMsg) + { + BigInteger m = key.Modulus; + BigInteger msg = blindedMsg; + BigInteger blindFactorInverse = blindingFactor.ModInverse(m); + msg = msg.Multiply(blindFactorInverse); + msg = msg.Mod(m); + + return msg; + } + } +} diff --git a/Crypto/src/crypto/engines/RSACoreEngine.cs b/Crypto/src/crypto/engines/RSACoreEngine.cs new file mode 100644
index 000000000..4e64d25d6 --- /dev/null +++ b/Crypto/src/crypto/engines/RSACoreEngine.cs
@@ -0,0 +1,156 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * this does your basic RSA algorithm. + */ + class RsaCoreEngine + { + private RsaKeyParameters key; + private bool forEncryption; + private int bitSize; + + /** + * initialise the RSA engine. + * + * @param forEncryption true if we are encrypting, false otherwise. + * @param param the necessary RSA key parameters. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (parameters is ParametersWithRandom) + { + parameters = ((ParametersWithRandom) parameters).Parameters; + } + + if (!(parameters is RsaKeyParameters)) + throw new InvalidKeyException("Not an RSA key"); + + this.key = (RsaKeyParameters) parameters; + this.forEncryption = forEncryption; + this.bitSize = key.Modulus.BitLength; + } + + /** + * 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() + { + if (forEncryption) + { + return (bitSize - 1) / 8; + } + + 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() + { + if (forEncryption) + { + return (bitSize + 7) / 8; + } + + return (bitSize - 1) / 8; + } + + public BigInteger ConvertInput( + byte[] inBuf, + int inOff, + int inLen) + { + int maxLength = (bitSize + 7) / 8; + + if (inLen > maxLength) + throw new DataLengthException("input too large for RSA cipher."); + + BigInteger input = new BigInteger(1, inBuf, inOff, inLen); + + if (input.CompareTo(key.Modulus) >= 0) + throw new DataLengthException("input too large for RSA cipher."); + + return input; + } + + public byte[] ConvertOutput( + BigInteger result) + { + byte[] output = result.ToByteArrayUnsigned(); + + if (forEncryption) + { + int outSize = GetOutputBlockSize(); + + // TODO To avoid this, create version of BigInteger.ToByteArray that + // writes to an existing array + if (output.Length < outSize) // have ended up with less bytes than normal, lengthen + { + byte[] tmp = new byte[outSize]; + output.CopyTo(tmp, tmp.Length - output.Length); + output = tmp; + } + } + + return output; + } + + public BigInteger ProcessBlock( + BigInteger input) + { + if (key is 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.P;; + BigInteger q = crtKey.Q; + BigInteger dP = crtKey.DP; + BigInteger dQ = crtKey.DQ; + BigInteger qInv = crtKey.QInv; + + 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; + } + + return input.ModPow(key.Exponent, key.Modulus); + } + } +} diff --git a/Crypto/src/crypto/engines/RijndaelEngine.cs b/Crypto/src/crypto/engines/RijndaelEngine.cs new file mode 100644
index 000000000..df2e5baea --- /dev/null +++ b/Crypto/src/crypto/engines/RijndaelEngine.cs
@@ -0,0 +1,747 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * 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 readonly NIST publication. + * </p> + */ + public class RijndaelEngine + : IBlockCipher + { + private static readonly int MAXROUNDS = 14; + + private static readonly int MAXKC = (256/4); + + private static readonly byte[] Logtable = + { + 0, 0, 25, 1, 50, 2, 26, 198, + 75, 199, 27, 104, 51, 238, 223, 3, + 100, 4, 224, 14, 52, 141, 129, 239, + 76, 113, 8, 200, 248, 105, 28, 193, + 125, 194, 29, 181, 249, 185, 39, 106, + 77, 228, 166, 114, 154, 201, 9, 120, + 101, 47, 138, 5, 33, 15, 225, 36, + 18, 240, 130, 69, 53, 147, 218, 142, + 150, 143, 219, 189, 54, 208, 206, 148, + 19, 92, 210, 241, 64, 70, 131, 56, + 102, 221, 253, 48, 191, 6, 139, 98, + 179, 37, 226, 152, 34, 136, 145, 16, + 126, 110, 72, 195, 163, 182, 30, 66, + 58, 107, 40, 84, 250, 133, 61, 186, + 43, 121, 10, 21, 155, 159, 94, 202, + 78, 212, 172, 229, 243, 115, 167, 87, + 175, 88, 168, 80, 244, 234, 214, 116, + 79, 174, 233, 213, 231, 230, 173, 232, + 44, 215, 117, 122, 235, 22, 11, 245, + 89, 203, 95, 176, 156, 169, 81, 160, + 127, 12, 246, 111, 23, 196, 73, 236, + 216, 67, 31, 45, 164, 118, 123, 183, + 204, 187, 62, 90, 251, 96, 177, 134, + 59, 82, 161, 108, 170, 85, 41, 157, + 151, 178, 135, 144, 97, 190, 220, 252, + 188, 149, 207, 205, 55, 63, 91, 209, + 83, 57, 132, 60, 65, 162, 109, 71, + 20, 42, 158, 93, 86, 242, 211, 171, + 68, 17, 146, 217, 35, 32, 46, 137, + 180, 124, 184, 38, 119, 153, 227, 165, + 103, 74, 237, 222, 197, 49, 254, 24, + 13, 99, 140, 128, 192, 247, 112, 7 + }; + + private static readonly byte[] Alogtable = + { + 0, 3, 5, 15, 17, 51, 85, 255, 26, 46, 114, 150, 161, 248, 19, 53, + 95, 225, 56, 72, 216, 115, 149, 164, 247, 2, 6, 10, 30, 34, 102, 170, + 229, 52, 92, 228, 55, 89, 235, 38, 106, 190, 217, 112, 144, 171, 230, 49, + 83, 245, 4, 12, 20, 60, 68, 204, 79, 209, 104, 184, 211, 110, 178, 205, + 76, 212, 103, 169, 224, 59, 77, 215, 98, 166, 241, 8, 24, 40, 120, 136, + 131, 158, 185, 208, 107, 189, 220, 127, 129, 152, 179, 206, 73, 219, 118, 154, + 181, 196, 87, 249, 16, 48, 80, 240, 11, 29, 39, 105, 187, 214, 97, 163, + 254, 25, 43, 125, 135, 146, 173, 236, 47, 113, 147, 174, 233, 32, 96, 160, + 251, 22, 58, 78, 210, 109, 183, 194, 93, 231, 50, 86, 250, 21, 63, 65, + 195, 94, 226, 61, 71, 201, 64, 192, 91, 237, 44, 116, 156, 191, 218, 117, + 159, 186, 213, 100, 172, 239, 42, 126, 130, 157, 188, 223, 122, 142, 137, 128, + 155, 182, 193, 88, 232, 35, 101, 175, 234, 37, 111, 177, 200, 67, 197, 84, + 252, 31, 33, 99, 165, 244, 7, 9, 27, 45, 119, 153, 176, 203, 70, 202, + 69, 207, 74, 222, 121, 139, 134, 145, 168, 227, 62, 66, 198, 81, 243, 14, + 18, 54, 90, 238, 41, 123, 141, 140, 143, 138, 133, 148, 167, 242, 13, 23, + 57, 75, 221, 124, 132, 151, 162, 253, 28, 36, 108, 180, 199, 82, 246, 1, + 3, 5, 15, 17, 51, 85, 255, 26, 46, 114, 150, 161, 248, 19, 53, + 95, 225, 56, 72, 216, 115, 149, 164, 247, 2, 6, 10, 30, 34, 102, 170, + 229, 52, 92, 228, 55, 89, 235, 38, 106, 190, 217, 112, 144, 171, 230, 49, + 83, 245, 4, 12, 20, 60, 68, 204, 79, 209, 104, 184, 211, 110, 178, 205, + 76, 212, 103, 169, 224, 59, 77, 215, 98, 166, 241, 8, 24, 40, 120, 136, + 131, 158, 185, 208, 107, 189, 220, 127, 129, 152, 179, 206, 73, 219, 118, 154, + 181, 196, 87, 249, 16, 48, 80, 240, 11, 29, 39, 105, 187, 214, 97, 163, + 254, 25, 43, 125, 135, 146, 173, 236, 47, 113, 147, 174, 233, 32, 96, 160, + 251, 22, 58, 78, 210, 109, 183, 194, 93, 231, 50, 86, 250, 21, 63, 65, + 195, 94, 226, 61, 71, 201, 64, 192, 91, 237, 44, 116, 156, 191, 218, 117, + 159, 186, 213, 100, 172, 239, 42, 126, 130, 157, 188, 223, 122, 142, 137, 128, + 155, 182, 193, 88, 232, 35, 101, 175, 234, 37, 111, 177, 200, 67, 197, 84, + 252, 31, 33, 99, 165, 244, 7, 9, 27, 45, 119, 153, 176, 203, 70, 202, + 69, 207, 74, 222, 121, 139, 134, 145, 168, 227, 62, 66, 198, 81, 243, 14, + 18, 54, 90, 238, 41, 123, 141, 140, 143, 138, 133, 148, 167, 242, 13, 23, + 57, 75, 221, 124, 132, 151, 162, 253, 28, 36, 108, 180, 199, 82, 246, 1, + }; + + private static readonly byte[] S = + { + 99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118, + 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192, + 183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21, + 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117, + 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132, + 83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207, + 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, + 81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210, + 205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115, + 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219, + 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, + 231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, + 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138, + 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158, + 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223, + 140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 22, + }; + + private static readonly byte[] Si = + { + 82, 9, 106, 213, 48, 54, 165, 56, 191, 64, 163, 158, 129, 243, 215, 251, + 124, 227, 57, 130, 155, 47, 255, 135, 52, 142, 67, 68, 196, 222, 233, 203, + 84, 123, 148, 50, 166, 194, 35, 61, 238, 76, 149, 11, 66, 250, 195, 78, + 8, 46, 161, 102, 40, 217, 36, 178, 118, 91, 162, 73, 109, 139, 209, 37, + 114, 248, 246, 100, 134, 104, 152, 22, 212, 164, 92, 204, 93, 101, 182, 146, + 108, 112, 72, 80, 253, 237, 185, 218, 94, 21, 70, 87, 167, 141, 157, 132, + 144, 216, 171, 0, 140, 188, 211, 10, 247, 228, 88, 5, 184, 179, 69, 6, + 208, 44, 30, 143, 202, 63, 15, 2, 193, 175, 189, 3, 1, 19, 138, 107, + 58, 145, 17, 65, 79, 103, 220, 234, 151, 242, 207, 206, 240, 180, 230, 115, + 150, 172, 116, 34, 231, 173, 53, 133, 226, 249, 55, 232, 28, 117, 223, 110, + 71, 241, 26, 113, 29, 41, 197, 137, 111, 183, 98, 14, 170, 24, 190, 27, + 252, 86, 62, 75, 198, 210, 121, 32, 154, 219, 192, 254, 120, 205, 90, 244, + 31, 221, 168, 51, 136, 7, 199, 49, 177, 18, 16, 89, 39, 128, 236, 95, + 96, 81, 127, 169, 25, 181, 74, 13, 45, 229, 122, 159, 147, 201, 156, 239, + 160, 224, 59, 77, 174, 42, 245, 176, 200, 235, 187, 60, 131, 83, 153, 97, + 23, 43, 4, 126, 186, 119, 214, 38, 225, 105, 20, 99, 85, 33, 12, 125, + }; + + private static readonly byte[] 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 readonly byte[][] shifts0 = new byte [][] + { + new byte[]{ 0, 8, 16, 24 }, + new byte[]{ 0, 8, 16, 24 }, + new byte[]{ 0, 8, 16, 24 }, + new byte[]{ 0, 8, 16, 32 }, + new byte[]{ 0, 8, 24, 32 } + }; + + static readonly byte[][] shifts1 = + { + new byte[]{ 0, 24, 16, 8 }, + new byte[]{ 0, 32, 24, 16 }, + new byte[]{ 0, 40, 32, 24 }, + new byte[]{ 0, 48, 40, 24 }, + new byte[]{ 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 (((long)((ulong) r >> shift) | (r << (BC - shift)))) & BC_MASK; + + ulong temp = (ulong) r >> shift; + + // NB: This corrects for Mono Bug #79087 (fixed in 1.1.17) + if (shift > 31) + { + temp &= 0xFFFFFFFFUL; + } + + return ((long) temp | (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]; + long[][] W = new long[MAXROUNDS+1][]; + + for (int i = 0; i < MAXROUNDS+1; i++) W[i] = new long[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 ArgumentException("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] ^= (byte) 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 bool 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 blocksize 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 = unchecked( (long)0xffffffffffffffffL); + shifts0SC = shifts0[4]; + shifts1SC = shifts1[4]; + break; + default: + throw new ArgumentException("unknown blocksize to Rijndael"); + } + + this.blockBits = blockBits; + } + + /** + * initialise a Rijndael cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (typeof(KeyParameter).IsInstanceOfType(parameters)) + { + workingKey = GenerateWorkingKey(((KeyParameter)parameters).GetKey()); + this.forEncryption = forEncryption; + return; + } + + throw new ArgumentException("invalid parameter passed to Rijndael init - " + parameters.GetType().ToString()); + } + + public string AlgorithmName + { + get { return "Rijndael"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return BC / 2; + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (workingKey == null) + { + throw new InvalidOperationException("Rijndael engine not initialised"); + } + + if ((inOff + (BC / 2)) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + (BC / 2)) > output.Length) + { + throw new DataLengthException("output buffer too short"); + } + + UnPackBlock(input, inOff); + + if (forEncryption) + { + EncryptBlock(workingKey); + } + else + { + DecryptBlock(workingKey); + } + + PackBlock(output, outOff); + + return BC / 2; + } + + public void Reset() + { + } + + private void UnPackBlock( + byte[] bytes, + int off) + { + int index = off; + + A0 = (long)(bytes[index++] & 0xff); + A1 = (long)(bytes[index++] & 0xff); + A2 = (long)(bytes[index++] & 0xff); + A3 = (long)(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/Crypto/src/crypto/engines/RsaEngine.cs b/Crypto/src/crypto/engines/RsaEngine.cs new file mode 100644
index 000000000..7e6dfb163 --- /dev/null +++ b/Crypto/src/crypto/engines/RsaEngine.cs
@@ -0,0 +1,78 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * this does your basic RSA algorithm. + */ + public class RsaEngine + : IAsymmetricBlockCipher + { + private RsaCoreEngine core; + + public string AlgorithmName + { + get { return "RSA"; } + } + + /** + * initialise the RSA engine. + * + * @param forEncryption true if we are encrypting, false otherwise. + * @param param the necessary RSA key parameters. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (core == null) + core = new RsaCoreEngine(); + + core.Init(forEncryption, parameters); + } + + /** + * 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 inBuf 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[] inBuf, + int inOff, + int inLen) + { + if (core == null) + throw new InvalidOperationException("RSA engine not initialised"); + + return core.ConvertOutput(core.ProcessBlock(core.ConvertInput(inBuf, inOff, inLen))); + } + } +} diff --git a/Crypto/src/crypto/engines/SEEDEngine.cs b/Crypto/src/crypto/engines/SEEDEngine.cs new file mode 100644
index 000000000..efea0f1fe --- /dev/null +++ b/Crypto/src/crypto/engines/SEEDEngine.cs
@@ -0,0 +1,361 @@ +using System; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * Implementation of the SEED algorithm as described in RFC 4009 + */ + public class SeedEngine + : IBlockCipher + { + private const int BlockSize = 16; + + private static readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] 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 readonly uint[] KC = + { + 0x9e3779b9, 0x3c6ef373, 0x78dde6e6, 0xf1bbcdcc, + 0xe3779b99, 0xc6ef3733, 0x8dde6e67, 0x1bbcdccf, + 0x3779b99e, 0x6ef3733c, 0xdde6e678, 0xbbcdccf1, + 0x779b99e3, 0xef3733c6, 0xde6e678d, 0xbcdccf1b + }; + + private int[] wKey; + private bool forEncryption; + + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + this.forEncryption = forEncryption; + wKey = createWorkingKey(((KeyParameter)parameters).GetKey()); + } + + public string AlgorithmName + { + get { return "SEED"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return BlockSize; + } + + public int ProcessBlock( + byte[] inBuf, + int inOff, + byte[] outBuf, + int outOff) + { + if (wKey == null) + throw new InvalidOperationException("SEED engine not initialised"); + if (inOff + BlockSize > inBuf.Length) + throw new DataLengthException("input buffer too short"); + if (outOff + BlockSize > outBuf.Length) + throw new DataLengthException("output buffer too short"); + + long l = bytesToLong(inBuf, inOff + 0); + long r = bytesToLong(inBuf, 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(outBuf, outOff + 0, r); + longToBytes(outBuf, outOff + 8, l); + + return BlockSize; + } + + 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 - (int)KC[i]); + key[2 * i + 1] = G(key1 - key3 + (int)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) | ((long)((ulong) x >> 56)); + } + + private long rotateRight8( + long x) + { + return ((long)((ulong) 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 (int)(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/Crypto/src/crypto/engines/SEEDWrapEngine.cs b/Crypto/src/crypto/engines/SEEDWrapEngine.cs new file mode 100644
index 000000000..6b71f940b --- /dev/null +++ b/Crypto/src/crypto/engines/SEEDWrapEngine.cs
@@ -0,0 +1,16 @@ +namespace Org.BouncyCastle.Crypto.Engines +{ + /// <remarks> + /// 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>. + /// </remarks> + public class SeedWrapEngine + : Rfc3394WrapEngine + { + public SeedWrapEngine() + : base(new SeedEngine()) + { + } + } +} diff --git a/Crypto/src/crypto/engines/Salsa20Engine.cs b/Crypto/src/crypto/engines/Salsa20Engine.cs new file mode 100644
index 000000000..7d68deab1 --- /dev/null +++ b/Crypto/src/crypto/engines/Salsa20Engine.cs
@@ -0,0 +1,299 @@ +using System; +using System.Text; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * Implementation of Daniel J. Bernstein's Salsa20 stream cipher, Snuffle 2005 + */ + public class Salsa20Engine + : IStreamCipher + { + /** Constants */ + private const int StateSize = 16; // 16, 32 bit ints = 64 bytes + + private readonly static byte[] + sigma = Strings.ToAsciiByteArray("expand 32-byte k"), + tau = Strings.ToAsciiByteArray("expand 16-byte k"); + + /* + * variables to hold the state of the engine + * during encryption and decryption + */ + private int index = 0; + private uint[] engineState = new uint[StateSize]; // state + private uint[] x = new uint[StateSize]; // internal buffer + private byte[] keyStream = new byte[StateSize * 4], // expanded state, 64 bytes + workingKey = null, + workingIV = null; + private bool initialised = false; + + /* + * internal counter + */ + private uint 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 ArgumentException if the params argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + /* + * Salsa20 encryption and decryption is completely + * symmetrical, so the 'forEncryption' is + * irrelevant. (Like 90% of stream ciphers) + */ + + ParametersWithIV ivParams = parameters as ParametersWithIV; + + if (ivParams == null) + throw new ArgumentException("Salsa20 Init requires an IV", "parameters"); + + byte[] iv = ivParams.GetIV(); + + if (iv == null || iv.Length != 8) + throw new ArgumentException("Salsa20 requires exactly 8 bytes of IV"); + + KeyParameter key = ivParams.Parameters as KeyParameter; + + if (key == null) + throw new ArgumentException("Salsa20 Init requires a key", "parameters"); + + workingKey = key.GetKey(); + workingIV = iv; + + SetKey(workingKey, workingIV); + } + + public string AlgorithmName + { + get { return "Salsa20"; } + } + + public byte ReturnByte( + byte input) + { + 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 output = (byte)(keyStream[index] ^ input); + index = (index + 1) & 63; + + return output; + } + + public void ProcessBytes( + byte[] inBytes, + int inOff, + int len, + byte[] outBytes, + int outOff) + { + if (!initialised) + { + throw new InvalidOperationException(AlgorithmName + " not initialised"); + } + + if ((inOff + len) > inBytes.Length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + len) > outBytes.Length) + { + throw new DataLengthException("output buffer too short"); + } + + if (LimitExceeded((uint)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]; + } + } + outBytes[i+outOff] = (byte)(keyStream[index]^inBytes[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.LE_To_UInt32(workingKey, 0); + engineState[2] = Pack.LE_To_UInt32(workingKey, 4); + engineState[3] = Pack.LE_To_UInt32(workingKey, 8); + engineState[4] = Pack.LE_To_UInt32(workingKey, 12); + + if (workingKey.Length == 32) + { + constants = sigma; + offset = 16; + } + else + { + constants = tau; + } + + engineState[11] = Pack.LE_To_UInt32(workingKey, offset); + engineState[12] = Pack.LE_To_UInt32(workingKey, offset + 4); + engineState[13] = Pack.LE_To_UInt32(workingKey, offset + 8); + engineState[14] = Pack.LE_To_UInt32(workingKey, offset + 12); + engineState[0] = Pack.LE_To_UInt32(constants, 0); + engineState[5] = Pack.LE_To_UInt32(constants, 4); + engineState[10] = Pack.LE_To_UInt32(constants, 8); + engineState[15] = Pack.LE_To_UInt32(constants, 12); + + // IV + engineState[6] = Pack.LE_To_UInt32(workingIV, 0); + engineState[7] = Pack.LE_To_UInt32(workingIV, 4); + engineState[8] = engineState[9] = 0; + + initialised = true; + } + + private void GenerateKeyStream(byte[] output) + { + SalsaCore(20, engineState, x); + Pack.UInt32_To_LE(x, output, 0); + } + + internal static void SalsaCore(int rounds, uint[] state, uint[] x) + { + // TODO Exception if rounds odd? + + Array.Copy(state, 0, x, 0, state.Length); + + for (int i = rounds; i > 0; i -= 2) + { + x[ 4] ^= R((x[ 0]+x[12]), 7); + x[ 8] ^= R((x[ 4]+x[ 0]), 9); + x[12] ^= R((x[ 8]+x[ 4]),13); + x[ 0] ^= R((x[12]+x[ 8]),18); + x[ 9] ^= R((x[ 5]+x[ 1]), 7); + x[13] ^= R((x[ 9]+x[ 5]), 9); + x[ 1] ^= R((x[13]+x[ 9]),13); + x[ 5] ^= R((x[ 1]+x[13]),18); + x[14] ^= R((x[10]+x[ 6]), 7); + x[ 2] ^= R((x[14]+x[10]), 9); + x[ 6] ^= R((x[ 2]+x[14]),13); + x[10] ^= R((x[ 6]+x[ 2]),18); + x[ 3] ^= R((x[15]+x[11]), 7); + x[ 7] ^= R((x[ 3]+x[15]), 9); + x[11] ^= R((x[ 7]+x[ 3]),13); + x[15] ^= R((x[11]+x[ 7]),18); + x[ 1] ^= R((x[ 0]+x[ 3]), 7); + x[ 2] ^= R((x[ 1]+x[ 0]), 9); + x[ 3] ^= R((x[ 2]+x[ 1]),13); + x[ 0] ^= R((x[ 3]+x[ 2]),18); + x[ 6] ^= R((x[ 5]+x[ 4]), 7); + x[ 7] ^= R((x[ 6]+x[ 5]), 9); + x[ 4] ^= R((x[ 7]+x[ 6]),13); + x[ 5] ^= R((x[ 4]+x[ 7]),18); + x[11] ^= R((x[10]+x[ 9]), 7); + x[ 8] ^= R((x[11]+x[10]), 9); + x[ 9] ^= R((x[ 8]+x[11]),13); + x[10] ^= R((x[ 9]+x[ 8]),18); + x[12] ^= R((x[15]+x[14]), 7); + x[13] ^= R((x[12]+x[15]), 9); + x[14] ^= R((x[13]+x[12]),13); + x[15] ^= R((x[14]+x[13]),18); + } + + for (int i = 0; i < StateSize; ++i) + { + x[i] += state[i]; + } + } + + private static uint R(uint x, int y) + { + return (x << y) | (x >> (32 - y)); + } + + private void ResetCounter() + { + cW0 = 0; + cW1 = 0; + cW2 = 0; + } + + private bool 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 bool LimitExceeded( + uint len) + { + uint old = cW0; + cW0 += len; + if (cW0 < old) + { + if (++cW1 == 0) + { + return (++cW2 & 0x20) != 0; // 2^(32 + 32 + 6) + } + } + + return false; + } + } +} diff --git a/Crypto/src/crypto/engines/SerpentEngine.cs b/Crypto/src/crypto/engines/SerpentEngine.cs new file mode 100644
index 000000000..92b25acc6 --- /dev/null +++ b/Crypto/src/crypto/engines/SerpentEngine.cs
@@ -0,0 +1,779 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * 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> + * <p> + * For full details see the <a href="http://www.cl.cam.ac.uk/~rja14/serpent.html">The Serpent home page</a> + * </p> + */ + public class SerpentEngine + : IBlockCipher + { + private const int BLOCK_SIZE = 16; + + static readonly int ROUNDS = 32; + static readonly int PHI = unchecked((int)0x9E3779B9); // (Sqrt(5) - 1) * 2**31 + + private bool encrypting; + private int[] wKey; + + private int X0, X1, X2, X3; // registers + + /** + * initialise a Serpent cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + throw new ArgumentException("invalid parameter passed to Serpent init - " + parameters.GetType().ToString()); + + this.encrypting = forEncryption; + this.wKey = MakeWorkingKey(((KeyParameter)parameters).GetKey()); + } + + public string AlgorithmName + { + get { return "Serpent"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + 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 InvalidOperationException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (wKey == null) + throw new InvalidOperationException("Serpent not initialised"); + if ((inOff + BLOCK_SIZE) > input.Length) + throw new DataLengthException("input buffer too short"); + if ((outOff + BLOCK_SIZE) > output.Length) + throw new DataLengthException("output buffer too short"); + + if (encrypting) + { + EncryptBlock(input, inOff, output, outOff); + } + else + { + DecryptBlock(input, inOff, output, 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 ArgumentException + */ + private int[] MakeWorkingKey( + byte[] key) + { + // + // 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 ArgumentException("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); + } + + Array.Copy(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) | (int) ((uint)x >> (32 - bits))); + } + + private int RotateRight( + int x, + int bits) + { + return ( (int)((uint)x >> bits) | (x << (32 - 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)((uint)word >> 8); + dst[dstOff + 1] = (byte)((uint)word >> 16); + dst[dstOff] = (byte)((uint)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[] input, + int inOff, + byte[] outBytes, + int outOff) + { + X3 = BytesToWord(input, inOff); + X2 = BytesToWord(input, inOff + 4); + X1 = BytesToWord(input, inOff + 8); + X0 = BytesToWord(input, 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, outBytes, outOff); + WordToBytes(wKey[130] ^ X2, outBytes, outOff + 4); + WordToBytes(wKey[129] ^ X1, outBytes, outOff + 8); + WordToBytes(wKey[128] ^ X0, outBytes, 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[] input, + int inOff, + byte[] outBytes, + int outOff) + { + X3 = wKey[131] ^ BytesToWord(input, inOff); + X2 = wKey[130] ^ BytesToWord(input, inOff + 4); + X1 = wKey[129] ^ BytesToWord(input, inOff + 8); + X0 = wKey[128] ^ BytesToWord(input, 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], outBytes, outOff); + WordToBytes(X2 ^ wKey[2], outBytes, outOff + 4); + WordToBytes(X1 ^ wKey[1], outBytes, outOff + 8); + WordToBytes(X0 ^ wKey[0], outBytes, 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/ + * </p> + */ + + /* Partially optimised Serpent S Box bool 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/Crypto/src/crypto/engines/SkipjackEngine.cs b/Crypto/src/crypto/engines/SkipjackEngine.cs new file mode 100644
index 000000000..3d2a781e6 --- /dev/null +++ b/Crypto/src/crypto/engines/SkipjackEngine.cs
@@ -0,0 +1,255 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * a class that provides a basic SKIPJACK engine. + */ + public class SkipjackEngine + : IBlockCipher + { + const int BLOCK_SIZE = 8; + + static readonly 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 bool encrypting; + + /** + * initialise a SKIPJACK cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + throw new ArgumentException("invalid parameter passed to SKIPJACK init - " + parameters.GetType().ToString()); + + byte[] keyBytes = ((KeyParameter)parameters).GetKey(); + + this.encrypting = forEncryption; + 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 AlgorithmName + { + get { return "SKIPJACK"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return BLOCK_SIZE; + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (key1 == null) + throw new InvalidOperationException("SKIPJACK engine not initialised"); + if ((inOff + BLOCK_SIZE) > input.Length) + throw new DataLengthException("input buffer too short"); + if ((outOff + BLOCK_SIZE) > output.Length) + throw new DataLengthException("output buffer too short"); + + if (encrypting) + { + EncryptBlock(input, inOff, output, outOff); + } + else + { + DecryptBlock(input, inOff, output, 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[] input, + int inOff, + byte[] outBytes, + int outOff) + { + int w1 = (input[inOff + 0] << 8) + (input[inOff + 1] & 0xff); + int w2 = (input[inOff + 2] << 8) + (input[inOff + 3] & 0xff); + int w3 = (input[inOff + 4] << 8) + (input[inOff + 5] & 0xff); + int w4 = (input[inOff + 6] << 8) + (input[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++; + } + } + + outBytes[outOff + 0] = (byte)((w1 >> 8)); + outBytes[outOff + 1] = (byte)(w1); + outBytes[outOff + 2] = (byte)((w2 >> 8)); + outBytes[outOff + 3] = (byte)(w2); + outBytes[outOff + 4] = (byte)((w3 >> 8)); + outBytes[outOff + 5] = (byte)(w3); + outBytes[outOff + 6] = (byte)((w4 >> 8)); + outBytes[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[] input, + int inOff, + byte[] outBytes, + int outOff) + { + int w2 = (input[inOff + 0] << 8) + (input[inOff + 1] & 0xff); + int w1 = (input[inOff + 2] << 8) + (input[inOff + 3] & 0xff); + int w4 = (input[inOff + 4] << 8) + (input[inOff + 5] & 0xff); + int w3 = (input[inOff + 6] << 8) + (input[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--; + } + } + + outBytes[outOff + 0] = (byte)((w2 >> 8)); + outBytes[outOff + 1] = (byte)(w2); + outBytes[outOff + 2] = (byte)((w1 >> 8)); + outBytes[outOff + 3] = (byte)(w1); + outBytes[outOff + 4] = (byte)((w4 >> 8)); + outBytes[outOff + 5] = (byte)(w4); + outBytes[outOff + 6] = (byte)((w3 >> 8)); + outBytes[outOff + 7] = (byte)(w3); + + return BLOCK_SIZE; + } + } + +} diff --git a/Crypto/src/crypto/engines/TEAEngine.cs b/Crypto/src/crypto/engines/TEAEngine.cs new file mode 100644
index 000000000..582dd0f73 --- /dev/null +++ b/Crypto/src/crypto/engines/TEAEngine.cs
@@ -0,0 +1,168 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * An TEA engine. + */ + public class TeaEngine + : IBlockCipher + { + private const int + rounds = 32, + block_size = 8; +// key_size = 16, + + private const uint + delta = 0x9E3779B9, + d_sum = 0xC6EF3720; // sum on decrypt + + /* + * the expanded key array of 4 subkeys + */ + private uint _a, _b, _c, _d; + private bool _initialised; + private bool _forEncryption; + + /** + * Create an instance of the TEA encryption algorithm + * and set some defaults + */ + public TeaEngine() + { + _initialised = false; + } + + public string AlgorithmName + { + get { return "TEA"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + 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 ArgumentException if the params argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + { + throw new ArgumentException("invalid parameter passed to TEA init - " + + parameters.GetType().FullName); + } + + _forEncryption = forEncryption; + _initialised = true; + + KeyParameter p = (KeyParameter) parameters; + + setKey(p.GetKey()); + } + + public int ProcessBlock( + byte[] inBytes, + int inOff, + byte[] outBytes, + int outOff) + { + if (!_initialised) + throw new InvalidOperationException(AlgorithmName + " not initialised"); + + if ((inOff + block_size) > inBytes.Length) + throw new DataLengthException("input buffer too short"); + + if ((outOff + block_size) > outBytes.Length) + throw new DataLengthException("output buffer too short"); + + return _forEncryption + ? encryptBlock(inBytes, inOff, outBytes, outOff) + : decryptBlock(inBytes, inOff, outBytes, outOff); + } + + public void Reset() + { + } + + /** + * Re-key the cipher. + * + * @param key the key to be used + */ + private void setKey( + byte[] key) + { + _a = Pack.BE_To_UInt32(key, 0); + _b = Pack.BE_To_UInt32(key, 4); + _c = Pack.BE_To_UInt32(key, 8); + _d = Pack.BE_To_UInt32(key, 12); + } + + private int encryptBlock( + byte[] inBytes, + int inOff, + byte[] outBytes, + int outOff) + { + // Pack bytes into integers + uint v0 = Pack.BE_To_UInt32(inBytes, inOff); + uint v1 = Pack.BE_To_UInt32(inBytes, inOff + 4); + + uint 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); + } + + Pack.UInt32_To_BE(v0, outBytes, outOff); + Pack.UInt32_To_BE(v1, outBytes, outOff + 4); + + return block_size; + } + + private int decryptBlock( + byte[] inBytes, + int inOff, + byte[] outBytes, + int outOff) + { + // Pack bytes into integers + uint v0 = Pack.BE_To_UInt32(inBytes, inOff); + uint v1 = Pack.BE_To_UInt32(inBytes, inOff + 4); + + uint 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; + } + + Pack.UInt32_To_BE(v0, outBytes, outOff); + Pack.UInt32_To_BE(v1, outBytes, outOff + 4); + + return block_size; + } + } +} diff --git a/Crypto/src/crypto/engines/TwofishEngine.cs b/Crypto/src/crypto/engines/TwofishEngine.cs new file mode 100644
index 000000000..b983d9d31 --- /dev/null +++ b/Crypto/src/crypto/engines/TwofishEngine.cs
@@ -0,0 +1,675 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * 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 sealed class TwofishEngine + : IBlockCipher + { + private static readonly 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 const int P_00 = 1; + private const int P_01 = 0; + private const int P_02 = 0; + private const int P_03 = P_01 ^ 1; + private const int P_04 = 1; + + private const int P_10 = 0; + private const int P_11 = 0; + private const int P_12 = 1; + private const int P_13 = P_11 ^ 1; + private const int P_14 = 0; + + private const int P_20 = 1; + private const int P_21 = 1; + private const int P_22 = 0; + private const int P_23 = P_21 ^ 1; + private const int P_24 = 0; + + private const int P_30 = 0; + private const int P_31 = 1; + private const int P_32 = 1; + private const int P_33 = P_31 ^ 1; + private const int P_34 = 1; + + /* Primitive polynomial for GF(256) */ + private const int GF256_FDBK = 0x169; + private const int GF256_FDBK_2 = GF256_FDBK / 2; + private const int GF256_FDBK_4 = GF256_FDBK / 4; + + private const int RS_GF_FDBK = 0x14D; // field generator + + //==================================== + // Useful constants + //==================================== + + private const int ROUNDS = 16; + private const int MAX_ROUNDS = 16; // bytes = 128 bits + private const int BLOCK_SIZE = 16; // bytes = 128 bits + private const int MAX_KEY_BITS = 256; + + private const int INPUT_WHITEN=0; + private const int OUTPUT_WHITEN=INPUT_WHITEN+BLOCK_SIZE/4; // 4 + private const int ROUND_SUBKEYS=OUTPUT_WHITEN+BLOCK_SIZE/4;// 8 + + private const int TOTAL_SUBKEYS=ROUND_SUBKEYS+2*MAX_ROUNDS;// 40 + + private const int SK_STEP = 0x02020202; + private const int SK_BUMP = 0x01010101; + private const int SK_ROTL = 9; + + private bool encrypting; + + 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; + + private byte[] workingKey; + + 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 forEncryption whether or not we are for encryption. + * @param parameters the parameters required to set up the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + throw new ArgumentException("invalid parameter passed to Twofish init - " + parameters.GetType().ToString()); + + this.encrypting = forEncryption; + this.workingKey = ((KeyParameter)parameters).GetKey(); + this.k64Cnt = (this.workingKey.Length / 8); // pre-padded ? + SetKey(this.workingKey); + } + + public string AlgorithmName + { + get { return "Twofish"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if (workingKey == null) + throw new InvalidOperationException("Twofish not initialised"); + if ((inOff + BLOCK_SIZE) > input.Length) + throw new DataLengthException("input buffer too short"); + if ((outOff + BLOCK_SIZE) > output.Length) + throw new DataLengthException("output buffer too short"); + + if (encrypting) + { + EncryptBlock(input, inOff, output, outOff); + } + else + { + DecryptBlock(input, inOff, output, 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 ArgumentException("Key size less than 64 bits"); + } + + if (k64Cnt > 4) + { + throw new ArgumentException("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,p=0; i<k64Cnt ; i++) + { + 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 | (int)((uint)B >> 24); + A += B; + gSubKeys[i*2] = A; + A += B; + gSubKeys[i*2 + 1] = A << SK_ROTL | (int)((uint)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) ^ M_b0(k0)]; + gSBox[i*2+1] = gMDS1[(P[P_11,b1] & 0xff) ^ M_b1(k0)]; + gSBox[i*2+0x200] = gMDS2[(P[P_21,b2] & 0xff) ^ M_b2(k0)]; + gSBox[i*2+0x201] = gMDS3[(P[P_31,b3] & 0xff) ^ M_b3(k0)]; + break; + case 0: // 256 bits of key + b0 = (P[P_04,b0] & 0xff) ^ M_b0(k3); + b1 = (P[P_14,b1] & 0xff) ^ M_b1(k3); + b2 = (P[P_24,b2] & 0xff) ^ M_b2(k3); + b3 = (P[P_34,b3] & 0xff) ^ M_b3(k3); + // fall through, having pre-processed b[0]..b[3] with k32[3] + goto case 3; + case 3: // 192 bits of key + b0 = (P[P_03,b0] & 0xff) ^ M_b0(k2); + b1 = (P[P_13,b1] & 0xff) ^ M_b1(k2); + b2 = (P[P_23,b2] & 0xff) ^ M_b2(k2); + b3 = (P[P_33,b3] & 0xff) ^ M_b3(k2); + // fall through, having pre-processed b[0]..b[3] with k32[2] + goto case 2; + case 2: // 128 bits of key + gSBox[i * 2] = gMDS0[(P[P_01, (P[P_02, b0] & 0xff) ^ M_b0(k1)] & 0xff) ^ M_b0(k0)]; + gSBox[i*2+1] = gMDS1[(P[P_11,(P[P_12,b1] & 0xff) ^ M_b1(k1)] & 0xff) ^ M_b1(k0)]; + gSBox[i*2+0x200] = gMDS2[(P[P_21,(P[P_22,b2] & 0xff) ^ M_b2(k1)] & 0xff) ^ M_b2(k0)]; + gSBox[i * 2 + 0x201] = gMDS3[(P[P_31, (P[P_32, b3] & 0xff) ^ M_b3(k1)] & 0xff) ^ M_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 = (int)((uint)x2 >>1) | x2 << 31; + x3 = (x3 << 1 | (int) ((uint)x3 >> 31)) ^ (t0 + 2*t1 + gSubKeys[k++]); + + t0 = Fe32_0(x2); + t1 = Fe32_3(x3); + x0 ^= t0 + t1 + gSubKeys[k++]; + x0 = (int) ((uint)x0 >>1) | x0 << 31; + x1 = (x1 << 1 | (int)((uint)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 | (int)((uint) x0 >> 31)) ^ (t0 + t1 + gSubKeys[k--]); + x1 = (int) ((uint)x1 >>1) | x1 << 31; + + t0 = Fe32_0(x0); + t1 = Fe32_3(x1); + x3 ^= t0 + 2*t1 + gSubKeys[k--]; + x2 = (x2 << 1 | (int)((uint)x2 >> 31)) ^ (t0 + t1 + gSubKeys[k--]); + x3 = (int)((uint)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 = M_b0(x); + int b1 = M_b1(x); + int b2 = M_b2(x); + int b3 = M_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) ^ M_b0(k0)] ^ + gMDS1[(P[P_11,b1] & 0xff) ^ M_b1(k0)] ^ + gMDS2[(P[P_21,b2] & 0xff) ^ M_b2(k0)] ^ + gMDS3[(P[P_31,b3] & 0xff) ^ M_b3(k0)]; + break; + case 0: /* 256 bits of key */ + b0 = (P[P_04,b0] & 0xff) ^ M_b0(k3); + b1 = (P[P_14,b1] & 0xff) ^ M_b1(k3); + b2 = (P[P_24,b2] & 0xff) ^ M_b2(k3); + b3 = (P[P_34,b3] & 0xff) ^ M_b3(k3); + goto case 3; + case 3: + b0 = (P[P_03,b0] & 0xff) ^ M_b0(k2); + b1 = (P[P_13,b1] & 0xff) ^ M_b1(k2); + b2 = (P[P_23,b2] & 0xff) ^ M_b2(k2); + b3 = (P[P_33,b3] & 0xff) ^ M_b3(k2); + goto case 2; + case 2: + result = + gMDS0[(P[P_01,(P[P_02,b0]&0xff)^M_b0(k1)]&0xff)^M_b0(k0)] ^ + gMDS1[(P[P_11,(P[P_12,b1]&0xff)^M_b1(k1)]&0xff)^M_b1(k0)] ^ + gMDS2[(P[P_21,(P[P_22,b2]&0xff)^M_b2(k1)]&0xff)^M_b2(k0)] ^ + gMDS3[(P[P_31,(P[P_32,b3]&0xff)^M_b3(k1)]&0xff)^M_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 + * </p> + */ + private int RS_rem(int x) + { + int b = (int) (((uint)x >> 24) & 0xff); + int g2 = ((b << 1) ^ + ((b & 0x80) != 0 ? RS_GF_FDBK : 0)) & 0xff; + int g3 = ( (int)((uint)b >> 1) ^ + ((b & 0x01) != 0 ? (int)((uint)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 M_b0(int x) + { + return x & 0xff; + } + + private int M_b1(int x) + { + return (int)((uint)x >> 8) & 0xff; + } + + private int M_b2(int x) + { + return (int)((uint)x >> 16) & 0xff; + } + + private int M_b3(int x) + { + return (int)((uint)x >> 24) & 0xff; + } + + private int Fe32_0(int x) + { + return gSBox[ 0x000 + 2*(x & 0xff) ] ^ + gSBox[ 0x001 + 2*((int)((uint)x >> 8) & 0xff) ] ^ + gSBox[ 0x200 + 2*((int)((uint)x >> 16) & 0xff) ] ^ + gSBox[ 0x201 + 2*((int)((uint)x >> 24) & 0xff) ]; + } + + private int Fe32_3(int x) + { + return gSBox[ 0x000 + 2*((int)((uint)x >> 24) & 0xff) ] ^ + gSBox[ 0x001 + 2*(x & 0xff) ] ^ + gSBox[ 0x200 + 2*((int)((uint)x >> 8) & 0xff) ] ^ + gSBox[ 0x201 + 2*((int)((uint)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 inData, byte[] b, int offset) + { + b[offset] = (byte)inData; + b[offset + 1] = (byte)(inData >> 8); + b[offset + 2] = (byte)(inData >> 16); + b[offset + 3] = (byte)(inData >> 24); + } + } + +} diff --git a/Crypto/src/crypto/engines/VMPCEngine.cs b/Crypto/src/crypto/engines/VMPCEngine.cs new file mode 100644
index 000000000..d467fbba5 --- /dev/null +++ b/Crypto/src/crypto/engines/VMPCEngine.cs
@@ -0,0 +1,139 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Engines +{ + public class VmpcEngine + : IStreamCipher + { + /* + * 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 virtual string AlgorithmName + { + get { 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 ArgumentException + * if the params argument is inappropriate. + */ + public virtual void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is ParametersWithIV)) + throw new ArgumentException("VMPC Init parameters must include an IV"); + + ParametersWithIV ivParams = (ParametersWithIV) parameters; + KeyParameter key = (KeyParameter) ivParams.Parameters; + + if (!(ivParams.Parameters is KeyParameter)) + throw new ArgumentException("VMPC Init parameters must include a key"); + + this.workingIV = ivParams.GetIV(); + + if (workingIV == null || workingIV.Length < 1 || workingIV.Length > 768) + throw new ArgumentException("VMPC requires 1 to 768 bytes of IV"); + + this.workingKey = key.GetKey(); + + InitKey(this.workingKey, this.workingIV); + } + + protected virtual 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 virtual void ProcessBytes( + byte[] input, + int inOff, + int len, + byte[] output, + int outOff) + { + if ((inOff + len) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + len) > output.Length) + { + throw new DataLengthException("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 + output[i + outOff] = (byte) (input[i + inOff] ^ z); + } + } + + public virtual void Reset() + { + InitKey(this.workingKey, this.workingIV); + } + + public virtual byte ReturnByte( + byte input) + { + 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) (input ^ z); + } + } +} diff --git a/Crypto/src/crypto/engines/VMPCKSA3Engine.cs b/Crypto/src/crypto/engines/VMPCKSA3Engine.cs new file mode 100644
index 000000000..95b6813b7 --- /dev/null +++ b/Crypto/src/crypto/engines/VMPCKSA3Engine.cs
@@ -0,0 +1,51 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Engines +{ + public class VmpcKsa3Engine + : VmpcEngine + { + public override string AlgorithmName + { + get { return "VMPC-KSA3"; } + } + + protected override 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/Crypto/src/crypto/engines/XTEAEngine.cs b/Crypto/src/crypto/engines/XTEAEngine.cs new file mode 100644
index 000000000..eb9291775 --- /dev/null +++ b/Crypto/src/crypto/engines/XTEAEngine.cs
@@ -0,0 +1,168 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /** + * An XTEA engine. + */ + public class XteaEngine + : IBlockCipher + { + private const int + rounds = 32, + block_size = 8, +// key_size = 16, + delta = unchecked((int) 0x9E3779B9); + + /* + * the expanded key array of 4 subkeys + */ + private uint[] _S = new uint[4], + _sum0 = new uint[32], + _sum1 = new uint[32]; + private bool _initialised, _forEncryption; + + /** + * Create an instance of the TEA encryption algorithm + * and set some defaults + */ + public XteaEngine() + { + _initialised = false; + } + + public string AlgorithmName + { + get { return "XTEA"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + 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 ArgumentException if the params argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (!(parameters is KeyParameter)) + { + throw new ArgumentException("invalid parameter passed to TEA init - " + + parameters.GetType().FullName); + } + + _forEncryption = forEncryption; + _initialised = true; + + KeyParameter p = (KeyParameter) parameters; + + setKey(p.GetKey()); + } + + public int ProcessBlock( + byte[] inBytes, + int inOff, + byte[] outBytes, + int outOff) + { + if (!_initialised) + throw new InvalidOperationException(AlgorithmName + " not initialised"); + + if ((inOff + block_size) > inBytes.Length) + throw new DataLengthException("input buffer too short"); + + if ((outOff + block_size) > outBytes.Length) + throw new DataLengthException("output buffer too short"); + + return _forEncryption + ? encryptBlock(inBytes, inOff, outBytes, outOff) + : decryptBlock(inBytes, inOff, outBytes, outOff); + } + + public void Reset() + { + } + + /** + * Re-key the cipher. + * + * @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] = Pack.BE_To_UInt32(key, j); + } + + for (i = j = 0; i < rounds; i++) + { + _sum0[i] = ((uint)j + _S[j & 3]); + j += delta; + _sum1[i] = ((uint)j + _S[j >> 11 & 3]); + } + } + + private int encryptBlock( + byte[] inBytes, + int inOff, + byte[] outBytes, + int outOff) + { + // Pack bytes into integers + uint v0 = Pack.BE_To_UInt32(inBytes, inOff); + uint v1 = Pack.BE_To_UInt32(inBytes, 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]; + } + + Pack.UInt32_To_BE(v0, outBytes, outOff); + Pack.UInt32_To_BE(v1, outBytes, outOff + 4); + + return block_size; + } + + private int decryptBlock( + byte[] inBytes, + int inOff, + byte[] outBytes, + int outOff) + { + // Pack bytes into integers + uint v0 = Pack.BE_To_UInt32(inBytes, inOff); + uint v1 = Pack.BE_To_UInt32(inBytes, 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]; + } + + Pack.UInt32_To_BE(v0, outBytes, outOff); + Pack.UInt32_To_BE(v1, outBytes, outOff + 4); + + return block_size; + } + } +} diff --git a/Crypto/src/crypto/generators/BaseKdfBytesGenerator.cs b/Crypto/src/crypto/generators/BaseKdfBytesGenerator.cs new file mode 100644
index 000000000..0366401d1 --- /dev/null +++ b/Crypto/src/crypto/generators/BaseKdfBytesGenerator.cs
@@ -0,0 +1,141 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * 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 + : IDerivationFunction + { + private int counterStart; + private IDigest digest; + private byte[] shared; + private byte[] iv; + + /** + * Construct a KDF Parameters generator. + * + * @param counterStart value of counter. + * @param digest the digest to be used as the source of derived keys. + */ + protected BaseKdfBytesGenerator( + int counterStart, + IDigest digest) + { + this.counterStart = counterStart; + this.digest = digest; + } + + public void Init( + IDerivationParameters parameters) + { + if (parameters is KdfParameters) + { + KdfParameters p = (KdfParameters)parameters; + + shared = p.GetSharedSecret(); + iv = p.GetIV(); + } + else if (parameters is Iso18033KdfParameters) + { + Iso18033KdfParameters p = (Iso18033KdfParameters)parameters; + + shared = p.GetSeed(); + iv = null; + } + else + { + throw new ArgumentException("KDF parameters required for KDF Generator"); + } + } + + /** + * return the underlying digest. + */ + public IDigest Digest + { + get + { + return digest; + } + } + + /** + * fill len bytes of the output buffer with bytes generated from + * the derivation function. + * + * @throws ArgumentException if the size of the request will cause an overflow. + * @throws DataLengthException if the out buffer is too small. + */ + public int GenerateBytes( + byte[] output, + int outOff, + int length) + { + if ((output.Length - length) < outOff) + { + throw new DataLengthException("output buffer too small"); + } + + long oBytes = length; + 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 ArgumentException("Output length too large"); + } + + int cThreshold = (int)((oBytes + outLen - 1) / outLen); + + byte[] dig = new byte[digest.GetDigestSize()]; + + int counter = counterStart; + + for (int i = 0; i < cThreshold; i++) + { + digest.BlockUpdate(shared, 0, shared.Length); + + digest.Update((byte)(counter >> 24)); + digest.Update((byte)(counter >> 16)); + digest.Update((byte)(counter >> 8)); + digest.Update((byte)counter); + + if (iv != null) + { + digest.BlockUpdate(iv, 0, iv.Length); + } + + digest.DoFinal(dig, 0); + + if (length > outLen) + { + Array.Copy(dig, 0, output, outOff, outLen); + outOff += outLen; + length -= outLen; + } + else + { + Array.Copy(dig, 0, output, outOff, length); + } + + counter++; + } + + digest.Reset(); + + return (int)oBytes; + } + } +} \ No newline at end of file diff --git a/Crypto/src/crypto/generators/DHBasicKeyPairGenerator.cs b/Crypto/src/crypto/generators/DHBasicKeyPairGenerator.cs new file mode 100644
index 000000000..51b3af687 --- /dev/null +++ b/Crypto/src/crypto/generators/DHBasicKeyPairGenerator.cs
@@ -0,0 +1,38 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * a basic Diffie-Hellman key pair generator. + * + * This generates keys consistent for use with the basic algorithm for + * Diffie-Hellman. + */ + public class DHBasicKeyPairGenerator + : IAsymmetricCipherKeyPairGenerator + { + private DHKeyGenerationParameters param; + + public virtual void Init( + KeyGenerationParameters parameters) + { + this.param = (DHKeyGenerationParameters)parameters; + } + + public virtual AsymmetricCipherKeyPair GenerateKeyPair() + { + DHKeyGeneratorHelper helper = DHKeyGeneratorHelper.Instance; + DHParameters dhp = param.Parameters; + + BigInteger x = helper.CalculatePrivate(dhp, param.Random); + BigInteger y = helper.CalculatePublic(dhp, x); + + return new AsymmetricCipherKeyPair( + new DHPublicKeyParameters(y, dhp), + new DHPrivateKeyParameters(x, dhp)); + } + } +} diff --git a/Crypto/src/crypto/generators/DHKeyGeneratorHelper.cs b/Crypto/src/crypto/generators/DHKeyGeneratorHelper.cs new file mode 100644
index 000000000..756e8482a --- /dev/null +++ b/Crypto/src/crypto/generators/DHKeyGeneratorHelper.cs
@@ -0,0 +1,53 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Generators +{ + class DHKeyGeneratorHelper + { + internal static readonly DHKeyGeneratorHelper Instance = new DHKeyGeneratorHelper(); + + private DHKeyGeneratorHelper() + { + } + + internal BigInteger CalculatePrivate( + DHParameters dhParams, + SecureRandom random) + { + int limit = dhParams.L; + + if (limit != 0) + { + return new BigInteger(limit, random).SetBit(limit - 1); + } + + BigInteger min = BigInteger.Two; + int m = dhParams.M; + if (m != 0) + { + min = BigInteger.One.ShiftLeft(m - 1); + } + + BigInteger max = dhParams.P.Subtract(BigInteger.Two); + BigInteger q = dhParams.Q; + if (q != null) + { + max = q.Subtract(BigInteger.Two); + } + + return BigIntegers.CreateRandomInRange(min, max, random); + } + + internal BigInteger CalculatePublic( + DHParameters dhParams, + BigInteger x) + { + return dhParams.G.ModPow(x, dhParams.P); + } + } +} diff --git a/Crypto/src/crypto/generators/DHKeyPairGenerator.cs b/Crypto/src/crypto/generators/DHKeyPairGenerator.cs new file mode 100644
index 000000000..3bf58ba1b --- /dev/null +++ b/Crypto/src/crypto/generators/DHKeyPairGenerator.cs
@@ -0,0 +1,38 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * 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 + : IAsymmetricCipherKeyPairGenerator + { + private DHKeyGenerationParameters param; + + public virtual void Init( + KeyGenerationParameters parameters) + { + this.param = (DHKeyGenerationParameters)parameters; + } + + public virtual AsymmetricCipherKeyPair GenerateKeyPair() + { + DHKeyGeneratorHelper helper = DHKeyGeneratorHelper.Instance; + DHParameters dhp = param.Parameters; + + BigInteger x = helper.CalculatePrivate(dhp, param.Random); + BigInteger y = helper.CalculatePublic(dhp, x); + + return new AsymmetricCipherKeyPair( + new DHPublicKeyParameters(y, dhp), + new DHPrivateKeyParameters(x, dhp)); + } + } +} diff --git a/Crypto/src/crypto/generators/DHParametersGenerator.cs b/Crypto/src/crypto/generators/DHParametersGenerator.cs new file mode 100644
index 000000000..e752c8456 --- /dev/null +++ b/Crypto/src/crypto/generators/DHParametersGenerator.cs
@@ -0,0 +1,45 @@ +using System; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Generators +{ + public class DHParametersGenerator + { + private int size; + private int certainty; + private SecureRandom random; + + public virtual 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...</p> + */ + public virtual 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, BigInteger.Two, null); + } + } +} diff --git a/Crypto/src/crypto/generators/DHParametersHelper.cs b/Crypto/src/crypto/generators/DHParametersHelper.cs new file mode 100644
index 000000000..7860cbe33 --- /dev/null +++ b/Crypto/src/crypto/generators/DHParametersHelper.cs
@@ -0,0 +1,234 @@ +using System; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Generators +{ + internal class DHParametersHelper + { + // The primes b/w 2 and ~2^10 + /* + 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 + */ + + // Each list has a product < 2^31 + private static readonly int[][] primeLists = new int[][] + { + new int[]{ 3, 5, 7, 11, 13, 17, 19, 23 }, + new int[]{ 29, 31, 37, 41, 43 }, + new int[]{ 47, 53, 59, 61, 67 }, + new int[]{ 71, 73, 79, 83 }, + new int[]{ 89, 97, 101, 103 }, + + new int[]{ 107, 109, 113, 127 }, + new int[]{ 131, 137, 139, 149 }, + new int[]{ 151, 157, 163, 167 }, + new int[]{ 173, 179, 181, 191 }, + new int[]{ 193, 197, 199, 211 }, + + new int[]{ 223, 227, 229 }, + new int[]{ 233, 239, 241 }, + new int[]{ 251, 257, 263 }, + new int[]{ 269, 271, 277 }, + new int[]{ 281, 283, 293 }, + + new int[]{ 307, 311, 313 }, + new int[]{ 317, 331, 337 }, + new int[]{ 347, 349, 353 }, + new int[]{ 359, 367, 373 }, + new int[]{ 379, 383, 389 }, + + new int[]{ 397, 401, 409 }, + new int[]{ 419, 421, 431 }, + new int[]{ 433, 439, 443 }, + new int[]{ 449, 457, 461 }, + new int[]{ 463, 467, 479 }, + + new int[]{ 487, 491, 499 }, + new int[]{ 503, 509, 521 }, + new int[]{ 523, 541, 547 }, + new int[]{ 557, 563, 569 }, + new int[]{ 571, 577, 587 }, + + new int[]{ 593, 599, 601 }, + new int[]{ 607, 613, 617 }, + new int[]{ 619, 631, 641 }, + new int[]{ 643, 647, 653 }, + new int[]{ 659, 661, 673 }, + + new int[]{ 677, 683, 691 }, + new int[]{ 701, 709, 719 }, + new int[]{ 727, 733, 739 }, + new int[]{ 743, 751, 757 }, + new int[]{ 761, 769, 773 }, + + new int[]{ 787, 797, 809 }, + new int[]{ 811, 821, 823 }, + new int[]{ 827, 829, 839 }, + new int[]{ 853, 857, 859 }, + new int[]{ 863, 877, 881 }, + + new int[]{ 883, 887, 907 }, + new int[]{ 911, 919, 929 }, + new int[]{ 937, 941, 947 }, + new int[]{ 953, 967, 971 }, + new int[]{ 977, 983, 991 }, + + new int[]{ 997, 1009, 1013 }, + new int[]{ 1019, 1021, 1031 }, + }; + + private static readonly BigInteger Six = BigInteger.ValueOf(6); + + private static readonly int[] primeProducts; + private static readonly BigInteger[] PrimeProducts; + + static DHParametersHelper() + { + primeProducts = new int[primeLists.Length]; + PrimeProducts = new BigInteger[primeLists.Length]; + + for (int i = 0; i < primeLists.Length; ++i) + { + int[] primeList = primeLists[i]; + int product = 1; + for (int j = 0; j < primeList.Length; ++j) + { + product *= primeList[j]; + } + primeProducts[i] = product; + PrimeProducts[i] = BigInteger.ValueOf(product); + } + } + + /* + * Finds a pair of prime BigInteger's {p, q: p = 2q + 1} + * + * (see: Handbook of Applied Cryptography 4.86) + */ + internal static BigInteger[] GenerateSafePrimes(int size, int certainty, SecureRandom random) + { + BigInteger p, q; + int qLength = size - 1; + + if (size <= 32) + { + for (;;) + { + q = new BigInteger(qLength, 2, random); + + p = q.ShiftLeft(1).Add(BigInteger.One); + + if (p.IsProbablePrime(certainty) + && (certainty <= 2 || q.IsProbablePrime(certainty))) + break; + } + } + else + { + // Note: Modified from Java version for speed + for (;;) + { + q = new BigInteger(qLength, 0, random); + + retry: + for (int i = 0; i < primeLists.Length; ++i) + { + int test = q.Remainder(PrimeProducts[i]).IntValue; + + if (i == 0) + { + int rem3 = test % 3; + if (rem3 != 2) + { + int diff = 2 * rem3 + 2; + q = q.Add(BigInteger.ValueOf(diff)); + test = (test + diff) % primeProducts[i]; + } + } + + int[] primeList = primeLists[i]; + for (int j = 0; j < primeList.Length; ++j) + { + int prime = primeList[j]; + int qRem = test % prime; + if (qRem == 0 || qRem == (prime >> 1)) + { + q = q.Add(Six); + goto retry; + } + } + } + + + if (q.BitLength != qLength) + continue; + + if (!q.RabinMillerTest(2, random)) + continue; + + p = q.ShiftLeft(1).Add(BigInteger.One); + + if (p.RabinMillerTest(certainty, random) + && (certainty <= 2 || q.RabinMillerTest(certainty - 2, random))) + 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) + */ + internal static BigInteger SelectGenerator(BigInteger p, BigInteger q, SecureRandom random) + { + BigInteger pMinusTwo = p.Subtract(BigInteger.Two); + BigInteger g; + + /* + * (see: Handbook of Applied Cryptography 4.80) + */ +// do +// { +// g = BigIntegers.CreateRandomInRange(BigInteger.Two, pMinusTwo, random); +// } +// while (g.ModPow(BigInteger.Two, p).Equals(BigInteger.One) +// || g.ModPow(q, p).Equals(BigInteger.One)); + + /* + * RFC 2631 2.2.1.2 (and see: Handbook of Applied Cryptography 4.81) + */ + do + { + BigInteger h = BigIntegers.CreateRandomInRange(BigInteger.Two, pMinusTwo, random); + + g = h.ModPow(BigInteger.Two, p); + } + while (g.Equals(BigInteger.One)); + + return g; + } + } +} diff --git a/Crypto/src/crypto/generators/DesEdeKeyGenerator.cs b/Crypto/src/crypto/generators/DesEdeKeyGenerator.cs new file mode 100644
index 000000000..5902643fd --- /dev/null +++ b/Crypto/src/crypto/generators/DesEdeKeyGenerator.cs
@@ -0,0 +1,67 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Generators +{ + public class DesEdeKeyGenerator + : DesKeyGenerator + { + public DesEdeKeyGenerator() + { + } + + internal DesEdeKeyGenerator( + int defaultStrength) + : base(defaultStrength) + { + } + + /** + * 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 + */ + protected override void engineInit( + KeyGenerationParameters parameters) + { + this.random = parameters.Random; + this.strength = (parameters.Strength + 7) / 8; + + if (strength == 0 || strength == (168 / 8)) + { + strength = DesEdeParameters.DesEdeKeyLength; + } + else if (strength == (112 / 8)) + { + strength = 2 * DesEdeParameters.DesKeyLength; + } + else if (strength != DesEdeParameters.DesEdeKeyLength + && strength != (2 * DesEdeParameters.DesKeyLength)) + { + throw new ArgumentException("DESede key must be " + + (DesEdeParameters.DesEdeKeyLength * 8) + " or " + + (2 * 8 * DesEdeParameters.DesKeyLength) + + " bits long."); + } + } + + protected override byte[] engineGenerateKey() + { + byte[] newKey; + + do + { + newKey = random.GenerateSeed(strength); + DesEdeParameters.SetOddParity(newKey); + } + while (DesEdeParameters.IsWeakKey(newKey, 0, newKey.Length)); + + return newKey; + } + } +} diff --git a/Crypto/src/crypto/generators/DesKeyGenerator.cs b/Crypto/src/crypto/generators/DesKeyGenerator.cs new file mode 100644
index 000000000..154e3471a --- /dev/null +++ b/Crypto/src/crypto/generators/DesKeyGenerator.cs
@@ -0,0 +1,57 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Generators +{ + public class DesKeyGenerator + : CipherKeyGenerator + { + public DesKeyGenerator() + { + } + + internal DesKeyGenerator( + int defaultStrength) + : base(defaultStrength) + { + } + + /** + * 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 + */ + protected override void engineInit( + KeyGenerationParameters parameters) + { + base.engineInit(parameters); + + if (strength == 0 || strength == (56 / 8)) + { + strength = DesParameters.DesKeyLength; + } + else if (strength != DesParameters.DesKeyLength) + { + throw new ArgumentException( + "DES key must be " + (DesParameters.DesKeyLength * 8) + " bits long."); + } + } + + protected override byte[] engineGenerateKey() + { + byte[] newKey; + + do + { + newKey = random.GenerateSeed(DesParameters.DesKeyLength); + DesParameters.SetOddParity(newKey); + } + while (DesParameters.IsWeakKey(newKey, 0)); + + return newKey; + } + } +} diff --git a/Crypto/src/crypto/generators/DsaKeyPairGenerator.cs b/Crypto/src/crypto/generators/DsaKeyPairGenerator.cs new file mode 100644
index 000000000..bb8ec591b --- /dev/null +++ b/Crypto/src/crypto/generators/DsaKeyPairGenerator.cs
@@ -0,0 +1,61 @@ +using System; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * 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 + : IAsymmetricCipherKeyPairGenerator + { + private DsaKeyGenerationParameters param; + + public void Init( + KeyGenerationParameters parameters) + { + if (parameters == null) + throw new ArgumentNullException("parameters"); + + // Note: If we start accepting instances of KeyGenerationParameters, + // must apply constraint checking on strength (see DsaParametersGenerator.Init) + + this.param = (DsaKeyGenerationParameters) parameters; + } + + public AsymmetricCipherKeyPair GenerateKeyPair() + { + DsaParameters dsaParams = param.Parameters; + + BigInteger x = GeneratePrivateKey(dsaParams.Q, param.Random); + BigInteger y = CalculatePublicKey(dsaParams.P, dsaParams.G, 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(BigInteger.One)).Add(BigInteger.One); + + // B.1.2 Key Pair Generation by Testing Candidates + return BigIntegers.CreateRandomInRange(BigInteger.One, q.Subtract(BigInteger.One), random); + } + + private static BigInteger CalculatePublicKey(BigInteger p, BigInteger g, BigInteger x) + { + return g.ModPow(x, p); + } + } +} diff --git a/Crypto/src/crypto/generators/DsaParametersGenerator.cs b/Crypto/src/crypto/generators/DsaParametersGenerator.cs new file mode 100644
index 000000000..3e9d4f021 --- /dev/null +++ b/Crypto/src/crypto/generators/DsaParametersGenerator.cs
@@ -0,0 +1,355 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Crypto.Generators +{ + // TODO Update docs to mention FIPS 186-3 when done + /** + * Generate suitable parameters for DSA, in line with FIPS 186-2. + */ + public class DsaParametersGenerator + { + private int L, N; + private int certainty; + private SecureRandom random; + + /** + * 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) + { + if (!IsValidDsaStrength(size)) + throw new ArgumentException("size must be from 512 - 1024 and a multiple of 64", "size"); + + Init(size, GetDefaultN(size), certainty, random); + } + + // TODO Make public to enable support for DSA keys > 1024 bits + private void Init( + int L, + int N, + int certainty, + SecureRandom random) + { + // TODO Check that the (L, N) pair is in the list of acceptable (L, N pairs) (see Section 4.2) + // TODO Should we enforce the minimum 'certainty' values as per C.3 Table C.1? + + this.L = L; + this.N = N; + this.certainty = certainty; + this.random = random; + } + +// /** +// * add value to b, returning the result in a. The a value is treated +// * as a BigInteger of length (a.Length * 8) bits. The result is +// * modulo 2^a.Length in case of overflow. +// */ +// private static void Add( +// byte[] a, +// byte[] b, +// int value) +// { +// int x = (b[b.Length - 1] & 0xff) + value; +// +// a[b.Length - 1] = (byte)x; +// x = (int) ((uint) x >>8); +// +// for (int i = b.Length - 2; i >= 0; i--) +// { +// x += (b[i] & 0xff); +// a[i] = (byte)x; +// x = (int) ((uint) x >>8); +// } +// } + + /** + * which Generates the p and g values from the given parameters, + * returning the DsaParameters object. + * <p> + * Note: can take a while...</p> + */ + public DsaParameters GenerateParameters() + { + return L > 1024 + ? 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]; + Sha1Digest sha1 = new Sha1Digest(); + int n = (L - 1) / 160; + byte[] w = new byte[L / 8]; + + for (;;) + { + random.NextBytes(seed); + + Hash(sha1, seed, part1); + Array.Copy(seed, 0, part2, 0, seed.Length); + Inc(part2); + Hash(sha1, 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(sha1, offset, part1); + Array.Copy(part1, 0, w, w.Length - (k + 1) * part1.Length, part1.Length); + } + + Inc(offset); + Hash(sha1, offset, part1); + Array.Copy(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(BigInteger.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(BigInteger.One).Divide(q); + BigInteger pSub2 = p.Subtract(BigInteger.Two); + + for (;;) + { + BigInteger h = BigIntegers.CreateRandomInRange(BigInteger.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) + IDigest d = new Sha256Digest(); + 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(BigInteger.One.ShiftLeft(N - 1)); + +// 7. q = 2^(N–1) + U + 1 – ( U mod 2). + BigInteger q = BigInteger.One.ShiftLeft(N - 1).Add(U).Add(BigInteger.One).Subtract( + U.Mod(BigInteger.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 = BigInteger.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(BigInteger.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(BigInteger.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(BigInteger.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. + // TODO Make configurable (8-bit unsigned)? +// int index = 1; +// BigInteger g = CalculateGenerator_FIPS186_3_Verifiable(d, p, q, seed, index); +// if (g != null) +// { +// // TODO Should 'index' be a part of the validation parameters? +// return new DsaParameters(p, q, g, new DsaValidationParameters(seed, counter)); +// } + + 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(IDigest d, BigInteger p, BigInteger q, + byte[] seed, int index) + { + // A.2.3 Verifiable Canonical Generation of the Generator g + BigInteger e = p.Subtract(BigInteger.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]; + Array.Copy(seed, 0, U, 0, seed.Length); + Array.Copy(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(BigInteger.Two) >= 0) + return g; + } + + return null; + } + + private static bool IsValidDsaStrength( + int strength) + { + return strength >= 512 && strength <= 1024 && strength % 64 == 0; + } + + private static void Hash(IDigest d, byte[] input, byte[] output) + { + d.BlockUpdate(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); + buf[i] = b; + + if (b != 0) + break; + } + } + } +} diff --git a/Crypto/src/crypto/generators/ECKeyPairGenerator.cs b/Crypto/src/crypto/generators/ECKeyPairGenerator.cs new file mode 100644
index 000000000..d1e4b7cf6 --- /dev/null +++ b/Crypto/src/crypto/generators/ECKeyPairGenerator.cs
@@ -0,0 +1,174 @@ +using System; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Sec; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Generators +{ + public class ECKeyPairGenerator + : IAsymmetricCipherKeyPairGenerator + { + private readonly string algorithm; + + private ECDomainParameters parameters; + private DerObjectIdentifier publicKeyParamSet; + private SecureRandom random; + + public ECKeyPairGenerator() + : this("EC") + { + } + + public ECKeyPairGenerator( + string algorithm) + { + if (algorithm == null) + throw new ArgumentNullException("algorithm"); + + this.algorithm = VerifyAlgorithmName(algorithm); + } + + public void Init( + KeyGenerationParameters parameters) + { + if (parameters is ECKeyGenerationParameters) + { + ECKeyGenerationParameters ecP = (ECKeyGenerationParameters) parameters; + + this.publicKeyParamSet = ecP.PublicKeyParamSet; + this.parameters = ecP.DomainParameters; + } + else + { + DerObjectIdentifier oid; + switch (parameters.Strength) + { + case 192: + oid = X9ObjectIdentifiers.Prime192v1; + break; + case 224: + oid = SecObjectIdentifiers.SecP224r1; + break; + case 239: + oid = X9ObjectIdentifiers.Prime239v1; + break; + case 256: + oid = X9ObjectIdentifiers.Prime256v1; + break; + case 384: + oid = SecObjectIdentifiers.SecP384r1; + break; + case 521: + oid = SecObjectIdentifiers.SecP521r1; + break; + default: + throw new InvalidParameterException("unknown key size."); + } + + X9ECParameters ecps = FindECCurveByOid(oid); + + this.parameters = new ECDomainParameters( + ecps.Curve, ecps.G, ecps.N, ecps.H, ecps.GetSeed()); + } + + this.random = parameters.Random; + } + + /** + * 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 = parameters.N; + BigInteger d; + + do + { + d = new BigInteger(n.BitLength, random); + } + while (d.SignValue == 0 || (d.CompareTo(n) >= 0)); + + ECPoint q = parameters.G.Multiply(d); + + if (publicKeyParamSet != null) + { + return new AsymmetricCipherKeyPair( + new ECPublicKeyParameters(algorithm, q, publicKeyParamSet), + new ECPrivateKeyParameters(algorithm, d, publicKeyParamSet)); + } + + return new AsymmetricCipherKeyPair( + new ECPublicKeyParameters(algorithm, q, parameters), + new ECPrivateKeyParameters(algorithm, d, parameters)); + } + + private string VerifyAlgorithmName( + string algorithm) + { + string upper = algorithm.ToUpperInvariant(); + + switch (upper) + { + case "EC": + case "ECDSA": + case "ECDH": + case "ECDHC": + case "ECGOST3410": + case "ECMQV": + break; + default: + throw new ArgumentException("unrecognised algorithm: " + algorithm, "algorithm"); + } + + return upper; + } + + internal static X9ECParameters FindECCurveByOid(DerObjectIdentifier oid) + { + // TODO ECGost3410NamedCurves support (returns ECDomainParameters though) + + X9ECParameters ecP = X962NamedCurves.GetByOid(oid); + + if (ecP == null) + { + ecP = SecNamedCurves.GetByOid(oid); + + if (ecP == null) + { + ecP = NistNamedCurves.GetByOid(oid); + + if (ecP == null) + { + ecP = TeleTrusTNamedCurves.GetByOid(oid); + } + } + } + + return ecP; + } + + internal static ECPublicKeyParameters GetCorrespondingPublicKey( + ECPrivateKeyParameters privKey) + { + ECDomainParameters parameters = privKey.Parameters; + ECPoint q = parameters.G.Multiply(privKey.D); + + if (privKey.PublicKeyParamSet != null) + { + return new ECPublicKeyParameters(privKey.AlgorithmName, q, privKey.PublicKeyParamSet); + } + + return new ECPublicKeyParameters(privKey.AlgorithmName, q, parameters); + } + } +} diff --git a/Crypto/src/crypto/generators/ElGamalKeyPairGenerator.cs b/Crypto/src/crypto/generators/ElGamalKeyPairGenerator.cs new file mode 100644
index 000000000..227e7fe94 --- /dev/null +++ b/Crypto/src/crypto/generators/ElGamalKeyPairGenerator.cs
@@ -0,0 +1,40 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * a ElGamal key pair generator. + * <p> + * This Generates keys consistent for use with ElGamal as described in + * page 164 of "Handbook of Applied Cryptography".</p> + */ + public class ElGamalKeyPairGenerator + : IAsymmetricCipherKeyPairGenerator + { + private ElGamalKeyGenerationParameters param; + + public void Init( + KeyGenerationParameters parameters) + { + this.param = (ElGamalKeyGenerationParameters) parameters; + } + + public AsymmetricCipherKeyPair GenerateKeyPair() + { + DHKeyGeneratorHelper helper = DHKeyGeneratorHelper.Instance; + ElGamalParameters egp = param.Parameters; + DHParameters dhp = new DHParameters(egp.P, egp.G, null, 0, egp.L); + + BigInteger x = helper.CalculatePrivate(dhp, param.Random); + BigInteger y = helper.CalculatePublic(dhp, x); + + return new AsymmetricCipherKeyPair( + new ElGamalPublicKeyParameters(y, egp), + new ElGamalPrivateKeyParameters(x, egp)); + } + } + +} diff --git a/Crypto/src/crypto/generators/ElGamalParametersGenerator.cs b/Crypto/src/crypto/generators/ElGamalParametersGenerator.cs new file mode 100644
index 000000000..8443bb00e --- /dev/null +++ b/Crypto/src/crypto/generators/ElGamalParametersGenerator.cs
@@ -0,0 +1,46 @@ +using System; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Generators +{ + 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... + * </p> + */ + 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/Crypto/src/crypto/generators/GOST3410KeyPairGenerator.cs b/Crypto/src/crypto/generators/GOST3410KeyPairGenerator.cs new file mode 100644
index 000000000..5878da64b --- /dev/null +++ b/Crypto/src/crypto/generators/GOST3410KeyPairGenerator.cs
@@ -0,0 +1,73 @@ +using System; + +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * a GOST3410 key pair generator. + * This generates GOST3410 keys in line with the method described + * in GOST R 34.10-94. + */ + public class Gost3410KeyPairGenerator + : IAsymmetricCipherKeyPairGenerator + { + private Gost3410KeyGenerationParameters param; + + public void Init( + KeyGenerationParameters parameters) + { + if (parameters is Gost3410KeyGenerationParameters) + { + this.param = (Gost3410KeyGenerationParameters) parameters; + } + else + { + Gost3410KeyGenerationParameters kgp = new Gost3410KeyGenerationParameters( + parameters.Random, + CryptoProObjectIdentifiers.GostR3410x94CryptoProA); + + if (parameters.Strength != kgp.Parameters.P.BitLength - 1) + { + // TODO Should we complain? + } + + this.param = kgp; + } + } + + public AsymmetricCipherKeyPair GenerateKeyPair() + { + SecureRandom random = param.Random; + Gost3410Parameters gost3410Params = param.Parameters; + + BigInteger q = gost3410Params.Q; + BigInteger x; + do + { + x = new BigInteger(256, random); + } + while (x.SignValue < 1 || x.CompareTo(q) >= 0); + + BigInteger p = gost3410Params.P; + BigInteger a = gost3410Params.A; + + // calculate the public key. + BigInteger y = a.ModPow(x, p); + + if (param.PublicKeyParamSet != null) + { + return new AsymmetricCipherKeyPair( + new Gost3410PublicKeyParameters(y, param.PublicKeyParamSet), + new Gost3410PrivateKeyParameters(x, param.PublicKeyParamSet)); + } + + return new AsymmetricCipherKeyPair( + new Gost3410PublicKeyParameters(y, gost3410Params), + new Gost3410PrivateKeyParameters(x, gost3410Params)); + } + } +} diff --git a/Crypto/src/crypto/generators/GOST3410ParametersGenerator.cs b/Crypto/src/crypto/generators/GOST3410ParametersGenerator.cs new file mode 100644
index 000000000..52a9f5a82 --- /dev/null +++ b/Crypto/src/crypto/generators/GOST3410ParametersGenerator.cs
@@ -0,0 +1,530 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * generate suitable parameters for GOST3410. + */ + public class Gost3410ParametersGenerator + { + private int size; + private int typeproc; + private SecureRandom init_random; + + /** + * initialise the key generator. + * + * @param size size of the key + * @param typeProcedure type procedure A,B = 1; A',B' - else + * @param random random byte source. + */ + public void Init( + int size, + int typeProcedure, + SecureRandom random) + { + this.size = size; + this.typeproc = typeProcedure; + 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 = BigInteger.ValueOf(c); + BigInteger constA16 = BigInteger.ValueOf(19381); + + //step1 + BigInteger[] y = new BigInteger[1]; // begin length = 1 + y[0] = BigInteger.ValueOf(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]; /////////////// + Array.Copy(t,0,tmp_t,0,t.Length); // extension + t = new int[tmp_t.Length]; // array t + Array.Copy(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]; //////////////// + Array.Copy(y,0,tmp_y,0,y.Length); // extension + y = new BigInteger[rm+1]; // array y + Array.Copy(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(BigInteger.Two.Pow(16)); + } + + //step 7 + BigInteger Ym = BigInteger.Zero; + for (int j=0; j<rm; j++) + { + Ym = Ym.Add(y[j].ShiftLeft(16*j)); + } + + y[0] = y[rm]; //step 8 + + //step 9 + BigInteger N = BigInteger.One.ShiftLeft(t[m]-1).Divide(p[m+1]).Add( + Ym.ShiftLeft(t[m]-1).Divide(p[m+1].ShiftLeft(16*rm))); + + if (N.TestBit(0)) + { + N = N.Add(BigInteger.One); + } + + //step 10 + + for(;;) + { + //step 11 + BigInteger NByLastP = N.Multiply(p[m+1]); + + if (NByLastP.BitLength > t[m]) + { + goto step6; //step 12 + } + + p[m] = NByLastP.Add(BigInteger.One); + + //step13 + if (BigInteger.Two.ModPow(NByLastP, p[m]).CompareTo(BigInteger.One) == 0 + && BigInteger.Two.ModPow(N, p[m]).CompareTo(BigInteger.One) != 0) + { + break; + } + + N = N.Add(BigInteger.Two); + } + + if (--m < 0) + { + pq[0] = p[0]; + pq[1] = p[1]; + return y[0].IntValue; //return for procedure B step 2 + } + + break; //step 14 + } + } + 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 = BigInteger.ValueOf(c); + BigInteger constA32 = BigInteger.ValueOf(97781173); + + //step1 + BigInteger[] y = new BigInteger[1]; // begin length = 1 + y[0] = BigInteger.ValueOf(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]; /////////////// + Array.Copy(t,0,tmp_t,0,t.Length); // extension + t = new int[tmp_t.Length]; // array t + Array.Copy(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]; //////////////// + Array.Copy(y,0,tmp_y,0,y.Length); // extension + y = new BigInteger[rm+1]; // array y + Array.Copy(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(BigInteger.Two.Pow(32)); + } + + //step 7 + BigInteger Ym = BigInteger.Zero; + for (int j=0; j<rm; j++) + { + Ym = Ym.Add(y[j].ShiftLeft(32*j)); + } + + y[0] = y[rm]; //step 8 + + //step 9 + BigInteger N = BigInteger.One.ShiftLeft(t[m]-1).Divide(p[m+1]).Add( + Ym.ShiftLeft(t[m]-1).Divide(p[m+1].ShiftLeft(32*rm))); + + if (N.TestBit(0)) + { + N = N.Add(BigInteger.One); + } + + //step 10 + + for(;;) + { + //step 11 + BigInteger NByLastP = N.Multiply(p[m+1]); + + if (NByLastP.BitLength > t[m]) + { + goto step6; //step 12 + } + + p[m] = NByLastP.Add(BigInteger.One); + + //step13 + if (BigInteger.Two.ModPow(NByLastP, p[m]).CompareTo(BigInteger.One) == 0 + && BigInteger.Two.ModPow(N, p[m]).CompareTo(BigInteger.One) != 0) + { + break; + } + + N = N.Add(BigInteger.Two); + } + + if (--m < 0) + { + pq[0] = p[0]; + pq[1] = p[1]; + return y[0].LongValue; //return for procedure B' step 2 + } + + break; //step 14 + } + } + 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 = BigInteger.ValueOf(c); + BigInteger constA16 = BigInteger.ValueOf(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] = BigInteger.ValueOf(x0); + + const int tp = 1024; + + BigInteger qQ = q.Multiply(Q); + +step3: + for(;;) + { + //step 3 + for (int j=0; j<64; j++) + { + y[j+1] = (y[j].Multiply(constA16).Add(C)).Mod(BigInteger.Two.Pow(16)); + } + + //step 4 + BigInteger Y = BigInteger.Zero; + + for (int j=0; j<64; j++) + { + Y = Y.Add(y[j].ShiftLeft(16*j)); + } + + y[0] = y[64]; //step 5 + + //step 6 + BigInteger N = BigInteger.One.ShiftLeft(tp-1).Divide(qQ).Add( + Y.ShiftLeft(tp-1).Divide(qQ.ShiftLeft(1024))); + + if (N.TestBit(0)) + { + N = N.Add(BigInteger.One); + } + + //step 7 + + for(;;) + { + //step 11 + BigInteger qQN = qQ.Multiply(N); + + if (qQN.BitLength > tp) + { + goto step3; //step 9 + } + + p = qQN.Add(BigInteger.One); + + //step10 + if (BigInteger.Two.ModPow(qQN, p).CompareTo(BigInteger.One) == 0 + && BigInteger.Two.ModPow(q.Multiply(N), p).CompareTo(BigInteger.One) != 0) + { + pq[0] = p; + pq[1] = q; + return; + } + + N = N.Add(BigInteger.Two); + } + } + } + + //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 = BigInteger.ValueOf(c); + BigInteger constA32 = BigInteger.ValueOf(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] = BigInteger.ValueOf(x0); + + const int tp = 1024; + + BigInteger qQ = q.Multiply(Q); + +step3: + for(;;) + { + //step 3 + for (int j=0; j<32; j++) + { + y[j+1] = (y[j].Multiply(constA32).Add(C)).Mod(BigInteger.Two.Pow(32)); + } + + //step 4 + BigInteger Y = BigInteger.Zero; + for (int j=0; j<32; j++) + { + Y = Y.Add(y[j].ShiftLeft(32*j)); + } + + y[0] = y[32]; //step 5 + + //step 6 + BigInteger N = BigInteger.One.ShiftLeft(tp-1).Divide(qQ).Add( + Y.ShiftLeft(tp-1).Divide(qQ.ShiftLeft(1024))); + + if (N.TestBit(0)) + { + N = N.Add(BigInteger.One); + } + + //step 7 + + for(;;) + { + //step 11 + BigInteger qQN = qQ.Multiply(N); + + if (qQN.BitLength > tp) + { + goto step3; //step 9 + } + + p = qQN.Add(BigInteger.One); + + //step10 + if (BigInteger.Two.ModPow(qQN, p).CompareTo(BigInteger.One) == 0 + && BigInteger.Two.ModPow(q.Multiply(N), p).CompareTo(BigInteger.One) != 0) + { + pq[0] = p; + pq[1] = q; + return; + } + + N = N.Add(BigInteger.Two); + } + } + } + + + /** + * 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(BigInteger.One); + BigInteger pSub1Divq = pSub1.Divide(q); + + for(;;) + { + BigInteger d = new BigInteger(p.BitLength, init_random); + + // 1 < d < p-1 + if (d.CompareTo(BigInteger.One) > 0 && d.CompareTo(pSub1) < 0) + { + BigInteger a = d.ModPow(pSub1Divq, p); + + if (a.CompareTo(BigInteger.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 ArgumentException("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 InvalidOperationException("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/Crypto/src/crypto/generators/Kdf1BytesGenerator.cs b/Crypto/src/crypto/generators/Kdf1BytesGenerator.cs new file mode 100644
index 000000000..2b4fb7efd --- /dev/null +++ b/Crypto/src/crypto/generators/Kdf1BytesGenerator.cs
@@ -0,0 +1,27 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * KFD2 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 Kdf1BytesGenerator + : BaseKdfBytesGenerator + { + /** + * Construct a KDF1 byte generator. + * + * @param digest the digest to be used as the source of derived keys. + */ + public Kdf1BytesGenerator( + IDigest digest) + : base(0, digest) + { + } + } +} diff --git a/Crypto/src/crypto/generators/Kdf2BytesGenerator.cs b/Crypto/src/crypto/generators/Kdf2BytesGenerator.cs new file mode 100644
index 000000000..be1cd158e --- /dev/null +++ b/Crypto/src/crypto/generators/Kdf2BytesGenerator.cs
@@ -0,0 +1,28 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * 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 + : BaseKdfBytesGenerator + { + /** + * Construct a KDF2 bytes generator. Generates key material + * according to IEEE P1363 or ISO 18033 depending on the initialisation. + * + * @param digest the digest to be used as the source of derived keys. + */ + public Kdf2BytesGenerator( + IDigest digest) + : base(1, digest) + { + } + } +} diff --git a/Crypto/src/crypto/generators/Mgf1BytesGenerator.cs b/Crypto/src/crypto/generators/Mgf1BytesGenerator.cs new file mode 100644
index 000000000..23a3aca25 --- /dev/null +++ b/Crypto/src/crypto/generators/Mgf1BytesGenerator.cs
@@ -0,0 +1,117 @@ +using System; +//using Org.BouncyCastle.Math; +//using Org.BouncyCastle.Security; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * Generator for MGF1 as defined in Pkcs 1v2 + */ + public class Mgf1BytesGenerator : IDerivationFunction + { + private IDigest digest; + private byte[] seed; + private int hLen; + + /** + * @param digest the digest to be used as the source of Generated bytes + */ + public Mgf1BytesGenerator( + IDigest digest) + { + this.digest = digest; + this.hLen = digest.GetDigestSize(); + } + + public void Init( + IDerivationParameters parameters) + { + if (!(typeof(MgfParameters).IsInstanceOfType(parameters))) + { + throw new ArgumentException("MGF parameters required for MGF1Generator"); + } + + MgfParameters p = (MgfParameters)parameters; + + seed = p.GetSeed(); + } + + /** + * return the underlying digest. + */ + public IDigest Digest + { + get + { + return digest; + } + } + + /** + * int to octet string. + */ + private void ItoOSP( + int i, + byte[] sp) + { + sp[0] = (byte)((uint) i >> 24); + sp[1] = (byte)((uint) i >> 16); + sp[2] = (byte)((uint) i >> 8); + sp[3] = (byte)((uint) 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[] output, + int outOff, + int length) + { + if ((output.Length - length) < outOff) + { + throw new DataLengthException("output buffer too small"); + } + + byte[] hashBuf = new byte[hLen]; + byte[] C = new byte[4]; + int counter = 0; + + digest.Reset(); + + if (length > hLen) + { + do + { + ItoOSP(counter, C); + + digest.BlockUpdate(seed, 0, seed.Length); + digest.BlockUpdate(C, 0, C.Length); + digest.DoFinal(hashBuf, 0); + + Array.Copy(hashBuf, 0, output, outOff + counter * hLen, hLen); + } + while (++counter < (length / hLen)); + } + + if ((counter * hLen) < length) + { + ItoOSP(counter, C); + + digest.BlockUpdate(seed, 0, seed.Length); + digest.BlockUpdate(C, 0, C.Length); + digest.DoFinal(hashBuf, 0); + + Array.Copy(hashBuf, 0, output, outOff + counter * hLen, length - (counter * hLen)); + } + + return length; + } + } + +} diff --git a/Crypto/src/crypto/generators/NaccacheSternKeyPairGenerator.cs b/Crypto/src/crypto/generators/NaccacheSternKeyPairGenerator.cs new file mode 100644
index 000000000..a00a6c8a6 --- /dev/null +++ b/Crypto/src/crypto/generators/NaccacheSternKeyPairGenerator.cs
@@ -0,0 +1,333 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * 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 + : IAsymmetricCipherKeyPairGenerator + { + private static readonly 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; + + /* + * (non-Javadoc) + * + * @see org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator#init(org.bouncycastle.crypto.KeyGenerationParameters) + */ + public void Init(KeyGenerationParameters parameters) + { + this.param = (NaccacheSternKeyGenerationParameters)parameters; + } + + /* + * (non-Javadoc) + * + * @see org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator#generateKeyPair() + */ + public AsymmetricCipherKeyPair GenerateKeyPair() + { + int strength = param.Strength; + SecureRandom rand = param.Random; + int certainty = param.Certainty; + bool debug = param.IsDebug; + + if (debug) + { + System.Diagnostics.Debug.WriteLine("Fetching first " + param.CountSmallPrimes + " primes."); + } + + IList smallPrimes = findFirstPrimes(param.CountSmallPrimes); + + smallPrimes = permuteList(smallPrimes, rand); + + BigInteger u = BigInteger.One; + BigInteger v = BigInteger.One; + + for (int i = 0; i < smallPrimes.Count / 2; i++) + { + u = u.Multiply((BigInteger)smallPrimes[i]); + } + for (int i = smallPrimes.Count / 2; i < smallPrimes.Count; i++) + { + v = v.Multiply((BigInteger)smallPrimes[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.Diagnostics.Debug.WriteLine("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(BigInteger.One); + + if (!p.IsProbablePrime(certainty)) + continue; + + for (;;) + { + _q = generatePrime(24, certainty, rand); + + if (_p.Equals(_q)) + continue; + + q = _q.Multiply(_2bv).Add(BigInteger.One); + + if (q.IsProbablePrime(certainty)) + break; + } + + if (!sigma.Gcd(_p.Multiply(_q)).Equals(BigInteger.One)) + { + System.Diagnostics.Debug.WriteLine("sigma.gcd(_p.mult(_q)) != 1!\n _p: " + _p + "\n _q: " + _q); + continue; + } + + if (p.Multiply(q).BitLength < strength) + { + if (debug) + { + System.Diagnostics.Debug.WriteLine("key size too small. Should be " + strength + " but is actually " + + p.Multiply(q).BitLength); + } + continue; + } + break; + } + + if (debug) + { + System.Diagnostics.Debug.WriteLine("needed " + tries + " tries to generate p and q."); + } + + BigInteger n = p.Multiply(q); + BigInteger phi_n = p.Subtract(BigInteger.One).Multiply(q.Subtract(BigInteger.One)); + BigInteger g; + tries = 0; + if (debug) + { + System.Diagnostics.Debug.WriteLine("generating g"); + } + for (;;) + { + // TODO After the first loop, just regenerate one randomly-selected gPart each time? + IList gParts = Platform.CreateArrayList(); + for (int ind = 0; ind != smallPrimes.Count; ind++) + { + BigInteger i = (BigInteger)smallPrimes[ind]; + BigInteger e = phi_n.Divide(i); + + for (;;) + { + tries++; + + g = generatePrime(strength, certainty, rand); + + if (!g.ModPow(e, n).Equals(BigInteger.One)) + { + gParts.Add(g); + break; + } + } + } + g = BigInteger.One; + for (int i = 0; i < smallPrimes.Count; i++) + { + BigInteger gPart = (BigInteger) gParts[i]; + BigInteger smallPrime = (BigInteger) smallPrimes[i]; + g = g.Multiply(gPart.ModPow(sigma.Divide(smallPrime), n)).Mod(n); + } + + // make sure that g is not divisible by p_i or q_i + bool divisible = false; + for (int i = 0; i < smallPrimes.Count; i++) + { + if (g.ModPow(phi_n.Divide((BigInteger)smallPrimes[i]), n).Equals(BigInteger.One)) + { + if (debug) + { + System.Diagnostics.Debug.WriteLine("g has order phi(n)/" + smallPrimes[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(BigInteger.One)) + if (g.ModPow(phi_n.ShiftRight(2), n).Equals(BigInteger.One)) + { + if (debug) + { + System.Diagnostics.Debug.WriteLine("g has order phi(n)/4\n g:" + g); + } + continue; + } + + if (g.ModPow(phi_n.Divide(_p), n).Equals(BigInteger.One)) + { + if (debug) + { + System.Diagnostics.Debug.WriteLine("g has order phi(n)/p'\n g: " + g); + } + continue; + } + if (g.ModPow(phi_n.Divide(_q), n).Equals(BigInteger.One)) + { + if (debug) + { + System.Diagnostics.Debug.WriteLine("g has order phi(n)/q'\n g: " + g); + } + continue; + } + if (g.ModPow(phi_n.Divide(a), n).Equals(BigInteger.One)) + { + if (debug) + { + System.Diagnostics.Debug.WriteLine("g has order phi(n)/a\n g: " + g); + } + continue; + } + if (g.ModPow(phi_n.Divide(b), n).Equals(BigInteger.One)) + { + if (debug) + { + System.Diagnostics.Debug.WriteLine("g has order phi(n)/b\n g: " + g); + } + continue; + } + break; + } + if (debug) + { + System.Diagnostics.Debug.WriteLine("needed " + tries + " tries to generate g"); + System.Diagnostics.Debug.WriteLine(""); + System.Diagnostics.Debug.WriteLine("found new NaccacheStern cipher variables:"); + System.Diagnostics.Debug.WriteLine("smallPrimes: " + CollectionUtilities.ToString(smallPrimes)); + System.Diagnostics.Debug.WriteLine("sigma:...... " + sigma + " (" + sigma.BitLength + " bits)"); + System.Diagnostics.Debug.WriteLine("a:.......... " + a); + System.Diagnostics.Debug.WriteLine("b:.......... " + b); + System.Diagnostics.Debug.WriteLine("p':......... " + _p); + System.Diagnostics.Debug.WriteLine("q':......... " + _q); + System.Diagnostics.Debug.WriteLine("p:.......... " + p); + System.Diagnostics.Debug.WriteLine("q:.......... " + q); + System.Diagnostics.Debug.WriteLine("n:.......... " + n); + System.Diagnostics.Debug.WriteLine("phi(n):..... " + phi_n); + System.Diagnostics.Debug.WriteLine("g:.......... " + g); + System.Diagnostics.Debug.WriteLine(""); + } + + 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) + { + return new BigInteger(bitLength, certainty, rand); + } + + /** + * 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 IList permuteList( + IList arr, + SecureRandom rand) + { + // TODO Create a utility method for generating permutation of first 'n' integers + + IList retval = Platform.CreateArrayList(arr.Count); + + foreach (object element in arr) + { + int index = rand.Next(retval.Count + 1); + retval.Insert(index, element); + } + + return retval; + } + + /** + * 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 IList findFirstPrimes( + int count) + { + IList primes = Platform.CreateArrayList(count); + + for (int i = 0; i != count; i++) + { + primes.Add(BigInteger.ValueOf(smallPrimes[i])); + } + + return primes; + } + + } +} diff --git a/Crypto/src/crypto/generators/OpenSSLPBEParametersGenerator.cs b/Crypto/src/crypto/generators/OpenSSLPBEParametersGenerator.cs new file mode 100644
index 000000000..8da5d3ad1 --- /dev/null +++ b/Crypto/src/crypto/generators/OpenSSLPBEParametersGenerator.cs
@@ -0,0 +1,167 @@ +using System; + +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * 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 + : PbeParametersGenerator + { + private readonly IDigest digest = new MD5Digest(); + + /** + * Construct a OpenSSL Parameters generator. + */ + public OpenSslPbeParametersGenerator() + { + } + + public override void Init( + byte[] password, + byte[] salt, + int iterationCount) + { + // Ignore the provided iterationCount + base.Init(password, salt, 1); + } + + /** + * Initialise - note the iteration count for this algorithm is fixed at 1. + * + * @param password password to use. + * @param salt salt to use. + */ + public virtual void Init( + byte[] password, + byte[] salt) + { + base.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.BlockUpdate(mPassword, 0, mPassword.Length); + digest.BlockUpdate(mSalt, 0, mSalt.Length); + + digest.DoFinal(buf, 0); + + int len = (bytesNeeded > buf.Length) ? buf.Length : bytesNeeded; + Array.Copy(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.BlockUpdate(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 ArgumentException if the key length larger than the base hash size. + */ + [Obsolete("Use version with 'algorithm' parameter")] + public override ICipherParameters GenerateDerivedParameters( + int keySize) + { + return GenerateDerivedMacParameters(keySize); + } + + public override ICipherParameters GenerateDerivedParameters( + string algorithm, + int keySize) + { + keySize /= 8; + + byte[] dKey = GenerateDerivedKey(keySize); + + return ParameterUtilities.CreateKeyParameter(algorithm, 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 ArgumentException if keySize + ivSize is larger than the base hash size. + */ + [Obsolete("Use version with 'algorithm' parameter")] + public override ICipherParameters 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); + } + + public override ICipherParameters GenerateDerivedParameters( + string algorithm, + int keySize, + int ivSize) + { + keySize /= 8; + ivSize /= 8; + + byte[] dKey = GenerateDerivedKey(keySize + ivSize); + KeyParameter key = ParameterUtilities.CreateKeyParameter(algorithm, dKey, 0, keySize); + + return new ParametersWithIV(key, 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 ArgumentException if the key length larger than the base hash size. + */ + public override ICipherParameters GenerateDerivedMacParameters( + int keySize) + { + keySize = keySize / 8; + + byte[] dKey = GenerateDerivedKey(keySize); + + return new KeyParameter(dKey, 0, keySize); + } + } +} diff --git a/Crypto/src/crypto/generators/Pkcs12ParametersGenerator.cs b/Crypto/src/crypto/generators/Pkcs12ParametersGenerator.cs new file mode 100644
index 000000000..d2da3f6fc --- /dev/null +++ b/Crypto/src/crypto/generators/Pkcs12ParametersGenerator.cs
@@ -0,0 +1,245 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * 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> + * </p> + */ + public class Pkcs12ParametersGenerator + : PbeParametersGenerator + { + public const int KeyMaterial = 1; + public const int IVMaterial = 2; + public const int MacMaterial = 3; + + private readonly IDigest digest; + + private readonly int u; + private readonly int v; + + /** + * Construct a Pkcs 12 Parameters generator. + * + * @param digest the digest to be used as the source of derived keys. + * @exception ArgumentException if an unknown digest is passed in. + */ + public Pkcs12ParametersGenerator( + IDigest digest) + { + this.digest = digest; + + u = digest.GetDigestSize(); + v = digest.GetByteLength(); + } + + /** + * 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 = (int) ((uint) 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 = (int) ((uint) 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 ((mSalt != null) && (mSalt.Length != 0)) + { + S = new byte[v * ((mSalt.Length + v - 1) / v)]; + + for (int i = 0; i != S.Length; i++) + { + S[i] = mSalt[i % mSalt.Length]; + } + } + else + { + S = new byte[0]; + } + + byte[] P; + + if ((mPassword != null) && (mPassword.Length != 0)) + { + P = new byte[v * ((mPassword.Length + v - 1) / v)]; + + for (int i = 0; i != P.Length; i++) + { + P[i] = mPassword[i % mPassword.Length]; + } + } + else + { + P = new byte[0]; + } + + byte[] I = new byte[S.Length + P.Length]; + + Array.Copy(S, 0, I, 0, S.Length); + Array.Copy(P, 0, I, S.Length, P.Length); + + byte[] B = new byte[v]; + int c = (n + u - 1) / u; + + for (int i = 1; i <= c; i++) + { + byte[] A = new byte[u]; + + digest.BlockUpdate(D, 0, D.Length); + digest.BlockUpdate(I, 0, I.Length); + digest.DoFinal(A, 0); + for (int j = 1; j != mIterationCount; j++) + { + digest.BlockUpdate(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) + { + Array.Copy(A, 0, dKey, (i - 1) * u, dKey.Length - ((i - 1) * u)); + } + else + { + Array.Copy(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. + */ + [Obsolete("Use version with 'algorithm' parameter")] + public override ICipherParameters GenerateDerivedParameters( + int keySize) + { + keySize /= 8; + + byte[] dKey = GenerateDerivedKey(KeyMaterial, keySize); + + return new KeyParameter(dKey, 0, keySize); + } + + public override ICipherParameters GenerateDerivedParameters( + string algorithm, + int keySize) + { + keySize /= 8; + + byte[] dKey = GenerateDerivedKey(KeyMaterial, keySize); + + return ParameterUtilities.CreateKeyParameter(algorithm, 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. + */ + [Obsolete("Use version with 'algorithm' parameter")] + public override ICipherParameters GenerateDerivedParameters( + int keySize, + int ivSize) + { + keySize /= 8; + ivSize /= 8; + + byte[] dKey = GenerateDerivedKey(KeyMaterial, keySize); + + byte[] iv = GenerateDerivedKey(IVMaterial, ivSize); + + return new ParametersWithIV(new KeyParameter(dKey, 0, keySize), iv, 0, ivSize); + } + + public override ICipherParameters GenerateDerivedParameters( + string algorithm, + int keySize, + int ivSize) + { + keySize /= 8; + ivSize /= 8; + + byte[] dKey = GenerateDerivedKey(KeyMaterial, keySize); + KeyParameter key = ParameterUtilities.CreateKeyParameter(algorithm, dKey, 0, keySize); + + byte[] iv = GenerateDerivedKey(IVMaterial, ivSize); + + return new ParametersWithIV(key, 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 override ICipherParameters GenerateDerivedMacParameters( + int keySize) + { + keySize /= 8; + + byte[] dKey = GenerateDerivedKey(MacMaterial, keySize); + + return new KeyParameter(dKey, 0, keySize); + } + } +} diff --git a/Crypto/src/crypto/generators/Pkcs5S1ParametersGenerator.cs b/Crypto/src/crypto/generators/Pkcs5S1ParametersGenerator.cs new file mode 100644
index 000000000..8586e1ca9 --- /dev/null +++ b/Crypto/src/crypto/generators/Pkcs5S1ParametersGenerator.cs
@@ -0,0 +1,162 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * 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> + * </p> + */ + public class Pkcs5S1ParametersGenerator + : PbeParametersGenerator + { + private readonly IDigest digest; + + /** + * Construct a Pkcs 5 Scheme 1 Parameters generator. + * + * @param digest the digest to be used as the source of derived keys. + */ + public Pkcs5S1ParametersGenerator( + IDigest digest) + { + this.digest = digest; + } + + /** + * the derived key function, the ith hash of the mPassword and the mSalt. + */ + private byte[] GenerateDerivedKey() + { + byte[] digestBytes = new byte[digest.GetDigestSize()]; + + digest.BlockUpdate(mPassword, 0, mPassword.Length); + digest.BlockUpdate(mSalt, 0, mSalt.Length); + + digest.DoFinal(digestBytes, 0); + for (int i = 1; i < mIterationCount; i++) + { + digest.BlockUpdate(digestBytes, 0, digestBytes.Length); + digest.DoFinal(digestBytes, 0); + } + + return digestBytes; + } + + /** + * Generate a key parameter derived from the mPassword, mSalt, and iteration + * count we are currently initialised with. + * + * @param keySize the size of the key we want (in bits) + * @return a KeyParameter object. + * @exception ArgumentException if the key length larger than the base hash size. + */ + [Obsolete("Use version with 'algorithm' parameter")] + public override ICipherParameters GenerateDerivedParameters( + int keySize) + { + return GenerateDerivedMacParameters(keySize); + } + + public override ICipherParameters GenerateDerivedParameters( + string algorithm, + int keySize) + { + keySize /= 8; + + if (keySize > digest.GetDigestSize()) + { + throw new ArgumentException( + "Can't Generate a derived key " + keySize + " bytes long."); + } + + byte[] dKey = GenerateDerivedKey(); + + return ParameterUtilities.CreateKeyParameter(algorithm, dKey, 0, keySize); + } + + /** + * Generate a key with initialisation vector parameter derived from + * the mPassword, mSalt, 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 ArgumentException if keySize + ivSize is larger than the base hash size. + */ + [Obsolete("Use version with 'algorithm' parameter")] + public override ICipherParameters GenerateDerivedParameters( + int keySize, + int ivSize) + { + keySize /= 8; + ivSize /= 8; + + if ((keySize + ivSize) > digest.GetDigestSize()) + { + throw new ArgumentException( + "Can't Generate a derived key " + (keySize + ivSize) + " bytes long."); + } + + byte[] dKey = GenerateDerivedKey(); + + return new ParametersWithIV(new KeyParameter(dKey, 0, keySize), dKey, keySize, ivSize); + } + + public override ICipherParameters GenerateDerivedParameters( + string algorithm, + int keySize, + int ivSize) + { + keySize /= 8; + ivSize /= 8; + + if ((keySize + ivSize) > digest.GetDigestSize()) + { + throw new ArgumentException( + "Can't Generate a derived key " + (keySize + ivSize) + " bytes long."); + } + + byte[] dKey = GenerateDerivedKey(); + KeyParameter key = ParameterUtilities.CreateKeyParameter(algorithm, dKey, 0, keySize); + + return new ParametersWithIV(key, dKey, keySize, ivSize); + } + + /** + * Generate a key parameter for use with a MAC derived from the mPassword, + * mSalt, and iteration count we are currently initialised with. + * + * @param keySize the size of the key we want (in bits) + * @return a KeyParameter object. + * @exception ArgumentException if the key length larger than the base hash size. + */ + public override ICipherParameters GenerateDerivedMacParameters( + int keySize) + { + keySize /= 8; + + if (keySize > digest.GetDigestSize()) + { + throw new ArgumentException( + "Can't Generate a derived key " + keySize + " bytes long."); + } + + byte[] dKey = GenerateDerivedKey(); + + return new KeyParameter(dKey, 0, keySize); + } + } +} diff --git a/Crypto/src/crypto/generators/Pkcs5S2ParametersGenerator.cs b/Crypto/src/crypto/generators/Pkcs5S2ParametersGenerator.cs new file mode 100644
index 000000000..58d7b5c37 --- /dev/null +++ b/Crypto/src/crypto/generators/Pkcs5S2ParametersGenerator.cs
@@ -0,0 +1,172 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Macs; +using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * 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></p> + */ + public class Pkcs5S2ParametersGenerator + : PbeParametersGenerator + { + private readonly IMac hMac; + + /** + * construct a Pkcs5 Scheme 2 Parameters generator. + */ + public Pkcs5S2ParametersGenerator() + : this(new Sha1Digest()) + { + } + + public Pkcs5S2ParametersGenerator(IDigest digest) + { + hMac = new HMac(digest); + } + + private void F( + byte[] P, + byte[] S, + int c, + byte[] iBuf, + byte[] outBytes, + int outOff) + { + byte[] state = new byte[hMac.GetMacSize()]; + ICipherParameters param = new KeyParameter(P); + + hMac.Init(param); + + if (S != null) + { + hMac.BlockUpdate(S, 0, S.Length); + } + + hMac.BlockUpdate(iBuf, 0, iBuf.Length); + + hMac.DoFinal(state, 0); + + Array.Copy(state, 0, outBytes, outOff, state.Length); + + for (int count = 1; count != c; count++) + { + hMac.Init(param); + hMac.BlockUpdate(state, 0, state.Length); + hMac.DoFinal(state, 0); + + for (int j = 0; j != state.Length; j++) + { + outBytes[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]; + + for (int i = 1; i <= l; i++) + { + Pack.UInt32_To_BE((uint)i, iBuf); + + F(mPassword, mSalt, mIterationCount, iBuf, outBytes, (i - 1) * 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. + */ + [Obsolete("Use version with 'algorithm' parameter")] + public override ICipherParameters GenerateDerivedParameters( + int keySize) + { + return GenerateDerivedMacParameters(keySize); + } + + public override ICipherParameters GenerateDerivedParameters( + string algorithm, + int keySize) + { + keySize /= 8; + + byte[] dKey = GenerateDerivedKey(keySize); + + return ParameterUtilities.CreateKeyParameter(algorithm, 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. + */ + [Obsolete("Use version with 'algorithm' parameter")] + public override ICipherParameters GenerateDerivedParameters( + int keySize, + int ivSize) + { + keySize /= 8; + ivSize /= 8; + + byte[] dKey = GenerateDerivedKey(keySize + ivSize); + + return new ParametersWithIV(new KeyParameter(dKey, 0, keySize), dKey, keySize, ivSize); + } + + public override ICipherParameters GenerateDerivedParameters( + string algorithm, + int keySize, + int ivSize) + { + keySize /= 8; + ivSize /= 8; + + byte[] dKey = GenerateDerivedKey(keySize + ivSize); + KeyParameter key = ParameterUtilities.CreateKeyParameter(algorithm, dKey, 0, keySize); + + return new ParametersWithIV(key, 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 override ICipherParameters GenerateDerivedMacParameters( + int keySize) + { + keySize /= 8; + + byte[] dKey = GenerateDerivedKey(keySize); + + return new KeyParameter(dKey, 0, keySize); + } + } +} diff --git a/Crypto/src/crypto/generators/RSABlindingFactorGenerator.cs b/Crypto/src/crypto/generators/RSABlindingFactorGenerator.cs new file mode 100644
index 000000000..e2f63face --- /dev/null +++ b/Crypto/src/crypto/generators/RSABlindingFactorGenerator.cs
@@ -0,0 +1,69 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * 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 RsaKeyParameters key; + private SecureRandom random; + + /** + * Initialise the factor generator + * + * @param param the necessary RSA key parameters. + */ + public void Init( + ICipherParameters param) + { + if (param is ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)param; + + key = (RsaKeyParameters)rParam.Parameters; + random = rParam.Random; + } + else + { + key = (RsaKeyParameters)param; + random = new SecureRandom(); + } + + if (key.IsPrivate) + throw new ArgumentException("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 InvalidOperationException("generator not initialised"); + + BigInteger m = key.Modulus; + 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.SignValue == 0 || factor.Equals(BigInteger.One) || !gcd.Equals(BigInteger.One)); + + return factor; + } + } +} diff --git a/Crypto/src/crypto/generators/RsaKeyPairGenerator.cs b/Crypto/src/crypto/generators/RsaKeyPairGenerator.cs new file mode 100644
index 000000000..3074aed04 --- /dev/null +++ b/Crypto/src/crypto/generators/RsaKeyPairGenerator.cs
@@ -0,0 +1,139 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /** + * an RSA key pair generator. + */ + public class RsaKeyPairGenerator + : IAsymmetricCipherKeyPairGenerator + { + private static readonly BigInteger DefaultPublicExponent = BigInteger.ValueOf(0x10001); + private const int DefaultTests = 12; + + private RsaKeyGenerationParameters param; + + public void Init( + KeyGenerationParameters parameters) + { + if (parameters is RsaKeyGenerationParameters) + { + this.param = (RsaKeyGenerationParameters)parameters; + } + else + { + this.param = new RsaKeyGenerationParameters( + DefaultPublicExponent, parameters.Random, parameters.Strength, DefaultTests); + } + } + + 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.Strength; + int pbitlength = (strength + 1) / 2; + int qbitlength = (strength - pbitlength); + int mindiffbits = strength / 3; + + e = param.PublicExponent; + + // 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.Random); + + if (p.Mod(e).Equals(BigInteger.One)) + continue; + + if (!p.IsProbablePrime(param.Certainty)) + continue; + + if (e.Gcd(p.Subtract(BigInteger.One)).Equals(BigInteger.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.Random); + + if (q.Subtract(p).Abs().BitLength < mindiffbits) + continue; + + if (q.Mod(e).Equals(BigInteger.One)) + continue; + + if (!q.IsProbablePrime(param.Certainty)) + continue; + + if (e.Gcd(q.Subtract(BigInteger.One)).Equals(BigInteger.One)) + break; + } + + // + // calculate the modulus + // + n = p.Multiply(q); + + if (n.BitLength == param.Strength) + 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(BigInteger.One); + qSub1 = q.Subtract(BigInteger.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/Crypto/src/crypto/generators/SCrypt.cs b/Crypto/src/crypto/generators/SCrypt.cs new file mode 100644
index 000000000..efa74d735 --- /dev/null +++ b/Crypto/src/crypto/generators/SCrypt.cs
@@ -0,0 +1,140 @@ +using System; +using System.Threading; + +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Generators +{ + 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); + + uint[] B = null; + + try + { + int BLen = bytes.Length >> 2; + B = new uint[BLen]; + + Pack.LE_To_UInt32(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.UInt32_To_LE(B, bytes, 0); + + return SingleIterationPBKDF2(P, bytes, dkLen); + } + finally + { + ClearAll(bytes, 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(uint[] B, int BOff, int N, int r) + { + int BCount = r * 32; + + uint[] blockX1 = new uint[16]; + uint[] blockX2 = new uint[16]; + uint[] blockY = new uint[BCount]; + + uint[] X = new uint[BCount]; + uint[][] V = new uint[N][]; + + try + { + Array.Copy(B, BOff, X, 0, BCount); + + for (int i = 0; i < N; ++i) + { + V[i] = (uint[])X.Clone(); + BlockMix(X, blockX1, blockX2, blockY, r); + } + + uint mask = (uint)N - 1; + for (int i = 0; i < N; ++i) + { + uint j = X[BCount - 16] & mask; + Xor(X, V[j], 0, X); + BlockMix(X, blockX1, blockX2, blockY, r); + } + + Array.Copy(X, 0, B, BOff, BCount); + } + finally + { + ClearAll(V); + ClearAll(X, blockX1, blockX2, blockY); + } + } + + private static void BlockMix(uint[] B, uint[] X1, uint[] X2, uint[] Y, int r) + { + Array.Copy(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); + Array.Copy(X1, 0, Y, YOff, 16); + + YOff = halfLen + BOff - YOff; + BOff += 16; + } + + Array.Copy(Y, 0, B, 0, Y.Length); + } + + private static void Xor(uint[] a, uint[] b, int bOff, uint[] output) + { + for (int i = output.Length - 1; i >= 0; --i) + { + output[i] = a[i] ^ b[bOff + i]; + } + } + + private static void Clear(Array array) + { + if (array != null) + { + Array.Clear(array, 0, array.Length); + } + } + + private static void ClearAll(params Array[] arrays) + { + foreach (Array array in arrays) + { + Clear(array); + } + } + } +} diff --git a/Crypto/src/crypto/io/CipherStream.cs b/Crypto/src/crypto/io/CipherStream.cs new file mode 100644
index 000000000..bf7effb0a --- /dev/null +++ b/Crypto/src/crypto/io/CipherStream.cs
@@ -0,0 +1,237 @@ +using System; +using System.Diagnostics; +using System.IO; + +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Crypto.IO +{ + public class CipherStream + : Stream + { + internal Stream stream; + internal IBufferedCipher inCipher, outCipher; + private byte[] mInBuf; + private int mInPos; + private bool inStreamEnded; + + public CipherStream( + Stream stream, + IBufferedCipher readCipher, + IBufferedCipher writeCipher) + { + this.stream = stream; + + if (readCipher != null) + { + this.inCipher = readCipher; + mInBuf = null; + } + + if (writeCipher != null) + { + this.outCipher = writeCipher; + } + } + + public IBufferedCipher ReadCipher + { + get { return inCipher; } + } + + public IBufferedCipher WriteCipher + { + get { return outCipher; } + } + + public override int ReadByte() + { + if (inCipher == null) + return stream.ReadByte(); + + if (mInBuf == null || mInPos >= mInBuf.Length) + { + if (!FillInBuf()) + return -1; + } + + return mInBuf[mInPos++]; + } + + public override int Read( + byte[] buffer, + int offset, + int count) + { + if (inCipher == null) + return stream.Read(buffer, offset, count); + + int num = 0; + while (num < count) + { + if (mInBuf == null || mInPos >= mInBuf.Length) + { + if (!FillInBuf()) + break; + } + + int numToCopy = System.Math.Min(count - num, mInBuf.Length - mInPos); + Array.Copy(mInBuf, mInPos, buffer, offset + num, numToCopy); + mInPos += numToCopy; + num += numToCopy; + } + + return num; + } + + private bool FillInBuf() + { + if (inStreamEnded) + return false; + + mInPos = 0; + + do + { + mInBuf = ReadAndProcessBlock(); + } + while (!inStreamEnded && mInBuf == null); + + return mInBuf != null; + } + + private byte[] ReadAndProcessBlock() + { + int blockSize = inCipher.GetBlockSize(); + int readSize = (blockSize == 0) ? 256 : blockSize; + + byte[] block = new byte[readSize]; + int numRead = 0; + do + { + int count = stream.Read(block, numRead, block.Length - numRead); + if (count < 1) + { + inStreamEnded = true; + break; + } + numRead += count; + } + while (numRead < block.Length); + + Debug.Assert(inStreamEnded || numRead == block.Length); + + byte[] bytes = inStreamEnded + ? inCipher.DoFinal(block, 0, numRead) + : inCipher.ProcessBytes(block); + + if (bytes != null && bytes.Length == 0) + { + bytes = null; + } + + return bytes; + } + + public override void Write( + byte[] buffer, + int offset, + int count) + { + Debug.Assert(buffer != null); + Debug.Assert(0 <= offset && offset <= buffer.Length); + Debug.Assert(count >= 0); + + int end = offset + count; + + Debug.Assert(0 <= end && end <= buffer.Length); + + if (outCipher == null) + { + stream.Write(buffer, offset, count); + return; + } + + byte[] data = outCipher.ProcessBytes(buffer, offset, count); + if (data != null) + { + stream.Write(data, 0, data.Length); + } + } + + public override void WriteByte( + byte b) + { + if (outCipher == null) + { + stream.WriteByte(b); + return; + } + + byte[] data = outCipher.ProcessByte(b); + if (data != null) + { + stream.Write(data, 0, data.Length); + } + } + + public override bool CanRead + { + get { return stream.CanRead && (inCipher != null); } + } + + public override bool CanWrite + { + get { return stream.CanWrite && (outCipher != null); } + } + + public override bool CanSeek + { + get { return false; } + } + + public sealed override long Length + { + get { throw new NotSupportedException(); } + } + + public sealed override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (outCipher != null) + { + byte[] data = outCipher.DoFinal(); + stream.Write(data, 0, data.Length); + stream.Flush(); + } + stream.Dispose(); + } + } + + public override void Flush() + { + // Note: outCipher.DoFinal is only called during Close() + stream.Flush(); + } + + public sealed override long Seek( + long offset, + SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public sealed override void SetLength( + long length) + { + throw new NotSupportedException(); + } + } +} diff --git a/Crypto/src/crypto/io/DigestStream.cs b/Crypto/src/crypto/io/DigestStream.cs new file mode 100644
index 000000000..a5b31f95c --- /dev/null +++ b/Crypto/src/crypto/io/DigestStream.cs
@@ -0,0 +1,140 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.IO +{ + public class DigestStream + : Stream + { + protected readonly Stream stream; + protected readonly IDigest inDigest; + protected readonly IDigest outDigest; + + public DigestStream( + Stream stream, + IDigest readDigest, + IDigest writeDigest) + { + this.stream = stream; + this.inDigest = readDigest; + this.outDigest = writeDigest; + } + + public virtual IDigest ReadDigest() + { + return inDigest; + } + + public virtual IDigest WriteDigest() + { + return outDigest; + } + + public override int Read( + byte[] buffer, + int offset, + int count) + { + int n = stream.Read(buffer, offset, count); + if (inDigest != null) + { + if (n > 0) + { + inDigest.BlockUpdate(buffer, offset, n); + } + } + return n; + } + + public override int ReadByte() + { + int b = stream.ReadByte(); + if (inDigest != null) + { + if (b >= 0) + { + inDigest.Update((byte)b); + } + } + return b; + } + + public override void Write( + byte[] buffer, + int offset, + int count) + { + if (outDigest != null) + { + if (count > 0) + { + outDigest.BlockUpdate(buffer, offset, count); + } + } + stream.Write(buffer, offset, count); + } + + public override void WriteByte( + byte b) + { + if (outDigest != null) + { + outDigest.Update(b); + } + stream.WriteByte(b); + } + + public override bool CanRead + { + get { return stream.CanRead; } + } + + public override bool CanWrite + { + get { return stream.CanWrite; } + } + + public override bool CanSeek + { + get { return stream.CanSeek; } + } + + public override long Length + { + get { return stream.Length; } + } + + public override long Position + { + get { return stream.Position; } + set { stream.Position = value; } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + stream.Dispose(); + } + } + + public override void Flush() + { + stream.Flush(); + } + + public override long Seek( + long offset, + SeekOrigin origin) + { + return stream.Seek(offset, origin); + } + + public override void SetLength( + long length) + { + stream.SetLength(length); + } + } +} + diff --git a/Crypto/src/crypto/io/MacStream.cs b/Crypto/src/crypto/io/MacStream.cs new file mode 100644
index 000000000..419eafb77 --- /dev/null +++ b/Crypto/src/crypto/io/MacStream.cs
@@ -0,0 +1,139 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.IO +{ + public class MacStream + : Stream + { + protected readonly Stream stream; + protected readonly IMac inMac; + protected readonly IMac outMac; + + public MacStream( + Stream stream, + IMac readMac, + IMac writeMac) + { + this.stream = stream; + this.inMac = readMac; + this.outMac = writeMac; + } + + public virtual IMac ReadMac() + { + return inMac; + } + + public virtual IMac WriteMac() + { + return outMac; + } + + public override int Read( + byte[] buffer, + int offset, + int count) + { + int n = stream.Read(buffer, offset, count); + if (inMac != null) + { + if (n > 0) + { + inMac.BlockUpdate(buffer, offset, n); + } + } + return n; + } + + public override int ReadByte() + { + int b = stream.ReadByte(); + if (inMac != null) + { + if (b >= 0) + { + inMac.Update((byte)b); + } + } + return b; + } + + public override void Write( + byte[] buffer, + int offset, + int count) + { + if (outMac != null) + { + if (count > 0) + { + outMac.BlockUpdate(buffer, offset, count); + } + } + stream.Write(buffer, offset, count); + } + + public override void WriteByte(byte b) + { + if (outMac != null) + { + outMac.Update(b); + } + stream.WriteByte(b); + } + + public override bool CanRead + { + get { return stream.CanRead; } + } + + public override bool CanWrite + { + get { return stream.CanWrite; } + } + + public override bool CanSeek + { + get { return stream.CanSeek; } + } + + public override long Length + { + get { return stream.Length; } + } + + public override long Position + { + get { return stream.Position; } + set { stream.Position = value; } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + stream.Dispose(); + } + } + + public override void Flush() + { + stream.Flush(); + } + + public override long Seek( + long offset, + SeekOrigin origin) + { + return stream.Seek(offset,origin); + } + + public override void SetLength( + long length) + { + stream.SetLength(length); + } + } +} + diff --git a/Crypto/src/crypto/io/SignerStream.cs b/Crypto/src/crypto/io/SignerStream.cs new file mode 100644
index 000000000..8be8ca84a --- /dev/null +++ b/Crypto/src/crypto/io/SignerStream.cs
@@ -0,0 +1,140 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.IO +{ + public class SignerStream + : Stream + { + protected readonly Stream stream; + protected readonly ISigner inSigner; + protected readonly ISigner outSigner; + + public SignerStream( + Stream stream, + ISigner readSigner, + ISigner writeSigner) + { + this.stream = stream; + this.inSigner = readSigner; + this.outSigner = writeSigner; + } + + public virtual ISigner ReadSigner() + { + return inSigner; + } + + public virtual ISigner WriteSigner() + { + return outSigner; + } + + public override int Read( + byte[] buffer, + int offset, + int count) + { + int n = stream.Read(buffer, offset, count); + if (inSigner != null) + { + if (n > 0) + { + inSigner.BlockUpdate(buffer, offset, n); + } + } + return n; + } + + public override int ReadByte() + { + int b = stream.ReadByte(); + if (inSigner != null) + { + if (b >= 0) + { + inSigner.Update((byte)b); + } + } + return b; + } + + public override void Write( + byte[] buffer, + int offset, + int count) + { + if (outSigner != null) + { + if (count > 0) + { + outSigner.BlockUpdate(buffer, offset, count); + } + } + stream.Write(buffer, offset, count); + } + + public override void WriteByte( + byte b) + { + if (outSigner != null) + { + outSigner.Update(b); + } + stream.WriteByte(b); + } + + public override bool CanRead + { + get { return stream.CanRead; } + } + + public override bool CanWrite + { + get { return stream.CanWrite; } + } + + public override bool CanSeek + { + get { return stream.CanSeek; } + } + + public override long Length + { + get { return stream.Length; } + } + + public override long Position + { + get { return stream.Position; } + set { stream.Position = value; } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + stream.Dispose(); + } + } + + public override void Flush() + { + stream.Flush(); + } + + public override long Seek( + long offset, + SeekOrigin origin) + { + return stream.Seek(offset, origin); + } + + public override void SetLength( + long length) + { + stream.SetLength(length); + } + } +} + diff --git a/Crypto/src/crypto/macs/CMac.cs b/Crypto/src/crypto/macs/CMac.cs new file mode 100644
index 000000000..ea1ce88f5 --- /dev/null +++ b/Crypto/src/crypto/macs/CMac.cs
@@ -0,0 +1,240 @@ +using System; + +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Paddings; + +namespace Org.BouncyCastle.Crypto.Macs +{ + /** + * 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 + : IMac + { + private const byte CONSTANT_128 = (byte)0x87; + private const byte CONSTANT_64 = (byte)0x1b; + + private byte[] ZEROES; + + private byte[] mac; + + private byte[] buf; + private int bufOff; + private IBlockCipher 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( + IBlockCipher 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 @lt;= 128. + */ + public CMac( + IBlockCipher cipher, + int macSizeInBits) + { + if ((macSizeInBits % 8) != 0) + throw new ArgumentException("MAC size must be multiple of 8"); + + if (macSizeInBits > (cipher.GetBlockSize() * 8)) + { + throw new ArgumentException( + "MAC size must be less or equal to " + + (cipher.GetBlockSize() * 8)); + } + + if (cipher.GetBlockSize() != 8 && cipher.GetBlockSize() != 16) + { + throw new ArgumentException( + "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 AlgorithmName + { + get { return cipher.AlgorithmName; } + } + + private byte[] doubleLu( + byte[] inBytes) + { + int FirstBit = (inBytes[0] & 0xFF) >> 7; + byte[] ret = new byte[inBytes.Length]; + for (int i = 0; i < inBytes.Length - 1; i++) + { + ret[i] = (byte)((inBytes[i] << 1) + ((inBytes[i + 1] & 0xFF) >> 7)); + } + ret[inBytes.Length - 1] = (byte)(inBytes[inBytes.Length - 1] << 1); + if (FirstBit == 1) + { + ret[inBytes.Length - 1] ^= inBytes.Length == 16 ? CONSTANT_128 : CONSTANT_64; + } + return ret; + } + + public void Init( + ICipherParameters parameters) + { + Reset(); + + cipher.Init(true, parameters); + + //initializes the L, Lu, Lu2 numbers + L = new byte[ZEROES.Length]; + cipher.ProcessBlock(ZEROES, 0, L, 0); + Lu = doubleLu(L); + Lu2 = doubleLu(Lu); + + cipher.Init(true, parameters); + } + + public int GetMacSize() + { + return macSize; + } + + public void Update( + byte input) + { + if (bufOff == buf.Length) + { + cipher.ProcessBlock(buf, 0, mac, 0); + bufOff = 0; + } + + buf[bufOff++] = input; + } + + public void BlockUpdate( + byte[] inBytes, + int inOff, + int len) + { + if (len < 0) + throw new ArgumentException("Can't have a negative input length!"); + + int blockSize = cipher.GetBlockSize(); + int gapLen = blockSize - bufOff; + + if (len > gapLen) + { + Array.Copy(inBytes, inOff, buf, bufOff, gapLen); + + cipher.ProcessBlock(buf, 0, mac, 0); + + bufOff = 0; + len -= gapLen; + inOff += gapLen; + + while (len > blockSize) + { + cipher.ProcessBlock(inBytes, inOff, mac, 0); + + len -= blockSize; + inOff += blockSize; + } + } + + Array.Copy(inBytes, inOff, buf, bufOff, len); + + bufOff += len; + } + + public int DoFinal( + byte[] outBytes, + 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); + + Array.Copy(mac, 0, outBytes, outOff, macSize); + + Reset(); + + return macSize; + } + + /** + * Reset the mac generator. + */ + public void Reset() + { + /* + * clean the buffer. + */ + Array.Clear(buf, 0, buf.Length); + bufOff = 0; + + /* + * Reset the underlying cipher. + */ + cipher.Reset(); + } + } +} diff --git a/Crypto/src/crypto/macs/CbcBlockCipherMac.cs b/Crypto/src/crypto/macs/CbcBlockCipherMac.cs new file mode 100644
index 000000000..146e16aa8 --- /dev/null +++ b/Crypto/src/crypto/macs/CbcBlockCipherMac.cs
@@ -0,0 +1,209 @@ +using System; + +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Paddings; + +namespace Org.BouncyCastle.Crypto.Macs +{ + /** + * standard CBC Block Cipher MAC - if no padding is specified the default of + * pad of zeroes is used. + */ + public class CbcBlockCipherMac + : IMac + { + private byte[] buf; + private int bufOff; + private IBlockCipher cipher; + private IBlockCipherPadding 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( + IBlockCipher 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( + IBlockCipher cipher, + IBlockCipherPadding 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). + * </p> + * @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( + IBlockCipher 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). + * </p> + * @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( + IBlockCipher cipher, + int macSizeInBits, + IBlockCipherPadding padding) + { + if ((macSizeInBits % 8) != 0) + throw new ArgumentException("MAC size must be multiple of 8"); + + this.cipher = new CbcBlockCipher(cipher); + this.padding = padding; + this.macSize = macSizeInBits / 8; + + buf = new byte[cipher.GetBlockSize()]; + bufOff = 0; + } + + public string AlgorithmName + { + get { return cipher.AlgorithmName; } + } + + public void Init( + ICipherParameters parameters) + { + Reset(); + + cipher.Init(true, parameters); + } + + public int GetMacSize() + { + return macSize; + } + + public void Update( + byte input) + { + if (bufOff == buf.Length) + { + cipher.ProcessBlock(buf, 0, buf, 0); + bufOff = 0; + } + + buf[bufOff++] = input; + } + + public void BlockUpdate( + byte[] input, + int inOff, + int len) + { + if (len < 0) + throw new ArgumentException("Can't have a negative input length!"); + + int blockSize = cipher.GetBlockSize(); + int gapLen = blockSize - bufOff; + + if (len > gapLen) + { + Array.Copy(input, inOff, buf, bufOff, gapLen); + + cipher.ProcessBlock(buf, 0, buf, 0); + + bufOff = 0; + len -= gapLen; + inOff += gapLen; + + while (len > blockSize) + { + cipher.ProcessBlock(input, inOff, buf, 0); + + len -= blockSize; + inOff += blockSize; + } + } + + Array.Copy(input, inOff, buf, bufOff, len); + + bufOff += len; + } + + public int DoFinal( + byte[] output, + int outOff) + { + int blockSize = cipher.GetBlockSize(); + + if (padding == null) + { + // pad with zeroes + while (bufOff < blockSize) + { + buf[bufOff++] = 0; + } + } + else + { + if (bufOff == blockSize) + { + cipher.ProcessBlock(buf, 0, buf, 0); + bufOff = 0; + } + + padding.AddPadding(buf, bufOff); + } + + cipher.ProcessBlock(buf, 0, buf, 0); + + Array.Copy(buf, 0, output, outOff, macSize); + + Reset(); + + return macSize; + } + + /** + * Reset the mac generator. + */ + public void Reset() + { + // Clear the buffer. + Array.Clear(buf, 0, buf.Length); + bufOff = 0; + + // Reset the underlying cipher. + cipher.Reset(); + } + } +} diff --git a/Crypto/src/crypto/macs/CfbBlockCipherMac.cs b/Crypto/src/crypto/macs/CfbBlockCipherMac.cs new file mode 100644
index 000000000..364cf8499 --- /dev/null +++ b/Crypto/src/crypto/macs/CfbBlockCipherMac.cs
@@ -0,0 +1,368 @@ +using System; + +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Paddings; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Macs +{ + /** + * implements a Cipher-FeedBack (CFB) mode on top of a simple cipher. + */ + class MacCFBBlockCipher + : IBlockCipher + { + private byte[] IV; + private byte[] cfbV; + private byte[] cfbOutV; + + private readonly int blockSize; + private readonly IBlockCipher 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 MacCFBBlockCipher( + IBlockCipher 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 ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (parameters is ParametersWithIV) + { + ParametersWithIV ivParam = (ParametersWithIV)parameters; + byte[] iv = ivParam.GetIV(); + + if (iv.Length < IV.Length) + { + Array.Copy(iv, 0, IV, IV.Length - iv.Length, iv.Length); + } + else + { + Array.Copy(iv, 0, IV, 0, IV.Length); + } + + parameters = ivParam.Parameters; + } + + Reset(); + + cipher.Init(true, parameters); + } + + /** + * return the algorithm name and mode. + * + * @return the name of the underlying algorithm followed by "/CFB" + * and the block size in bits. + */ + public string AlgorithmName + { + get { return cipher.AlgorithmName + "/CFB" + (blockSize * 8); } + } + + public bool IsPartialBlockOkay + { + get { return true; } + } + + /** + * 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 InvalidOperationException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + public int ProcessBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + if ((inOff + blockSize) > input.Length) + throw new DataLengthException("input buffer too short"); + + if ((outOff + blockSize) > outBytes.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++) + { + outBytes[outOff + i] = (byte)(cfbOutV[i] ^ input[inOff + i]); + } + + // + // change over the input block. + // + Array.Copy(cfbV, blockSize, cfbV, 0, cfbV.Length - blockSize); + Array.Copy(outBytes, outOff, cfbV, cfbV.Length - blockSize, blockSize); + + return blockSize; + } + + /** + * reset the chaining vector back to the IV and reset the underlying + * cipher. + */ + public void Reset() + { + IV.CopyTo(cfbV, 0); + + cipher.Reset(); + } + + public void GetMacBlock( + byte[] mac) + { + cipher.ProcessBlock(cfbV, 0, mac, 0); + } + } + + public class CfbBlockCipherMac + : IMac + { + private byte[] mac; + private byte[] Buffer; + private int bufOff; + private MacCFBBlockCipher cipher; + private IBlockCipherPadding padding; + 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( + IBlockCipher 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( + IBlockCipher cipher, + IBlockCipherPadding 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). + * </p> + * @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( + IBlockCipher 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). + * </p> + * @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( + IBlockCipher cipher, + int cfbBitSize, + int macSizeInBits, + IBlockCipherPadding padding) + { + if ((macSizeInBits % 8) != 0) + throw new ArgumentException("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; + + Buffer = new byte[this.cipher.GetBlockSize()]; + bufOff = 0; + } + + public string AlgorithmName + { + get { return cipher.AlgorithmName; } + } + + public void Init( + ICipherParameters parameters) + { + Reset(); + + cipher.Init(true, parameters); + } + + public int GetMacSize() + { + return macSize; + } + + public void Update( + byte input) + { + if (bufOff == Buffer.Length) + { + cipher.ProcessBlock(Buffer, 0, mac, 0); + bufOff = 0; + } + + Buffer[bufOff++] = input; + } + + public void BlockUpdate( + byte[] input, + int inOff, + int len) + { + if (len < 0) + throw new ArgumentException("Can't have a negative input length!"); + + int blockSize = cipher.GetBlockSize(); + int resultLen = 0; + int gapLen = blockSize - bufOff; + + if (len > gapLen) + { + Array.Copy(input, inOff, Buffer, bufOff, gapLen); + + resultLen += cipher.ProcessBlock(Buffer, 0, mac, 0); + + bufOff = 0; + len -= gapLen; + inOff += gapLen; + + while (len > blockSize) + { + resultLen += cipher.ProcessBlock(input, inOff, mac, 0); + + len -= blockSize; + inOff += blockSize; + } + } + + Array.Copy(input, inOff, Buffer, bufOff, len); + + bufOff += len; + } + + public int DoFinal( + byte[] output, + int outOff) + { + int blockSize = cipher.GetBlockSize(); + + // pad with zeroes + if (this.padding == null) + { + while (bufOff < blockSize) + { + Buffer[bufOff++] = 0; + } + } + else + { + padding.AddPadding(Buffer, bufOff); + } + + cipher.ProcessBlock(Buffer, 0, mac, 0); + + cipher.GetMacBlock(mac); + + Array.Copy(mac, 0, output, outOff, macSize); + + Reset(); + + return macSize; + } + + /** + * Reset the mac generator. + */ + public void Reset() + { + // Clear the buffer. + Array.Clear(Buffer, 0, Buffer.Length); + bufOff = 0; + + // Reset the underlying cipher. + cipher.Reset(); + } + } + +} diff --git a/Crypto/src/crypto/macs/GOST28147Mac.cs b/Crypto/src/crypto/macs/GOST28147Mac.cs new file mode 100644
index 000000000..9a8f1b730 --- /dev/null +++ b/Crypto/src/crypto/macs/GOST28147Mac.cs
@@ -0,0 +1,296 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Macs +{ + /** + * implementation of GOST 28147-89 MAC + */ + public class Gost28147Mac : IMac + { + private const int blockSize = 8; + private const int macSize = 4; + private int bufOff; + private byte[] buf; + private byte[] mac; + private bool firstStep = true; + private int[] workingKey; + + // + // 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 static int[] generateWorkingKey( + byte[] userKey) + { + if (userKey.Length != 32) + throw new ArgumentException("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( + ICipherParameters parameters) + { + Reset(); + buf = new byte[blockSize]; + if (parameters is ParametersWithSBox) + { + ParametersWithSBox param = (ParametersWithSBox)parameters; + + // + // Set the S-Box + // + param.GetSBox().CopyTo(this.S, 0); + + // + // set key if there is one + // + if (param.Parameters != null) + { + workingKey = generateWorkingKey(((KeyParameter)param.Parameters).GetKey()); + } + } + else if (parameters is KeyParameter) + { + workingKey = generateWorkingKey(((KeyParameter)parameters).GetKey()); + } + else + { + throw new ArgumentException("invalid parameter passed to Gost28147 init - " + + parameters.GetType().Name); + } + } + + public string AlgorithmName + { + get { 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 + int omLeft = om << 11; + int omRight = (int)(((uint) om) >> (32 - 11)); // Note: Casts required to get unsigned bit rotation + + return omLeft | omRight; + } + + private void gost28147MacFunc( + int[] workingKey, + byte[] input, + int inOff, + byte[] output, + int outOff) + { + int N1, N2, tmp; //tmp -> for saving N1 + N1 = bytesToint(input, inOff); + N2 = bytesToint(input, 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, output, outOff); + intTobytes(N2, output, outOff + 4); + } + + //array of bytes to type int + private static int bytesToint( + byte[] input, + int inOff) + { + return (int)((input[inOff + 3] << 24) & 0xff000000) + ((input[inOff + 2] << 16) & 0xff0000) + + ((input[inOff + 1] << 8) & 0xff00) + (input[inOff] & 0xff); + } + + //int to array of bytes + private static void intTobytes( + int num, + byte[] output, + int outOff) + { + output[outOff + 3] = (byte)(num >> 24); + output[outOff + 2] = (byte)(num >> 16); + output[outOff + 1] = (byte)(num >> 8); + output[outOff] = (byte)num; + } + + private static byte[] CM5func( + byte[] buf, + int bufOff, + byte[] mac) + { + byte[] sum = new byte[buf.Length - bufOff]; + + Array.Copy(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 input) + { + if (bufOff == buf.Length) + { + byte[] sumbuf = new byte[buf.Length]; + Array.Copy(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++] = input; + } + + public void BlockUpdate( + byte[] input, + int inOff, + int len) + { + if (len < 0) + throw new ArgumentException("Can't have a negative input length!"); + + int gapLen = blockSize - bufOff; + + if (len > gapLen) + { + Array.Copy(input, inOff, buf, bufOff, gapLen); + + byte[] sumbuf = new byte[buf.Length]; + Array.Copy(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(input, inOff, mac); + gost28147MacFunc(workingKey, sumbuf, 0, mac, 0); + + len -= blockSize; + inOff += blockSize; + } + } + + Array.Copy(input, inOff, buf, bufOff, len); + + bufOff += len; + } + + public int DoFinal( + byte[] output, + int outOff) + { + //padding with zero + while (bufOff < blockSize) + { + buf[bufOff++] = 0; + } + + byte[] sumbuf = new byte[buf.Length]; + Array.Copy(buf, 0, sumbuf, 0, mac.Length); + + if (firstStep) + { + firstStep = false; + } + else + { + sumbuf = CM5func(buf, 0, mac); + } + + gost28147MacFunc(workingKey, sumbuf, 0, mac, 0); + + Array.Copy(mac, (mac.Length/2)-macSize, output, outOff, macSize); + + Reset(); + + return macSize; + } + + public void Reset() + { + // Clear the buffer. + Array.Clear(buf, 0, buf.Length); + bufOff = 0; + + firstStep = true; + } + } +} diff --git a/Crypto/src/crypto/macs/HMac.cs b/Crypto/src/crypto/macs/HMac.cs new file mode 100644
index 000000000..3f9b0cef0 --- /dev/null +++ b/Crypto/src/crypto/macs/HMac.cs
@@ -0,0 +1,134 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Macs +{ + /** + * HMAC implementation based on RFC2104 + * + * H(K XOR opad, H(K XOR ipad, text)) + */ + public class HMac + : IMac + { + private const byte IPAD = (byte)0x36; + private const byte OPAD = (byte)0x5C; + + private readonly IDigest digest; + private readonly int digestSize; + private readonly int blockLength; + + private readonly byte[] inputPad; + private readonly byte[] outputPad; + + public HMac( + IDigest digest) + { + this.digest = digest; + this.digestSize = digest.GetDigestSize(); + this.blockLength = digest.GetByteLength(); + this.inputPad = new byte[blockLength]; + this.outputPad = new byte[blockLength]; + } + + public string AlgorithmName + { + get { return digest.AlgorithmName + "/HMAC"; } + } + + public IDigest GetUnderlyingDigest() + { + return digest; + } + + public void Init( + ICipherParameters parameters) + { + digest.Reset(); + + byte[] key = ((KeyParameter)parameters).GetKey(); + int keyLength = key.Length; + + if (keyLength > blockLength) + { + digest.BlockUpdate(key, 0, key.Length); + digest.DoFinal(inputPad, 0); + + keyLength = digestSize; + } + else + { + Array.Copy(key, 0, inputPad, 0, keyLength); + } + + Array.Clear(inputPad, keyLength, blockLength - keyLength); + Array.Copy(inputPad, 0, outputPad, 0, blockLength); + + xor(inputPad, IPAD); + xor(outputPad, OPAD); + + // Initialise the digest + digest.BlockUpdate(inputPad, 0, inputPad.Length); + } + + public int GetMacSize() + { + return digestSize; + } + + public void Update( + byte input) + { + digest.Update(input); + } + + public void BlockUpdate( + byte[] input, + int inOff, + int len) + { + digest.BlockUpdate(input, inOff, len); + } + + public int DoFinal( + byte[] output, + int outOff) + { + byte[] tmp = new byte[digestSize]; + digest.DoFinal(tmp, 0); + + digest.BlockUpdate(outputPad, 0, outputPad.Length); + digest.BlockUpdate(tmp, 0, tmp.Length); + + int len = digest.DoFinal(output, outOff); + + // Initialise the digest + digest.BlockUpdate(inputPad, 0, inputPad.Length); + + return len; + } + + /** + * Reset the mac generator. + */ + public void Reset() + { + // Reset underlying digest + digest.Reset(); + + // Initialise the digest + digest.BlockUpdate(inputPad, 0, inputPad.Length); + } + + private static void xor(byte[] a, byte n) + { + for (int i = 0; i < a.Length; ++i) + { + a[i] ^= n; + } + } + } +} diff --git a/Crypto/src/crypto/macs/ISO9797Alg3Mac.cs b/Crypto/src/crypto/macs/ISO9797Alg3Mac.cs new file mode 100644
index 000000000..6fee619c1 --- /dev/null +++ b/Crypto/src/crypto/macs/ISO9797Alg3Mac.cs
@@ -0,0 +1,275 @@ +using System; + +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Paddings; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Macs +{ + /** + * 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 : IMac + { + private byte[] mac; + private byte[] buf; + private int bufOff; + private IBlockCipher cipher; + private IBlockCipherPadding 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( + IBlockCipher 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( + IBlockCipher cipher, + IBlockCipherPadding 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). + * </p> + * @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( + IBlockCipher 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). + * </p> + * @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( + IBlockCipher cipher, + int macSizeInBits, + IBlockCipherPadding padding) + { + if ((macSizeInBits % 8) != 0) + throw new ArgumentException("MAC size must be multiple of 8"); + + if (!(cipher is DesEngine)) + throw new ArgumentException("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 AlgorithmName + { + get { return "ISO9797Alg3"; } + } + + public void Init( + ICipherParameters parameters) + { + Reset(); + + if (!(parameters is KeyParameter || parameters is ParametersWithIV)) + throw new ArgumentException("parameters 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 (parameters is KeyParameter) + { + kp = (KeyParameter)parameters; + } + else + { + kp = (KeyParameter)((ParametersWithIV)parameters).Parameters; + } + + 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 ArgumentException("Key must be either 112 or 168 bit long"); + } + + if (parameters is ParametersWithIV) + { + cipher.Init(true, new ParametersWithIV(key1, ((ParametersWithIV)parameters).GetIV())); + } + else + { + cipher.Init(true, key1); + } + } + + public int GetMacSize() + { + return macSize; + } + + public void Update( + byte input) + { + if (bufOff == buf.Length) + { + cipher.ProcessBlock(buf, 0, mac, 0); + bufOff = 0; + } + + buf[bufOff++] = input; + } + + public void BlockUpdate( + byte[] input, + int inOff, + int len) + { + if (len < 0) + throw new ArgumentException("Can't have a negative input length!"); + + int blockSize = cipher.GetBlockSize(); + int resultLen = 0; + int gapLen = blockSize - bufOff; + + if (len > gapLen) + { + Array.Copy(input, inOff, buf, bufOff, gapLen); + + resultLen += cipher.ProcessBlock(buf, 0, mac, 0); + + bufOff = 0; + len -= gapLen; + inOff += gapLen; + + while (len > blockSize) + { + resultLen += cipher.ProcessBlock(input, inOff, mac, 0); + + len -= blockSize; + inOff += blockSize; + } + } + + Array.Copy(input, inOff, buf, bufOff, len); + + bufOff += len; + } + + public int DoFinal( + byte[] output, + int outOff) + { + int blockSize = cipher.GetBlockSize(); + + if (padding == null) + { + // pad with zeroes + while (bufOff < blockSize) + { + buf[bufOff++] = 0; + } + } + 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); + // **** + + Array.Copy(mac, 0, output, outOff, macSize); + + Reset(); + + return macSize; + } + + /** + * Reset the mac generator. + */ + public void Reset() + { + Array.Clear(buf, 0, buf.Length); + bufOff = 0; + + // reset the underlying cipher. + cipher.Reset(); + } + } +} diff --git a/Crypto/src/crypto/macs/VMPCMac.cs b/Crypto/src/crypto/macs/VMPCMac.cs new file mode 100644
index 000000000..89916355c --- /dev/null +++ b/Crypto/src/crypto/macs/VMPCMac.cs
@@ -0,0 +1,173 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Macs +{ + public class VmpcMac + : IMac + { + 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 virtual int DoFinal(byte[] output, int outOff) + { + // 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 input 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; + } + + Array.Copy(M, 0, output, outOff, M.Length); + Reset(); + + return M.Length; + } + + public virtual string AlgorithmName + { + get { return "VMPC-MAC"; } + } + + public virtual int GetMacSize() + { + return 20; + } + + public virtual void Init(ICipherParameters parameters) + { + if (!(parameters is ParametersWithIV)) + throw new ArgumentException("VMPC-MAC Init parameters must include an IV", "parameters"); + + ParametersWithIV ivParams = (ParametersWithIV) parameters; + KeyParameter key = (KeyParameter) ivParams.Parameters; + + if (!(ivParams.Parameters is KeyParameter)) + throw new ArgumentException("VMPC-MAC Init parameters must include a key", "parameters"); + + this.workingIV = ivParams.GetIV(); + + if (workingIV == null || workingIV.Length < 1 || workingIV.Length > 768) + throw new ArgumentException("VMPC-MAC requires 1 to 768 bytes of IV", "parameters"); + + 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 virtual 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 virtual void Update(byte input) + { + s = P[(s + P[n & 0xff]) & 0xff]; + byte c = (byte) (input ^ 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 virtual void BlockUpdate(byte[] input, int inOff, int len) + { + if ((inOff + len) > input.Length) + throw new DataLengthException("input buffer too short"); + + for (int i = 0; i < len; i++) + { + Update(input[i]); + } + } + } +} diff --git a/Crypto/src/crypto/modes/CbcBlockCipher.cs b/Crypto/src/crypto/modes/CbcBlockCipher.cs new file mode 100644
index 000000000..0bbc0cb24 --- /dev/null +++ b/Crypto/src/crypto/modes/CbcBlockCipher.cs
@@ -0,0 +1,231 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Modes +{ + /** + * implements Cipher-Block-Chaining (CBC) mode on top of a simple cipher. + */ + public class CbcBlockCipher + : IBlockCipher + { + private byte[] IV, cbcV, cbcNextV; + private int blockSize; + private IBlockCipher cipher; + private bool encrypting; + + /** + * Basic constructor. + * + * @param cipher the block cipher to be used as the basis of chaining. + */ + public CbcBlockCipher( + IBlockCipher 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 IBlockCipher 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 forEncryption if true the cipher is initialised for + * encryption, if false for decryption. + * @param param the key and other data required by the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + this.encrypting = forEncryption; + + if (parameters is ParametersWithIV) + { + ParametersWithIV ivParam = (ParametersWithIV)parameters; + byte[] iv = ivParam.GetIV(); + + if (iv.Length != blockSize) + { + throw new ArgumentException("initialisation vector must be the same length as block size"); + } + + Array.Copy(iv, 0, IV, 0, iv.Length); + + parameters = ivParam.Parameters; + } + + Reset(); + + cipher.Init(encrypting, parameters); + } + + /** + * return the algorithm name and mode. + * + * @return the name of the underlying algorithm followed by "/CBC". + */ + public string AlgorithmName + { + get { return cipher.AlgorithmName + "/CBC"; } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + /** + * 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 InvalidOperationException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + return (encrypting) + ? EncryptBlock(input, inOff, output, outOff) + : DecryptBlock(input, inOff, output, outOff); + } + + /** + * reset the chaining vector back to the IV and reset the underlying + * cipher. + */ + public void Reset() + { + Array.Copy(IV, 0, cbcV, 0, IV.Length); + Array.Clear(cbcNextV, 0, cbcNextV.Length); + + 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 InvalidOperationException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + private int EncryptBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + if ((inOff + blockSize) > input.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] ^= input[inOff + i]; + } + + int length = cipher.ProcessBlock(cbcV, 0, outBytes, outOff); + + /* + * copy ciphertext to cbcV + */ + Array.Copy(outBytes, 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 InvalidOperationException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + private int DecryptBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + if ((inOff + blockSize) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + + Array.Copy(input, inOff, cbcNextV, 0, blockSize); + + int length = cipher.ProcessBlock(input, inOff, outBytes, outOff); + + /* + * XOR the cbcV and the output + */ + for (int i = 0; i < blockSize; i++) + { + outBytes[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/Crypto/src/crypto/modes/CcmBlockCipher.cs b/Crypto/src/crypto/modes/CcmBlockCipher.cs new file mode 100644
index 000000000..abfde237e --- /dev/null +++ b/Crypto/src/crypto/modes/CcmBlockCipher.cs
@@ -0,0 +1,345 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Macs; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Modes +{ + /** + * 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. + * </p> + */ + public class CcmBlockCipher + : IAeadBlockCipher + { + private static readonly int BlockSize = 16; + + private readonly IBlockCipher cipher; + private readonly byte[] macBlock; + private bool forEncryption; + private byte[] nonce; + private byte[] associatedText; + private int macSize; + private ICipherParameters keyParam; + private readonly MemoryStream data = new MemoryStream(); + + /** + * Basic constructor. + * + * @param cipher the block cipher to be used. + */ + public CcmBlockCipher( + IBlockCipher cipher) + { + this.cipher = cipher; + this.macBlock = new byte[BlockSize]; + + if (cipher.GetBlockSize() != BlockSize) + throw new ArgumentException("cipher required with a block size of " + BlockSize + "."); + } + + /** + * return the underlying block cipher that we are wrapping. + * + * @return the underlying block cipher that we are wrapping. + */ + public virtual IBlockCipher GetUnderlyingCipher() + { + return cipher; + } + + public virtual void Init( + bool forEncryption, + ICipherParameters parameters) + { + this.forEncryption = forEncryption; + + if (parameters is AeadParameters) + { + AeadParameters param = (AeadParameters) parameters; + + nonce = param.GetNonce(); + associatedText = param.GetAssociatedText(); + macSize = param.MacSize / 8; + keyParam = param.Key; + } + else if (parameters is ParametersWithIV) + { + ParametersWithIV param = (ParametersWithIV) parameters; + + nonce = param.GetIV(); + associatedText = null; + macSize = macBlock.Length / 2; + keyParam = param.Parameters; + } + else + { + throw new ArgumentException("invalid parameters passed to CCM"); + } + } + + public virtual string AlgorithmName + { + get { return cipher.AlgorithmName + "/CCM"; } + } + + public virtual int GetBlockSize() + { + return cipher.GetBlockSize(); + } + + public virtual int ProcessByte( + byte input, + byte[] outBytes, + int outOff) + { + data.WriteByte(input); + + return 0; + } + + public virtual int ProcessBytes( + byte[] inBytes, + int inOff, + int inLen, + byte[] outBytes, + int outOff) + { + data.Write(inBytes, inOff, inLen); + + return 0; + } + + public virtual int DoFinal( + byte[] outBytes, + int outOff) + { + byte[] text = data.ToArray(); + byte[] enc = ProcessPacket(text, 0, text.Length); + + Array.Copy(enc, 0, outBytes, outOff, enc.Length); + + Reset(); + + return enc.Length; + } + + public virtual void Reset() + { + cipher.Reset(); + data.SetLength(0); + } + + /** + * Returns a byte array containing the mac calculated as part of the + * last encrypt or decrypt operation. + * + * @return the last mac calculated. + */ + public virtual byte[] GetMac() + { + byte[] mac = new byte[macSize]; + + Array.Copy(macBlock, 0, mac, 0, mac.Length); + + return mac; + } + + public virtual int GetUpdateOutputSize( + int len) + { + return 0; + } + + public int GetOutputSize( + int len) + { + if (forEncryption) + { + return (int) data.Length + len + macSize; + } + + return (int) data.Length + len - macSize; + } + + public byte[] ProcessPacket( + byte[] input, + int inOff, + int inLen) + { + if (keyParam == null) + throw new InvalidOperationException("CCM cipher unitialized."); + + IBlockCipher ctrCipher = new SicBlockCipher(cipher); + byte[] iv = new byte[BlockSize]; + byte[] output; + + iv[0] = (byte)(((15 - nonce.Length) - 1) & 0x7); + + Array.Copy(nonce, 0, iv, 1, nonce.Length); + + ctrCipher.Init(forEncryption, new ParametersWithIV(keyParam, iv)); + + if (forEncryption) + { + int index = inOff; + int outOff = 0; + + output = new byte[inLen + macSize]; + + calculateMac(input, inOff, inLen, macBlock); + + ctrCipher.ProcessBlock(macBlock, 0, macBlock, 0); // S0 + + while (index < inLen - BlockSize) // S1... + { + ctrCipher.ProcessBlock(input, index, output, outOff); + outOff += BlockSize; + index += BlockSize; + } + + byte[] block = new byte[BlockSize]; + + Array.Copy(input, index, block, 0, inLen - index); + + ctrCipher.ProcessBlock(block, 0, block, 0); + + Array.Copy(block, 0, output, outOff, inLen - index); + + outOff += inLen - index; + + Array.Copy(macBlock, 0, output, outOff, output.Length - outOff); + } + else + { + int index = inOff; + int outOff = 0; + + output = new byte[inLen - macSize]; + + Array.Copy(input, 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(input, index, output, outOff); + outOff += BlockSize; + index += BlockSize; + } + + byte[] block = new byte[BlockSize]; + + Array.Copy(input, index, block, 0, output.Length - outOff); + + ctrCipher.ProcessBlock(block, 0, block, 0); + + Array.Copy(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) + { + IMac cMac = new CbcBlockCipherMac(cipher, macSize * 8); + + cMac.Init(keyParam); + + // + // build b0 + // + byte[] b0 = new byte[16]; + + if (hasAssociatedText()) + { + b0[0] |= 0x40; + } + + b0[0] |= (byte)((((cMac.GetMacSize() - 2) / 2) & 0x7) << 3); + + b0[0] |= (byte)(((15 - nonce.Length) - 1) & 0x7); + + Array.Copy(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.BlockUpdate(b0, 0, b0.Length); + + // + // process associated text + // + if (hasAssociatedText()) + { + int extra; + + if (associatedText.Length < ((1 << 16) - (1 << 8))) + { + cMac.Update((byte)(associatedText.Length >> 8)); + cMac.Update((byte)associatedText.Length); + + extra = 2; + } + else // can't go any higher than 2^32 + { + cMac.Update((byte)0xff); + cMac.Update((byte)0xfe); + cMac.Update((byte)(associatedText.Length >> 24)); + cMac.Update((byte)(associatedText.Length >> 16)); + cMac.Update((byte)(associatedText.Length >> 8)); + cMac.Update((byte)associatedText.Length); + + extra = 6; + } + + cMac.BlockUpdate(associatedText, 0, associatedText.Length); + + extra = (extra + associatedText.Length) % 16; + if (extra != 0) + { + for (int i = 0; i != 16 - extra; i++) + { + cMac.Update((byte)0x00); + } + } + } + + // + // add the text + // + cMac.BlockUpdate(data, dataOff, dataLen); + + return cMac.DoFinal(macBlock, 0); + } + + private bool hasAssociatedText() + { + return associatedText != null && associatedText.Length != 0; + } + } +} diff --git a/Crypto/src/crypto/modes/CfbBlockCipher.cs b/Crypto/src/crypto/modes/CfbBlockCipher.cs new file mode 100644
index 000000000..b400a72f4 --- /dev/null +++ b/Crypto/src/crypto/modes/CfbBlockCipher.cs
@@ -0,0 +1,218 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Modes +{ + /** + * implements a Cipher-FeedBack (CFB) mode on top of a simple cipher. + */ + public class CfbBlockCipher + : IBlockCipher + { + private byte[] IV; + private byte[] cfbV; + private byte[] cfbOutV; + private bool encrypting; + + private readonly int blockSize; + private readonly IBlockCipher 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 CfbBlockCipher( + IBlockCipher 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 IBlockCipher 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 forEncryption if true the cipher is initialised for + * encryption, if false for decryption. + * @param param the key and other data required by the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + this.encrypting = forEncryption; + if (parameters is ParametersWithIV) + { + ParametersWithIV ivParam = (ParametersWithIV) parameters; + byte[] iv = ivParam.GetIV(); + int diff = IV.Length - iv.Length; + Array.Copy(iv, 0, IV, diff, iv.Length); + Array.Clear(IV, 0, diff); + + parameters = ivParam.Parameters; + } + Reset(); + cipher.Init(true, parameters); + } + /** + * return the algorithm name and mode. + * + * @return the name of the underlying algorithm followed by "/CFB" + * and the block size in bits. + */ + public string AlgorithmName + { + get { return cipher.AlgorithmName + "/CFB" + (blockSize * 8); } + } + + public bool IsPartialBlockOkay + { + get { return true; } + } + + /** + * 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 InvalidOperationException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + return (encrypting) + ? EncryptBlock(input, inOff, output, outOff) + : DecryptBlock(input, inOff, output, 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 InvalidOperationException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + public int EncryptBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + if ((inOff + blockSize) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + if ((outOff + blockSize) > outBytes.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++) + { + outBytes[outOff + i] = (byte)(cfbOutV[i] ^ input[inOff + i]); + } + // + // change over the input block. + // + Array.Copy(cfbV, blockSize, cfbV, 0, cfbV.Length - blockSize); + Array.Copy(outBytes, 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 InvalidOperationException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + public int DecryptBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + if ((inOff + blockSize) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + if ((outOff + blockSize) > outBytes.Length) + { + throw new DataLengthException("output buffer too short"); + } + cipher.ProcessBlock(cfbV, 0, cfbOutV, 0); + // + // change over the input block. + // + Array.Copy(cfbV, blockSize, cfbV, 0, cfbV.Length - blockSize); + Array.Copy(input, inOff, cfbV, cfbV.Length - blockSize, blockSize); + // + // XOR the cfbV with the ciphertext producing the plaintext + // + for (int i = 0; i < blockSize; i++) + { + outBytes[outOff + i] = (byte)(cfbOutV[i] ^ input[inOff + i]); + } + return blockSize; + } + /** + * reset the chaining vector back to the IV and reset the underlying + * cipher. + */ + public void Reset() + { + Array.Copy(IV, 0, cfbV, 0, IV.Length); + cipher.Reset(); + } + } +} diff --git a/Crypto/src/crypto/modes/CtsBlockCipher.cs b/Crypto/src/crypto/modes/CtsBlockCipher.cs new file mode 100644
index 000000000..a32b49675 --- /dev/null +++ b/Crypto/src/crypto/modes/CtsBlockCipher.cs
@@ -0,0 +1,253 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Modes +{ + /** + * A Cipher Text Stealing (CTS) mode cipher. CTS allows block ciphers to + * be used to produce cipher text which is the same outLength as the plain text. + */ + public class CtsBlockCipher + : BufferedBlockCipher + { + private readonly int blockSize; + + /** + * Create a buffered block cipher that uses Cipher Text Stealing + * + * @param cipher the underlying block cipher this buffering object wraps. + */ + public CtsBlockCipher( + IBlockCipher cipher) + { + // TODO Should this test for acceptable ones instead? + if (cipher is OfbBlockCipher || cipher is CfbBlockCipher) + throw new ArgumentException("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 of 'length' bytes. + * + * @param length the outLength of the input. + * @return the space required to accommodate a call to update + * with length bytes of input. + */ + public override int GetUpdateOutputSize( + int length) + { + int total = length + 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 length bytes. + * + * @param length the outLength of the input. + * @return the space required to accommodate a call to update and doFinal + * with length bytes of input. + */ + public override int GetOutputSize( + int length) + { + 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 InvalidOperationException if the cipher isn't initialised. + */ + public override int ProcessByte( + byte input, + byte[] output, + int outOff) + { + int resultLen = 0; + + if (bufOff == buf.Length) + { + resultLen = cipher.ProcessBlock(buf, 0, output, outOff); + Debug.Assert(resultLen == blockSize); + + Array.Copy(buf, blockSize, buf, 0, blockSize); + bufOff = blockSize; + } + + buf[bufOff++] = input; + + 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 length 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 InvalidOperationException if the cipher isn't initialised. + */ + public override int ProcessBytes( + byte[] input, + int inOff, + int length, + byte[] output, + int outOff) + { + if (length < 0) + { + throw new ArgumentException("Can't have a negative input outLength!"); + } + + int blockSize = GetBlockSize(); + int outLength = GetUpdateOutputSize(length); + + if (outLength > 0) + { + if ((outOff + outLength) > output.Length) + { + throw new DataLengthException("output buffer too short"); + } + } + + int resultLen = 0; + int gapLen = buf.Length - bufOff; + + if (length > gapLen) + { + Array.Copy(input, inOff, buf, bufOff, gapLen); + + resultLen += cipher.ProcessBlock(buf, 0, output, outOff); + Array.Copy(buf, blockSize, buf, 0, blockSize); + + bufOff = blockSize; + + length -= gapLen; + inOff += gapLen; + + while (length > blockSize) + { + Array.Copy(input, inOff, buf, bufOff, blockSize); + resultLen += cipher.ProcessBlock(buf, 0, output, outOff + resultLen); + Array.Copy(buf, blockSize, buf, 0, blockSize); + + length -= blockSize; + inOff += blockSize; + } + } + + Array.Copy(input, inOff, buf, bufOff, length); + + bufOff += length; + + 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 InvalidOperationException if the underlying cipher is not + * initialised. + * @exception InvalidCipherTextException if cipher text decrypts wrongly (in + * case the exception will never Get thrown). + */ + public override int DoFinal( + byte[] output, + int outOff) + { + if (bufOff + outOff > output.Length) + { + throw new DataLengthException("output buffer too small in doFinal"); + } + + int blockSize = cipher.GetBlockSize(); + int length = 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]; + } + + IBlockCipher c = (cipher is CbcBlockCipher) + ? ((CbcBlockCipher)cipher).GetUnderlyingCipher() + : cipher; + + c.ProcessBlock(buf, blockSize, output, outOff); + + Array.Copy(block, 0, output, outOff + blockSize, length); + } + else + { + byte[] lastBlock = new byte[blockSize]; + + IBlockCipher c = (cipher is CbcBlockCipher) + ? ((CbcBlockCipher)cipher).GetUnderlyingCipher() + : cipher; + + c.ProcessBlock(buf, 0, block, 0); + + for (int i = blockSize; i != bufOff; i++) + { + lastBlock[i - blockSize] = (byte)(block[i - blockSize] ^ buf[i]); + } + + Array.Copy(buf, blockSize, block, 0, length); + + cipher.ProcessBlock(block, 0, output, outOff); + Array.Copy(lastBlock, 0, output, outOff + blockSize, length); + } + + int offset = bufOff; + + Reset(); + + return offset; + } + } +} diff --git a/Crypto/src/crypto/modes/EAXBlockCipher.cs b/Crypto/src/crypto/modes/EAXBlockCipher.cs new file mode 100644
index 000000000..b3016d79c --- /dev/null +++ b/Crypto/src/crypto/modes/EAXBlockCipher.cs
@@ -0,0 +1,302 @@ +using System; + +using Org.BouncyCastle.Crypto.Macs; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Modes +{ + /** + * 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 + : IAeadBlockCipher + { + private enum Tag : byte { N, H, C }; + + private SicBlockCipher cipher; + + private bool forEncryption; + + private int blockSize; + + private IMac mac; + + private byte[] nonceMac; + private byte[] associatedTextMac; + private byte[] macBlock; + + private int macSize; + private byte[] bufBlock; + private int bufOff; + + /** + * Constructor that accepts an instance of a block cipher engine. + * + * @param cipher the engine to use + */ + public EaxBlockCipher( + IBlockCipher 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 virtual string AlgorithmName + { + get { return cipher.GetUnderlyingCipher().AlgorithmName + "/EAX"; } + } + + public virtual int GetBlockSize() + { + return cipher.GetBlockSize(); + } + + public virtual void Init( + bool forEncryption, + ICipherParameters parameters) + { + this.forEncryption = forEncryption; + + byte[] nonce, associatedText; + ICipherParameters keyParam; + + if (parameters is AeadParameters) + { + AeadParameters param = (AeadParameters) parameters; + + nonce = param.GetNonce(); + associatedText = param.GetAssociatedText(); + macSize = param.MacSize / 8; + keyParam = param.Key; + } + else if (parameters is ParametersWithIV) + { + ParametersWithIV param = (ParametersWithIV) parameters; + + nonce = param.GetIV(); + associatedText = new byte[0]; + macSize = mac.GetMacSize() / 2; + keyParam = param.Parameters; + } + else + { + throw new ArgumentException("invalid parameters passed to EAX"); + } + + byte[] tag = new byte[blockSize]; + + mac.Init(keyParam); + tag[blockSize - 1] = (byte) Tag.H; + mac.BlockUpdate(tag, 0, blockSize); + mac.BlockUpdate(associatedText, 0, associatedText.Length); + mac.DoFinal(associatedTextMac, 0); + + tag[blockSize - 1] = (byte) Tag.N; + mac.BlockUpdate(tag, 0, blockSize); + mac.BlockUpdate(nonce, 0, nonce.Length); + mac.DoFinal(nonceMac, 0); + + tag[blockSize - 1] = (byte) Tag.C; + mac.BlockUpdate(tag, 0, blockSize); + + cipher.Init(true, new ParametersWithIV(keyParam, nonceMac)); + } + + 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 virtual void Reset() + { + Reset(true); + } + + private void Reset( + bool clearMac) + { + cipher.Reset(); + mac.Reset(); + + bufOff = 0; + Array.Clear(bufBlock, 0, bufBlock.Length); + + if (clearMac) + { + Array.Clear(macBlock, 0, macBlock.Length); + } + + byte[] tag = new byte[blockSize]; + tag[blockSize - 1] = (byte) Tag.C; + mac.BlockUpdate(tag, 0, blockSize); + } + + public virtual int ProcessByte( + byte input, + byte[] outBytes, + int outOff) + { + return process(input, outBytes, outOff); + } + + public virtual int ProcessBytes( + byte[] inBytes, + int inOff, + int len, + byte[] outBytes, + int outOff) + { + int resultLen = 0; + + for (int i = 0; i != len; i++) + { + resultLen += process(inBytes[inOff + i], outBytes, outOff + resultLen); + } + + return resultLen; + } + + public virtual int DoFinal( + byte[] outBytes, + int outOff) + { + 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); + + Array.Copy(tmp, 0, outBytes, outOff, extra); + + mac.BlockUpdate(tmp, 0, extra); + + calculateMac(); + + Array.Copy(macBlock, 0, outBytes, outOff + extra, macSize); + + Reset(false); + + return extra + macSize; + } + else + { + if (extra > macSize) + { + mac.BlockUpdate(bufBlock, 0, extra - macSize); + + cipher.ProcessBlock(bufBlock, 0, tmp, 0); + cipher.ProcessBlock(bufBlock, blockSize, tmp, blockSize); + + Array.Copy(tmp, 0, outBytes, outOff, extra - macSize); + } + + calculateMac(); + + if (!verifyMac(bufBlock, extra - macSize)) + throw new InvalidCipherTextException("mac check in EAX failed"); + + Reset(false); + + return extra - macSize; + } + } + + public virtual byte[] GetMac() + { + byte[] mac = new byte[macSize]; + + Array.Copy(macBlock, 0, mac, 0, macSize); + + return mac; + } + + public virtual int GetUpdateOutputSize( + int len) + { + return ((len + bufOff) / blockSize) * blockSize; + } + + public virtual int GetOutputSize( + int len) + { + if (forEncryption) + { + return len + bufOff + macSize; + } + + return len + bufOff - macSize; + } + + private int process( + byte b, + byte[] outBytes, + int outOff) + { + bufBlock[bufOff++] = b; + + if (bufOff == bufBlock.Length) + { + int size; + + if (forEncryption) + { + size = cipher.ProcessBlock(bufBlock, 0, outBytes, outOff); + + mac.BlockUpdate(outBytes, outOff, blockSize); + } + else + { + mac.BlockUpdate(bufBlock, 0, blockSize); + + size = cipher.ProcessBlock(bufBlock, 0, outBytes, outOff); + } + + bufOff = blockSize; + Array.Copy(bufBlock, blockSize, bufBlock, 0, blockSize); + + return size; + } + + return 0; + } + + private bool verifyMac(byte[] mac, int off) + { + for (int i = 0; i < macSize; i++) + { + if (macBlock[i] != mac[off + i]) + { + return false; + } + } + + return true; + } + } +} diff --git a/Crypto/src/crypto/modes/GCMBlockCipher.cs b/Crypto/src/crypto/modes/GCMBlockCipher.cs new file mode 100644
index 000000000..6a3a4463d --- /dev/null +++ b/Crypto/src/crypto/modes/GCMBlockCipher.cs
@@ -0,0 +1,400 @@ +using System; + +using Org.BouncyCastle.Crypto.Macs; +using Org.BouncyCastle.Crypto.Modes.Gcm; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Modes +{ + /// <summary> + /// Implements the Galois/Counter mode (GCM) detailed in + /// NIST Special Publication 800-38D. + /// </summary> + public class GcmBlockCipher + : IAeadBlockCipher + { + private const int BlockSize = 16; + private static readonly byte[] Zeroes = new byte[BlockSize]; + + private readonly IBlockCipher cipher; + private readonly IGcmMultiplier multiplier; + + // These fields are set by Init and not modified by processing + private bool forEncryption; + private int macSize; + private byte[] nonce; + private byte[] A; + private KeyParameter keyParam; + private byte[] H; + private byte[] initS; + private byte[] J0; + + // These fields are modified during processing + private byte[] bufBlock; + private byte[] macBlock; + private byte[] S; + private byte[] counter; + private int bufOff; + private ulong totalLength; + + public GcmBlockCipher( + IBlockCipher c) + : this(c, null) + { + } + + public GcmBlockCipher( + IBlockCipher c, + IGcmMultiplier m) + { + if (c.GetBlockSize() != BlockSize) + throw new ArgumentException("cipher required with a block size of " + BlockSize + "."); + + if (m == null) + { + // TODO Consider a static property specifying default multiplier + m = new Tables8kGcmMultiplier(); + } + + this.cipher = c; + this.multiplier = m; + } + + public virtual string AlgorithmName + { + get { return cipher.AlgorithmName + "/GCM"; } + } + + public virtual int GetBlockSize() + { + return BlockSize; + } + + public virtual void Init( + bool forEncryption, + ICipherParameters parameters) + { + this.forEncryption = forEncryption; + this.macBlock = null; + + if (parameters is AeadParameters) + { + AeadParameters param = (AeadParameters)parameters; + + nonce = param.GetNonce(); + A = param.GetAssociatedText(); + + int macSizeBits = param.MacSize; + if (macSizeBits < 96 || macSizeBits > 128 || macSizeBits % 8 != 0) + { + throw new ArgumentException("Invalid value for MAC size: " + macSizeBits); + } + + macSize = macSizeBits / 8; + keyParam = param.Key; + } + else if (parameters is ParametersWithIV) + { + ParametersWithIV param = (ParametersWithIV)parameters; + + nonce = param.GetIV(); + A = null; + macSize = 16; + keyParam = (KeyParameter)param.Parameters; + } + else + { + throw new ArgumentException("invalid parameters passed to GCM"); + } + + int bufLength = forEncryption ? BlockSize : (BlockSize + macSize); + this.bufBlock = new byte[bufLength]; + + if (nonce == null || nonce.Length < 1) + { + throw new ArgumentException("IV must be at least 1 byte"); + } + + if (A == null) + { + // Avoid lots of null checks + A = new byte[0]; + } + + // Cipher always used in forward mode + cipher.Init(true, keyParam); + + // TODO This should be configurable by Init parameters + // (but must be 16 if nonce length not 12) (BlockSize?) +// this.tagLength = 16; + + this.H = new byte[BlockSize]; + cipher.ProcessBlock(H, 0, H, 0); + multiplier.Init(H); + + this.initS = gHASH(A); + + if (nonce.Length == 12) + { + this.J0 = new byte[16]; + Array.Copy(nonce, 0, J0, 0, nonce.Length); + this.J0[15] = 0x01; + } + else + { + this.J0 = gHASH(nonce); + byte[] X = new byte[16]; + packLength((ulong)nonce.Length * 8UL, X, 8); + GcmUtilities.Xor(this.J0, X); + multiplier.MultiplyH(this.J0); + } + + this.S = Arrays.Clone(initS); + this.counter = Arrays.Clone(J0); + this.bufOff = 0; + this.totalLength = 0; + } + + public virtual byte[] GetMac() + { + return Arrays.Clone(macBlock); + } + + public virtual int GetOutputSize( + int len) + { + if (forEncryption) + { + return len + bufOff + macSize; + } + + return len + bufOff - macSize; + } + + public virtual int GetUpdateOutputSize( + int len) + { + return ((len + bufOff) / BlockSize) * BlockSize; + } + + public virtual int ProcessByte( + byte input, + byte[] output, + int outOff) + { + return Process(input, output, outOff); + } + + public virtual int ProcessBytes( + byte[] input, + int inOff, + int len, + byte[] output, + int outOff) + { + int resultLen = 0; + + for (int i = 0; i != len; i++) + { +// resultLen += Process(input[inOff + i], output, outOff + resultLen); + bufBlock[bufOff++] = input[inOff + i]; + + if (bufOff == bufBlock.Length) + { + gCTRBlock(bufBlock, BlockSize, output, outOff + resultLen); + if (!forEncryption) + { + Array.Copy(bufBlock, BlockSize, bufBlock, 0, macSize); + } +// bufOff = 0; + bufOff = bufBlock.Length - BlockSize; +// return bufBlock.Length; + resultLen += BlockSize; + } + } + + return resultLen; + } + + private int Process( + byte input, + byte[] output, + int outOff) + { + bufBlock[bufOff++] = input; + + if (bufOff == bufBlock.Length) + { + gCTRBlock(bufBlock, BlockSize, output, outOff); + if (!forEncryption) + { + Array.Copy(bufBlock, BlockSize, bufBlock, 0, macSize); + } + // bufOff = 0; + bufOff = bufBlock.Length - BlockSize; + // return bufBlock.Length; + return BlockSize; + } + + return 0; + } + + public int DoFinal(byte[] output, int outOff) + { + int extra = bufOff; + if (!forEncryption) + { + if (extra < macSize) + throw new InvalidCipherTextException("data too short"); + + extra -= macSize; + } + + if (extra > 0) + { + byte[] tmp = new byte[BlockSize]; + Array.Copy(bufBlock, 0, tmp, 0, extra); + gCTRBlock(tmp, extra, output, outOff); + } + + // Final gHASH + byte[] X = new byte[16]; + packLength((ulong)A.Length * 8UL, X, 0); + packLength(totalLength * 8UL, X, 8); + + GcmUtilities.Xor(S, X); + multiplier.MultiplyH(S); + + // TODO Fix this if tagLength becomes configurable + // T = MSBt(GCTRk(J0,S)) + byte[] tag = new byte[BlockSize]; + cipher.ProcessBlock(J0, 0, tag, 0); + GcmUtilities.Xor(tag, S); + + int resultLen = extra; + + // We place into macBlock our calculated value for T + this.macBlock = new byte[macSize]; + Array.Copy(tag, 0, macBlock, 0, macSize); + + if (forEncryption) + { + // Append T to the message + Array.Copy(macBlock, 0, output, outOff + bufOff, macSize); + resultLen += macSize; + } + else + { + // Retrieve the T value from the message and compare to calculated one + byte[] msgMac = new byte[macSize]; + Array.Copy(bufBlock, extra, msgMac, 0, macSize); + if (!Arrays.ConstantTimeAreEqual(this.macBlock, msgMac)) + throw new InvalidCipherTextException("mac check in GCM failed"); + } + + Reset(false); + + return resultLen; + } + + public virtual void Reset() + { + Reset(true); + } + + private void Reset( + bool clearMac) + { + S = Arrays.Clone(initS); + counter = Arrays.Clone(J0); + bufOff = 0; + totalLength = 0; + + if (bufBlock != null) + { + Array.Clear(bufBlock, 0, bufBlock.Length); + } + + if (clearMac) + { + macBlock = null; + } + + cipher.Reset(); + } + + private void gCTRBlock(byte[] buf, int bufCount, byte[] output, int outOff) + { +// inc(counter); + for (int i = 15; i >= 12; --i) + { + if (++counter[i] != 0) break; + } + + byte[] tmp = new byte[BlockSize]; + cipher.ProcessBlock(counter, 0, tmp, 0); + + byte[] hashBytes; + if (forEncryption) + { + Array.Copy(Zeroes, bufCount, tmp, bufCount, BlockSize - bufCount); + hashBytes = tmp; + } + else + { + hashBytes = buf; + } + + for (int i = bufCount - 1; i >= 0; --i) + { + tmp[i] ^= buf[i]; + output[outOff + i] = tmp[i]; + } + +// gHASHBlock(hashBytes); + GcmUtilities.Xor(S, hashBytes); + multiplier.MultiplyH(S); + + totalLength += (ulong)bufCount; + } + + private byte[] gHASH(byte[] b) + { + byte[] Y = new byte[16]; + + for (int pos = 0; pos < b.Length; pos += 16) + { + byte[] X = new byte[16]; + int num = System.Math.Min(b.Length - pos, 16); + Array.Copy(b, pos, X, 0, num); + GcmUtilities.Xor(Y, X); + multiplier.MultiplyH(Y); + } + + return Y; + } + +// private void gHASHBlock(byte[] block) +// { +// GcmUtilities.Xor(S, block); +// multiplier.MultiplyH(S); +// } + +// private static void inc(byte[] block) +// { +// for (int i = 15; i >= 12; --i) +// { +// if (++block[i] != 0) break; +// } +// } + + private static void packLength(ulong len, byte[] bs, int off) + { + Pack.UInt32_To_BE((uint)(len >> 32), bs, off); + Pack.UInt32_To_BE((uint)len, bs, off + 4); + } + } +} \ No newline at end of file diff --git a/Crypto/src/crypto/modes/GOFBBlockCipher.cs b/Crypto/src/crypto/modes/GOFBBlockCipher.cs new file mode 100644
index 000000000..7db843115 --- /dev/null +++ b/Crypto/src/crypto/modes/GOFBBlockCipher.cs
@@ -0,0 +1,223 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Modes +{ + /** + * implements the GOST 28147 OFB counter mode (GCTR). + */ + public class GOfbBlockCipher + : IBlockCipher + { + private byte[] IV; + private byte[] ofbV; + private byte[] ofbOutV; + + private readonly int blockSize; + private readonly IBlockCipher cipher; + + bool firstStep = true; + int N3; + int N4; + const int C1 = 16843012; //00000001000000010000000100000100 + const 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( + IBlockCipher cipher) + { + this.cipher = cipher; + this.blockSize = cipher.GetBlockSize(); + + if (blockSize != 8) + { + throw new ArgumentException("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 IBlockCipher 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 parameters the key and other data required by the cipher. + * @exception ArgumentException if the parameters argument is inappropriate. + */ + public void Init( + bool forEncryption, //ignored by this CTR mode + ICipherParameters parameters) + { + firstStep = true; + N3 = 0; + N4 = 0; + + if (parameters is ParametersWithIV) + { + ParametersWithIV ivParam = (ParametersWithIV)parameters; + byte[] iv = ivParam.GetIV(); + + if (iv.Length < IV.Length) + { + // prepend the supplied IV with zeros (per FIPS PUB 81) + Array.Copy(iv, 0, IV, IV.Length - iv.Length, iv.Length); + for (int i = 0; i < IV.Length - iv.Length; i++) + { + IV[i] = 0; + } + } + else + { + Array.Copy(iv, 0, IV, 0, IV.Length); + } + + parameters = ivParam.Parameters; + } + + Reset(); + + cipher.Init(true, parameters); + } + + /** + * return the algorithm name and mode. + * + * @return the name of the underlying algorithm followed by "/GCTR" + * and the block size in bits + */ + public string AlgorithmName + { + get { return cipher.AlgorithmName + "/GCTR"; } + } + + public bool IsPartialBlockOkay + { + get { return true; } + } + + /** + * 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 InvalidOperationException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if ((inOff + blockSize) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + blockSize) > output.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++) + { + output[outOff + i] = (byte)(ofbOutV[i] ^ input[inOff + i]); + } + + // + // change over the input block. + // + Array.Copy(ofbV, blockSize, ofbV, 0, ofbV.Length - blockSize); + Array.Copy(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() + { + Array.Copy(IV, 0, ofbV, 0, IV.Length); + + cipher.Reset(); + } + + //array of bytes to type int + private int bytesToint( + byte[] inBytes, + int inOff) + { + return (int)((inBytes[inOff + 3] << 24) & 0xff000000) + ((inBytes[inOff + 2] << 16) & 0xff0000) + + ((inBytes[inOff + 1] << 8) & 0xff00) + (inBytes[inOff] & 0xff); + } + + //int to array of bytes + private void intTobytes( + int num, + byte[] outBytes, + int outOff) + { + outBytes[outOff + 3] = (byte)(num >> 24); + outBytes[outOff + 2] = (byte)(num >> 16); + outBytes[outOff + 1] = (byte)(num >> 8); + outBytes[outOff] = (byte)num; + } + } +} diff --git a/Crypto/src/crypto/modes/IAeadBlockCipher.cs b/Crypto/src/crypto/modes/IAeadBlockCipher.cs new file mode 100644
index 000000000..ca7dab44c --- /dev/null +++ b/Crypto/src/crypto/modes/IAeadBlockCipher.cs
@@ -0,0 +1,90 @@ +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Modes +{ + /// <summary> + /// A block cipher mode that includes authenticated encryption with a streaming mode + /// and optional associated data.</summary> + /// <see cref="AeadParameters"/> + public interface IAeadBlockCipher + { + /// <summary>The name of the algorithm this cipher implements.</summary> + string AlgorithmName { get; } + + /// <summary>Initialise the cipher.</summary> + /// <remarks>Parameter can either be an AeadParameters or a ParametersWithIV object.</remarks> + /// <param name="forEncryption">Initialise for encryption if true, for decryption if false.</param> + /// <param name="parameters">The key or other data required by the cipher.</param> + void Init(bool forEncryption, ICipherParameters parameters); + + /// <returns>The block size for this cipher, in bytes.</returns> + int GetBlockSize(); + + /** + * Encrypt/decrypt a single byte. + * + * @param input the byte to be processed. + * @param outBytes 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. + */ + int ProcessByte(byte input, byte[] outBytes, int outOff); + + /** + * Process a block of bytes from in putting the result into out. + * + * @param inBytes 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 outBytes 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. + */ + int ProcessBytes(byte[] inBytes, int inOff, int len, byte[] outBytes, int outOff); + + /** + * Finish the operation either appending or verifying the MAC at the end of the data. + * + * @param outBytes 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 InvalidOperationException if the cipher is in an inappropriate state. + * @throws InvalidCipherTextException if the MAC fails to match. + */ + int DoFinal(byte[] outBytes, int outOff); + + /** + * Return the value of the MAC associated with the last stream processed. + * + * @return MAC for plaintext data. + */ + 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. + */ + 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. + */ + int GetOutputSize(int len); + + /// <summary> + /// Reset the cipher to the same state as it was after the last init (if there was one). + /// </summary> + void Reset(); + } +} diff --git a/Crypto/src/crypto/modes/OfbBlockCipher.cs b/Crypto/src/crypto/modes/OfbBlockCipher.cs new file mode 100644
index 000000000..9408a74d4 --- /dev/null +++ b/Crypto/src/crypto/modes/OfbBlockCipher.cs
@@ -0,0 +1,178 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Modes +{ + /** + * implements a Output-FeedBack (OFB) mode on top of a simple cipher. + */ + public class OfbBlockCipher + : IBlockCipher + { + private byte[] IV; + private byte[] ofbV; + private byte[] ofbOutV; + + private readonly int blockSize; + private readonly IBlockCipher 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( + IBlockCipher 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 IBlockCipher 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 forEncryption if true the cipher is initialised for + * encryption, if false for decryption. + * @param param the key and other data required by the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, //ignored by this OFB mode + ICipherParameters parameters) + { + if (parameters is ParametersWithIV) + { + ParametersWithIV ivParam = (ParametersWithIV)parameters; + byte[] iv = ivParam.GetIV(); + + if (iv.Length < IV.Length) + { + // prepend the supplied IV with zeros (per FIPS PUB 81) + Array.Copy(iv, 0, IV, IV.Length - iv.Length, iv.Length); + for (int i = 0; i < IV.Length - iv.Length; i++) + { + IV[i] = 0; + } + } + else + { + Array.Copy(iv, 0, IV, 0, IV.Length); + } + + parameters = ivParam.Parameters; + } + + Reset(); + + cipher.Init(true, parameters); + } + + /** + * return the algorithm name and mode. + * + * @return the name of the underlying algorithm followed by "/OFB" + * and the block size in bits + */ + public string AlgorithmName + { + get { return cipher.AlgorithmName + "/OFB" + (blockSize * 8); } + } + + public bool IsPartialBlockOkay + { + get { return true; } + } + + /** + * 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 InvalidOperationException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + if ((inOff + blockSize) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + blockSize) > output.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++) + { + output[outOff + i] = (byte)(ofbOutV[i] ^ input[inOff + i]); + } + + // + // change over the input block. + // + Array.Copy(ofbV, blockSize, ofbV, 0, ofbV.Length - blockSize); + Array.Copy(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() + { + Array.Copy(IV, 0, ofbV, 0, IV.Length); + + cipher.Reset(); + } + } + +} diff --git a/Crypto/src/crypto/modes/OpenPgpCfbBlockCipher.cs b/Crypto/src/crypto/modes/OpenPgpCfbBlockCipher.cs new file mode 100644
index 000000000..038ca783d --- /dev/null +++ b/Crypto/src/crypto/modes/OpenPgpCfbBlockCipher.cs
@@ -0,0 +1,337 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Modes +{ + /** + * 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>. + * </p> + */ + public class OpenPgpCfbBlockCipher + : IBlockCipher + { + private byte[] IV; + private byte[] FR; + private byte[] FRE; + + private readonly IBlockCipher cipher; + private readonly int blockSize; + + private int count; + private bool forEncryption; + + /** + * Basic constructor. + * + * @param cipher the block cipher to be used as the basis of the + * feedback mode. + */ + public OpenPgpCfbBlockCipher( + IBlockCipher 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 IBlockCipher 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 AlgorithmName + { + get { return cipher.AlgorithmName + "/OpenPGPCFB"; } + } + + public bool IsPartialBlockOkay + { + get { return true; } + } + + /** + * 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 InvalidOperationException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + return (forEncryption) ? EncryptBlock(input, inOff, output, outOff) : DecryptBlock(input, inOff, output, outOff); + } + + /** + * reset the chaining vector back to the IV and reset the underlying + * cipher. + */ + public void Reset() + { + count = 0; + + Array.Copy(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 parameters the key and other data required by the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public void Init( + bool forEncryption, + ICipherParameters parameters) + { + this.forEncryption = forEncryption; + + if (parameters is ParametersWithIV) + { + ParametersWithIV ivParam = (ParametersWithIV)parameters; + byte[] iv = ivParam.GetIV(); + + if (iv.Length < IV.Length) + { + // prepend the supplied IV with zeros (per FIPS PUB 81) + Array.Copy(iv, 0, IV, IV.Length - iv.Length, iv.Length); + for (int i = 0; i < IV.Length - iv.Length; i++) + { + IV[i] = 0; + } + } + else + { + Array.Copy(iv, 0, IV, 0, IV.Length); + } + + parameters = ivParam.Parameters; + } + + Reset(); + + cipher.Init(true, parameters); + } + + /** + * Encrypt one byte of data according to CFB mode. + * @param data the byte to encrypt + * @param blockOff offset in the current 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 InvalidOperationException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + private int EncryptBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + if ((inOff + blockSize) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + blockSize) > outBytes.Length) + { + throw new DataLengthException("output buffer too short"); + } + + if (count > blockSize) + { + FR[blockSize - 2] = outBytes[outOff] = EncryptByte(input[inOff], blockSize - 2); + FR[blockSize - 1] = outBytes[outOff + 1] = EncryptByte(input[inOff + 1], blockSize - 1); + + cipher.ProcessBlock(FR, 0, FRE, 0); + + for (int n = 2; n < blockSize; n++) + { + FR[n - 2] = outBytes[outOff + n] = EncryptByte(input[inOff + n], n - 2); + } + } + else if (count == 0) + { + cipher.ProcessBlock(FR, 0, FRE, 0); + + for (int n = 0; n < blockSize; n++) + { + FR[n] = outBytes[outOff + n] = EncryptByte(input[inOff + n], n); + } + + count += blockSize; + } + else if (count == blockSize) + { + cipher.ProcessBlock(FR, 0, FRE, 0); + + outBytes[outOff] = EncryptByte(input[inOff], 0); + outBytes[outOff + 1] = EncryptByte(input[inOff + 1], 1); + + // + // do reset + // + Array.Copy(FR, 2, FR, 0, blockSize - 2); + Array.Copy(outBytes, outOff, FR, blockSize - 2, 2); + + cipher.ProcessBlock(FR, 0, FRE, 0); + + for (int n = 2; n < blockSize; n++) + { + FR[n - 2] = outBytes[outOff + n] = EncryptByte(input[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 InvalidOperationException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + private int DecryptBlock( + byte[] input, + int inOff, + byte[] outBytes, + int outOff) + { + if ((inOff + blockSize) > input.Length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + blockSize) > outBytes.Length) + { + throw new DataLengthException("output buffer too short"); + } + + if (count > blockSize) + { + byte inVal = input[inOff]; + FR[blockSize - 2] = inVal; + outBytes[outOff] = EncryptByte(inVal, blockSize - 2); + + inVal = input[inOff + 1]; + FR[blockSize - 1] = inVal; + outBytes[outOff + 1] = EncryptByte(inVal, blockSize - 1); + + cipher.ProcessBlock(FR, 0, FRE, 0); + + for (int n = 2; n < blockSize; n++) + { + inVal = input[inOff + n]; + FR[n - 2] = inVal; + outBytes[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] = input[inOff + n]; + outBytes[n] = EncryptByte(input[inOff + n], n); + } + + count += blockSize; + } + else if (count == blockSize) + { + cipher.ProcessBlock(FR, 0, FRE, 0); + + byte inVal1 = input[inOff]; + byte inVal2 = input[inOff + 1]; + outBytes[outOff ] = EncryptByte(inVal1, 0); + outBytes[outOff + 1] = EncryptByte(inVal2, 1); + + Array.Copy(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 = input[inOff + n]; + FR[n - 2] = inVal; + outBytes[outOff + n] = EncryptByte(inVal, n - 2); + } + + count += blockSize; + } + + return blockSize; + } + } +} diff --git a/Crypto/src/crypto/modes/SicBlockCipher.cs b/Crypto/src/crypto/modes/SicBlockCipher.cs new file mode 100644
index 000000000..c45026e82 --- /dev/null +++ b/Crypto/src/crypto/modes/SicBlockCipher.cs
@@ -0,0 +1,110 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Modes +{ + /** + * Implements the Segmented Integer Counter (SIC) mode on top of a simple + * block cipher. + */ + public class SicBlockCipher + : IBlockCipher + { + private readonly IBlockCipher cipher; + private readonly int blockSize; + private readonly byte[] IV; + private readonly byte[] counter; + private readonly byte[] counterOut; + + /** + * Basic constructor. + * + * @param c the block cipher to be used. + */ + public SicBlockCipher(IBlockCipher cipher) + { + this.cipher = cipher; + 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 IBlockCipher GetUnderlyingCipher() + { + return cipher; + } + + public void Init( + bool forEncryption, //ignored by this CTR mode + ICipherParameters parameters) + { + if (parameters is ParametersWithIV) + { + ParametersWithIV ivParam = (ParametersWithIV) parameters; + byte[] iv = ivParam.GetIV(); + Array.Copy(iv, 0, IV, 0, IV.Length); + + Reset(); + cipher.Init(true, ivParam.Parameters); + } + else + { + throw new ArgumentException("SIC mode requires ParametersWithIV", "parameters"); + } + } + + public string AlgorithmName + { + get { return cipher.AlgorithmName + "/SIC"; } + } + + public bool IsPartialBlockOkay + { + get { return true; } + } + + public int GetBlockSize() + { + return cipher.GetBlockSize(); + } + + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + cipher.ProcessBlock(counter, 0, counterOut, 0); + + // + // XOR the counterOut with the plaintext producing the cipher text + // + for (int i = 0; i < counterOut.Length; i++) + { + output[outOff + i] = (byte)(counterOut[i] ^ input[inOff + i]); + } + + // Increment the counter + int j = counter.Length; + while (--j >= 0 && ++counter[j] == 0) + { + } + + return counter.Length; + } + + public void Reset() + { + Array.Copy(IV, 0, counter, 0, counter.Length); + cipher.Reset(); + } + } +} diff --git a/Crypto/src/crypto/modes/gcm/BasicGcmExponentiator.cs b/Crypto/src/crypto/modes/gcm/BasicGcmExponentiator.cs new file mode 100644
index 000000000..98049e1db --- /dev/null +++ b/Crypto/src/crypto/modes/gcm/BasicGcmExponentiator.cs
@@ -0,0 +1,40 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Modes.Gcm +{ + public class BasicGcmExponentiator + : IGcmExponentiator + { + 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 = GcmUtilities.OneAsBytes(); + + if (pow > 0) + { + byte[] powX = Arrays.Clone(x); + do + { + if ((pow & 1L) != 0) + { + GcmUtilities.Multiply(y, powX); + } + GcmUtilities.Multiply(powX, powX); + pow >>= 1; + } + while (pow > 0); + } + + Array.Copy(y, 0, output, 0, 16); + } + } +} diff --git a/Crypto/src/crypto/modes/gcm/BasicGcmMultiplier.cs b/Crypto/src/crypto/modes/gcm/BasicGcmMultiplier.cs new file mode 100644
index 000000000..4076de990 --- /dev/null +++ b/Crypto/src/crypto/modes/gcm/BasicGcmMultiplier.cs
@@ -0,0 +1,20 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Modes.Gcm +{ + public class BasicGcmMultiplier + : IGcmMultiplier + { + private byte[] H; + + public void Init(byte[] H) + { + this.H = (byte[])H.Clone(); + } + + public void MultiplyH(byte[] x) + { + GcmUtilities.Multiply(x, H); + } + } +} diff --git a/Crypto/src/crypto/modes/gcm/GcmUtilities.cs b/Crypto/src/crypto/modes/gcm/GcmUtilities.cs new file mode 100644
index 000000000..8da125641 --- /dev/null +++ b/Crypto/src/crypto/modes/gcm/GcmUtilities.cs
@@ -0,0 +1,149 @@ +using System; + +using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Modes.Gcm +{ + internal abstract class GcmUtilities + { + internal static byte[] OneAsBytes() + { + byte[] tmp = new byte[16]; + tmp[0] = 0x80; + return tmp; + } + + internal static uint[] OneAsUints() + { + uint[] tmp = new uint[4]; + tmp[0] = 0x80000000; + return tmp; + } + + internal static uint[] AsUints(byte[] bs) + { + uint[] us = new uint[4]; + us[0] = Pack.BE_To_UInt32(bs, 0); + us[1] = Pack.BE_To_UInt32(bs, 4); + us[2] = Pack.BE_To_UInt32(bs, 8); + us[3] = Pack.BE_To_UInt32(bs, 12); + return us; + } + + internal 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); + } + + bool lsb = (tmp[15] & 1) != 0; + ShiftRight(tmp); + if (lsb) + { + // R = new byte[]{ 0xe1, ... }; + //GCMUtilities.Xor(tmp, R); + tmp[0] ^= (byte)0xe1; + } + } + } + + Array.Copy(c, 0, block, 0, 16); + } + + // P is the value with only bit i=1 set + internal static void MultiplyP(uint[] x) + { + bool lsb = (x[3] & 1) != 0; + ShiftRight(x); + if (lsb) + { + // R = new uint[]{ 0xe1000000, 0, 0, 0 }; + //Xor(v, R); + x[0] ^= 0xe1000000; + } + } + + internal static void MultiplyP8(uint[] x) + { +// for (int i = 8; i != 0; --i) +// { +// MultiplyP(x); +// } + + uint lsw = x[3]; + ShiftRightN(x, 8); + for (int i = 7; i >= 0; --i) + { + if ((lsw & (1 << i)) != 0) + { + x[0] ^= (0xe1000000 >> (7 - i)); + } + } + } + + internal static void ShiftRight(byte[] block) + { + int i = 0; + byte bit = 0; + for (;;) + { + byte b = block[i]; + block[i] = (byte)((b >> 1) | bit); + if (++i == 16) break; + bit = (byte)(b << 7); + } + } + + internal static void ShiftRight(uint[] block) + { + int i = 0; + uint bit = 0; + for (;;) + { + uint b = block[i]; + block[i] = (b >> 1) | bit; + if (++i == 4) break; + bit = b << 31; + } + } + + internal static void ShiftRightN(uint[] block, int n) + { + int i = 0; + uint bit = 0; + for (;;) + { + uint b = block[i]; + block[i] = (b >> n) | bit; + if (++i == 4) break; + bit = b << (32 - n); + } + } + + internal static void Xor(byte[] block, byte[] val) + { + for (int i = 15; i >= 0; --i) + { + block[i] ^= val[i]; + } + } + + internal static void Xor(uint[] block, uint[] val) + { + for (int i = 3; i >= 0; --i) + { + block[i] ^= val[i]; + } + } + } +} diff --git a/Crypto/src/crypto/modes/gcm/IGcmExponentiator.cs b/Crypto/src/crypto/modes/gcm/IGcmExponentiator.cs new file mode 100644
index 000000000..5b4ce9d7a --- /dev/null +++ b/Crypto/src/crypto/modes/gcm/IGcmExponentiator.cs
@@ -0,0 +1,10 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Modes.Gcm +{ + public interface IGcmExponentiator + { + void Init(byte[] x); + void ExponentiateX(long pow, byte[] output); + } +} diff --git a/Crypto/src/crypto/modes/gcm/IGcmMultiplier.cs b/Crypto/src/crypto/modes/gcm/IGcmMultiplier.cs new file mode 100644
index 000000000..ec7b906ee --- /dev/null +++ b/Crypto/src/crypto/modes/gcm/IGcmMultiplier.cs
@@ -0,0 +1,10 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Modes.Gcm +{ + public interface IGcmMultiplier + { + void Init(byte[] H); + void MultiplyH(byte[] x); + } +} diff --git a/Crypto/src/crypto/modes/gcm/Tables1kGcmExponentiator.cs b/Crypto/src/crypto/modes/gcm/Tables1kGcmExponentiator.cs new file mode 100644
index 000000000..9425a3d9d --- /dev/null +++ b/Crypto/src/crypto/modes/gcm/Tables1kGcmExponentiator.cs
@@ -0,0 +1,44 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Modes.Gcm +{ + public class Tables1kGcmExponentiator + : IGcmExponentiator + { + // A lookup table of the power-of-two powers of 'x' + private byte[][] lookupPowX2 = new byte[64][]; + + public void Init(byte[] x) + { + lookupPowX2[0] = GcmUtilities.OneAsBytes(); + lookupPowX2[1] = Arrays.Clone(x); + + for (int i = 2; i != 64; ++i) + { + byte[] tmp = Arrays.Clone(lookupPowX2[i - 1]); + GcmUtilities.Multiply(tmp, tmp); + lookupPowX2[i] = tmp; + } + } + + public void ExponentiateX(long pow, byte[] output) + { + byte[] y = GcmUtilities.OneAsBytes(); + int powX2 = 1; + + while (pow > 0) + { + if ((pow & 1L) != 0) + { + GcmUtilities.Multiply(y, lookupPowX2[powX2]); + } + ++powX2; + pow >>= 1; + } + + Array.Copy(y, 0, output, 0, 16); + } + } +} diff --git a/Crypto/src/crypto/modes/gcm/Tables64kGcmMultiplier.cs b/Crypto/src/crypto/modes/gcm/Tables64kGcmMultiplier.cs new file mode 100644
index 000000000..f089dfe8d --- /dev/null +++ b/Crypto/src/crypto/modes/gcm/Tables64kGcmMultiplier.cs
@@ -0,0 +1,64 @@ +using System; + +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Modes.Gcm +{ + public class Tables64kGcmMultiplier + : IGcmMultiplier + { + private readonly uint[][][] M = new uint[16][][]; + + public void Init(byte[] H) + { + M[0] = new uint[256][]; + M[0][0] = new uint[4]; + M[0][128] = GcmUtilities.AsUints(H); + for (int j = 64; j >= 1; j >>= 1) + { + uint[] tmp = (uint[])M[0][j + j].Clone(); + GcmUtilities.MultiplyP(tmp); + M[0][j] = tmp; + } + for (int i = 0;;) + { + for (int j = 2; j < 256; j += j) + { + for (int k = 1; k < j; ++k) + { + uint[] tmp = (uint[])M[i][j].Clone(); + GcmUtilities.Xor(tmp, M[i][k]); + M[i][j + k] = tmp; + } + } + + if (++i == 16) return; + + M[i] = new uint[256][]; + M[i][0] = new uint[4]; + for (int j = 128; j > 0; j >>= 1) + { + uint[] tmp = (uint[])M[i - 1][j].Clone(); + GcmUtilities.MultiplyP8(tmp); + M[i][j] = tmp; + } + } + } + + public void MultiplyH(byte[] x) + { + uint[] z = new uint[4]; + for (int i = 0; i != 16; ++i) + { + //GcmUtilities.Xor(z, M[i][x[i]]); + uint[] m = M[i][x[i]]; + z[0] ^= m[0]; + z[1] ^= m[1]; + z[2] ^= m[2]; + z[3] ^= m[3]; + } + + Pack.UInt32_To_BE(z, x, 0); + } + } +} diff --git a/Crypto/src/crypto/modes/gcm/Tables8kGcmMultiplier.cs b/Crypto/src/crypto/modes/gcm/Tables8kGcmMultiplier.cs new file mode 100644
index 000000000..91d58fab8 --- /dev/null +++ b/Crypto/src/crypto/modes/gcm/Tables8kGcmMultiplier.cs
@@ -0,0 +1,90 @@ +using System; + +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Modes.Gcm +{ + public class Tables8kGcmMultiplier + : IGcmMultiplier + { + private readonly uint[][][] M = new uint[32][][]; + + public void Init(byte[] H) + { + M[0] = new uint[16][]; + M[1] = new uint[16][]; + M[0][0] = new uint[4]; + M[1][0] = new uint[4]; + M[1][8] = GcmUtilities.AsUints(H); + + for (int j = 4; j >= 1; j >>= 1) + { + uint[] tmp = (uint[])M[1][j + j].Clone(); + GcmUtilities.MultiplyP(tmp); + M[1][j] = tmp; + } + + { + uint[] tmp = (uint[])M[1][1].Clone(); + GcmUtilities.MultiplyP(tmp); + M[0][8] = tmp; + } + + for (int j = 4; j >= 1; j >>= 1) + { + uint[] tmp = (uint[])M[0][j + j].Clone(); + GcmUtilities.MultiplyP(tmp); + M[0][j] = tmp; + } + + for (int i = 0;;) + { + for (int j = 2; j < 16; j += j) + { + for (int k = 1; k < j; ++k) + { + uint[] tmp = (uint[])M[i][j].Clone(); + GcmUtilities.Xor(tmp, M[i][k]); + M[i][j + k] = tmp; + } + } + + if (++i == 32) return; + + if (i > 1) + { + M[i] = new uint[16][]; + M[i][0] = new uint[4]; + for(int j = 8; j > 0; j >>= 1) + { + uint[] tmp = (uint[])M[i - 2][j].Clone(); + GcmUtilities.MultiplyP8(tmp); + M[i][j] = tmp; + } + } + } + } + + public void MultiplyH(byte[] x) + { + uint[] z = new uint[4]; + for (int i = 15; i >= 0; --i) + { + //GcmUtilities.Xor(z, M[i + i][x[i] & 0x0f]); + uint[] m = M[i + i][x[i] & 0x0f]; + z[0] ^= m[0]; + z[1] ^= m[1]; + z[2] ^= m[2]; + z[3] ^= m[3]; + //GcmUtilities.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.UInt32_To_BE(z, x, 0); + } + } +} diff --git a/Crypto/src/crypto/paddings/BlockCipherPadding.cs b/Crypto/src/crypto/paddings/BlockCipherPadding.cs new file mode 100644
index 000000000..33a5f9f0f --- /dev/null +++ b/Crypto/src/crypto/paddings/BlockCipherPadding.cs
@@ -0,0 +1,43 @@ +using System; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; + + +namespace Org.BouncyCastle.Crypto.Paddings +{ + /** + * Block cipher padders are expected to conform to this interface + */ + public interface IBlockCipherPadding + { + /** + * Initialise the padder. + * + * @param param parameters, if any required. + */ + void Init(SecureRandom random); + //throws ArgumentException; + + /** + * Return the name of the algorithm the cipher implements. + * + * @return the name of the algorithm the cipher implements. + */ + string PaddingName { get; } + + /** + * add the pad bytes to the passed in block, returning the + * number of bytes added. + */ + int AddPadding(byte[] input, int inOff); + + /** + * return the number of pad bytes present in the block. + * @exception InvalidCipherTextException if the padding is badly formed + * or invalid. + */ + int PadCount(byte[] input); + //throws InvalidCipherTextException; + } + +} diff --git a/Crypto/src/crypto/paddings/ISO10126d2Padding.cs b/Crypto/src/crypto/paddings/ISO10126d2Padding.cs new file mode 100644
index 000000000..e132a62dd --- /dev/null +++ b/Crypto/src/crypto/paddings/ISO10126d2Padding.cs
@@ -0,0 +1,76 @@ +using System; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; + + +namespace Org.BouncyCastle.Crypto.Paddings +{ + + /** + * A padder that adds ISO10126-2 padding to a block. + */ + public class ISO10126d2Padding: IBlockCipherPadding + { + private SecureRandom random; + + /** + * Initialise the padder. + * + * @param random a SecureRandom if available. + */ + public void Init( + SecureRandom random) + //throws ArgumentException + { + this.random = (random != null) ? random : new SecureRandom(); + } + + /** + * Return the name of the algorithm the cipher implements. + * + * @return the name of the algorithm the cipher implements. + */ + public string PaddingName + { + get { return "ISO10126-2"; } + } + + /** + * add the pad bytes to the passed in block, returning the + * number of bytes added. + */ + public int AddPadding( + byte[] input, + int inOff) + { + byte code = (byte)(input.Length - inOff); + + while (inOff < (input.Length - 1)) + { + input[inOff] = (byte)random.NextInt(); + inOff++; + } + + input[inOff] = code; + + return code; + } + + /** + * return the number of pad bytes present in the block. + */ + public int PadCount(byte[] input) + //throws InvalidCipherTextException + { + int count = input[input.Length - 1] & 0xff; + + if (count > input.Length) + { + throw new InvalidCipherTextException("pad block corrupted"); + } + + return count; + } + } + +} diff --git a/Crypto/src/crypto/paddings/ISO7816d4Padding.cs b/Crypto/src/crypto/paddings/ISO7816d4Padding.cs new file mode 100644
index 000000000..016b25a81 --- /dev/null +++ b/Crypto/src/crypto/paddings/ISO7816d4Padding.cs
@@ -0,0 +1,79 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Paddings +{ + /** + * 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 + : IBlockCipherPadding + { + /** + * Initialise the padder. + * + * @param random - a SecureRandom if available. + */ + public void Init( + SecureRandom random) + { + // nothing to do. + } + + /** + * Return the name of the algorithm the padder implements. + * + * @return the name of the algorithm the padder implements. + */ + public string PaddingName + { + get { return "ISO7816-4"; } + } + + /** + * add the pad bytes to the passed in block, returning the + * number of bytes added. + */ + public int AddPadding( + byte[] input, + int inOff) + { + int added = (input.Length - inOff); + + input[inOff]= (byte) 0x80; + inOff ++; + + while (inOff < input.Length) + { + input[inOff] = (byte) 0; + inOff++; + } + + return added; + } + + /** + * return the number of pad bytes present in the block. + */ + public int PadCount( + byte[] input) + { + int count = input.Length - 1; + + while (count > 0 && input[count] == 0) + { + count--; + } + + if (input[count] != (byte)0x80) + { + throw new InvalidCipherTextException("pad block corrupted"); + } + + return input.Length - count; + } + } +} diff --git a/Crypto/src/crypto/paddings/PaddedBufferedBlockCipher.cs b/Crypto/src/crypto/paddings/PaddedBufferedBlockCipher.cs new file mode 100644
index 000000000..fb8a92ba3 --- /dev/null +++ b/Crypto/src/crypto/paddings/PaddedBufferedBlockCipher.cs
@@ -0,0 +1,288 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Paddings +{ + /** + * 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 + : BufferedBlockCipher + { + private readonly IBlockCipherPadding 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( + IBlockCipher cipher, + IBlockCipherPadding 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( + IBlockCipher cipher) + : this(cipher, new Pkcs7Padding()) { } + + /** + * 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. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public override void Init( + bool forEncryption, + ICipherParameters parameters) + { + this.forEncryption = forEncryption; + + SecureRandom initRandom = null; + if (parameters is ParametersWithRandom) + { + ParametersWithRandom p = (ParametersWithRandom)parameters; + initRandom = p.Random; + parameters = p.Parameters; + } + + Reset(); + padding.Init(initRandom); + cipher.Init(forEncryption, parameters); + } + + /** + * 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 override int GetOutputSize( + int length) + { + int total = length + 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 override int GetUpdateOutputSize( + int length) + { + int total = length + 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 InvalidOperationException if the cipher isn't initialised. + */ + public override int ProcessByte( + byte input, + byte[] output, + int outOff) + { + int resultLen = 0; + + if (bufOff == buf.Length) + { + resultLen = cipher.ProcessBlock(buf, 0, output, outOff); + bufOff = 0; + } + + buf[bufOff++] = input; + + 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 InvalidOperationException if the cipher isn't initialised. + */ + public override int ProcessBytes( + byte[] input, + int inOff, + int length, + byte[] output, + int outOff) + { + if (length < 0) + { + throw new ArgumentException("Can't have a negative input length!"); + } + + int blockSize = GetBlockSize(); + int outLength = GetUpdateOutputSize(length); + + if (outLength > 0) + { + if ((outOff + outLength) > output.Length) + { + throw new DataLengthException("output buffer too short"); + } + } + + int resultLen = 0; + int gapLen = buf.Length - bufOff; + + if (length > gapLen) + { + Array.Copy(input, inOff, buf, bufOff, gapLen); + + resultLen += cipher.ProcessBlock(buf, 0, output, outOff); + + bufOff = 0; + length -= gapLen; + inOff += gapLen; + + while (length > buf.Length) + { + resultLen += cipher.ProcessBlock(input, inOff, output, outOff + resultLen); + + length -= blockSize; + inOff += blockSize; + } + } + + Array.Copy(input, inOff, buf, bufOff, length); + + bufOff += length; + + 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 InvalidOperationException if the underlying cipher is not + * initialised. + * @exception InvalidCipherTextException if padding is expected and not found. + */ + public override int DoFinal( + byte[] output, + int outOff) + { + int blockSize = cipher.GetBlockSize(); + int resultLen = 0; + + if (forEncryption) + { + if (bufOff == blockSize) + { + if ((outOff + 2 * blockSize) > output.Length) + { + Reset(); + + throw new DataLengthException("output buffer too short"); + } + + resultLen = cipher.ProcessBlock(buf, 0, output, outOff); + bufOff = 0; + } + + padding.AddPadding(buf, bufOff); + + resultLen += cipher.ProcessBlock(buf, 0, output, 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); + + Array.Copy(buf, 0, output, outOff, resultLen); + } + finally + { + Reset(); + } + } + + return resultLen; + } + } + +} diff --git a/Crypto/src/crypto/paddings/Pkcs7Padding.cs b/Crypto/src/crypto/paddings/Pkcs7Padding.cs new file mode 100644
index 000000000..f3166fd96 --- /dev/null +++ b/Crypto/src/crypto/paddings/Pkcs7Padding.cs
@@ -0,0 +1,79 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Paddings +{ + /** + * A padder that adds Pkcs7/Pkcs5 padding to a block. + */ + public class Pkcs7Padding + : IBlockCipherPadding + { + /** + * Initialise the padder. + * + * @param random - a SecureRandom if available. + */ + public void Init( + SecureRandom random) + { + // nothing to do. + } + + /** + * Return the name of the algorithm the cipher implements. + * + * @return the name of the algorithm the cipher implements. + */ + public string PaddingName + { + get { return "PKCS7"; } + } + + /** + * add the pad bytes to the passed in block, returning the + * number of bytes added. + */ + public int AddPadding( + byte[] input, + int inOff) + { + byte code = (byte)(input.Length - inOff); + + while (inOff < input.Length) + { + input[inOff] = code; + inOff++; + } + + return code; + } + + /** + * return the number of pad bytes present in the block. + */ + public int PadCount( + byte[] input) + { + int count = (int) input[input.Length - 1]; + + if (count < 1 || count > input.Length) + { + throw new InvalidCipherTextException("pad block corrupted"); + } + + for (int i = 1; i <= count; i++) + { + if (input[input.Length - i] != count) + { + throw new InvalidCipherTextException("pad block corrupted"); + } + } + + return count; + } + } + +} diff --git a/Crypto/src/crypto/paddings/TbcPadding.cs b/Crypto/src/crypto/paddings/TbcPadding.cs new file mode 100644
index 000000000..74b64e8e1 --- /dev/null +++ b/Crypto/src/crypto/paddings/TbcPadding.cs
@@ -0,0 +1,79 @@ +using System; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Paddings +{ + + /// <summary> A padder that adds Trailing-Bit-Compliment padding to a block. + /// <p> + /// This padding pads the block out compliment of the last bit + /// of the plain text. + /// </p> + /// </summary> + public class TbcPadding + : IBlockCipherPadding + { + /// <summary> Return the name of the algorithm the cipher implements.</summary> + /// <returns> the name of the algorithm the cipher implements. + /// </returns> + public string PaddingName + { + get { return "TBC"; } + } + + /// <summary> Initialise the padder.</summary> + /// <param name="random">- a SecureRandom if available. + /// </param> + public virtual void Init(SecureRandom random) + { + // nothing to do. + } + + /// <summary> 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> + /// </summary> + public virtual int AddPadding(byte[] input, int inOff) + { + int count = input.Length - inOff; + byte code; + + if (inOff > 0) + { + code = (byte)((input[inOff - 1] & 0x01) == 0?0xff:0x00); + } + else + { + code = (byte)((input[input.Length - 1] & 0x01) == 0?0xff:0x00); + } + + while (inOff < input.Length) + { + input[inOff] = code; + inOff++; + } + + return count; + } + + /// <summary> return the number of pad bytes present in the block.</summary> + public virtual int PadCount(byte[] input) + { + byte code = input[input.Length - 1]; + + int index = input.Length - 1; + while (index > 0 && input[index - 1] == code) + { + index--; + } + + return input.Length - index; + } + } +} diff --git a/Crypto/src/crypto/paddings/X923Padding.cs b/Crypto/src/crypto/paddings/X923Padding.cs new file mode 100644
index 000000000..cc1b52b3e --- /dev/null +++ b/Crypto/src/crypto/paddings/X923Padding.cs
@@ -0,0 +1,82 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Paddings +{ + /** + * 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 + : IBlockCipherPadding + { + private SecureRandom random; + + /** + * Initialise the padder. + * + * @param random a SecureRandom if one is available. + */ + public void Init( + SecureRandom random) + { + this.random = random; + } + + /** + * Return the name of the algorithm the cipher implements. + * + * @return the name of the algorithm the cipher implements. + */ + public string PaddingName + { + get { return "X9.23"; } + } + + /** + * add the pad bytes to the passed in block, returning the + * number of bytes added. + */ + public int AddPadding( + byte[] input, + int inOff) + { + byte code = (byte)(input.Length - inOff); + + while (inOff < input.Length - 1) + { + if (random == null) + { + input[inOff] = 0; + } + else + { + input[inOff] = (byte)random.NextInt(); + } + inOff++; + } + + input[inOff] = code; + + return code; + } + + /** + * return the number of pad bytes present in the block. + */ + public int PadCount( + byte[] input) + { + int count = input[input.Length - 1] & 0xff; + + if (count > input.Length) + { + throw new InvalidCipherTextException("pad block corrupted"); + } + + return count; + } + } +} diff --git a/Crypto/src/crypto/paddings/ZeroBytePadding.cs b/Crypto/src/crypto/paddings/ZeroBytePadding.cs new file mode 100644
index 000000000..0d55ca4c2 --- /dev/null +++ b/Crypto/src/crypto/paddings/ZeroBytePadding.cs
@@ -0,0 +1,68 @@ +using System; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Paddings +{ + + /// <summary> A padder that adds Null byte padding to a block.</summary> + public class ZeroBytePadding : IBlockCipherPadding + { + /// <summary> Return the name of the algorithm the cipher implements. + /// + /// </summary> + /// <returns> the name of the algorithm the cipher implements. + /// </returns> + public string PaddingName + { + get { return "ZeroBytePadding"; } + } + + /// <summary> Initialise the padder. + /// + /// </summary> + /// <param name="random">- a SecureRandom if available. + /// </param> + public void Init(SecureRandom random) + { + // nothing to do. + } + + /// <summary> add the pad bytes to the passed in block, returning the + /// number of bytes added. + /// </summary> + public int AddPadding( + byte[] input, + int inOff) + { + int added = (input.Length - inOff); + + while (inOff < input.Length) + { + input[inOff] = (byte) 0; + inOff++; + } + + return added; + } + + /// <summary> return the number of pad bytes present in the block.</summary> + public int PadCount( + byte[] input) + { + int count = input.Length; + + while (count > 0) + { + if (input[count - 1] != 0) + { + break; + } + + count--; + } + + return input.Length - count; + } + } +} diff --git a/Crypto/src/crypto/parameters/AEADParameters.cs b/Crypto/src/crypto/parameters/AEADParameters.cs new file mode 100644
index 000000000..06b2f5c38 --- /dev/null +++ b/Crypto/src/crypto/parameters/AEADParameters.cs
@@ -0,0 +1,53 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class AeadParameters + : ICipherParameters + { + private readonly byte[] associatedText; + private readonly byte[] nonce; + private readonly KeyParameter key; + private readonly int macSize; + + /** + * 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 AeadParameters( + KeyParameter key, + int macSize, + byte[] nonce, + byte[] associatedText) + { + this.key = key; + this.nonce = nonce; + this.macSize = macSize; + this.associatedText = associatedText; + } + + public virtual KeyParameter Key + { + get { return key; } + } + + public virtual int MacSize + { + get { return macSize; } + } + + public virtual byte[] GetAssociatedText() + { + return associatedText; + } + + public virtual byte[] GetNonce() + { + return nonce; + } + } +} diff --git a/Crypto/src/crypto/parameters/CcmParameters.cs b/Crypto/src/crypto/parameters/CcmParameters.cs new file mode 100644
index 000000000..8dc981e1f --- /dev/null +++ b/Crypto/src/crypto/parameters/CcmParameters.cs
@@ -0,0 +1,25 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class CcmParameters + : 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) + : base(key, macSize, nonce, associatedText) + { + } + } +} diff --git a/Crypto/src/crypto/parameters/DHKeyGenerationParameters.cs b/Crypto/src/crypto/parameters/DHKeyGenerationParameters.cs new file mode 100644
index 000000000..ab3e18f09 --- /dev/null +++ b/Crypto/src/crypto/parameters/DHKeyGenerationParameters.cs
@@ -0,0 +1,31 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class DHKeyGenerationParameters + : KeyGenerationParameters + { + private readonly DHParameters parameters; + + public DHKeyGenerationParameters( + SecureRandom random, + DHParameters parameters) + : base(random, GetStrength(parameters)) + { + this.parameters = parameters; + } + + public DHParameters Parameters + { + get { return parameters; } + } + + internal static int GetStrength( + DHParameters parameters) + { + return parameters.L != 0 ? parameters.L : parameters.P.BitLength; + } + } +} diff --git a/Crypto/src/crypto/parameters/DHKeyParameters.cs b/Crypto/src/crypto/parameters/DHKeyParameters.cs new file mode 100644
index 000000000..1a5c1386f --- /dev/null +++ b/Crypto/src/crypto/parameters/DHKeyParameters.cs
@@ -0,0 +1,76 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class DHKeyParameters + : AsymmetricKeyParameter + { + private readonly DHParameters parameters; + private readonly DerObjectIdentifier algorithmOid; + + protected DHKeyParameters( + bool isPrivate, + DHParameters parameters) + : this(isPrivate, parameters, PkcsObjectIdentifiers.DhKeyAgreement) + { + } + + protected DHKeyParameters( + bool isPrivate, + DHParameters parameters, + DerObjectIdentifier algorithmOid) + : base(isPrivate) + { + // TODO Should we allow parameters to be null? + this.parameters = parameters; + this.algorithmOid = algorithmOid; + } + + public DHParameters Parameters + { + get { return parameters; } + } + + public DerObjectIdentifier AlgorithmOid + { + get { return algorithmOid; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + DHKeyParameters other = obj as DHKeyParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + DHKeyParameters other) + { + return Platform.Equals(parameters, other.parameters) + && base.Equals(other); + } + + public override int GetHashCode() + { + int hc = base.GetHashCode(); + + if (parameters != null) + { + hc ^= parameters.GetHashCode(); + } + + return hc; + } + } +} diff --git a/Crypto/src/crypto/parameters/DHParameters.cs b/Crypto/src/crypto/parameters/DHParameters.cs new file mode 100644
index 000000000..a0544e73b --- /dev/null +++ b/Crypto/src/crypto/parameters/DHParameters.cs
@@ -0,0 +1,184 @@ +using System; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class DHParameters + : ICipherParameters + { + private const int DefaultMinimumLength = 160; + + private readonly BigInteger p, g, q, j; + private readonly int m, l; + private readonly DHValidationParameters validation; + + private static int GetDefaultMParam( + int lParam) + { + if (lParam == 0) + return DefaultMinimumLength; + + return System.Math.Min(lParam, DefaultMinimumLength); + } + + 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, DefaultMinimumLength, 0, j, validation) + { + } + + public DHParameters( + BigInteger p, + BigInteger g, + BigInteger q, + int m, + int l, + BigInteger j, + DHValidationParameters validation) + { + if (p == null) + throw new ArgumentNullException("p"); + if (g == null) + throw new ArgumentNullException("g"); + if (!p.TestBit(0)) + throw new ArgumentException("field must be an odd prime", "p"); + if (g.CompareTo(BigInteger.Two) < 0 + || g.CompareTo(p.Subtract(BigInteger.Two)) > 0) + throw new ArgumentException("generator must in the range [2, p - 2]", "g"); + if (q != null && q.BitLength >= p.BitLength) + throw new ArgumentException("q too big to be a factor of (p-1)", "q"); + if (m >= p.BitLength) + throw new ArgumentException("m value must be < bitlength of p", "m"); + if (l != 0) + { + if (l >= p.BitLength) + throw new ArgumentException("when l value specified, it must be less than bitlength(p)", "l"); + if (l < m) + throw new ArgumentException("when l value specified, it may not be less than m value", "l"); + } + if (j != null && j.CompareTo(BigInteger.Two) < 0) + throw new ArgumentException("subgroup factor must be >= 2", "j"); + + // TODO If q, j both provided, validate p = jq + 1 ? + + this.p = p; + this.g = g; + this.q = q; + this.m = m; + this.l = l; + this.j = j; + this.validation = validation; + } + + public BigInteger P + { + get { return p; } + } + + public BigInteger G + { + get { return g; } + } + + public BigInteger Q + { + get { return q; } + } + + public BigInteger J + { + get { return j; } + } + + /// <summary>The minimum bitlength of the private value.</summary> + public int M + { + get { return m; } + } + + /// <summary>The bitlength of the private value.</summary> + public int L + { + get { return l; } + } + + public DHValidationParameters ValidationParameters + { + get { return validation; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + DHParameters other = obj as DHParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + DHParameters other) + { + return p.Equals(other.p) + && g.Equals(other.g) + && Platform.Equals(q, other.q); + } + + public override int GetHashCode() + { + int hc = p.GetHashCode() ^ g.GetHashCode(); + + if (q != null) + { + hc ^= q.GetHashCode(); + } + + return hc; + } + } +} diff --git a/Crypto/src/crypto/parameters/DHPrivateKeyParameters.cs b/Crypto/src/crypto/parameters/DHPrivateKeyParameters.cs new file mode 100644
index 000000000..fc724df81 --- /dev/null +++ b/Crypto/src/crypto/parameters/DHPrivateKeyParameters.cs
@@ -0,0 +1,60 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class DHPrivateKeyParameters + : DHKeyParameters + { + private readonly BigInteger x; + + public DHPrivateKeyParameters( + BigInteger x, + DHParameters parameters) + : base(true, parameters) + { + this.x = x; + } + + public DHPrivateKeyParameters( + BigInteger x, + DHParameters parameters, + DerObjectIdentifier algorithmOid) + : base(true, parameters, algorithmOid) + { + this.x = x; + } + + public BigInteger X + { + get { return x; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + DHPrivateKeyParameters other = obj as DHPrivateKeyParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + DHPrivateKeyParameters other) + { + return x.Equals(other.x) && base.Equals(other); + } + + public override int GetHashCode() + { + return x.GetHashCode() ^ base.GetHashCode(); + } + } +} diff --git a/Crypto/src/crypto/parameters/DHPublicKeyParameters.cs b/Crypto/src/crypto/parameters/DHPublicKeyParameters.cs new file mode 100644
index 000000000..e79375f71 --- /dev/null +++ b/Crypto/src/crypto/parameters/DHPublicKeyParameters.cs
@@ -0,0 +1,66 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class DHPublicKeyParameters + : DHKeyParameters + { + private readonly BigInteger y; + + public DHPublicKeyParameters( + BigInteger y, + DHParameters parameters) + : base(false, parameters) + { + if (y == null) + throw new ArgumentNullException("y"); + + this.y = y; + } + + public DHPublicKeyParameters( + BigInteger y, + DHParameters parameters, + DerObjectIdentifier algorithmOid) + : base(false, parameters, algorithmOid) + { + if (y == null) + throw new ArgumentNullException("y"); + + this.y = y; + } + + public BigInteger Y + { + get { return y; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + DHPublicKeyParameters other = obj as DHPublicKeyParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + DHPublicKeyParameters other) + { + return y.Equals(other.y) && base.Equals(other); + } + + public override int GetHashCode() + { + return y.GetHashCode() ^ base.GetHashCode(); + } + } +} diff --git a/Crypto/src/crypto/parameters/DHValidationParameters.cs b/Crypto/src/crypto/parameters/DHValidationParameters.cs new file mode 100644
index 000000000..50c0739fa --- /dev/null +++ b/Crypto/src/crypto/parameters/DHValidationParameters.cs
@@ -0,0 +1,59 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class DHValidationParameters + { + private readonly byte[] seed; + private readonly int counter; + + public DHValidationParameters( + byte[] seed, + int counter) + { + if (seed == null) + throw new ArgumentNullException("seed"); + + this.seed = (byte[]) seed.Clone(); + this.counter = counter; + } + + public byte[] GetSeed() + { + return (byte[]) seed.Clone(); + } + + public int Counter + { + get { return counter; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + DHValidationParameters other = obj as DHValidationParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + DHValidationParameters other) + { + return counter == other.counter + && Arrays.AreEqual(this.seed, other.seed); + } + + public override int GetHashCode() + { + return counter.GetHashCode() ^ Arrays.GetHashCode(seed); + } + } +} diff --git a/Crypto/src/crypto/parameters/DesEdeParameters.cs b/Crypto/src/crypto/parameters/DesEdeParameters.cs new file mode 100644
index 000000000..420aaecea --- /dev/null +++ b/Crypto/src/crypto/parameters/DesEdeParameters.cs
@@ -0,0 +1,95 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class DesEdeParameters + : DesParameters + { + /* + * DES-EDE Key length in bytes. + */ + public const int DesEdeKeyLength = 24; + + private static byte[] FixKey( + byte[] key, + int keyOff, + int keyLen) + { + byte[] tmp = new byte[24]; + + switch (keyLen) + { + case 16: + Array.Copy(key, keyOff, tmp, 0, 16); + Array.Copy(key, keyOff, tmp, 16, 8); + break; + case 24: + Array.Copy(key, keyOff, tmp, 0, 24); + break; + default: + throw new ArgumentException("Bad length for DESede key: " + keyLen, "keyLen"); + } + + if (IsWeakKey(tmp)) + throw new ArgumentException("attempt to create weak DESede key"); + + return tmp; + } + + public DesEdeParameters( + byte[] key) + : base(FixKey(key, 0, key.Length)) + { + } + + public DesEdeParameters( + byte[] key, + int keyOff, + int keyLen) + : base(FixKey(key, keyOff, keyLen)) + { + } + + /** + * 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 bool IsWeakKey( + byte[] key, + int offset, + int length) + { + for (int i = offset; i < length; i += DesKeyLength) + { + 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 new bool IsWeakKey( + byte[] key, + int offset) + { + return IsWeakKey(key, offset, key.Length - offset); + } + + public static new bool IsWeakKey( + byte[] key) + { + return IsWeakKey(key, 0, key.Length); + } + } +} diff --git a/Crypto/src/crypto/parameters/DesParameters.cs b/Crypto/src/crypto/parameters/DesParameters.cs new file mode 100644
index 000000000..ee37cd861 --- /dev/null +++ b/Crypto/src/crypto/parameters/DesParameters.cs
@@ -0,0 +1,130 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class DesParameters + : KeyParameter + { + public DesParameters( + byte[] key) + : base(key) + { + if (IsWeakKey(key)) + throw new ArgumentException("attempt to create weak DES key"); + } + + public DesParameters( + byte[] key, + int keyOff, + int keyLen) + : base(key, keyOff, keyLen) + { + if (IsWeakKey(key, keyOff)) + throw new ArgumentException("attempt to create weak DES key"); + } + + /* + * DES Key Length in bytes. + */ + public const int DesKeyLength = 8; + + /* + * Table of weak and semi-weak keys taken from Schneier pp281 + */ + private const int N_DES_WEAK_KEYS = 16; + + private static readonly 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. + * </p> + * @return true if the given DES key material is weak or semi-weak, + * false otherwise. + */ + public static bool IsWeakKey( + byte[] key, + int offset) + { + if (key.Length - offset < DesKeyLength) + throw new ArgumentException("key material too short."); + + //nextkey: + for (int i = 0; i < N_DES_WEAK_KEYS; i++) + { + bool unmatch = false; + for (int j = 0; j < DesKeyLength; j++) + { + if (key[j + offset] != DES_weak_keys[i * DesKeyLength + j]) + { + //continue nextkey; + unmatch = true; + break; + } + } + + if (!unmatch) + { + return true; + } + } + + return false; + } + + public static bool IsWeakKey( + byte[] key) + { + return IsWeakKey(key, 0); + } + + /** + * 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/Crypto/src/crypto/parameters/DsaKeyGenerationParameters.cs b/Crypto/src/crypto/parameters/DsaKeyGenerationParameters.cs new file mode 100644
index 000000000..86d6f5bd4 --- /dev/null +++ b/Crypto/src/crypto/parameters/DsaKeyGenerationParameters.cs
@@ -0,0 +1,26 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class DsaKeyGenerationParameters + : KeyGenerationParameters + { + private readonly DsaParameters parameters; + + public DsaKeyGenerationParameters( + SecureRandom random, + DsaParameters parameters) + : base(random, parameters.P.BitLength - 1) + { + this.parameters = parameters; + } + + public DsaParameters Parameters + { + get { return parameters; } + } + } + +} diff --git a/Crypto/src/crypto/parameters/DsaKeyParameters.cs b/Crypto/src/crypto/parameters/DsaKeyParameters.cs new file mode 100644
index 000000000..5fe6d7ab4 --- /dev/null +++ b/Crypto/src/crypto/parameters/DsaKeyParameters.cs
@@ -0,0 +1,59 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public abstract class DsaKeyParameters + : AsymmetricKeyParameter + { + private readonly DsaParameters parameters; + + protected DsaKeyParameters( + bool isPrivate, + DsaParameters parameters) + : base(isPrivate) + { + // Note: parameters may be null + this.parameters = parameters; + } + + public DsaParameters Parameters + { + get { return parameters; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + DsaKeyParameters other = obj as DsaKeyParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + DsaKeyParameters other) + { + return Platform.Equals(parameters, other.parameters) + && base.Equals(other); + } + + public override int GetHashCode() + { + int hc = base.GetHashCode(); + + if (parameters != null) + { + hc ^= parameters.GetHashCode(); + } + + return hc; + } + } +} diff --git a/Crypto/src/crypto/parameters/DsaParameters.cs b/Crypto/src/crypto/parameters/DsaParameters.cs new file mode 100644
index 000000000..50d080ee2 --- /dev/null +++ b/Crypto/src/crypto/parameters/DsaParameters.cs
@@ -0,0 +1,85 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class DsaParameters + : ICipherParameters + { + private readonly BigInteger p, q , g; + private readonly DsaValidationParameters validation; + + public DsaParameters( + BigInteger p, + BigInteger q, + BigInteger g) + : this(p, q, g, null) + { + } + + public DsaParameters( + BigInteger p, + BigInteger q, + BigInteger g, + DsaValidationParameters parameters) + { + if (p == null) + throw new ArgumentNullException("p"); + if (q == null) + throw new ArgumentNullException("q"); + if (g == null) + throw new ArgumentNullException("g"); + + this.p = p; + this.q = q; + this.g = g; + this.validation = parameters; + } + + public BigInteger P + { + get { return p; } + } + + public BigInteger Q + { + get { return q; } + } + + public BigInteger G + { + get { return g; } + } + + public DsaValidationParameters ValidationParameters + { + get { return validation; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + DsaParameters other = obj as DsaParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + DsaParameters other) + { + return p.Equals(other.p) && q.Equals(other.q) && g.Equals(other.g); + } + + public override int GetHashCode() + { + return p.GetHashCode() ^ q.GetHashCode() ^ g.GetHashCode(); + } + } +} diff --git a/Crypto/src/crypto/parameters/DsaPrivateKeyParameters.cs b/Crypto/src/crypto/parameters/DsaPrivateKeyParameters.cs new file mode 100644
index 000000000..2abdd0e4f --- /dev/null +++ b/Crypto/src/crypto/parameters/DsaPrivateKeyParameters.cs
@@ -0,0 +1,53 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class DsaPrivateKeyParameters + : DsaKeyParameters + { + private readonly BigInteger x; + + public DsaPrivateKeyParameters( + BigInteger x, + DsaParameters parameters) + : base(true, parameters) + { + if (x == null) + throw new ArgumentNullException("x"); + + this.x = x; + } + + public BigInteger X + { + get { return x; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + DsaPrivateKeyParameters other = obj as DsaPrivateKeyParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + DsaPrivateKeyParameters other) + { + return x.Equals(other.x) && base.Equals(other); + } + + public override int GetHashCode() + { + return x.GetHashCode() ^ base.GetHashCode(); + } + } +} diff --git a/Crypto/src/crypto/parameters/DsaPublicKeyParameters.cs b/Crypto/src/crypto/parameters/DsaPublicKeyParameters.cs new file mode 100644
index 000000000..f11f858f3 --- /dev/null +++ b/Crypto/src/crypto/parameters/DsaPublicKeyParameters.cs
@@ -0,0 +1,52 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class DsaPublicKeyParameters + : DsaKeyParameters + { + private readonly BigInteger y; + + public DsaPublicKeyParameters( + BigInteger y, + DsaParameters parameters) + : base(false, parameters) + { + if (y == null) + throw new ArgumentNullException("y"); + + this.y = y; + } + + public BigInteger Y + { + get { return y; } + } + + public override bool Equals(object obj) + { + if (obj == this) + return true; + + DsaPublicKeyParameters other = obj as DsaPublicKeyParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + DsaPublicKeyParameters other) + { + return y.Equals(other.y) && base.Equals(other); + } + + public override int GetHashCode() + { + return y.GetHashCode() ^ base.GetHashCode(); + } + } +} diff --git a/Crypto/src/crypto/parameters/DsaValidationParameters.cs b/Crypto/src/crypto/parameters/DsaValidationParameters.cs new file mode 100644
index 000000000..b9cdc4a79 --- /dev/null +++ b/Crypto/src/crypto/parameters/DsaValidationParameters.cs
@@ -0,0 +1,59 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class DsaValidationParameters + { + private readonly byte[] seed; + private readonly int counter; + + public DsaValidationParameters( + byte[] seed, + int counter) + { + if (seed == null) + throw new ArgumentNullException("seed"); + + this.seed = (byte[]) seed.Clone(); + this.counter = counter; + } + + public byte[] GetSeed() + { + return (byte[]) seed.Clone(); + } + + public int Counter + { + get { return counter; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + DsaValidationParameters other = obj as DsaValidationParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + DsaValidationParameters other) + { + return counter == other.counter + && Arrays.AreEqual(seed, other.seed); + } + + public override int GetHashCode() + { + return counter.GetHashCode() ^ Arrays.GetHashCode(seed); + } + } +} diff --git a/Crypto/src/crypto/parameters/ECDomainParameters.cs b/Crypto/src/crypto/parameters/ECDomainParameters.cs new file mode 100644
index 000000000..c6a3e4e72 --- /dev/null +++ b/Crypto/src/crypto/parameters/ECDomainParameters.cs
@@ -0,0 +1,116 @@ +using System; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class ECDomainParameters + { + internal ECCurve curve; + internal byte[] seed; + internal ECPoint g; + internal BigInteger n; + internal BigInteger h; + + public ECDomainParameters( + ECCurve curve, + ECPoint g, + BigInteger n) + : this(curve, g, n, BigInteger.One) + { + } + + 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) + { + if (curve == null) + throw new ArgumentNullException("curve"); + if (g == null) + throw new ArgumentNullException("g"); + if (n == null) + throw new ArgumentNullException("n"); + if (h == null) + throw new ArgumentNullException("h"); + + this.curve = curve; + this.g = g; + this.n = n; + this.h = h; + this.seed = Arrays.Clone(seed); + } + + public ECCurve Curve + { + get { return curve; } + } + + public ECPoint G + { + get { return g; } + } + + public BigInteger N + { + get { return n; } + } + + public BigInteger H + { + get { return h; } + } + + public byte[] GetSeed() + { + return Arrays.Clone(seed); + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + ECDomainParameters other = obj as ECDomainParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + ECDomainParameters other) + { + return curve.Equals(other.curve) + && g.Equals(other.g) + && n.Equals(other.n) + && h.Equals(other.h) + && Arrays.AreEqual(seed, other.seed); + } + + public override int GetHashCode() + { + return curve.GetHashCode() + ^ g.GetHashCode() + ^ n.GetHashCode() + ^ h.GetHashCode() + ^ Arrays.GetHashCode(seed); + } + } + +} diff --git a/Crypto/src/crypto/parameters/ECKeyGenerationParameters.cs b/Crypto/src/crypto/parameters/ECKeyGenerationParameters.cs new file mode 100644
index 000000000..9b2b98845 --- /dev/null +++ b/Crypto/src/crypto/parameters/ECKeyGenerationParameters.cs
@@ -0,0 +1,41 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class ECKeyGenerationParameters + : KeyGenerationParameters + { + private readonly ECDomainParameters domainParams; + private readonly DerObjectIdentifier publicKeyParamSet; + + public ECKeyGenerationParameters( + ECDomainParameters domainParameters, + SecureRandom random) + : base(random, domainParameters.N.BitLength) + { + this.domainParams = domainParameters; + } + + public ECKeyGenerationParameters( + DerObjectIdentifier publicKeyParamSet, + SecureRandom random) + : this(ECKeyParameters.LookupParameters(publicKeyParamSet), random) + { + this.publicKeyParamSet = publicKeyParamSet; + } + + public ECDomainParameters DomainParameters + { + get { return domainParams; } + } + + public DerObjectIdentifier PublicKeyParamSet + { + get { return publicKeyParamSet; } + } + } +} diff --git a/Crypto/src/crypto/parameters/ECKeyParameters.cs b/Crypto/src/crypto/parameters/ECKeyParameters.cs new file mode 100644
index 000000000..4d4622ced --- /dev/null +++ b/Crypto/src/crypto/parameters/ECKeyParameters.cs
@@ -0,0 +1,145 @@ +using System; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public abstract class ECKeyParameters + : AsymmetricKeyParameter + { + private readonly string algorithm; + private readonly ECDomainParameters parameters; + private readonly DerObjectIdentifier publicKeyParamSet; + + protected ECKeyParameters( + string algorithm, + bool isPrivate, + ECDomainParameters parameters) + : base(isPrivate) + { + if (algorithm == null) + throw new ArgumentNullException("algorithm"); + if (parameters == null) + throw new ArgumentNullException("parameters"); + + this.algorithm = VerifyAlgorithmName(algorithm); + this.parameters = parameters; + } + + protected ECKeyParameters( + string algorithm, + bool isPrivate, + DerObjectIdentifier publicKeyParamSet) + : base(isPrivate) + { + if (algorithm == null) + throw new ArgumentNullException("algorithm"); + if (publicKeyParamSet == null) + throw new ArgumentNullException("publicKeyParamSet"); + + this.algorithm = VerifyAlgorithmName(algorithm); + this.parameters = LookupParameters(publicKeyParamSet); + this.publicKeyParamSet = publicKeyParamSet; + } + + public string AlgorithmName + { + get { return algorithm; } + } + + public ECDomainParameters Parameters + { + get { return parameters; } + } + + public DerObjectIdentifier PublicKeyParamSet + { + get { return publicKeyParamSet; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + ECDomainParameters other = obj as ECDomainParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + ECKeyParameters other) + { + return parameters.Equals(other.parameters) && base.Equals(other); + } + + public override int GetHashCode() + { + return parameters.GetHashCode() ^ base.GetHashCode(); + } + + internal ECKeyGenerationParameters CreateKeyGenerationParameters( + SecureRandom random) + { + if (publicKeyParamSet != null) + { + return new ECKeyGenerationParameters(publicKeyParamSet, random); + } + + return new ECKeyGenerationParameters(parameters, random); + } + + private string VerifyAlgorithmName( + string algorithm) + { + string upper = algorithm.ToUpperInvariant(); + + switch (upper) + { + case "EC": + case "ECDSA": + case "ECDH": + case "ECDHC": + case "ECGOST3410": + case "ECMQV": + break; + default: + throw new ArgumentException("unrecognised algorithm: " + algorithm, "algorithm"); + } + + return upper; + } + + internal static ECDomainParameters LookupParameters( + DerObjectIdentifier publicKeyParamSet) + { + if (publicKeyParamSet == null) + throw new ArgumentNullException("publicKeyParamSet"); + + ECDomainParameters p = ECGost3410NamedCurves.GetByOid(publicKeyParamSet); + + if (p == null) + { + X9ECParameters x9 = ECKeyPairGenerator.FindECCurveByOid(publicKeyParamSet); + + if (x9 == null) + { + throw new ArgumentException("OID is not a valid public key parameter set", "publicKeyParamSet"); + } + + p = new ECDomainParameters(x9.Curve, x9.G, x9.N, x9.H, x9.GetSeed()); + } + + return p; + } + } +} diff --git a/Crypto/src/crypto/parameters/ECPrivateKeyParameters.cs b/Crypto/src/crypto/parameters/ECPrivateKeyParameters.cs new file mode 100644
index 000000000..e6352d5d1 --- /dev/null +++ b/Crypto/src/crypto/parameters/ECPrivateKeyParameters.cs
@@ -0,0 +1,87 @@ +using System; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class ECPrivateKeyParameters + : ECKeyParameters + { + private readonly BigInteger d; + + public ECPrivateKeyParameters( + BigInteger d, + ECDomainParameters parameters) + : this("EC", d, parameters) + { + } + + [Obsolete("Use version with explicit 'algorithm' parameter")] + public ECPrivateKeyParameters( + BigInteger d, + DerObjectIdentifier publicKeyParamSet) + : base("ECGOST3410", true, publicKeyParamSet) + { + if (d == null) + throw new ArgumentNullException("d"); + + this.d = d; + } + + public ECPrivateKeyParameters( + string algorithm, + BigInteger d, + ECDomainParameters parameters) + : base(algorithm, true, parameters) + { + if (d == null) + throw new ArgumentNullException("d"); + + this.d = d; + } + + public ECPrivateKeyParameters( + string algorithm, + BigInteger d, + DerObjectIdentifier publicKeyParamSet) + : base(algorithm, true, publicKeyParamSet) + { + if (d == null) + throw new ArgumentNullException("d"); + + this.d = d; + } + + public BigInteger D + { + get { return d; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + ECPrivateKeyParameters other = obj as ECPrivateKeyParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + ECPrivateKeyParameters other) + { + return d.Equals(other.d) && base.Equals(other); + } + + public override int GetHashCode() + { + return d.GetHashCode() ^ base.GetHashCode(); + } + } +} diff --git a/Crypto/src/crypto/parameters/ECPublicKeyParameters.cs b/Crypto/src/crypto/parameters/ECPublicKeyParameters.cs new file mode 100644
index 000000000..9e71c2a25 --- /dev/null +++ b/Crypto/src/crypto/parameters/ECPublicKeyParameters.cs
@@ -0,0 +1,86 @@ +using System; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Math.EC; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class ECPublicKeyParameters + : ECKeyParameters + { + private readonly ECPoint q; + + public ECPublicKeyParameters( + ECPoint q, + ECDomainParameters parameters) + : this("EC", q, parameters) + { + } + + [Obsolete("Use version with explicit 'algorithm' parameter")] + public ECPublicKeyParameters( + ECPoint q, + DerObjectIdentifier publicKeyParamSet) + : base("ECGOST3410", false, publicKeyParamSet) + { + if (q == null) + throw new ArgumentNullException("q"); + + this.q = q; + } + + public ECPublicKeyParameters( + string algorithm, + ECPoint q, + ECDomainParameters parameters) + : base(algorithm, false, parameters) + { + if (q == null) + throw new ArgumentNullException("q"); + + this.q = q; + } + + public ECPublicKeyParameters( + string algorithm, + ECPoint q, + DerObjectIdentifier publicKeyParamSet) + : base(algorithm, false, publicKeyParamSet) + { + if (q == null) + throw new ArgumentNullException("q"); + + this.q = q; + } + + public ECPoint Q + { + get { return q; } + } + + public override bool Equals(object obj) + { + if (obj == this) + return true; + + ECPublicKeyParameters other = obj as ECPublicKeyParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + ECPublicKeyParameters other) + { + return q.Equals(other.q) && base.Equals(other); + } + + public override int GetHashCode() + { + return q.GetHashCode() ^ base.GetHashCode(); + } + } +} diff --git a/Crypto/src/crypto/parameters/ElGamalKeyGenerationParameters.cs b/Crypto/src/crypto/parameters/ElGamalKeyGenerationParameters.cs new file mode 100644
index 000000000..40ca70df4 --- /dev/null +++ b/Crypto/src/crypto/parameters/ElGamalKeyGenerationParameters.cs
@@ -0,0 +1,31 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class ElGamalKeyGenerationParameters + : KeyGenerationParameters + { + private readonly ElGamalParameters parameters; + + public ElGamalKeyGenerationParameters( + SecureRandom random, + ElGamalParameters parameters) + : base(random, GetStrength(parameters)) + { + this.parameters = parameters; + } + + public ElGamalParameters Parameters + { + get { return parameters; } + } + + internal static int GetStrength( + ElGamalParameters parameters) + { + return parameters.L != 0 ? parameters.L : parameters.P.BitLength; + } + } +} diff --git a/Crypto/src/crypto/parameters/ElGamalKeyParameters.cs b/Crypto/src/crypto/parameters/ElGamalKeyParameters.cs new file mode 100644
index 000000000..8b6e27957 --- /dev/null +++ b/Crypto/src/crypto/parameters/ElGamalKeyParameters.cs
@@ -0,0 +1,59 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class ElGamalKeyParameters + : AsymmetricKeyParameter + { + private readonly ElGamalParameters parameters; + + protected ElGamalKeyParameters( + bool isPrivate, + ElGamalParameters parameters) + : base(isPrivate) + { + // TODO Should we allow 'parameters' to be null? + this.parameters = parameters; + } + + public ElGamalParameters Parameters + { + get { return parameters; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + ElGamalKeyParameters other = obj as ElGamalKeyParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + ElGamalKeyParameters other) + { + return Platform.Equals(parameters, other.parameters) + && base.Equals(other); + } + + public override int GetHashCode() + { + int hc = base.GetHashCode(); + + if (parameters != null) + { + hc ^= parameters.GetHashCode(); + } + + return hc; + } + } +} diff --git a/Crypto/src/crypto/parameters/ElGamalParameters.cs b/Crypto/src/crypto/parameters/ElGamalParameters.cs new file mode 100644
index 000000000..ab6d3e710 --- /dev/null +++ b/Crypto/src/crypto/parameters/ElGamalParameters.cs
@@ -0,0 +1,81 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class ElGamalParameters + : ICipherParameters + { + private readonly BigInteger p, g; + private readonly int l; + + public ElGamalParameters( + BigInteger p, + BigInteger g) + : this(p, g, 0) + { + } + + public ElGamalParameters( + BigInteger p, + BigInteger g, + int l) + { + if (p == null) + throw new ArgumentNullException("p"); + if (g == null) + throw new ArgumentNullException("g"); + + this.p = p; + this.g = g; + this.l = l; + } + + public BigInteger P + { + get { return p; } + } + + /** + * return the generator - g + */ + public BigInteger G + { + get { return g; } + } + + /** + * return private value limit - l + */ + public int L + { + get { return l; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + ElGamalParameters other = obj as ElGamalParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + ElGamalParameters other) + { + return p.Equals(other.p) && g.Equals(other.g) && l == other.l; + } + + public override int GetHashCode() + { + return p.GetHashCode() ^ g.GetHashCode() ^ l; + } + } +} diff --git a/Crypto/src/crypto/parameters/ElGamalPrivateKeyParameters.cs b/Crypto/src/crypto/parameters/ElGamalPrivateKeyParameters.cs new file mode 100644
index 000000000..6363f2bbb --- /dev/null +++ b/Crypto/src/crypto/parameters/ElGamalPrivateKeyParameters.cs
@@ -0,0 +1,53 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class ElGamalPrivateKeyParameters + : ElGamalKeyParameters + { + private readonly BigInteger x; + + public ElGamalPrivateKeyParameters( + BigInteger x, + ElGamalParameters parameters) + : base(true, parameters) + { + if (x == null) + throw new ArgumentNullException("x"); + + this.x = x; + } + + public BigInteger X + { + get { return x; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + ElGamalPrivateKeyParameters other = obj as ElGamalPrivateKeyParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + ElGamalPrivateKeyParameters other) + { + return other.x.Equals(x) && base.Equals(other); + } + + public override int GetHashCode() + { + return x.GetHashCode() ^ base.GetHashCode(); + } + } +} diff --git a/Crypto/src/crypto/parameters/ElGamalPublicKeyParameters.cs b/Crypto/src/crypto/parameters/ElGamalPublicKeyParameters.cs new file mode 100644
index 000000000..25ac625d5 --- /dev/null +++ b/Crypto/src/crypto/parameters/ElGamalPublicKeyParameters.cs
@@ -0,0 +1,53 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class ElGamalPublicKeyParameters + : ElGamalKeyParameters + { + private readonly BigInteger y; + + public ElGamalPublicKeyParameters( + BigInteger y, + ElGamalParameters parameters) + : base(false, parameters) + { + if (y == null) + throw new ArgumentNullException("y"); + + this.y = y; + } + + public BigInteger Y + { + get { return y; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + ElGamalPublicKeyParameters other = obj as ElGamalPublicKeyParameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + ElGamalPublicKeyParameters other) + { + return y.Equals(other.y) && base.Equals(other); + } + + public override int GetHashCode() + { + return y.GetHashCode() ^ base.GetHashCode(); + } + } +} diff --git a/Crypto/src/crypto/parameters/GOST3410KeyGenerationParameters.cs b/Crypto/src/crypto/parameters/GOST3410KeyGenerationParameters.cs new file mode 100644
index 000000000..b06a5d896 --- /dev/null +++ b/Crypto/src/crypto/parameters/GOST3410KeyGenerationParameters.cs
@@ -0,0 +1,55 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class Gost3410KeyGenerationParameters + : KeyGenerationParameters + { + private readonly Gost3410Parameters parameters; + private readonly DerObjectIdentifier publicKeyParamSet; + + public Gost3410KeyGenerationParameters( + SecureRandom random, + Gost3410Parameters parameters) + : base(random, parameters.P.BitLength - 1) + { + this.parameters = parameters; + } + + public Gost3410KeyGenerationParameters( + SecureRandom random, + DerObjectIdentifier publicKeyParamSet) + : this(random, LookupParameters(publicKeyParamSet)) + { + this.publicKeyParamSet = publicKeyParamSet; + } + + public Gost3410Parameters Parameters + { + get { return parameters; } + } + + public DerObjectIdentifier PublicKeyParamSet + { + get { return publicKeyParamSet; } + } + + private static Gost3410Parameters LookupParameters( + DerObjectIdentifier publicKeyParamSet) + { + if (publicKeyParamSet == null) + throw new ArgumentNullException("publicKeyParamSet"); + + Gost3410ParamSetParameters p = Gost3410NamedParameters.GetByOid(publicKeyParamSet); + + if (p == null) + throw new ArgumentException("OID is not a valid CryptoPro public key parameter set", "publicKeyParamSet"); + + return new Gost3410Parameters(p.P, p.Q, p.A); + } + } +} diff --git a/Crypto/src/crypto/parameters/GOST3410KeyParameters.cs b/Crypto/src/crypto/parameters/GOST3410KeyParameters.cs new file mode 100644
index 000000000..f771c4d97 --- /dev/null +++ b/Crypto/src/crypto/parameters/GOST3410KeyParameters.cs
@@ -0,0 +1,58 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public abstract class Gost3410KeyParameters + : AsymmetricKeyParameter + { + private readonly Gost3410Parameters parameters; + private readonly DerObjectIdentifier publicKeyParamSet; + + protected Gost3410KeyParameters( + bool isPrivate, + Gost3410Parameters parameters) + : base(isPrivate) + { + this.parameters = parameters; + } + + protected Gost3410KeyParameters( + bool isPrivate, + DerObjectIdentifier publicKeyParamSet) + : base(isPrivate) + { + this.parameters = LookupParameters(publicKeyParamSet); + this.publicKeyParamSet = publicKeyParamSet; + } + + public Gost3410Parameters Parameters + { + get { return parameters; } + } + + public DerObjectIdentifier PublicKeyParamSet + { + get { return publicKeyParamSet; } + } + + // TODO Implement Equals/GetHashCode + + private static Gost3410Parameters LookupParameters( + DerObjectIdentifier publicKeyParamSet) + { + if (publicKeyParamSet == null) + throw new ArgumentNullException("publicKeyParamSet"); + + Gost3410ParamSetParameters p = Gost3410NamedParameters.GetByOid(publicKeyParamSet); + + if (p == null) + throw new ArgumentException("OID is not a valid CryptoPro public key parameter set", "publicKeyParamSet"); + + return new Gost3410Parameters(p.P, p.Q, p.A); + } + } +} diff --git a/Crypto/src/crypto/parameters/GOST3410Parameters.cs b/Crypto/src/crypto/parameters/GOST3410Parameters.cs new file mode 100644
index 000000000..2ec167ef0 --- /dev/null +++ b/Crypto/src/crypto/parameters/GOST3410Parameters.cs
@@ -0,0 +1,86 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class Gost3410Parameters + : ICipherParameters + { + private readonly BigInteger p, q, a; + private readonly Gost3410ValidationParameters validation; + + public Gost3410Parameters( + BigInteger p, + BigInteger q, + BigInteger a) + : this(p, q, a, null) + { + } + + public Gost3410Parameters( + BigInteger p, + BigInteger q, + BigInteger a, + Gost3410ValidationParameters validation) + { + if (p == null) + throw new ArgumentNullException("p"); + if (q == null) + throw new ArgumentNullException("q"); + if (a == null) + throw new ArgumentNullException("a"); + + this.p = p; + this.q = q; + this.a = a; + this.validation = validation; + } + + public BigInteger P + { + get { return p; } + } + + public BigInteger Q + { + get { return q; } + } + + public BigInteger A + { + get { return a; } + } + + public Gost3410ValidationParameters ValidationParameters + { + get { return validation; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + Gost3410Parameters other = obj as Gost3410Parameters; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + Gost3410Parameters other) + { + return p.Equals(other.p) && q.Equals(other.q) && a.Equals(other.a); + } + + public override int GetHashCode() + { + return p.GetHashCode() ^ q.GetHashCode() ^ a.GetHashCode(); + } + } +} diff --git a/Crypto/src/crypto/parameters/GOST3410PrivateKeyParameters.cs b/Crypto/src/crypto/parameters/GOST3410PrivateKeyParameters.cs new file mode 100644
index 000000000..e3a613de6 --- /dev/null +++ b/Crypto/src/crypto/parameters/GOST3410PrivateKeyParameters.cs
@@ -0,0 +1,41 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class Gost3410PrivateKeyParameters + : Gost3410KeyParameters + { + private readonly BigInteger x; + + public Gost3410PrivateKeyParameters( + BigInteger x, + Gost3410Parameters parameters) + : base(true, parameters) + { + if (x.SignValue < 1 || x.BitLength > 256 || x.CompareTo(Parameters.Q) >= 0) + throw new ArgumentException("Invalid x for GOST3410 private key", "x"); + + this.x = x; + } + + public Gost3410PrivateKeyParameters( + BigInteger x, + DerObjectIdentifier publicKeyParamSet) + : base(true, publicKeyParamSet) + { + if (x.SignValue < 1 || x.BitLength > 256 || x.CompareTo(Parameters.Q) >= 0) + throw new ArgumentException("Invalid x for GOST3410 private key", "x"); + + this.x = x; + } + + public BigInteger X + { + get { return x; } + } + } +} diff --git a/Crypto/src/crypto/parameters/GOST3410PublicKeyParameters.cs b/Crypto/src/crypto/parameters/GOST3410PublicKeyParameters.cs new file mode 100644
index 000000000..96b7e91ea --- /dev/null +++ b/Crypto/src/crypto/parameters/GOST3410PublicKeyParameters.cs
@@ -0,0 +1,40 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class Gost3410PublicKeyParameters + : Gost3410KeyParameters + { + private readonly BigInteger y; + + public Gost3410PublicKeyParameters( + BigInteger y, + Gost3410Parameters parameters) + : base(false, parameters) + { + if (y.SignValue < 1 || y.CompareTo(Parameters.P) >= 0) + throw new ArgumentException("Invalid y for GOST3410 public key", "y"); + + this.y = y; + } + + public Gost3410PublicKeyParameters( + BigInteger y, + DerObjectIdentifier publicKeyParamSet) + : base(false, publicKeyParamSet) + { + if (y.SignValue < 1 || y.CompareTo(Parameters.P) >= 0) + throw new ArgumentException("Invalid y for GOST3410 public key", "y"); + + this.y = y; + } + + public BigInteger Y + { + get { return y; } + } + } +} diff --git a/Crypto/src/crypto/parameters/GOST3410ValidationParameters.cs b/Crypto/src/crypto/parameters/GOST3410ValidationParameters.cs new file mode 100644
index 000000000..21e5af823 --- /dev/null +++ b/Crypto/src/crypto/parameters/GOST3410ValidationParameters.cs
@@ -0,0 +1,51 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + 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 C { get { return c; } } + public int X0 { get { return x0; } } + public long CL { get { return cL; } } + public long X0L { get { return x0L; } } + + public override bool Equals( + object obj) + { + Gost3410ValidationParameters other = obj as Gost3410ValidationParameters; + + return other != null + && other.c == this.c + && other.x0 == this.x0 + && other.cL == this.cL + && other.x0L == this.x0L; + } + + public override int GetHashCode() + { + return c.GetHashCode() ^ x0.GetHashCode() ^ cL.GetHashCode() ^ x0L.GetHashCode(); + } + + } +} diff --git a/Crypto/src/crypto/parameters/ISO18033KDFParameters.cs b/Crypto/src/crypto/parameters/ISO18033KDFParameters.cs new file mode 100644
index 000000000..2d8fff8e3 --- /dev/null +++ b/Crypto/src/crypto/parameters/ISO18033KDFParameters.cs
@@ -0,0 +1,25 @@ +using System; +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + /** + * parameters for Key derivation functions for ISO-18033 + */ + public class Iso18033KdfParameters + : IDerivationParameters + { + byte[] seed; + + public Iso18033KdfParameters( + byte[] seed) + { + this.seed = seed; + } + + public byte[] GetSeed() + { + return seed; + } + } +} diff --git a/Crypto/src/crypto/parameters/IesParameters.cs b/Crypto/src/crypto/parameters/IesParameters.cs new file mode 100644
index 000000000..d306b2c33 --- /dev/null +++ b/Crypto/src/crypto/parameters/IesParameters.cs
@@ -0,0 +1,49 @@ +using System; +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + /** + * parameters for using an integrated cipher in stream mode. + */ + public class IesParameters : ICipherParameters + { + 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 MacKeySize + { + get + { + return macKeySize; + } + } + } + +} diff --git a/Crypto/src/crypto/parameters/IesWithCipherParameters.cs b/Crypto/src/crypto/parameters/IesWithCipherParameters.cs new file mode 100644
index 000000000..70ef55d54 --- /dev/null +++ b/Crypto/src/crypto/parameters/IesWithCipherParameters.cs
@@ -0,0 +1,33 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class IesWithCipherParameters : 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) : base(derivation, encoding, macKeySize) + { + this.cipherKeySize = cipherKeySize; + } + + public int CipherKeySize + { + get + { + return cipherKeySize; + } + } + } + +} diff --git a/Crypto/src/crypto/parameters/KdfParameters.cs b/Crypto/src/crypto/parameters/KdfParameters.cs new file mode 100644
index 000000000..bc5c905d0 --- /dev/null +++ b/Crypto/src/crypto/parameters/KdfParameters.cs
@@ -0,0 +1,33 @@ +using System; +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + /** + * parameters for Key derivation functions for IEEE P1363a + */ + public class KdfParameters : IDerivationParameters + { + 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/Crypto/src/crypto/parameters/KeyParameter.cs b/Crypto/src/crypto/parameters/KeyParameter.cs new file mode 100644
index 000000000..33dff96d7 --- /dev/null +++ b/Crypto/src/crypto/parameters/KeyParameter.cs
@@ -0,0 +1,43 @@ +using System; + +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class KeyParameter + : ICipherParameters + { + private readonly byte[] key; + + public KeyParameter( + byte[] key) + { + if (key == null) + throw new ArgumentNullException("key"); + + this.key = (byte[]) key.Clone(); + } + + public KeyParameter( + byte[] key, + int keyOff, + int keyLen) + { + if (key == null) + throw new ArgumentNullException("key"); + if (keyOff < 0 || keyOff > key.Length) + throw new ArgumentOutOfRangeException("keyOff"); + if (keyLen < 0 || (keyOff + keyLen) > key.Length) + throw new ArgumentOutOfRangeException("keyLen"); + + this.key = new byte[keyLen]; + Array.Copy(key, keyOff, this.key, 0, keyLen); + } + + public byte[] GetKey() + { + return (byte[]) key.Clone(); + } + } + +} diff --git a/Crypto/src/crypto/parameters/MgfParameters.cs b/Crypto/src/crypto/parameters/MgfParameters.cs new file mode 100644
index 000000000..11983b877 --- /dev/null +++ b/Crypto/src/crypto/parameters/MgfParameters.cs
@@ -0,0 +1,31 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + /// <remarks>Parameters for mask derivation functions.</remarks> + public class MgfParameters + : IDerivationParameters + { + private readonly byte[] seed; + + public MgfParameters( + byte[] seed) + : this(seed, 0, seed.Length) + { + } + + public MgfParameters( + byte[] seed, + int off, + int len) + { + this.seed = new byte[len]; + Array.Copy(seed, off, this.seed, 0, len); + } + + public byte[] GetSeed() + { + return (byte[]) seed.Clone(); + } + } +} diff --git a/Crypto/src/crypto/parameters/MqvPrivateParameters.cs b/Crypto/src/crypto/parameters/MqvPrivateParameters.cs new file mode 100644
index 000000000..4bf33e347 --- /dev/null +++ b/Crypto/src/crypto/parameters/MqvPrivateParameters.cs
@@ -0,0 +1,44 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class MqvPrivateParameters + : ICipherParameters + { + private readonly ECPrivateKeyParameters staticPrivateKey; + private readonly ECPrivateKeyParameters ephemeralPrivateKey; + private readonly 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 StaticPrivateKey + { + get { return staticPrivateKey; } + } + + public ECPrivateKeyParameters EphemeralPrivateKey + { + get { return ephemeralPrivateKey; } + } + + public ECPublicKeyParameters EphemeralPublicKey + { + get { return ephemeralPublicKey; } + } + } +} diff --git a/Crypto/src/crypto/parameters/MqvPublicParameters.cs b/Crypto/src/crypto/parameters/MqvPublicParameters.cs new file mode 100644
index 000000000..a0e273ac4 --- /dev/null +++ b/Crypto/src/crypto/parameters/MqvPublicParameters.cs
@@ -0,0 +1,29 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class MqvPublicParameters + : ICipherParameters + { + private readonly ECPublicKeyParameters staticPublicKey; + private readonly ECPublicKeyParameters ephemeralPublicKey; + + public MqvPublicParameters( + ECPublicKeyParameters staticPublicKey, + ECPublicKeyParameters ephemeralPublicKey) + { + this.staticPublicKey = staticPublicKey; + this.ephemeralPublicKey = ephemeralPublicKey; + } + + public ECPublicKeyParameters StaticPublicKey + { + get { return staticPublicKey; } + } + + public ECPublicKeyParameters EphemeralPublicKey + { + get { return ephemeralPublicKey; } + } + } +} diff --git a/Crypto/src/crypto/parameters/NaccacheSternKeyGenerationParameters.cs b/Crypto/src/crypto/parameters/NaccacheSternKeyGenerationParameters.cs new file mode 100644
index 000000000..5b4052505 --- /dev/null +++ b/Crypto/src/crypto/parameters/NaccacheSternKeyGenerationParameters.cs
@@ -0,0 +1,101 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + /** + * 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 : KeyGenerationParameters + { + // private BigInteger publicExponent; + private readonly int certainty; + private readonly int countSmallPrimes; + private bool debug; + + /** + * 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 countSmallPrimes + * How many small key factors are desired + */ + public NaccacheSternKeyGenerationParameters( + SecureRandom random, + int strength, + int certainty, + int countSmallPrimes) + : this(random, strength, certainty, countSmallPrimes, 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 countSmallPrimes, + bool debug) + : base(random, strength) + { + if (countSmallPrimes % 2 == 1) + { + throw new ArgumentException("countSmallPrimes must be a multiple of 2"); + } + if (countSmallPrimes < 30) + { + throw new ArgumentException("countSmallPrimes must be >= 30 for security reasons"); + } + this.certainty = certainty; + this.countSmallPrimes = countSmallPrimes; + this.debug = debug; + } + + /** + * @return Returns the certainty. + */ + public int Certainty + { + get { return certainty; } + } + + /** + * @return Returns the countSmallPrimes. + */ + public int CountSmallPrimes + { + get { return countSmallPrimes; } + } + + public bool IsDebug + { + get { return debug; } + } + } +} diff --git a/Crypto/src/crypto/parameters/NaccacheSternKeyParameters.cs b/Crypto/src/crypto/parameters/NaccacheSternKeyParameters.cs new file mode 100644
index 000000000..8be7ad835 --- /dev/null +++ b/Crypto/src/crypto/parameters/NaccacheSternKeyParameters.cs
@@ -0,0 +1,44 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + /** + * 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 : AsymmetricKeyParameter + { + private readonly BigInteger g, n; + private readonly int lowerSigmaBound; + + /** + * @param privateKey + */ + public NaccacheSternKeyParameters(bool privateKey, BigInteger g, BigInteger n, int lowerSigmaBound) + : base(privateKey) + { + this.g = g; + this.n = n; + this.lowerSigmaBound = lowerSigmaBound; + } + + /** + * @return Returns the g. + */ + public BigInteger G { get { return g; } } + + /** + * @return Returns the lowerSigmaBound. + */ + public int LowerSigmaBound { get { return lowerSigmaBound; } } + + /** + * @return Returns the n. + */ + public BigInteger Modulus { get { return n; } } + } +} diff --git a/Crypto/src/crypto/parameters/NaccacheSternPrivateKeyParameters.cs b/Crypto/src/crypto/parameters/NaccacheSternPrivateKeyParameters.cs new file mode 100644
index 000000000..42a0454a1 --- /dev/null +++ b/Crypto/src/crypto/parameters/NaccacheSternPrivateKeyParameters.cs
@@ -0,0 +1,79 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + /** + * 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 : NaccacheSternKeyParameters + { + private readonly BigInteger phiN; + private readonly IList smallPrimes; + +#if !(SILVERLIGHT || PORTABLE) + [Obsolete] + public NaccacheSternPrivateKeyParameters( + BigInteger g, + BigInteger n, + int lowerSigmaBound, + ArrayList smallPrimes, + BigInteger phiN) + : base(true, g, n, lowerSigmaBound) + { + this.smallPrimes = smallPrimes; + this.phiN = phiN; + } +#endif + + /** + * 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, + IList smallPrimes, + BigInteger phiN) + : base(true, g, n, lowerSigmaBound) + { + this.smallPrimes = smallPrimes; + this.phiN = phiN; + } + + public BigInteger PhiN + { + get { return phiN; } + } + +#if !(SILVERLIGHT || PORTABLE) + [Obsolete("Use 'SmallPrimesList' instead")] + public ArrayList SmallPrimes + { + get { return new ArrayList(smallPrimes); } + } +#endif + + public IList SmallPrimesList + { + get { return smallPrimes; } + } + } +} diff --git a/Crypto/src/crypto/parameters/ParametersWithIV.cs b/Crypto/src/crypto/parameters/ParametersWithIV.cs new file mode 100644
index 000000000..e00abce58 --- /dev/null +++ b/Crypto/src/crypto/parameters/ParametersWithIV.cs
@@ -0,0 +1,44 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class ParametersWithIV + : ICipherParameters + { + private readonly ICipherParameters parameters; + private readonly byte[] iv; + + public ParametersWithIV( + ICipherParameters parameters, + byte[] iv) + : this(parameters, iv, 0, iv.Length) + { + } + + public ParametersWithIV( + ICipherParameters parameters, + byte[] iv, + int ivOff, + int ivLen) + { + if (parameters == null) + throw new ArgumentNullException("parameters"); + if (iv == null) + throw new ArgumentNullException("iv"); + + this.parameters = parameters; + this.iv = new byte[ivLen]; + Array.Copy(iv, ivOff, this.iv, 0, ivLen); + } + + public byte[] GetIV() + { + return (byte[]) iv.Clone(); + } + + public ICipherParameters Parameters + { + get { return parameters; } + } + } +} diff --git a/Crypto/src/crypto/parameters/ParametersWithRandom.cs b/Crypto/src/crypto/parameters/ParametersWithRandom.cs new file mode 100644
index 000000000..a05e77409 --- /dev/null +++ b/Crypto/src/crypto/parameters/ParametersWithRandom.cs
@@ -0,0 +1,48 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class ParametersWithRandom + : ICipherParameters + { + private readonly ICipherParameters parameters; + private readonly SecureRandom random; + + public ParametersWithRandom( + ICipherParameters parameters, + SecureRandom random) + { + if (parameters == null) + throw new ArgumentNullException("random"); + if (random == null) + throw new ArgumentNullException("random"); + + this.parameters = parameters; + this.random = random; + } + + public ParametersWithRandom( + ICipherParameters parameters) + : this(parameters, new SecureRandom()) + { + } + + [Obsolete("Use Random property instead")] + public SecureRandom GetRandom() + { + return Random; + } + + public SecureRandom Random + { + get { return random; } + } + + public ICipherParameters Parameters + { + get { return parameters; } + } + } +} diff --git a/Crypto/src/crypto/parameters/ParametersWithSBox.cs b/Crypto/src/crypto/parameters/ParametersWithSBox.cs new file mode 100644
index 000000000..6473796e3 --- /dev/null +++ b/Crypto/src/crypto/parameters/ParametersWithSBox.cs
@@ -0,0 +1,24 @@ +using System; + +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class ParametersWithSBox : ICipherParameters + { + private ICipherParameters parameters; + private byte[] sBox; + + public ParametersWithSBox( + ICipherParameters parameters, + byte[] sBox) + { + this.parameters = parameters; + this.sBox = sBox; + } + + public byte[] GetSBox() { return sBox; } + + public ICipherParameters Parameters { get { return parameters; } } + } +} diff --git a/Crypto/src/crypto/parameters/ParametersWithSalt.cs b/Crypto/src/crypto/parameters/ParametersWithSalt.cs new file mode 100644
index 000000000..7f4cd6cd1 --- /dev/null +++ b/Crypto/src/crypto/parameters/ParametersWithSalt.cs
@@ -0,0 +1,39 @@ +using System; + +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + + /// <summary> Cipher parameters with a fixed salt value associated with them.</summary> + public class ParametersWithSalt : ICipherParameters + { + private byte[] salt; + private ICipherParameters parameters; + + public ParametersWithSalt(ICipherParameters parameters, byte[] salt):this(parameters, salt, 0, salt.Length) + { + } + + public ParametersWithSalt(ICipherParameters parameters, byte[] salt, int saltOff, int saltLen) + { + this.salt = new byte[saltLen]; + this.parameters = parameters; + + Array.Copy(salt, saltOff, this.salt, 0, saltLen); + } + + public byte[] GetSalt() + { + return salt; + } + + public ICipherParameters Parameters + { + get + { + return parameters; + } + } + } +} diff --git a/Crypto/src/crypto/parameters/RC2Parameters.cs b/Crypto/src/crypto/parameters/RC2Parameters.cs new file mode 100644
index 000000000..7a6d5bb6e --- /dev/null +++ b/Crypto/src/crypto/parameters/RC2Parameters.cs
@@ -0,0 +1,47 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class RC2Parameters + : KeyParameter + { + private readonly int bits; + + public RC2Parameters( + byte[] key) + : this(key, (key.Length > 128) ? 1024 : (key.Length * 8)) + { + } + + public RC2Parameters( + byte[] key, + int keyOff, + int keyLen) + : this(key, keyOff, keyLen, (keyLen > 128) ? 1024 : (keyLen * 8)) + { + } + + public RC2Parameters( + byte[] key, + int bits) + : base(key) + { + this.bits = bits; + } + + public RC2Parameters( + byte[] key, + int keyOff, + int keyLen, + int bits) + : base(key, keyOff, keyLen) + { + this.bits = bits; + } + + public int EffectiveKeyBits + { + get { return bits; } + } + } +} diff --git a/Crypto/src/crypto/parameters/RC5Parameters.cs b/Crypto/src/crypto/parameters/RC5Parameters.cs new file mode 100644
index 000000000..88a59e197 --- /dev/null +++ b/Crypto/src/crypto/parameters/RC5Parameters.cs
@@ -0,0 +1,27 @@ +using System; +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class RC5Parameters + : KeyParameter + { + private readonly int rounds; + + public RC5Parameters( + byte[] key, + int rounds) + : base(key) + { + if (key.Length > 255) + throw new ArgumentException("RC5 key length can be no greater than 255"); + + this.rounds = rounds; + } + + public int Rounds + { + get { return rounds; } + } + } +} diff --git a/Crypto/src/crypto/parameters/RSABlindingParameters.cs b/Crypto/src/crypto/parameters/RSABlindingParameters.cs new file mode 100644
index 000000000..49c7bcce6 --- /dev/null +++ b/Crypto/src/crypto/parameters/RSABlindingParameters.cs
@@ -0,0 +1,34 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class RsaBlindingParameters + : ICipherParameters + { + private readonly RsaKeyParameters publicKey; + private readonly BigInteger blindingFactor; + + public RsaBlindingParameters( + RsaKeyParameters publicKey, + BigInteger blindingFactor) + { + if (publicKey.IsPrivate) + throw new ArgumentException("RSA parameters should be for a public key"); + + this.publicKey = publicKey; + this.blindingFactor = blindingFactor; + } + + public RsaKeyParameters PublicKey + { + get { return publicKey; } + } + + public BigInteger BlindingFactor + { + get { return blindingFactor; } + } + } +} diff --git a/Crypto/src/crypto/parameters/RsaKeyGenerationParameters.cs b/Crypto/src/crypto/parameters/RsaKeyGenerationParameters.cs new file mode 100644
index 000000000..619ab65b4 --- /dev/null +++ b/Crypto/src/crypto/parameters/RsaKeyGenerationParameters.cs
@@ -0,0 +1,55 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class RsaKeyGenerationParameters + : KeyGenerationParameters + { + private readonly BigInteger publicExponent; + private readonly int certainty; + + public RsaKeyGenerationParameters( + BigInteger publicExponent, + SecureRandom random, + int strength, + int certainty) + : base(random, strength) + { + this.publicExponent = publicExponent; + this.certainty = certainty; + } + + public BigInteger PublicExponent + { + get { return publicExponent; } + } + + public int Certainty + { + get { return certainty; } + } + + public override bool Equals( + object obj) + { + RsaKeyGenerationParameters other = obj as RsaKeyGenerationParameters; + + if (other == null) + { + return false; + } + + return certainty == other.certainty + && publicExponent.Equals(other.publicExponent); + } + + public override int GetHashCode() + { + return certainty.GetHashCode() ^ publicExponent.GetHashCode(); + } + } +} diff --git a/Crypto/src/crypto/parameters/RsaKeyParameters.cs b/Crypto/src/crypto/parameters/RsaKeyParameters.cs new file mode 100644
index 000000000..72c0d806f --- /dev/null +++ b/Crypto/src/crypto/parameters/RsaKeyParameters.cs
@@ -0,0 +1,63 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class RsaKeyParameters + : AsymmetricKeyParameter + { + private readonly BigInteger modulus; + private readonly BigInteger exponent; + + public RsaKeyParameters( + bool isPrivate, + BigInteger modulus, + BigInteger exponent) + : base(isPrivate) + { + if (modulus == null) + throw new ArgumentNullException("modulus"); + if (exponent == null) + throw new ArgumentNullException("exponent"); + if (modulus.SignValue <= 0) + throw new ArgumentException("Not a valid RSA modulus", "modulus"); + if (exponent.SignValue <= 0) + throw new ArgumentException("Not a valid RSA exponent", "exponent"); + + this.modulus = modulus; + this.exponent = exponent; + } + + public BigInteger Modulus + { + get { return modulus; } + } + + public BigInteger Exponent + { + get { return exponent; } + } + + public override bool Equals( + object obj) + { + RsaKeyParameters kp = obj as RsaKeyParameters; + + if (kp == null) + { + return false; + } + + return kp.IsPrivate == this.IsPrivate + && kp.Modulus.Equals(this.modulus) + && kp.Exponent.Equals(this.exponent); + } + + public override int GetHashCode() + { + return modulus.GetHashCode() ^ exponent.GetHashCode() ^ IsPrivate.GetHashCode(); + } + } +} diff --git a/Crypto/src/crypto/parameters/RsaPrivateCrtKeyParameters.cs b/Crypto/src/crypto/parameters/RsaPrivateCrtKeyParameters.cs new file mode 100644
index 000000000..7bd8abd76 --- /dev/null +++ b/Crypto/src/crypto/parameters/RsaPrivateCrtKeyParameters.cs
@@ -0,0 +1,104 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class RsaPrivateCrtKeyParameters + : RsaKeyParameters + { + private readonly BigInteger e, p, q, dP, dQ, qInv; + + public RsaPrivateCrtKeyParameters( + BigInteger modulus, + BigInteger publicExponent, + BigInteger privateExponent, + BigInteger p, + BigInteger q, + BigInteger dP, + BigInteger dQ, + BigInteger qInv) + : base(true, modulus, privateExponent) + { + ValidateValue(publicExponent, "publicExponent", "exponent"); + ValidateValue(p, "p", "P value"); + ValidateValue(q, "q", "Q value"); + ValidateValue(dP, "dP", "DP value"); + ValidateValue(dQ, "dQ", "DQ value"); + ValidateValue(qInv, "qInv", "InverseQ value"); + + this.e = publicExponent; + this.p = p; + this.q = q; + this.dP = dP; + this.dQ = dQ; + this.qInv = qInv; + } + + public BigInteger PublicExponent + { + get { return e; } + } + + public BigInteger P + { + get { return p; } + } + + public BigInteger Q + { + get { return q; } + } + + public BigInteger DP + { + get { return dP; } + } + + public BigInteger DQ + { + get { return dQ; } + } + + public BigInteger QInv + { + get { return qInv; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + RsaPrivateCrtKeyParameters kp = obj as RsaPrivateCrtKeyParameters; + + if (kp == null) + return false; + + return kp.DP.Equals(dP) + && kp.DQ.Equals(dQ) + && kp.Exponent.Equals(this.Exponent) + && kp.Modulus.Equals(this.Modulus) + && kp.P.Equals(p) + && kp.Q.Equals(q) + && kp.PublicExponent.Equals(e) + && kp.QInv.Equals(qInv); + } + + public override int GetHashCode() + { + return DP.GetHashCode() ^ DQ.GetHashCode() ^ Exponent.GetHashCode() ^ Modulus.GetHashCode() + ^ P.GetHashCode() ^ Q.GetHashCode() ^ PublicExponent.GetHashCode() ^ QInv.GetHashCode(); + } + + private static void ValidateValue(BigInteger x, string name, string desc) + { + if (x == null) + throw new ArgumentNullException(name); + if (x.SignValue <= 0) + throw new ArgumentException("Not a valid RSA " + desc, name); + } + } +} diff --git a/Crypto/src/crypto/prng/CryptoApiRandomGenerator.cs b/Crypto/src/crypto/prng/CryptoApiRandomGenerator.cs new file mode 100644
index 000000000..9e9e29cf1 --- /dev/null +++ b/Crypto/src/crypto/prng/CryptoApiRandomGenerator.cs
@@ -0,0 +1,66 @@ +#if !(NETCF_1_0 || PORTABLE) + +using System; +using System.Security.Cryptography; + +namespace Org.BouncyCastle.Crypto.Prng +{ + /// <summary> + /// Uses Microsoft's RNGCryptoServiceProvider + /// </summary> + public class CryptoApiRandomGenerator + : IRandomGenerator + { + private readonly RandomNumberGenerator rndProv; + + public CryptoApiRandomGenerator() + : this(new RNGCryptoServiceProvider()) + { + } + + public CryptoApiRandomGenerator(RandomNumberGenerator rng) + { + this.rndProv = rng; + } + + #region IRandomGenerator Members + + public virtual void AddSeedMaterial(byte[] seed) + { + // We don't care about the seed + } + + public virtual void AddSeedMaterial(long seed) + { + // We don't care about the seed + } + + public virtual void NextBytes(byte[] bytes) + { + rndProv.GetBytes(bytes); + } + + public virtual void NextBytes(byte[] bytes, int start, int len) + { + if (start < 0) + throw new ArgumentException("Start offset cannot be negative", "start"); + if (bytes.Length < (start + len)) + throw new ArgumentException("Byte array too small for requested offset and length"); + + if (bytes.Length == len && start == 0) + { + NextBytes(bytes); + } + else + { + byte[] tmpBuf = new byte[len]; + rndProv.GetBytes(tmpBuf); + Array.Copy(tmpBuf, 0, bytes, start, len); + } + } + + #endregion + } +} + +#endif diff --git a/Crypto/src/crypto/prng/DigestRandomGenerator.cs b/Crypto/src/crypto/prng/DigestRandomGenerator.cs new file mode 100644
index 000000000..cbd2ef060 --- /dev/null +++ b/Crypto/src/crypto/prng/DigestRandomGenerator.cs
@@ -0,0 +1,129 @@ +using System; + +using Org.BouncyCastle.Crypto.Digests; + +namespace Org.BouncyCastle.Crypto.Prng +{ + /** + * 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 + : IRandomGenerator + { + private const long CYCLE_COUNT = 10; + + private long stateCounter; + private long seedCounter; + private IDigest digest; + private byte[] state; + private byte[] seed; + + public DigestRandomGenerator( + IDigest 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) + { + lock (this) + { + DigestUpdate(inSeed); + DigestUpdate(seed); + DigestDoFinal(seed); + } + } + + public void AddSeedMaterial( + long rSeed) + { + lock (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) + { + lock (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 seedVal) + { + ulong seed = (ulong)seedVal; + for (int i = 0; i != 8; i++) + { + digest.Update((byte)seed); + seed >>= 8; + } + } + + private void DigestUpdate(byte[] inSeed) + { + digest.BlockUpdate(inSeed, 0, inSeed.Length); + } + + private void DigestDoFinal(byte[] result) + { + digest.DoFinal(result, 0); + } + } +} diff --git a/Crypto/src/crypto/prng/IRandomGenerator.cs b/Crypto/src/crypto/prng/IRandomGenerator.cs new file mode 100644
index 000000000..8dbe4068f --- /dev/null +++ b/Crypto/src/crypto/prng/IRandomGenerator.cs
@@ -0,0 +1,26 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Prng +{ + /// <remarks>Generic interface for objects generating random bytes.</remarks> + public interface IRandomGenerator + { + /// <summary>Add more seed material to the generator.</summary> + /// <param name="seed">A byte array to be mixed into the generator's state.</param> + void AddSeedMaterial(byte[] seed); + + /// <summary>Add more seed material to the generator.</summary> + /// <param name="seed">A long value to be mixed into the generator's state.</param> + void AddSeedMaterial(long seed); + + /// <summary>Fill byte array with random values.</summary> + /// <param name="bytes">Array to be filled.</param> + void NextBytes(byte[] bytes); + + /// <summary>Fill byte array with random values.</summary> + /// <param name="bytes">Array to receive bytes.</param> + /// <param name="start">Index to start filling at.</param> + /// <param name="len">Length of segment to fill.</param> + void NextBytes(byte[] bytes, int start, int len); + } +} diff --git a/Crypto/src/crypto/prng/ReversedWindowGenerator.cs b/Crypto/src/crypto/prng/ReversedWindowGenerator.cs new file mode 100644
index 000000000..dd28c525a --- /dev/null +++ b/Crypto/src/crypto/prng/ReversedWindowGenerator.cs
@@ -0,0 +1,98 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Prng +{ + /// <remarks> + /// 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> + /// </remarks> + public class ReversedWindowGenerator + : IRandomGenerator + { + private readonly IRandomGenerator generator; + + private byte[] window; + private int windowCount; + + public ReversedWindowGenerator( + IRandomGenerator generator, + int windowSize) + { + if (generator == null) + throw new ArgumentNullException("generator"); + if (windowSize < 2) + throw new ArgumentException("Window size must be at least 2", "windowSize"); + + this.generator = generator; + this.window = new byte[windowSize]; + } + + /// <summary>Add more seed material to the generator.</summary> + /// <param name="seed">A byte array to be mixed into the generator's state.</param> + public virtual void AddSeedMaterial( + byte[] seed) + { + lock (this) + { + windowCount = 0; + generator.AddSeedMaterial(seed); + } + } + + /// <summary>Add more seed material to the generator.</summary> + /// <param name="seed">A long value to be mixed into the generator's state.</param> + public virtual void AddSeedMaterial( + long seed) + { + lock (this) + { + windowCount = 0; + generator.AddSeedMaterial(seed); + } + } + + /// <summary>Fill byte array with random values.</summary> + /// <param name="bytes">Array to be filled.</param> + public virtual void NextBytes( + byte[] bytes) + { + doNextBytes(bytes, 0, bytes.Length); + } + + /// <summary>Fill byte array with random values.</summary> + /// <param name="bytes">Array to receive bytes.</param> + /// <param name="start">Index to start filling at.</param> + /// <param name="len">Length of segment to fill.</param> + public virtual void NextBytes( + byte[] bytes, + int start, + int len) + { + doNextBytes(bytes, start, len); + } + + private void doNextBytes( + byte[] bytes, + int start, + int len) + { + lock (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/Crypto/src/crypto/prng/ThreadedSeedGenerator.cs b/Crypto/src/crypto/prng/ThreadedSeedGenerator.cs new file mode 100644
index 000000000..9f918ea6e --- /dev/null +++ b/Crypto/src/crypto/prng/ThreadedSeedGenerator.cs
@@ -0,0 +1,99 @@ +using System; +using System.Threading; + +namespace 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 + { +#if NETCF_1_0 + // No volatile keyword, but all fields implicitly volatile anyway + private int counter = 0; + private bool stop = false; +#else + private volatile int counter = 0; + private volatile bool stop = false; +#endif + + private void Run(object ignored) + { + while (!this.stop) + { + this.counter++; + } + } + + public byte[] GenerateSeed( + int numBytes, + bool fast) + { + this.counter = 0; + this.stop = false; + + byte[] result = new byte[numBytes]; + int last = 0; + int end = fast ? numBytes : numBytes * 8; + + ThreadPool.QueueUserWorkItem(new WaitCallback(Run)); + + for (int i = 0; i < end; i++) + { + var waitEvent = new ManualResetEvent(false); + + while (this.counter == last) + { + try + { + waitEvent.WaitOne(1); + } + catch (Exception) + { + // ignore + } + } + + last = this.counter; + + if (fast) + { + result[i] = (byte) last; + } + else + { + int bytepos = i / 8; + result[bytepos] = (byte) ((result[bytepos] << 1) | (last & 1)); + } + } + + this.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, + bool fast) + { + return new SeedGenerator().GenerateSeed(numBytes, fast); + } + } +} diff --git a/Crypto/src/crypto/prng/VMPCRandomGenerator.cs b/Crypto/src/crypto/prng/VMPCRandomGenerator.cs new file mode 100644
index 000000000..2ab079999 --- /dev/null +++ b/Crypto/src/crypto/prng/VMPCRandomGenerator.cs
@@ -0,0 +1,115 @@ +namespace Org.BouncyCastle.Crypto.Prng +{ + public class VmpcRandomGenerator + : IRandomGenerator + { + private byte n = 0; + + /// <remarks> + /// 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 &lt; 256; i++) + /// { + /// P[i] = (byte) i; + /// } + /// for (int m = 0; m &lt; 768; m++) + /// { + /// s = P[(s + P[m &amp; 0xff] + key[m % key.length]) &amp; 0xff]; + /// byte temp = P[m &amp; 0xff]; + /// P[m &amp; 0xff] = P[s &amp; 0xff]; + /// P[s &amp; 0xff] = temp; + /// } </code> + /// </remarks> + 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 + }; + + /// <remarks>Value generated in the same way as <c>P</c>.</remarks> + private byte s = (byte) 0xbe; + + public VmpcRandomGenerator() + { + } + + public virtual 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 virtual void AddSeedMaterial(long seed) + { + byte[] s = new byte[4]; + s[3] = (byte) (seed & 0x000000ff); + s[2] = (byte) ((seed & 0x0000ff00) >> 8); + s[1] = (byte) ((seed & 0x00ff0000) >> 16); + s[0] = (byte) ((seed & 0xff000000) >> 24); + AddSeedMaterial(s); + } + + public virtual void NextBytes(byte[] bytes) + { + NextBytes(bytes, 0, bytes.Length); + } + + public virtual void NextBytes(byte[] bytes, int start, int len) + { + lock (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/Crypto/src/crypto/signers/DsaDigestSigner.cs b/Crypto/src/crypto/signers/DsaDigestSigner.cs new file mode 100644
index 000000000..aee713450 --- /dev/null +++ b/Crypto/src/crypto/signers/DsaDigestSigner.cs
@@ -0,0 +1,145 @@ +using System; +using System.Collections; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Crypto.Signers; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Signers +{ + public class DsaDigestSigner + : ISigner + { + private readonly IDigest digest; + private readonly IDsa dsaSigner; + private bool forSigning; + + public DsaDigestSigner( + IDsa signer, + IDigest digest) + { + this.digest = digest; + this.dsaSigner = signer; + } + + public string AlgorithmName + { + get { return digest.AlgorithmName + "with" + dsaSigner.AlgorithmName; } + } + + public void Init( + bool forSigning, + ICipherParameters parameters) + { + this.forSigning = forSigning; + + AsymmetricKeyParameter k; + + if (parameters is ParametersWithRandom) + { + k = (AsymmetricKeyParameter)((ParametersWithRandom)parameters).Parameters; + } + else + { + k = (AsymmetricKeyParameter)parameters; + } + + if (forSigning && !k.IsPrivate) + throw new InvalidKeyException("Signing Requires Private Key."); + + if (!forSigning && k.IsPrivate) + throw new InvalidKeyException("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 BlockUpdate( + byte[] input, + int inOff, + int length) + { + digest.BlockUpdate(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 InvalidOperationException("DSADigestSigner not initialised for signature generation."); + + byte[] hash = new byte[digest.GetDigestSize()]; + digest.DoFinal(hash, 0); + + BigInteger[] sig = dsaSigner.GenerateSignature(hash); + + return DerEncode(sig[0], sig[1]); + } + + /// <returns>true if the internal state represents the signature described in the passed in array.</returns> + public bool VerifySignature( + byte[] signature) + { + if (forSigning) + throw new InvalidOperationException("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) + { + return false; + } + } + + /// <summary>Reset the internal state</summary> + public void Reset() + { + digest.Reset(); + } + + private byte[] DerEncode( + BigInteger r, + BigInteger s) + { + return new DerSequence(new DerInteger(r), new DerInteger(s)).GetDerEncoded(); + } + + private BigInteger[] DerDecode( + byte[] encoding) + { + Asn1Sequence s = (Asn1Sequence) Asn1Object.FromByteArray(encoding); + + return new BigInteger[] + { + ((DerInteger) s[0]).Value, + ((DerInteger) s[1]).Value + }; + } + } +} diff --git a/Crypto/src/crypto/signers/DsaSigner.cs b/Crypto/src/crypto/signers/DsaSigner.cs new file mode 100644
index 000000000..419b1972e --- /dev/null +++ b/Crypto/src/crypto/signers/DsaSigner.cs
@@ -0,0 +1,136 @@ +using System; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Signers +{ + /** + * The Digital Signature Algorithm - as described in "Handbook of Applied + * Cryptography", pages 452 - 453. + */ + public class DsaSigner + : IDsa + { + private DsaKeyParameters key; + private SecureRandom random; + + public string AlgorithmName + { + get { return "DSA"; } + } + + public void Init( + bool forSigning, + ICipherParameters parameters) + { + if (forSigning) + { + if (parameters is ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)parameters; + + this.random = rParam.Random; + parameters = rParam.Parameters; + } + else + { + this.random = new SecureRandom(); + } + + if (!(parameters is DsaPrivateKeyParameters)) + throw new InvalidKeyException("DSA private key required for signing"); + + this.key = (DsaPrivateKeyParameters) parameters; + } + else + { + if (!(parameters is DsaPublicKeyParameters)) + throw new InvalidKeyException("DSA public key required for verification"); + + this.key = (DsaPublicKeyParameters) parameters; + } + } + + /** + * 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 parameters = key.Parameters; + BigInteger q = parameters.Q; + BigInteger m = calculateE(q, message); + BigInteger k; + + do + { + k = new BigInteger(q.BitLength, random); + } + while (k.CompareTo(q) >= 0); + + BigInteger r = parameters.G.ModPow(k, parameters.P).Mod(q); + + k = k.ModInverse(q).Multiply( + m.Add(((DsaPrivateKeyParameters)key).X.Multiply(r))); + + BigInteger s = k.Mod(q); + + return new BigInteger[]{ r, s }; + } + + /** + * 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 bool VerifySignature( + byte[] message, + BigInteger r, + BigInteger s) + { + DsaParameters parameters = key.Parameters; + BigInteger q = parameters.Q; + BigInteger m = calculateE(q, message); + + if (r.SignValue <= 0 || q.CompareTo(r) <= 0) + { + return false; + } + + if (s.SignValue <= 0 || q.CompareTo(s) <= 0) + { + return false; + } + + BigInteger w = s.ModInverse(q); + + BigInteger u1 = m.Multiply(w).Mod(q); + BigInteger u2 = r.Multiply(w).Mod(q); + + BigInteger p = parameters.P; + u1 = parameters.G.ModPow(u1, p); + u2 = ((DsaPublicKeyParameters)key).Y.ModPow(u2, p); + + BigInteger v = u1.Multiply(u2).Mod(p).Mod(q); + + return v.Equals(r); + } + + private BigInteger calculateE( + BigInteger n, + byte[] message) + { + int length = System.Math.Min(message.Length, n.BitLength / 8); + + return new BigInteger(1, message, 0, length); + } + } +} diff --git a/Crypto/src/crypto/signers/ECDsaSigner.cs b/Crypto/src/crypto/signers/ECDsaSigner.cs new file mode 100644
index 000000000..4254e5590 --- /dev/null +++ b/Crypto/src/crypto/signers/ECDsaSigner.cs
@@ -0,0 +1,156 @@ +using System; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Signers +{ + /** + * EC-DSA as described in X9.62 + */ + public class ECDsaSigner + : IDsa + { + private ECKeyParameters key; + private SecureRandom random; + + public string AlgorithmName + { + get { return "ECDSA"; } + } + + public void Init( + bool forSigning, + ICipherParameters parameters) + { + if (forSigning) + { + if (parameters is ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom) parameters; + + this.random = rParam.Random; + parameters = rParam.Parameters; + } + else + { + this.random = new SecureRandom(); + } + + if (!(parameters is ECPrivateKeyParameters)) + throw new InvalidKeyException("EC private key required for signing"); + + this.key = (ECPrivateKeyParameters) parameters; + } + else + { + if (!(parameters is ECPublicKeyParameters)) + throw new InvalidKeyException("EC public key required for verification"); + + this.key = (ECPublicKeyParameters) parameters; + } + } + + // 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.Parameters.N; + BigInteger e = calculateE(n, message); + + BigInteger r = null; + BigInteger s = null; + + // 5.3.2 + do // Generate s + { + BigInteger k = null; + + do // Generate r + { + do + { + k = new BigInteger(n.BitLength, random); + } + while (k.SignValue == 0 || k.CompareTo(n) >= 0); + + ECPoint p = key.Parameters.G.Multiply(k); + + // 5.3.3 + BigInteger x = p.X.ToBigInteger(); + + r = x.Mod(n); + } + while (r.SignValue == 0); + + BigInteger d = ((ECPrivateKeyParameters)key).D; + + s = k.ModInverse(n).Multiply(e.Add(d.Multiply(r).Mod(n))).Mod(n); + } + while (s.SignValue == 0); + + return new BigInteger[]{ r, s }; + } + + // 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 bool VerifySignature( + byte[] message, + BigInteger r, + BigInteger s) + { + BigInteger n = key.Parameters.N; + + // r and s should both in the range [1,n-1] + if (r.SignValue < 1 || s.SignValue < 1 + || r.CompareTo(n) >= 0 || s.CompareTo(n) >= 0) + { + return false; + } + + BigInteger e = calculateE(n, message); + BigInteger c = s.ModInverse(n); + + BigInteger u1 = e.Multiply(c).Mod(n); + BigInteger u2 = r.Multiply(c).Mod(n); + + ECPoint G = key.Parameters.G; + ECPoint Q = ((ECPublicKeyParameters) key).Q; + + ECPoint point = ECAlgorithms.SumOfTwoMultiplies(G, u1, Q, u2); + + BigInteger v = point.X.ToBigInteger().Mod(n); + + return v.Equals(r); + } + + private BigInteger calculateE( + BigInteger n, + byte[] message) + { + int messageBitLength = message.Length * 8; + BigInteger trunc = new BigInteger(1, message); + + if (n.BitLength < messageBitLength) + { + trunc = trunc.ShiftRight(messageBitLength - n.BitLength); + } + + return trunc; + } + } +} diff --git a/Crypto/src/crypto/signers/ECGOST3410Signer.cs b/Crypto/src/crypto/signers/ECGOST3410Signer.cs new file mode 100644
index 000000000..d68b83f67 --- /dev/null +++ b/Crypto/src/crypto/signers/ECGOST3410Signer.cs
@@ -0,0 +1,154 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Signers +{ + /** + * GOST R 34.10-2001 Signature Algorithm + */ + public class ECGost3410Signer + : IDsa + { + private ECKeyParameters key; + private SecureRandom random; + + public string AlgorithmName + { + get { return "ECGOST3410"; } + } + + public void Init( + bool forSigning, + ICipherParameters parameters) + { + if (forSigning) + { + if (parameters is ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)parameters; + + this.random = rParam.Random; + parameters = rParam.Parameters; + } + else + { + this.random = new SecureRandom(); + } + + if (!(parameters is ECPrivateKeyParameters)) + throw new InvalidKeyException("EC private key required for signing"); + + this.key = (ECPrivateKeyParameters) parameters; + } + else + { + if (!(parameters is ECPublicKeyParameters)) + throw new InvalidKeyException("EC public key required for verification"); + + this.key = (ECPublicKeyParameters)parameters; + } + } + + /** + * 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.Parameters.N; + + BigInteger r = null; + BigInteger s = null; + + do // generate s + { + BigInteger k = null; + + do // generate r + { + do + { + k = new BigInteger(n.BitLength, random); + } + while (k.SignValue == 0); + + ECPoint p = key.Parameters.G.Multiply(k); + + BigInteger x = p.X.ToBigInteger(); + + r = x.Mod(n); + } + while (r.SignValue == 0); + + BigInteger d = ((ECPrivateKeyParameters)key).D; + + s = (k.Multiply(e)).Add(d.Multiply(r)).Mod(n); + } + while (s.SignValue == 0); + + return new BigInteger[]{ r, s }; + } + + /** + * 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 bool 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.Parameters.N; + + // r in the range [1,n-1] + if (r.CompareTo(BigInteger.One) < 0 || r.CompareTo(n) >= 0) + { + return false; + } + + // s in the range [1,n-1] + if (s.CompareTo(BigInteger.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.Parameters.G; // P + ECPoint Q = ((ECPublicKeyParameters)key).Q; + + ECPoint point = ECAlgorithms.SumOfTwoMultiplies(G, z1, Q, z2); + + BigInteger R = point.X.ToBigInteger().Mod(n); + + return R.Equals(r); + } + } +} diff --git a/Crypto/src/crypto/signers/ECNRSigner.cs b/Crypto/src/crypto/signers/ECNRSigner.cs new file mode 100644
index 000000000..63865d731 --- /dev/null +++ b/Crypto/src/crypto/signers/ECNRSigner.cs
@@ -0,0 +1,186 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Signers +{ + /** + * EC-NR as described in IEEE 1363-2000 + */ + public class ECNRSigner + : IDsa + { + private bool forSigning; + private ECKeyParameters key; + private SecureRandom random; + + public string AlgorithmName + { + get { return "ECNR"; } + } + + public void Init( + bool forSigning, + ICipherParameters parameters) + { + this.forSigning = forSigning; + + if (forSigning) + { + if (parameters is ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom) parameters; + + this.random = rParam.Random; + parameters = rParam.Parameters; + } + else + { + this.random = new SecureRandom(); + } + + if (!(parameters is ECPrivateKeyParameters)) + throw new InvalidKeyException("EC private key required for signing"); + + this.key = (ECPrivateKeyParameters) parameters; + } + else + { + if (!(parameters is ECPublicKeyParameters)) + throw new InvalidKeyException("EC public key required for verification"); + + this.key = (ECPublicKeyParameters) parameters; + } + } + + // Section 7.2.5 ECSP-NR, pg 34 + /** + * generate a signature for the given message using the key we were + * initialised with. Generally, the order of the curve should be at + * least as long as the hash of the message of interest, and with + * ECNR it *must* be at least as long. + * + * @param digest the digest to be signed. + * @exception DataLengthException if the digest is longer than the key allows + */ + public BigInteger[] GenerateSignature( + byte[] message) + { + if (!this.forSigning) + { + // not properly initilaized... deal with it + throw new InvalidOperationException("not initialised for signing"); + } + + BigInteger n = ((ECPrivateKeyParameters) this.key).Parameters.N; + int nBitLength = n.BitLength; + + BigInteger e = new BigInteger(1, message); + int eBitLength = e.BitLength; + + ECPrivateKeyParameters privKey = (ECPrivateKeyParameters)key; + + if (eBitLength > nBitLength) + { + throw new DataLengthException("input too large for ECNR key."); + } + + BigInteger r = null; + BigInteger s = null; + + AsymmetricCipherKeyPair tempPair; + do // generate r + { + // generate another, but very temporary, key pair using + // the same EC parameters + ECKeyPairGenerator keyGen = new ECKeyPairGenerator(); + + keyGen.Init(new ECKeyGenerationParameters(privKey.Parameters, this.random)); + + tempPair = keyGen.GenerateKeyPair(); + + // BigInteger Vx = tempPair.getPublic().getW().getAffineX(); + ECPublicKeyParameters V = (ECPublicKeyParameters) tempPair.Public; // get temp's public key + BigInteger Vx = V.Q.X.ToBigInteger(); // get the point's x coordinate + + r = Vx.Add(e).Mod(n); + } + while (r.SignValue == 0); + + // generate s + BigInteger x = privKey.D; // private key value + BigInteger u = ((ECPrivateKeyParameters) tempPair.Private).D; // temp's private key value + s = u.Subtract(r.Multiply(x)).Mod(n); + + return new BigInteger[]{ r, s }; + } + + // Section 7.2.6 ECVP-NR, pg 35 + /** + * return true if the value r and s represent a signature for the + * message passed in. Generally, the order of the curve should be at + * least as long as the hash of the message of interest, and with + * ECNR, it *must* be at least as long. But just in case the signer + * applied mod(n) to the longer digest, this implementation will + * apply mod(n) during verification. + * + * @param digest the digest to be verified. + * @param r the r value of the signature. + * @param s the s value of the signature. + * @exception DataLengthException if the digest is longer than the key allows + */ + public bool VerifySignature( + byte[] message, + BigInteger r, + BigInteger s) + { + if (this.forSigning) + { + // not properly initilaized... deal with it + throw new InvalidOperationException("not initialised for verifying"); + } + + ECPublicKeyParameters pubKey = (ECPublicKeyParameters)key; + BigInteger n = pubKey.Parameters.N; + int nBitLength = n.BitLength; + + BigInteger e = new BigInteger(1, message); + int eBitLength = e.BitLength; + + if (eBitLength > nBitLength) + { + throw new DataLengthException("input too large for ECNR key."); + } + + // r in the range [1,n-1] + if (r.CompareTo(BigInteger.One) < 0 || r.CompareTo(n) >= 0) + { + return false; + } + + // TODO So why is this different from the spec? + // s in the range [0,n-1] NB: ECNR spec says 0 + if (s.CompareTo(BigInteger.Zero) < 0 || s.CompareTo(n) >= 0) + { + return false; + } + + // compute P = sG + rW + + ECPoint G = pubKey.Parameters.G; + ECPoint W = pubKey.Q; + // calculate P using Bouncy math + ECPoint P = ECAlgorithms.SumOfTwoMultiplies(G, s, W, r); + + BigInteger x = P.X.ToBigInteger(); + BigInteger t = r.Subtract(x).Mod(n); + + return t.Equals(e); + } + } +} diff --git a/Crypto/src/crypto/signers/GOST3410DigestSigner.cs b/Crypto/src/crypto/signers/GOST3410DigestSigner.cs new file mode 100644
index 000000000..58aefa368 --- /dev/null +++ b/Crypto/src/crypto/signers/GOST3410DigestSigner.cs
@@ -0,0 +1,145 @@ +using System; +using System.Collections; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Crypto.Signers; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Signers +{ + public class Gost3410DigestSigner + : ISigner + { + private readonly IDigest digest; + private readonly IDsa dsaSigner; + private bool forSigning; + + public Gost3410DigestSigner( + IDsa signer, + IDigest digest) + { + this.dsaSigner = signer; + this.digest = digest; + } + + public string AlgorithmName + { + get { return digest.AlgorithmName + "with" + dsaSigner.AlgorithmName; } + } + + public void Init( + bool forSigning, + ICipherParameters parameters) + { + this.forSigning = forSigning; + + AsymmetricKeyParameter k; + if (parameters is ParametersWithRandom) + { + k = (AsymmetricKeyParameter)((ParametersWithRandom)parameters).Parameters; + } + else + { + k = (AsymmetricKeyParameter)parameters; + } + + if (forSigning && !k.IsPrivate) + { + throw new InvalidKeyException("Signing Requires Private Key."); + } + + if (!forSigning && k.IsPrivate) + { + throw new InvalidKeyException("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 BlockUpdate( + byte[] input, + int inOff, + int length) + { + digest.BlockUpdate(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 InvalidOperationException("GOST3410DigestSigner not initialised for signature generation."); + + byte[] hash = new byte[digest.GetDigestSize()]; + digest.DoFinal(hash, 0); + + try + { + BigInteger[] sig = dsaSigner.GenerateSignature(hash); + byte[] sigBytes = new byte[64]; + + // TODO Add methods to allow writing BigInteger to existing byte array? + byte[] r = sig[0].ToByteArrayUnsigned(); + byte[] s = sig[1].ToByteArrayUnsigned(); + s.CopyTo(sigBytes, 32 - s.Length); + r.CopyTo(sigBytes, 64 - r.Length); + return sigBytes; + } + catch (Exception e) + { + throw new SignatureException(e.Message, e); + } + } + + /// <returns>true if the internal state represents the signature described in the passed in array.</returns> + public bool VerifySignature( + byte[] signature) + { + if (forSigning) + throw new InvalidOperationException("DSADigestSigner not initialised for verification"); + + byte[] hash = new byte[digest.GetDigestSize()]; + digest.DoFinal(hash, 0); + + BigInteger R, S; + try + { + R = new BigInteger(1, signature, 32, 32); + S = new BigInteger(1, signature, 0, 32); + } + catch (Exception e) + { + throw new SignatureException("error decoding signature bytes.", e); + } + + return dsaSigner.VerifySignature(hash, R, S); + } + + /// <summary>Reset the internal state</summary> + public void Reset() + { + digest.Reset(); + } + } +} diff --git a/Crypto/src/crypto/signers/GOST3410Signer.cs b/Crypto/src/crypto/signers/GOST3410Signer.cs new file mode 100644
index 000000000..375eeb5cc --- /dev/null +++ b/Crypto/src/crypto/signers/GOST3410Signer.cs
@@ -0,0 +1,132 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Signers +{ + /** + * Gost R 34.10-94 Signature Algorithm + */ + public class Gost3410Signer + : IDsa + { + private Gost3410KeyParameters key; + private SecureRandom random; + + public string AlgorithmName + { + get { return "GOST3410"; } + } + + public void Init( + bool forSigning, + ICipherParameters parameters) + { + if (forSigning) + { + if (parameters is ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)parameters; + + this.random = rParam.Random; + parameters = rParam.Parameters; + } + else + { + this.random = new SecureRandom(); + } + + if (!(parameters is Gost3410PrivateKeyParameters)) + throw new InvalidKeyException("GOST3410 private key required for signing"); + + this.key = (Gost3410PrivateKeyParameters) parameters; + } + else + { + if (!(parameters is Gost3410PublicKeyParameters)) + throw new InvalidKeyException("GOST3410 public key required for signing"); + + this.key = (Gost3410PublicKeyParameters) parameters; + } + } + + /** + * 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 parameters = key.Parameters; + BigInteger k; + + do + { + k = new BigInteger(parameters.Q.BitLength, random); + } + while (k.CompareTo(parameters.Q) >= 0); + + BigInteger r = parameters.A.ModPow(k, parameters.P).Mod(parameters.Q); + + BigInteger s = k.Multiply(m). + Add(((Gost3410PrivateKeyParameters)key).X.Multiply(r)). + Mod(parameters.Q); + + return new BigInteger[]{ r, s }; + } + + /** + * 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 bool 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 parameters = key.Parameters; + + if (r.SignValue < 0 || parameters.Q.CompareTo(r) <= 0) + { + return false; + } + + if (s.SignValue < 0 || parameters.Q.CompareTo(s) <= 0) + { + return false; + } + + BigInteger v = m.ModPow(parameters.Q.Subtract(BigInteger.Two), parameters.Q); + + BigInteger z1 = s.Multiply(v).Mod(parameters.Q); + BigInteger z2 = (parameters.Q.Subtract(r)).Multiply(v).Mod(parameters.Q); + + z1 = parameters.A.ModPow(z1, parameters.P); + z2 = ((Gost3410PublicKeyParameters)key).Y.ModPow(z2, parameters.P); + + BigInteger u = z1.Multiply(z2).Mod(parameters.P).Mod(parameters.Q); + + return u.Equals(r); + } + } +} diff --git a/Crypto/src/crypto/signers/GenericSigner.cs b/Crypto/src/crypto/signers/GenericSigner.cs new file mode 100644
index 000000000..1a53eee2b --- /dev/null +++ b/Crypto/src/crypto/signers/GenericSigner.cs
@@ -0,0 +1,129 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Signers +{ + public class GenericSigner + : ISigner + { + private readonly IAsymmetricBlockCipher engine; + private readonly IDigest digest; + private bool forSigning; + + public GenericSigner( + IAsymmetricBlockCipher engine, + IDigest digest) + { + this.engine = engine; + this.digest = digest; + } + + public string AlgorithmName + { + get { return "Generic(" + engine.AlgorithmName + "/" + digest.AlgorithmName + ")"; } + } + + /** + * initialise the signer for signing or verification. + * + * @param forSigning + * true if for signing, false otherwise + * @param parameters + * necessary parameters. + */ + public void Init( + bool forSigning, + ICipherParameters parameters) + { + this.forSigning = forSigning; + AsymmetricKeyParameter k; + + if (parameters is ParametersWithRandom) + { + k = (AsymmetricKeyParameter)((ParametersWithRandom)parameters).Parameters; + } + else + { + k = (AsymmetricKeyParameter)parameters; + } + + if (forSigning && !k.IsPrivate) + throw new InvalidKeyException("Signing requires private key."); + + if (!forSigning && k.IsPrivate) + throw new InvalidKeyException("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 BlockUpdate( + byte[] input, + int inOff, + int length) + { + digest.BlockUpdate(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 InvalidOperationException("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 bool VerifySignature( + byte[] signature) + { + if (forSigning) + throw new InvalidOperationException("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) + { + return false; + } + } + + public void Reset() + { + digest.Reset(); + } + } +} diff --git a/Crypto/src/crypto/signers/Iso9796d2PssSigner.cs b/Crypto/src/crypto/signers/Iso9796d2PssSigner.cs new file mode 100644
index 000000000..48cd719e9 --- /dev/null +++ b/Crypto/src/crypto/signers/Iso9796d2PssSigner.cs
@@ -0,0 +1,576 @@ +using System; + +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Signers +{ + /// <summary> 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.</p> + /// </summary> + public class Iso9796d2PssSigner + : ISignerWithRecovery + { + /// <summary> + /// Return a reference to the recoveredMessage message. + /// </summary> + /// <returns>The full/partial recoveredMessage message.</returns> + /// <seealso cref="ISignerWithRecovery.GetRecoveredMessage"/> + public byte[] GetRecoveredMessage() + { + return recoveredMessage; + } + + public const int TrailerImplicit = 0xBC; + public const int TrailerRipeMD160 = 0x31CC; + public const int TrailerRipeMD128 = 0x32CC; + public const int TrailerSha1 = 0x33CC; + + private IDigest digest; + private IAsymmetricBlockCipher 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 readonly int saltLength; + private bool fullMessage; + private byte[] recoveredMessage; + + /// <summary> + /// Generate a signer for the with either implicit or explicit trailers + /// for ISO9796-2, scheme 2 or 3. + /// </summary> + /// <param name="cipher">base cipher to use for signature creation/verification</param> + /// <param name="digest">digest to use.</param> + /// <param name="saltLength">length of salt in bytes.</param> + /// <param name="isImplicit">whether or not the trailer is implicit or gives the hash.</param> + public Iso9796d2PssSigner( + IAsymmetricBlockCipher cipher, + IDigest digest, + int saltLength, + bool isImplicit) + { + this.cipher = cipher; + this.digest = digest; + this.hLen = digest.GetDigestSize(); + this.saltLength = saltLength; + + if (isImplicit) + { + trailer = TrailerImplicit; + } + else + { + if (digest is Sha1Digest) + { + trailer = TrailerSha1; + } + else if (digest is RipeMD160Digest) + { + trailer = TrailerRipeMD160; + } + else if (digest is RipeMD128Digest) + { + trailer = TrailerRipeMD128; + } + else + { + throw new ArgumentException("no valid trailer for digest"); + } + } + } + + /// <summary> Constructor for a signer with an explicit digest trailer. + /// + /// </summary> + /// <param name="cipher">cipher to use. + /// </param> + /// <param name="digest">digest to sign with. + /// </param> + /// <param name="saltLength">length of salt in bytes. + /// </param> + public Iso9796d2PssSigner( + IAsymmetricBlockCipher cipher, + IDigest digest, + int saltLength) + : this(cipher, digest, saltLength, false) + { + } + + public string AlgorithmName + { + get { return digest.AlgorithmName + "with" + "ISO9796-2S2"; } + } + + /// <summary>Initialise the signer.</summary> + /// <param name="forSigning">true if for signing, false if for verification.</param> + /// <param name="parameters">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. + /// </param> + /// <exception cref="ArgumentException">if wrong parameter type or a fixed + /// salt is passed in which is the wrong length. + /// </exception> + public virtual void Init( + bool forSigning, + ICipherParameters parameters) + { + RsaKeyParameters kParam; + if (parameters is ParametersWithRandom) + { + ParametersWithRandom p = (ParametersWithRandom) parameters; + + kParam = (RsaKeyParameters) p.Parameters; + + if (forSigning) + { + random = p.Random; + } + } + else if (parameters is ParametersWithSalt) + { + if (!forSigning) + throw new ArgumentException("ParametersWithSalt only valid for signing", "parameters"); + + ParametersWithSalt p = (ParametersWithSalt) parameters; + + kParam = (RsaKeyParameters) p.Parameters; + standardSalt = p.GetSalt(); + + if (standardSalt.Length != saltLength) + throw new ArgumentException("Fixed salt is of wrong length"); + } + else + { + kParam = (RsaKeyParameters) parameters; + + if (forSigning) + { + random = new SecureRandom(); + } + } + + cipher.Init(forSigning, kParam); + + keyBits = kParam.Modulus.BitLength; + + block = new byte[(keyBits + 7) / 8]; + + if (trailer == TrailerImplicit) + { + mBuf = new byte[block.Length - digest.GetDigestSize() - saltLength - 1 - 1]; + } + else + { + mBuf = new byte[block.Length - digest.GetDigestSize() - saltLength - 1 - 2]; + } + + Reset(); + } + + /// <summary> compare two byte arrays - constant time.</summary> + private bool IsSameAs(byte[] a, byte[] b) + { + if (messageLength != b.Length) + { + return false; + } + + bool isOkay = true; + + for (int i = 0; i != b.Length; i++) + { + if (a[i] != b[i]) + { + isOkay = false; + } + } + + return isOkay; + } + + /// <summary> clear possible sensitive data</summary> + private void ClearBlock( + byte[] block) + { + Array.Clear(block, 0, block.Length); + } + + public virtual void UpdateWithRecoveredMessage( + byte[] signature) + { + // TODO + throw Platform.CreateNotImplementedException("UpdateWithRecoveredMessage"); + } + + /// <summary> update the internal digest with the byte b</summary> + public virtual void Update( + byte input) + { + if (messageLength < mBuf.Length) + { + mBuf[messageLength++] = input; + } + else + { + digest.Update(input); + } + } + + /// <summary> update the internal digest with the byte array in</summary> + public virtual void BlockUpdate( + byte[] input, + int inOff, + int length) + { + while (length > 0 && messageLength < mBuf.Length) + { + this.Update(input[inOff]); + inOff++; + length--; + } + + if (length > 0) + { + digest.BlockUpdate(input, inOff, length); + } + } + + /// <summary> reset the internal state</summary> + public virtual void Reset() + { + digest.Reset(); + messageLength = 0; + if (mBuf != null) + { + ClearBlock(mBuf); + } + if (recoveredMessage != null) + { + ClearBlock(recoveredMessage); + recoveredMessage = null; + } + fullMessage = false; + } + + /// <summary> Generate a signature for the loaded message using the key we were + /// initialised with. + /// </summary> + public byte[] GenerateSignature() + { + int digSize = digest.GetDigestSize(); + byte[] m2Hash = new byte[digSize]; + digest.DoFinal(m2Hash, 0); + + byte[] C = new byte[8]; + LtoOSP(messageLength * 8, C); + + digest.BlockUpdate(C, 0, C.Length); + digest.BlockUpdate(mBuf, 0, messageLength); + digest.BlockUpdate(m2Hash, 0, m2Hash.Length); + + byte[] salt; + if (standardSalt != null) + { + salt = standardSalt; + } + else + { + salt = new byte[saltLength]; + random.NextBytes(salt); + } + + digest.BlockUpdate(salt, 0, salt.Length); + + byte[] hash = new byte[digest.GetDigestSize()]; + digest.DoFinal(hash, 0); + + int tLength = 2; + if (trailer == TrailerImplicit) + { + tLength = 1; + } + + int off = block.Length - messageLength - salt.Length - hLen - tLength - 1; + + block[off] = (byte) (0x01); + + Array.Copy(mBuf, 0, block, off + 1, messageLength); + Array.Copy(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]; + } + + Array.Copy(hash, 0, block, block.Length - hLen - tLength, hLen); + + if (trailer == TrailerImplicit) + { + block[block.Length - 1] = (byte)TrailerImplicit; + } + else + { + block[block.Length - 2] = (byte) ((uint)trailer >> 8); + block[block.Length - 1] = (byte) trailer; + } + + block[0] &= (byte) (0x7f); + + byte[] b = cipher.ProcessBlock(block, 0, block.Length); + + ClearBlock(mBuf); + ClearBlock(block); + messageLength = 0; + + return b; + } + + /// <summary> return true if the signature represents a ISO9796-2 signature + /// for the passed in message. + /// </summary> + public virtual bool VerifySignature( + byte[] signature) + { + byte[] block = cipher.ProcessBlock(signature, 0, signature.Length); + + // + // adjust block size for leading zeroes if necessary + // + int expectedSize = (keyBits + 7) / 8; + if (block.Length < expectedSize) + { + byte[] tmp = new byte[expectedSize]; + block.CopyTo(tmp, tmp.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); + + switch (sigTrail) + { + case TrailerRipeMD160: + if (!(digest is RipeMD160Digest)) + { + throw new ArgumentException("signer should be initialised with RipeMD160"); + } + break; + case TrailerSha1: + if (!(digest is Sha1Digest)) + { + throw new ArgumentException("signer should be initialised with SHA1"); + } + break; + case TrailerRipeMD128: + if (!(digest is RipeMD128Digest)) + { + throw new ArgumentException("signer should be initialised with RipeMD128"); + } + break; + default: + throw new ArgumentException("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; + while (mStart < block.Length) + { + if (block[mStart++] == 0x01) + break; + } + + if (mStart >= block.Length) + { + ClearBlock(block); + return false; + } + + fullMessage = (mStart > 1); + + // TODO Should we check if a standardSalt was set and, if so, use its length instead? + recoveredMessage = new byte[dbMask.Length - mStart - saltLength]; + + Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length); + + // + // check the hashes + // + byte[] C = new byte[8]; + LtoOSP(recoveredMessage.Length * 8, C); + + digest.BlockUpdate(C, 0, C.Length); + + if (recoveredMessage.Length != 0) + { + digest.BlockUpdate(recoveredMessage, 0, recoveredMessage.Length); + } + + digest.BlockUpdate(m2Hash, 0, m2Hash.Length); + + // Update for the salt + digest.BlockUpdate(block, mStart + recoveredMessage.Length, saltLength); + + byte[] hash = new byte[digest.GetDigestSize()]; + digest.DoFinal(hash, 0); + + int off = block.Length - tLength - hash.Length; + + // TODO ConstantTimeAreEqual with offset for one array + + bool 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; + } + + /// <summary> + /// Return true if the full message was recoveredMessage. + /// </summary> + /// <returns>true on full message recovery, false otherwise, or if not sure.</returns> + /// <seealso cref="ISignerWithRecovery.HasFullMessage"/> + public virtual bool HasFullMessage() + { + return fullMessage; + } + + /// <summary> int to octet string.</summary> + /// <summary> int to octet string.</summary> + private void ItoOSP( + int i, + byte[] sp) + { + sp[0] = (byte)((uint)i >> 24); + sp[1] = (byte)((uint)i >> 16); + sp[2] = (byte)((uint)i >> 8); + sp[3] = (byte)((uint)i >> 0); + } + + /// <summary> long to octet string.</summary> + private void LtoOSP(long l, byte[] sp) + { + sp[0] = (byte)((ulong)l >> 56); + sp[1] = (byte)((ulong)l >> 48); + sp[2] = (byte)((ulong)l >> 40); + sp[3] = (byte)((ulong)l >> 32); + sp[4] = (byte)((ulong)l >> 24); + sp[5] = (byte)((ulong)l >> 16); + sp[6] = (byte)((ulong)l >> 8); + sp[7] = (byte)((ulong)l >> 0); + } + + /// <summary> mask generator function, as described in Pkcs1v2.</summary> + 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(); + + do + { + ItoOSP(counter, C); + + digest.BlockUpdate(Z, zOff, zLen); + digest.BlockUpdate(C, 0, C.Length); + digest.DoFinal(hashBuf, 0); + + Array.Copy(hashBuf, 0, mask, counter * hLen, hLen); + } + while (++counter < (length / hLen)); + + if ((counter * hLen) < length) + { + ItoOSP(counter, C); + + digest.BlockUpdate(Z, zOff, zLen); + digest.BlockUpdate(C, 0, C.Length); + digest.DoFinal(hashBuf, 0); + + Array.Copy(hashBuf, 0, mask, counter * hLen, mask.Length - (counter * hLen)); + } + + return mask; + } + } +} diff --git a/Crypto/src/crypto/signers/Iso9796d2Signer.cs b/Crypto/src/crypto/signers/Iso9796d2Signer.cs new file mode 100644
index 000000000..8ff87e8ee --- /dev/null +++ b/Crypto/src/crypto/signers/Iso9796d2Signer.cs
@@ -0,0 +1,557 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Signers +{ + /// <summary> ISO9796-2 - mechanism using a hash function with recovery (scheme 1)</summary> + public class Iso9796d2Signer : ISignerWithRecovery + { + /// <summary> + /// Return a reference to the recoveredMessage message. + /// </summary> + /// <returns>The full/partial recoveredMessage message.</returns> + /// <seealso cref="ISignerWithRecovery.GetRecoveredMessage"/> + public byte[] GetRecoveredMessage() + { + return recoveredMessage; + } + + public const int TrailerImplicit = 0xBC; + public const int TrailerRipeMD160 = 0x31CC; + public const int TrailerRipeMD128 = 0x32CC; + public const int TrailerSha1 = 0x33CC; + public const int TrailerSha256 = 0x34CC; + public const int TrailerSha512 = 0x35CC; + public const int TrailerSha384 = 0x36CC; + public const int TrailerWhirlpool = 0x37CC; + + private static IDictionary trailerMap = Platform.CreateHashtable(); + + static Iso9796d2Signer() + { + trailerMap.Add("RIPEMD128", TrailerRipeMD128); + trailerMap.Add("RIPEMD160", TrailerRipeMD160); + + trailerMap.Add("SHA-1", TrailerSha1); + trailerMap.Add("SHA-256", TrailerSha256); + trailerMap.Add("SHA-384", TrailerSha384); + trailerMap.Add("SHA-512", TrailerSha512); + + trailerMap.Add("Whirlpool", TrailerWhirlpool); + } + + private IDigest digest; + private IAsymmetricBlockCipher cipher; + + private int trailer; + private int keyBits; + private byte[] block; + private byte[] mBuf; + private int messageLength; + private bool fullMessage; + private byte[] recoveredMessage; + + private byte[] preSig; + private byte[] preBlock; + + /// <summary> + /// Generate a signer for the with either implicit or explicit trailers + /// for ISO9796-2. + /// </summary> + /// <param name="cipher">base cipher to use for signature creation/verification</param> + /// <param name="digest">digest to use.</param> + /// <param name="isImplicit">whether or not the trailer is implicit or gives the hash.</param> + public Iso9796d2Signer( + IAsymmetricBlockCipher cipher, + IDigest digest, + bool isImplicit) + { + this.cipher = cipher; + this.digest = digest; + + if (isImplicit) + { + trailer = TrailerImplicit; + } + else + { + string digestName = digest.AlgorithmName; + + if (trailerMap.Contains(digestName)) + { + trailer = (int)trailerMap[digest.AlgorithmName]; + } + else + { + throw new System.ArgumentException("no valid trailer for digest"); + } + } + } + + /// <summary> Constructor for a signer with an explicit digest trailer. + /// + /// </summary> + /// <param name="cipher">cipher to use. + /// </param> + /// <param name="digest">digest to sign with. + /// </param> + public Iso9796d2Signer(IAsymmetricBlockCipher cipher, IDigest digest) + : this(cipher, digest, false) + { + } + + public string AlgorithmName + { + get { return digest.AlgorithmName + "with" + "ISO9796-2S1"; } + } + + public virtual void Init(bool forSigning, ICipherParameters parameters) + { + RsaKeyParameters kParam = (RsaKeyParameters) parameters; + + cipher.Init(forSigning, kParam); + + keyBits = kParam.Modulus.BitLength; + + block = new byte[(keyBits + 7) / 8]; + if (trailer == TrailerImplicit) + { + mBuf = new byte[block.Length - digest.GetDigestSize() - 2]; + } + else + { + mBuf = new byte[block.Length - digest.GetDigestSize() - 3]; + } + + Reset(); + } + + /// <summary> compare two byte arrays - constant time.</summary> + private bool IsSameAs(byte[] a, byte[] b) + { + int checkLen; + if (messageLength > mBuf.Length) + { + if (mBuf.Length > b.Length) + { + return false; + } + + checkLen = mBuf.Length; + } + else + { + if (messageLength != b.Length) + { + return false; + } + + checkLen = b.Length; + } + + bool isOkay = true; + + for (int i = 0; i != checkLen; i++) + { + if (a[i] != b[i]) + { + isOkay = false; + } + } + + return isOkay; + } + + /// <summary> clear possible sensitive data</summary> + private void ClearBlock( + byte[] block) + { + Array.Clear(block, 0, block.Length); + } + + public virtual void UpdateWithRecoveredMessage( + byte[] signature) + { + 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); + + string digestName = digest.AlgorithmName; + if (!trailerMap.Contains(digestName)) + throw new ArgumentException("unrecognised hash in signature"); + if (sigTrail != (int)trailerMap[digestName]) + throw new InvalidOperationException("signer initialised with wrong digest for trailer " + sigTrail); + + 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]; + Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length); + } + else + { + fullMessage = false; + + recoveredMessage = new byte[off - mStart]; + Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length); + } + + preSig = signature; + preBlock = block; + + digest.BlockUpdate(recoveredMessage, 0, recoveredMessage.Length); + messageLength = recoveredMessage.Length; + } + + /// <summary> update the internal digest with the byte b</summary> + public void Update( + byte input) + { + digest.Update(input); + + if (preSig == null && messageLength < mBuf.Length) + { + mBuf[messageLength] = input; + } + + messageLength++; + } + + /// <summary> update the internal digest with the byte array in</summary> + public void BlockUpdate( + byte[] input, + int inOff, + int length) + { + digest.BlockUpdate(input, inOff, length); + + if (preSig == null && messageLength < mBuf.Length) + { + for (int i = 0; i < length && (i + messageLength) < mBuf.Length; i++) + { + mBuf[messageLength + i] = input[inOff + i]; + } + } + + messageLength += length; + } + + /// <summary> reset the internal state</summary> + public virtual void Reset() + { + digest.Reset(); + messageLength = 0; + ClearBlock(mBuf); + + if (recoveredMessage != null) + { + ClearBlock(recoveredMessage); + } + + recoveredMessage = null; + fullMessage = false; + } + + /// <summary> Generate a signature for the loaded message using the key we were + /// initialised with. + /// </summary> + public virtual byte[] GenerateSignature() + { + int digSize = digest.GetDigestSize(); + + int t = 0; + int delta = 0; + + if (trailer == TrailerImplicit) + { + t = 8; + delta = block.Length - digSize - 1; + digest.DoFinal(block, delta); + block[block.Length - 1] = (byte) TrailerImplicit; + } + else + { + t = 16; + delta = block.Length - digSize - 2; + digest.DoFinal(block, delta); + block[block.Length - 2] = (byte) ((uint)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 = (byte) (0x60); + + delta -= mR; + + Array.Copy(mBuf, 0, block, delta, mR); + } + else + { + header = (byte) (0x40); + delta -= messageLength; + + Array.Copy(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; + } + + /// <summary> return true if the signature represents a ISO9796-2 signature + /// for the passed in message. + /// </summary> + public virtual bool VerifySignature(byte[] signature) + { + byte[] block; + bool updateWithRecoveredCalled; + + if (preSig == null) + { + updateWithRecoveredCalled = false; + try + { + block = cipher.ProcessBlock(signature, 0, signature.Length); + } + catch (Exception) + { + return false; + } + } + else + { + if (!Arrays.AreEqual(preSig, signature)) + throw new InvalidOperationException("updateWithRecoveredMessage called on different signature"); + + updateWithRecoveredCalled = true; + 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); + + string digestName = digest.AlgorithmName; + if (!trailerMap.Contains(digestName)) + throw new ArgumentException("unrecognised hash in signature"); + if (sigTrail != (int)trailerMap[digestName]) + throw new InvalidOperationException("signer initialised with wrong digest for trailer " + sigTrail); + + delta = 2; + } + + // + // find out how much padding we've got + // + int mStart = 0; + for (; 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.BlockUpdate(block, mStart, off - mStart); + digest.DoFinal(hash, 0); + + bool 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]; + Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length); + } + else + { + fullMessage = false; + + digest.DoFinal(hash, 0); + + bool 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]; + Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length); + } + + // + // if they've input a message check what we've recovered against + // what was input. + // + if (messageLength != 0 && !updateWithRecoveredCalled) + { + if (!IsSameAs(mBuf, recoveredMessage)) + { +// ClearBlock(recoveredMessage); + return ReturnFalse(block); + } + } + + ClearBlock(mBuf); + ClearBlock(block); + + return true; + } + + private bool ReturnFalse(byte[] block) + { + ClearBlock(mBuf); + ClearBlock(block); + + return false; + } + + /// <summary> + /// Return true if the full message was recoveredMessage. + /// </summary> + /// <returns> true on full message recovery, false otherwise.</returns> + /// <seealso cref="ISignerWithRecovery.HasFullMessage"/> + public virtual bool HasFullMessage() + { + return fullMessage; + } + } +} diff --git a/Crypto/src/crypto/signers/PssSigner.cs b/Crypto/src/crypto/signers/PssSigner.cs new file mode 100644
index 000000000..6900224f3 --- /dev/null +++ b/Crypto/src/crypto/signers/PssSigner.cs
@@ -0,0 +1,345 @@ +using System; + +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Signers +{ + /// <summary> 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.</p> + /// </summary> + public class PssSigner + : ISigner + { + public const byte TrailerImplicit = (byte)0xBC; + + private readonly IDigest contentDigest1, contentDigest2; + private readonly IDigest mgfDigest; + private readonly IAsymmetricBlockCipher 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; + + public static PssSigner CreateRawSigner( + IAsymmetricBlockCipher cipher, + IDigest digest) + { + return new PssSigner(cipher, new NullDigest(), digest, digest, digest.GetDigestSize(), TrailerImplicit); + } + + public static PssSigner CreateRawSigner( + IAsymmetricBlockCipher cipher, + IDigest contentDigest, + IDigest mgfDigest, + int saltLen, + byte trailer) + { + return new PssSigner(cipher, new NullDigest(), contentDigest, mgfDigest, saltLen, trailer); + } + + public PssSigner( + IAsymmetricBlockCipher cipher, + IDigest digest) + : this(cipher, digest, digest.GetDigestSize()) + { + } + + /// <summary>Basic constructor</summary> + /// <param name="cipher">the asymmetric cipher to use.</param> + /// <param name="digest">the digest to use.</param> + /// <param name="saltLen">the length of the salt to use (in bytes).</param> + public PssSigner( + IAsymmetricBlockCipher cipher, + IDigest digest, + int saltLen) + : this(cipher, digest, saltLen, TrailerImplicit) + { + } + + public PssSigner( + IAsymmetricBlockCipher cipher, + IDigest contentDigest, + IDigest mgfDigest, + int saltLen) + : this(cipher, contentDigest, mgfDigest, saltLen, TrailerImplicit) + { + } + + public PssSigner( + IAsymmetricBlockCipher cipher, + IDigest digest, + int saltLen, + byte trailer) + : this(cipher, digest, digest, saltLen, TrailerImplicit) + { + } + + public PssSigner( + IAsymmetricBlockCipher cipher, + IDigest contentDigest, + IDigest mgfDigest, + int saltLen, + byte trailer) + : this(cipher, contentDigest, contentDigest, mgfDigest, saltLen, trailer) + { + } + + private PssSigner( + IAsymmetricBlockCipher cipher, + IDigest contentDigest1, + IDigest contentDigest2, + IDigest mgfDigest, + int saltLen, + byte trailer) + { + this.cipher = cipher; + this.contentDigest1 = contentDigest1; + this.contentDigest2 = contentDigest2; + this.mgfDigest = mgfDigest; + this.hLen = contentDigest2.GetDigestSize(); + this.mgfhLen = mgfDigest.GetDigestSize(); + this.sLen = saltLen; + this.salt = new byte[saltLen]; + this.mDash = new byte[8 + saltLen + hLen]; + this.trailer = trailer; + } + + public string AlgorithmName + { + get { return mgfDigest.AlgorithmName + "withRSAandMGF1"; } + } + + public virtual void Init( + bool forSigning, + ICipherParameters parameters) + { + if (parameters is ParametersWithRandom) + { + ParametersWithRandom p = (ParametersWithRandom) parameters; + + parameters = p.Parameters; + random = p.Random; + } + else + { + if (forSigning) + { + random = new SecureRandom(); + } + } + + cipher.Init(forSigning, parameters); + + RsaKeyParameters kParam; + if (parameters is RsaBlindingParameters) + { + kParam = ((RsaBlindingParameters) parameters).PublicKey; + } + else + { + kParam = (RsaKeyParameters) parameters; + } + + emBits = kParam.Modulus.BitLength - 1; + + if (emBits < (8 * hLen + 8 * sLen + 9)) + throw new ArgumentException("key too small for specified hash and salt lengths"); + + block = new byte[(emBits + 7) / 8]; + } + + /// <summary> clear possible sensitive data</summary> + private void ClearBlock( + byte[] block) + { + Array.Clear(block, 0, block.Length); + } + + /// <summary> update the internal digest with the byte b</summary> + public virtual void Update( + byte input) + { + contentDigest1.Update(input); + } + + /// <summary> update the internal digest with the byte array in</summary> + public virtual void BlockUpdate( + byte[] input, + int inOff, + int length) + { + contentDigest1.BlockUpdate(input, inOff, length); + } + + /// <summary> reset the internal state</summary> + public virtual void Reset() + { + contentDigest1.Reset(); + } + + /// <summary> Generate a signature for the message we've been loaded with using + /// the key we were initialised with. + /// </summary> + public virtual byte[] GenerateSignature() + { + contentDigest1.DoFinal(mDash, mDash.Length - hLen - sLen); + + if (sLen != 0) + { + random.NextBytes(salt); + salt.CopyTo(mDash, mDash.Length - sLen); + } + + byte[] h = new byte[hLen]; + + contentDigest2.BlockUpdate(mDash, 0, mDash.Length); + + contentDigest2.DoFinal(h, 0); + + block[block.Length - sLen - 1 - hLen - 1] = (byte) (0x01); + salt.CopyTo(block, block.Length - sLen - hLen - 1); + + 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] &= (byte) ((0xff >> ((block.Length * 8) - emBits))); + + h.CopyTo(block, block.Length - hLen - 1); + + block[block.Length - 1] = trailer; + + byte[] b = cipher.ProcessBlock(block, 0, block.Length); + + ClearBlock(block); + + return b; + } + + /// <summary> return true if the internal state represents the signature described + /// in the passed in array. + /// </summary> + public virtual bool VerifySignature( + byte[] signature) + { + contentDigest1.DoFinal(mDash, mDash.Length - hLen - sLen); + + byte[] b = cipher.ProcessBlock(signature, 0, signature.Length); + b.CopyTo(block, block.Length - b.Length); + + 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] &= (byte) ((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; + } + + Array.Copy(block, block.Length - sLen - hLen - 1, mDash, mDash.Length - sLen, sLen); + + contentDigest2.BlockUpdate(mDash, 0, mDash.Length); + contentDigest2.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; + } + + /// <summary> int to octet string.</summary> + private void ItoOSP( + int i, + byte[] sp) + { + sp[0] = (byte)((uint) i >> 24); + sp[1] = (byte)((uint) i >> 16); + sp[2] = (byte)((uint) i >> 8); + sp[3] = (byte)((uint) i >> 0); + } + + /// <summary> mask generator function, as described in Pkcs1v2.</summary> + 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.BlockUpdate(Z, zOff, zLen); + mgfDigest.BlockUpdate(C, 0, C.Length); + mgfDigest.DoFinal(hashBuf, 0); + + hashBuf.CopyTo(mask, counter * mgfhLen); + ++counter; + } + + if ((counter * mgfhLen) < length) + { + ItoOSP(counter, C); + + mgfDigest.BlockUpdate(Z, zOff, zLen); + mgfDigest.BlockUpdate(C, 0, C.Length); + mgfDigest.DoFinal(hashBuf, 0); + + Array.Copy(hashBuf, 0, mask, counter * mgfhLen, mask.Length - (counter * mgfhLen)); + } + + return mask; + } + } +} diff --git a/Crypto/src/crypto/signers/RsaDigestSigner.cs b/Crypto/src/crypto/signers/RsaDigestSigner.cs new file mode 100644
index 000000000..f57bfc83d --- /dev/null +++ b/Crypto/src/crypto/signers/RsaDigestSigner.cs
@@ -0,0 +1,228 @@ +using System; +using System.Collections; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Asn1.Utilities; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Encodings; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Signers; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Signers +{ + public class RsaDigestSigner + : ISigner + { + private readonly IAsymmetricBlockCipher rsaEngine = new Pkcs1Encoding(new RsaBlindedEngine()); + private readonly AlgorithmIdentifier algId; + private readonly IDigest digest; + private bool forSigning; + + private static readonly IDictionary oidMap = Platform.CreateHashtable(); + + /// <summary> + /// Load oid table. + /// </summary> + static RsaDigestSigner() + { + oidMap["RIPEMD128"] = TeleTrusTObjectIdentifiers.RipeMD128; + oidMap["RIPEMD160"] = TeleTrusTObjectIdentifiers.RipeMD160; + oidMap["RIPEMD256"] = TeleTrusTObjectIdentifiers.RipeMD256; + + oidMap["SHA-1"] = X509ObjectIdentifiers.IdSha1; + oidMap["SHA-224"] = NistObjectIdentifiers.IdSha224; + oidMap["SHA-256"] = NistObjectIdentifiers.IdSha256; + oidMap["SHA-384"] = NistObjectIdentifiers.IdSha384; + oidMap["SHA-512"] = NistObjectIdentifiers.IdSha512; + + oidMap["MD2"] = PkcsObjectIdentifiers.MD2; + oidMap["MD4"] = PkcsObjectIdentifiers.MD4; + oidMap["MD5"] = PkcsObjectIdentifiers.MD5; + } + + public RsaDigestSigner( + IDigest digest) + { + this.digest = digest; + + string algName = digest.AlgorithmName; + if (algName.Equals("NULL")) + { + this.algId = null; + } + else + { + this.algId = new AlgorithmIdentifier( + (DerObjectIdentifier)oidMap[digest.AlgorithmName], DerNull.Instance); + } + } + + public string AlgorithmName + { + get { return digest.AlgorithmName + "withRSA"; } + } + + /** + * Initialise the signer for signing or verification. + * + * @param forSigning true if for signing, false otherwise + * @param param necessary parameters. + */ + public void Init( + bool forSigning, + ICipherParameters parameters) + { + this.forSigning = forSigning; + AsymmetricKeyParameter k; + + if (parameters is ParametersWithRandom) + { + k = (AsymmetricKeyParameter)((ParametersWithRandom)parameters).Parameters; + } + else + { + k = (AsymmetricKeyParameter)parameters; + } + + if (forSigning && !k.IsPrivate) + throw new InvalidKeyException("Signing requires private key."); + + if (!forSigning && k.IsPrivate) + throw new InvalidKeyException("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 BlockUpdate( + byte[] input, + int inOff, + int length) + { + digest.BlockUpdate(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 InvalidOperationException("RsaDigestSigner not initialised for signature generation."); + + byte[] hash = new byte[digest.GetDigestSize()]; + digest.DoFinal(hash, 0); + + byte[] data = DerEncode(hash); + return rsaEngine.ProcessBlock(data, 0, data.Length); + } + + /** + * return true if the internal state represents the signature described + * in the passed in array. + */ + public bool VerifySignature( + byte[] signature) + { + if (forSigning) + throw new InvalidOperationException("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) + { + return false; + } + + if (sig.Length == expected.Length) + { + for (int i = 0; i < sig.Length; i++) + { + if (sig[i] != expected[i]) + { + return false; + } + } + } + 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; + + for (int i = 0; i < hash.Length; i++) + { + if (sig[sigOffset + i] != expected[expectedOffset + i]) // check hash + { + return false; + } + } + + for (int i = 0; i < sigOffset; i++) + { + if (sig[i] != expected[i]) // check header less NULL + { + return false; + } + } + } + else + { + return false; + } + + return true; + } + + public void Reset() + { + digest.Reset(); + } + + private byte[] DerEncode(byte[] hash) + { + if (algId == null) + { + // For raw RSA, the DigestInfo must be prepared externally + return hash; + } + + DigestInfo dInfo = new DigestInfo(algId, hash); + + return dInfo.GetDerEncoded(); + } + } +} diff --git a/Crypto/src/crypto/tls/AlertDescription.cs b/Crypto/src/crypto/tls/AlertDescription.cs new file mode 100644
index 000000000..e1229a4a3 --- /dev/null +++ b/Crypto/src/crypto/tls/AlertDescription.cs
@@ -0,0 +1,47 @@ +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <summary> + /// RFC 2246 7.2 + /// </summary> + public enum AlertDescription : byte + { + close_notify = 0, + unexpected_message = 10, + bad_record_mac = 20, + decryption_failed = 21, + record_overflow = 22, + decompression_failure = 30, + handshake_failure = 40, + /* 41 is not defined, for historical reasons */ + bad_certificate = 42, + unsupported_certificate = 43, + certificate_revoked = 44, + certificate_expired = 45, + certificate_unknown = 46, + illegal_parameter = 47, + unknown_ca = 48, + access_denied = 49, + decode_error = 50, + decrypt_error = 51, + export_restriction = 60, + protocol_version = 70, + insufficient_security = 71, + internal_error = 80, + user_canceled = 90, + no_renegotiation = 100, + + /* + * RFC 3546 + */ + unsupported_extension = 110, + certificate_unobtainable = 111, + unrecognized_name = 112, + bad_certificate_status_response = 113, + bad_certificate_hash_value = 114, + + /* + * RFC 4279 + */ + unknown_psk_identity = 115, + } +} diff --git a/Crypto/src/crypto/tls/AlertLevel.cs b/Crypto/src/crypto/tls/AlertLevel.cs new file mode 100644
index 000000000..afb04308b --- /dev/null +++ b/Crypto/src/crypto/tls/AlertLevel.cs
@@ -0,0 +1,11 @@ +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <summary> + /// RFC 2246 7.2 + /// </summary> + public enum AlertLevel : byte + { + warning = 1, + fatal = 2, + } +} diff --git a/Crypto/src/crypto/tls/AlwaysValidVerifyer.cs b/Crypto/src/crypto/tls/AlwaysValidVerifyer.cs new file mode 100644
index 000000000..e26c6fc3f --- /dev/null +++ b/Crypto/src/crypto/tls/AlwaysValidVerifyer.cs
@@ -0,0 +1,24 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <remarks> + /// A certificate verifyer, that will always return true. + /// <pre> + /// DO NOT USE THIS FILE UNLESS YOU KNOW EXACTLY WHAT YOU ARE DOING. + /// </pre> + /// </remarks> + [Obsolete("Perform certificate verification in TlsAuthentication implementation")] + public class AlwaysValidVerifyer + : ICertificateVerifyer + { + /// <summary>Return true.</summary> + public bool IsValid( + X509CertificateStructure[] certs) + { + return true; + } + } +} diff --git a/Crypto/src/crypto/tls/ByteQueue.cs b/Crypto/src/crypto/tls/ByteQueue.cs new file mode 100644
index 000000000..96062402b --- /dev/null +++ b/Crypto/src/crypto/tls/ByteQueue.cs
@@ -0,0 +1,125 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <remarks> + /// A queue for bytes. + /// <p> + /// This file could be more optimized. + /// </p> + /// </remarks> + public class ByteQueue + { + /// <returns>The smallest number which can be written as 2^x which is bigger than i.</returns> + public static 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 const 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; + + /// <summary>Read data from the buffer.</summary> + /// <param name="buf">The buffer where the read data will be copied to.</param> + /// <param name="offset">How many bytes to skip at the beginning of buf.</param> + /// <param name="len">How many bytes to read at all.</param> + /// <param name="skip">How many bytes from our data to skip.</param> + public void Read( + byte[] buf, + int offset, + int len, + int skip) + { + if ((available - skip) < len) + { + throw new TlsException("Not enough data to read"); + } + if ((buf.Length - offset) < len) + { + throw new TlsException("Buffer size of " + buf.Length + " is too small for a read of " + len + " bytes"); + } + Array.Copy(databuf, skipped + skip, buf, offset, len); + } + + /// <summary>Add some data to our buffer.</summary> + /// <param name="data">A byte-array to read data from.</param> + /// <param name="offset">How many bytes to skip at the beginning of the array.</param> + /// <param name="len">How many bytes to read from the array.</param> + public void AddData( + byte[] data, + int offset, + int len) + { + if ((skipped + available + len) > databuf.Length) + { + byte[] tmp = new byte[ByteQueue.NextTwoPow(data.Length)]; + Array.Copy(databuf, skipped, tmp, 0, available); + skipped = 0; + databuf = tmp; + } + Array.Copy(data, offset, databuf, skipped + available, len); + available += len; + } + + /// <summary>Remove some bytes from our data from the beginning.</summary> + /// <param name="i">How many bytes to remove.</param> + public void RemoveData( + int i) + { + if (i > available) + { + throw new TlsException("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)) + { + Array.Copy(databuf, skipped, databuf, 0, available); + skipped = 0; + } + } + + /// <summary>The number of bytes which are available in this buffer.</summary> + public int Available + { + get { return available; } + } + } +} diff --git a/Crypto/src/crypto/tls/Certificate.cs b/Crypto/src/crypto/tls/Certificate.cs new file mode 100644
index 000000000..e4df041e2 --- /dev/null +++ b/Crypto/src/crypto/tls/Certificate.cs
@@ -0,0 +1,111 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /** + * A representation for a certificate chain. + */ + public class Certificate + { + public static readonly Certificate EmptyChain = new Certificate(new X509CertificateStructure[0]); + + /** + * The certificates. + */ + internal X509CertificateStructure[] certs; + + /** + * Parse the ServerCertificate message. + * + * @param inStr The stream where to parse from. + * @return A Certificate object with the certs, the server has sended. + * @throws IOException If something goes wrong during parsing. + */ + internal static Certificate Parse( + Stream inStr) + { + int left = TlsUtilities.ReadUint24(inStr); + if (left == 0) + { + return EmptyChain; + } + IList tmp = Platform.CreateArrayList(); + while (left > 0) + { + int size = TlsUtilities.ReadUint24(inStr); + left -= 3 + size; + byte[] buf = new byte[size]; + TlsUtilities.ReadFully(buf, inStr); + MemoryStream bis = new MemoryStream(buf, false); + Asn1Object o = Asn1Object.FromStream(bis); + tmp.Add(X509CertificateStructure.GetInstance(o)); + if (bis.Position < bis.Length) + { + throw new ArgumentException("Sorry, there is garbage data left after the certificate"); + } + } + X509CertificateStructure[] certs = new X509CertificateStructure[tmp.Count]; + for (int i = 0; i < tmp.Count; ++i) + { + certs[i] = (X509CertificateStructure)tmp[i]; + } + return new Certificate(certs); + } + + /** + * Encodes version of the ClientCertificate message + * + * @param outStr stream to write the message to + * @throws IOException If something goes wrong + */ + internal void Encode( + Stream outStr) + { + IList encCerts = Platform.CreateArrayList(); + int totalSize = 0; + foreach (X509CertificateStructure cert in certs) + { + byte[] encCert = cert.GetEncoded(Asn1Encodable.Der); + encCerts.Add(encCert); + totalSize += encCert.Length + 3; + } + + TlsUtilities.WriteUint24(totalSize, outStr); + + foreach (byte[] encCert in encCerts) + { + TlsUtilities.WriteOpaque24(encCert, outStr); + } + } + + /** + * Private constructor from a cert array. + * + * @param certs The certs the chain should contain. + */ + public Certificate(X509CertificateStructure[] certs) + { + if (certs == null) + throw new ArgumentNullException("certs"); + + this.certs = certs; + } + + /// <returns>An array which contains the certs, this chain contains.</returns> + public X509CertificateStructure[] GetCerts() + { + return (X509CertificateStructure[]) certs.Clone(); + } + + public bool IsEmpty + { + get { return certs.Length == 0; } + } + } +} diff --git a/Crypto/src/crypto/tls/CertificateRequest.cs b/Crypto/src/crypto/tls/CertificateRequest.cs new file mode 100644
index 000000000..49d8ba6fb --- /dev/null +++ b/Crypto/src/crypto/tls/CertificateRequest.cs
@@ -0,0 +1,28 @@ +using System; +using System.Collections; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class CertificateRequest + { + private ClientCertificateType[] certificateTypes; + private IList certificateAuthorities; + + public CertificateRequest(ClientCertificateType[] certificateTypes, IList certificateAuthorities) + { + this.certificateTypes = certificateTypes; + this.certificateAuthorities = certificateAuthorities; + } + + public ClientCertificateType[] CertificateTypes + { + get { return certificateTypes; } + } + + /// <returns>A <see cref="IList"/> of X509Name</returns> + public IList CertificateAuthorities + { + get { return certificateAuthorities; } + } + } +} \ No newline at end of file diff --git a/Crypto/src/crypto/tls/CipherSuite.cs b/Crypto/src/crypto/tls/CipherSuite.cs new file mode 100644
index 000000000..6e1f7a545 --- /dev/null +++ b/Crypto/src/crypto/tls/CipherSuite.cs
@@ -0,0 +1,136 @@ +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <summary> + /// RFC 2246 A.5 + /// </summary> + public enum CipherSuite : int + { + TLS_NULL_WITH_NULL_NULL = 0x0000, + TLS_RSA_WITH_NULL_MD5 = 0x0001, + TLS_RSA_WITH_NULL_SHA = 0x0002, + TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x0003, + TLS_RSA_WITH_RC4_128_MD5 = 0x0004, + TLS_RSA_WITH_RC4_128_SHA = 0x0005, + TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0x0006, + TLS_RSA_WITH_IDEA_CBC_SHA = 0x0007, + TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0008, + TLS_RSA_WITH_DES_CBC_SHA = 0x0009, + TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A, + TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x000B, + TLS_DH_DSS_WITH_DES_CBC_SHA = 0x000C, + TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x000D, + TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x000E, + TLS_DH_RSA_WITH_DES_CBC_SHA = 0x000F, + TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x0010, + TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x0011, + TLS_DHE_DSS_WITH_DES_CBC_SHA = 0x0012, + TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x0013, + TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0014, + TLS_DHE_RSA_WITH_DES_CBC_SHA = 0x0015, + TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x0016, + TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x0017, + TLS_DH_anon_WITH_RC4_128_MD5 = 0x0018, + TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0x0019, + TLS_DH_anon_WITH_DES_CBC_SHA = 0x001A, + TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0x001B, + + /* + * RFC 3268 + */ + TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F, + TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0x0030, + TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0x0031, + TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x0032, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033, + TLS_DH_anon_WITH_AES_128_CBC_SHA = 0x0034, + TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035, + TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0x0036, + TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0x0037, + TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x0038, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039, + TLS_DH_anon_WITH_AES_256_CBC_SHA = 0x003A, + + /* + * RFC 4279 + */ + TLS_PSK_WITH_RC4_128_SHA = 0x008A, + TLS_PSK_WITH_3DES_EDE_CBC_SHA = 0x008B, + TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C, + TLS_PSK_WITH_AES_256_CBC_SHA = 0x008D, + TLS_DHE_PSK_WITH_RC4_128_SHA = 0x008E, + TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA = 0x008F, + TLS_DHE_PSK_WITH_AES_128_CBC_SHA = 0x0090, + TLS_DHE_PSK_WITH_AES_256_CBC_SHA = 0x0091, + TLS_RSA_PSK_WITH_RC4_128_SHA = 0x0092, + TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA = 0x0093, + TLS_RSA_PSK_WITH_AES_128_CBC_SHA = 0x0094, + TLS_RSA_PSK_WITH_AES_256_CBC_SHA = 0x0095, + + /* + * RFC 4492 + */ + TLS_ECDH_ECDSA_WITH_NULL_SHA = 0xC001, + TLS_ECDH_ECDSA_WITH_RC4_128_SHA = 0xC002, + TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC003, + TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA = 0xC004, + TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA = 0xC005, + TLS_ECDHE_ECDSA_WITH_NULL_SHA = 0xC006, + TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = 0xC007, + TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC008, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A, + TLS_ECDH_RSA_WITH_NULL_SHA = 0xC00B, + TLS_ECDH_RSA_WITH_RC4_128_SHA = 0xC00C, + TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA = 0xC00D, + TLS_ECDH_RSA_WITH_AES_128_CBC_SHA = 0xC00E, + TLS_ECDH_RSA_WITH_AES_256_CBC_SHA = 0xC00F, + TLS_ECDHE_RSA_WITH_NULL_SHA = 0xC010, + TLS_ECDHE_RSA_WITH_RC4_128_SHA = 0xC011, + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = 0xC012, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014, + TLS_ECDH_anon_WITH_NULL_SHA = 0xC015, + TLS_ECDH_anon_WITH_RC4_128_SHA = 0xC016, + TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA = 0xC017, + TLS_ECDH_anon_WITH_AES_128_CBC_SHA = 0xC018, + TLS_ECDH_anon_WITH_AES_256_CBC_SHA = 0xC019, + + /* + * RFC 5054 + */ + TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA = 0xC01A, + TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA = 0xC01B, + TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA = 0xC01C, + TLS_SRP_SHA_WITH_AES_128_CBC_SHA = 0xC01D, + TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA = 0xC01E, + TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA = 0xC01F, + TLS_SRP_SHA_WITH_AES_256_CBC_SHA = 0xC020, + TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA = 0xC021, + TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA = 0xC022, + + /* + * RFC 5289 + */ + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024, + TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC025, + TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC026, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028, + TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 = 0xC029, + TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 = 0xC02A, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C, + TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02D, + TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02E, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030, + TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 = 0xC031, + TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 = 0xC032, + + /* + * RFC 5746 + */ + TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF, + } +} diff --git a/Crypto/src/crypto/tls/ClientCertificateType.cs b/Crypto/src/crypto/tls/ClientCertificateType.cs new file mode 100644
index 000000000..58f5d4276 --- /dev/null +++ b/Crypto/src/crypto/tls/ClientCertificateType.cs
@@ -0,0 +1,20 @@ +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <summary> + /// RFC 2246 7.4.4 + /// </summary> + public enum ClientCertificateType : byte + { + rsa_sign = 1, + dss_sign = 2, + rsa_fixed_dh = 3, + dss_fixed_dh = 4, + + /* + * RFC 4492 5.5 + */ + ecdsa_sign = 64, + rsa_fixed_ecdh = 65, + ecdsa_fixed_ecdh = 66, + } +} \ No newline at end of file diff --git a/Crypto/src/crypto/tls/CombinedHash.cs b/Crypto/src/crypto/tls/CombinedHash.cs new file mode 100644
index 000000000..59ad87a7b --- /dev/null +++ b/Crypto/src/crypto/tls/CombinedHash.cs
@@ -0,0 +1,82 @@ +using System; + +using Org.BouncyCastle.Crypto.Digests; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <remarks>A combined hash, which implements md5(m) || sha1(m).</remarks> + internal class CombinedHash + : IDigest + { + private readonly MD5Digest md5; + private readonly Sha1Digest sha1; + + internal CombinedHash() + { + this.md5 = new MD5Digest(); + this.sha1 = new Sha1Digest(); + } + + internal CombinedHash(CombinedHash t) + { + this.md5 = new MD5Digest(t.md5); + this.sha1 = new Sha1Digest(t.sha1); + } + + /// <seealso cref="IDigest.AlgorithmName"/> + public string AlgorithmName + { + get + { + return md5.AlgorithmName + " and " + sha1.AlgorithmName + " for TLS 1.0"; + } + } + + /// <seealso cref="IDigest.GetByteLength"/> + public int GetByteLength() + { + return System.Math.Max(md5.GetByteLength(), sha1.GetByteLength()); + } + + /// <seealso cref="IDigest.GetDigestSize"/> + public int GetDigestSize() + { + return md5.GetDigestSize() + sha1.GetDigestSize(); + } + + /// <seealso cref="IDigest.Update"/> + public void Update( + byte input) + { + md5.Update(input); + sha1.Update(input); + } + + /// <seealso cref="IDigest.BlockUpdate"/> + public void BlockUpdate( + byte[] input, + int inOff, + int len) + { + md5.BlockUpdate(input, inOff, len); + sha1.BlockUpdate(input, inOff, len); + } + + /// <seealso cref="IDigest.DoFinal"/> + public int DoFinal( + byte[] output, + int outOff) + { + int i1 = md5.DoFinal(output, outOff); + int i2 = sha1.DoFinal(output, outOff + i1); + return i1 + i2; + } + + /// <seealso cref="IDigest.Reset"/> + public void Reset() + { + md5.Reset(); + sha1.Reset(); + } + } +} diff --git a/Crypto/src/crypto/tls/CompressionMethod.cs b/Crypto/src/crypto/tls/CompressionMethod.cs new file mode 100644
index 000000000..4a127a63e --- /dev/null +++ b/Crypto/src/crypto/tls/CompressionMethod.cs
@@ -0,0 +1,20 @@ +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <summary> + /// RFC 2246 6.1 + /// </summary> + public enum CompressionMethod : byte + { + NULL = 0, + + /* + * RFC 3749 2 + */ + DEFLATE = 1 + + /* + * Values from 224 decimal (0xE0) through 255 decimal (0xFF) + * inclusive are reserved for private use. + */ + } +} diff --git a/Crypto/src/crypto/tls/ContentType.cs b/Crypto/src/crypto/tls/ContentType.cs new file mode 100644
index 000000000..a664e3a38 --- /dev/null +++ b/Crypto/src/crypto/tls/ContentType.cs
@@ -0,0 +1,13 @@ +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <summary> + /// RFC 2246 6.2.1 + /// </summary> + public enum ContentType : byte + { + change_cipher_spec = 20, + alert = 21, + handshake = 22, + application_data = 23, + } +} diff --git a/Crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs b/Crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs new file mode 100644
index 000000000..2dfe526d1 --- /dev/null +++ b/Crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs
@@ -0,0 +1,67 @@ +using System; + +using Org.BouncyCastle.Crypto.Agreement; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class DefaultTlsAgreementCredentials + : TlsAgreementCredentials + { + protected Certificate clientCert; + protected AsymmetricKeyParameter clientPrivateKey; + + protected IBasicAgreement basicAgreement; + + public DefaultTlsAgreementCredentials(Certificate clientCertificate, AsymmetricKeyParameter clientPrivateKey) + { + if (clientCertificate == null) + { + throw new ArgumentNullException("clientCertificate"); + } + if (clientCertificate.certs.Length == 0) + { + throw new ArgumentException("cannot be empty", "clientCertificate"); + } + if (clientPrivateKey == null) + { + throw new ArgumentNullException("clientPrivateKey"); + } + if (!clientPrivateKey.IsPrivate) + { + throw new ArgumentException("must be private", "clientPrivateKey"); + } + + if (clientPrivateKey is DHPrivateKeyParameters) + { + basicAgreement = new DHBasicAgreement(); + } + else if (clientPrivateKey is ECPrivateKeyParameters) + { + basicAgreement = new ECDHBasicAgreement(); + } + else + { + throw new ArgumentException("type not supported: " + + clientPrivateKey.GetType().FullName, "clientPrivateKey"); + } + + this.clientCert = clientCertificate; + this.clientPrivateKey = clientPrivateKey; + } + + public virtual Certificate Certificate + { + get { return clientCert; } + } + + public virtual byte[] GenerateAgreement(AsymmetricKeyParameter serverPublicKey) + { + basicAgreement.Init(clientPrivateKey); + BigInteger agreementValue = basicAgreement.CalculateAgreement(serverPublicKey); + return BigIntegers.AsUnsignedByteArray(agreementValue); + } + } +} diff --git a/Crypto/src/crypto/tls/DefaultTlsCipherFactory.cs b/Crypto/src/crypto/tls/DefaultTlsCipherFactory.cs new file mode 100644
index 000000000..53e3438d9 --- /dev/null +++ b/Crypto/src/crypto/tls/DefaultTlsCipherFactory.cs
@@ -0,0 +1,73 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Modes; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class DefaultTlsCipherFactory + : TlsCipherFactory + { + public virtual TlsCipher CreateCipher(TlsClientContext context, + EncryptionAlgorithm encryptionAlgorithm, DigestAlgorithm digestAlgorithm) + { + switch (encryptionAlgorithm) + { + case EncryptionAlgorithm.cls_3DES_EDE_CBC: + return CreateDesEdeCipher(context, 24, digestAlgorithm); + case EncryptionAlgorithm.AES_128_CBC: + return CreateAesCipher(context, 16, digestAlgorithm); + case EncryptionAlgorithm.AES_256_CBC: + return CreateAesCipher(context, 32, digestAlgorithm); + default: + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + /// <exception cref="IOException"></exception> + protected virtual TlsCipher CreateAesCipher(TlsClientContext context, int cipherKeySize, + DigestAlgorithm digestAlgorithm) + { + return new TlsBlockCipher(context, CreateAesBlockCipher(), CreateAesBlockCipher(), + CreateDigest(digestAlgorithm), CreateDigest(digestAlgorithm), cipherKeySize); + } + + /// <exception cref="IOException"></exception> + protected virtual TlsCipher CreateDesEdeCipher(TlsClientContext context, int cipherKeySize, + DigestAlgorithm digestAlgorithm) + { + return new TlsBlockCipher(context, CreateDesEdeBlockCipher(), CreateDesEdeBlockCipher(), + CreateDigest(digestAlgorithm), CreateDigest(digestAlgorithm), cipherKeySize); + } + + protected virtual IBlockCipher CreateAesBlockCipher() + { + return new CbcBlockCipher(new AesFastEngine()); + } + + protected virtual IBlockCipher CreateDesEdeBlockCipher() + { + return new CbcBlockCipher(new DesEdeEngine()); + } + + /// <exception cref="IOException"></exception> + protected virtual IDigest CreateDigest(DigestAlgorithm digestAlgorithm) + { + switch (digestAlgorithm) + { + case DigestAlgorithm.MD5: + return new MD5Digest(); + case DigestAlgorithm.SHA: + return new Sha1Digest(); + case DigestAlgorithm.SHA256: + return new Sha256Digest(); + case DigestAlgorithm.SHA384: + return new Sha384Digest(); + default: + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + } +} diff --git a/Crypto/src/crypto/tls/DefaultTlsClient.cs b/Crypto/src/crypto/tls/DefaultTlsClient.cs new file mode 100644
index 000000000..c5b59a06b --- /dev/null +++ b/Crypto/src/crypto/tls/DefaultTlsClient.cs
@@ -0,0 +1,259 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class DefaultTlsClient + : TlsClient + { + protected TlsCipherFactory cipherFactory; + + protected TlsClientContext context; + + protected CompressionMethod selectedCompressionMethod; + protected CipherSuite selectedCipherSuite; + + public DefaultTlsClient() + : this(new DefaultTlsCipherFactory()) + { + } + + public DefaultTlsClient(TlsCipherFactory cipherFactory) + { + this.cipherFactory = cipherFactory; + } + + public virtual void Init(TlsClientContext context) + { + this.context = context; + } + + public virtual CipherSuite[] GetCipherSuites() + { + return new CipherSuite[] { + CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, + CipherSuite.TLS_DHE_DSS_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 virtual CompressionMethod[] GetCompressionMethods() + { + /* + * To offer DEFLATE compression, override this method: + * return new CompressionMethod[] { CompressionMethod.DEFLATE, CompressionMethod.NULL }; + */ + + return new CompressionMethod[] { CompressionMethod.NULL }; + } + + public virtual IDictionary GetClientExtensions() + { + return null; + } + + public virtual void NotifySessionID(byte[] sessionID) + { + // Currently ignored + } + + public virtual void NotifySelectedCipherSuite(CipherSuite selectedCipherSuite) + { + this.selectedCipherSuite = selectedCipherSuite; + } + + public virtual void NotifySelectedCompressionMethod(CompressionMethod selectedCompressionMethod) + { + this.selectedCompressionMethod = selectedCompressionMethod; + } + + public virtual void NotifySecureRenegotiation(bool secureRenegotiation) + { + if (!secureRenegotiation) + { + /* + * RFC 5746 3.4. + * If the extension is not present, the server does not support + * secure renegotiation; set secure_renegotiation flag to FALSE. + * 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 virtual void ProcessServerExtensions(IDictionary serverExtensions) + { + } + + public virtual TlsKeyExchange GetKeyExchange() + { + 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_256_CBC_SHA: + return CreateRsaKeyExchange(); + + 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_256_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_256_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_256_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_256_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_256_CBC_SHA: + return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_ECDSA); + + 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: + return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_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_256_CBC_SHA: + return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_RSA); + + 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: + return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_RSA); + + default: + /* + * Note: internal error here; the TlsProtocolHandler 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 abstract TlsAuthentication GetAuthentication(); + + public virtual TlsCompression GetCompression() + { + switch (selectedCompressionMethod) + { + case CompressionMethod.NULL: + return new TlsNullCompression(); + + case CompressionMethod.DEFLATE: + return new TlsDeflateCompression(); + + default: + /* + * Note: internal error here; the TlsProtocolHandler 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 virtual TlsCipher GetCipher() + { + switch (selectedCipherSuite) + { + case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA: + 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_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: + return cipherFactory.CreateCipher(context, EncryptionAlgorithm.cls_3DES_EDE_CBC, DigestAlgorithm.SHA); + + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA: + 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_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_128_CBC, DigestAlgorithm.SHA); + + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA: + 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_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_256_CBC, DigestAlgorithm.SHA); + + default: + /* + * Note: internal error here; the TlsProtocolHandler 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 virtual TlsKeyExchange CreateDHKeyExchange(KeyExchangeAlgorithm keyExchange) + { + return new TlsDHKeyExchange(context, keyExchange); + } + + protected virtual TlsKeyExchange CreateDheKeyExchange(KeyExchangeAlgorithm keyExchange) + { + return new TlsDheKeyExchange(context, keyExchange); + } + + protected virtual TlsKeyExchange CreateECDHKeyExchange(KeyExchangeAlgorithm keyExchange) + { + return new TlsECDHKeyExchange(context, keyExchange); + } + + protected virtual TlsKeyExchange CreateECDheKeyExchange(KeyExchangeAlgorithm keyExchange) + { + return new TlsECDheKeyExchange(context, keyExchange); + } + + protected virtual TlsKeyExchange CreateRsaKeyExchange() + { + return new TlsRsaKeyExchange(context); + } + } +} diff --git a/Crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs b/Crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs new file mode 100644
index 000000000..23d607d85 --- /dev/null +++ b/Crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs
@@ -0,0 +1,76 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class DefaultTlsSignerCredentials + : TlsSignerCredentials + { + protected TlsClientContext context; + protected Certificate clientCert; + protected AsymmetricKeyParameter clientPrivateKey; + + protected TlsSigner clientSigner; + + public DefaultTlsSignerCredentials(TlsClientContext context, + Certificate clientCertificate, AsymmetricKeyParameter clientPrivateKey) + { + if (clientCertificate == null) + { + throw new ArgumentNullException("clientCertificate"); + } + if (clientCertificate.certs.Length == 0) + { + throw new ArgumentException("cannot be empty", "clientCertificate"); + } + if (clientPrivateKey == null) + { + throw new ArgumentNullException("clientPrivateKey"); + } + if (!clientPrivateKey.IsPrivate) + { + throw new ArgumentException("must be private", "clientPrivateKey"); + } + + if (clientPrivateKey is RsaKeyParameters) + { + clientSigner = new TlsRsaSigner(); + } + else if (clientPrivateKey is DsaPrivateKeyParameters) + { + clientSigner = new TlsDssSigner(); + } + else if (clientPrivateKey is ECPrivateKeyParameters) + { + clientSigner = new TlsECDsaSigner(); + } + else + { + throw new ArgumentException("type not supported: " + + clientPrivateKey.GetType().FullName, "clientPrivateKey"); + } + + this.context = context; + this.clientCert = clientCertificate; + this.clientPrivateKey = clientPrivateKey; + } + + public virtual Certificate Certificate + { + get { return clientCert; } + } + + public virtual byte[] GenerateCertificateSignature(byte[] md5andsha1) + { + try + { + return clientSigner.CalculateRawSignature(context.SecureRandom, clientPrivateKey, md5andsha1); + } + catch (CryptoException) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + } +} diff --git a/Crypto/src/crypto/tls/DigestAlgorithm.cs b/Crypto/src/crypto/tls/DigestAlgorithm.cs new file mode 100644
index 000000000..cede6b7f8 --- /dev/null +++ b/Crypto/src/crypto/tls/DigestAlgorithm.cs
@@ -0,0 +1,21 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public enum DigestAlgorithm + { + /* + * Note that the values here are implementation-specific and arbitrary. + * It is recommended not to depend on the particular values (e.g. serialization). + */ + NULL, + MD5, + SHA, + + /* + * RFC 5289 + */ + SHA256, + SHA384, + } +} diff --git a/Crypto/src/crypto/tls/ECCurveType.cs b/Crypto/src/crypto/tls/ECCurveType.cs new file mode 100644
index 000000000..15d5d7b42 --- /dev/null +++ b/Crypto/src/crypto/tls/ECCurveType.cs
@@ -0,0 +1,29 @@ +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <summary> + /// RFC 4492 5.4 + /// </summary> + public enum ECCurveType : byte + { + /** + * Indicates the elliptic curve domain parameters are conveyed verbosely, and the + * underlying finite field is a prime field. + */ + explicit_prime = 1, + + /** + * Indicates the elliptic curve domain parameters are conveyed verbosely, and the + * underlying finite field is a characteristic-2 field. + */ + explicit_char2 = 2, + + /** + * Indicates that a named curve is used. This option SHOULD be used when applicable. + */ + named_curve = 3, + + /* + * Values 248 through 255 are reserved for private use. + */ + } +} diff --git a/Crypto/src/crypto/tls/ECPointFormat.cs b/Crypto/src/crypto/tls/ECPointFormat.cs new file mode 100644
index 000000000..4e0dd0067 --- /dev/null +++ b/Crypto/src/crypto/tls/ECPointFormat.cs
@@ -0,0 +1,16 @@ +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <summary> + /// RFC 4492 5.1.2 + /// </summary> + public enum ECPointFormat : byte + { + uncompressed = 0, + ansiX962_compressed_prime = 1, + ansiX962_compressed_char2 = 2, + + /* + * reserved (248..255) + */ + } +} diff --git a/Crypto/src/crypto/tls/EncryptionAlgorithm.cs b/Crypto/src/crypto/tls/EncryptionAlgorithm.cs new file mode 100644
index 000000000..79d3b63b5 --- /dev/null +++ b/Crypto/src/crypto/tls/EncryptionAlgorithm.cs
@@ -0,0 +1,32 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public enum EncryptionAlgorithm + { + /* + * Note that the values here are implementation-specific and arbitrary. + * It is recommended not to depend on the particular values (e.g. serialization). + */ + NULL, + RC4_40, + RC4_128, + RC2_CBC_40, + IDEA_CBC, + DES40_CBC, + DES_CBC, + cls_3DES_EDE_CBC, + + /* + * RFC 3268 + */ + AES_128_CBC, + AES_256_CBC, + + /* + * RFC 5289 + */ + AES_128_GCM, + AES_256_GCM, + } +} diff --git a/Crypto/src/crypto/tls/ExtensionType.cs b/Crypto/src/crypto/tls/ExtensionType.cs new file mode 100644
index 000000000..f00e34e3f --- /dev/null +++ b/Crypto/src/crypto/tls/ExtensionType.cs
@@ -0,0 +1,31 @@ +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <summary> + /// RFC 4366 2.3 + /// </summary> + public enum ExtensionType : int + { + server_name = 0, + max_fragment_length = 1, + client_certificate_url = 2, + trusted_ca_keys = 3, + truncated_hmac = 4, + status_request = 5, + + /* + * RFC 4492 + */ + elliptic_curves = 10, + ec_point_formats = 11, + + /* + * RFC 5054 2.8.1 + */ + srp = 12, + + /* + * RFC 5746 6 + */ + renegotiation_info = 0xff01, + } +} diff --git a/Crypto/src/crypto/tls/HandshakeType.cs b/Crypto/src/crypto/tls/HandshakeType.cs new file mode 100644
index 000000000..deedb1f84 --- /dev/null +++ b/Crypto/src/crypto/tls/HandshakeType.cs
@@ -0,0 +1,19 @@ +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <summary> + /// RFC 2246 7.4 + /// </summary> + public enum HandshakeType : byte + { + hello_request = 0, + client_hello = 1, + server_hello = 2, + certificate = 11, + server_key_exchange = 12, + certificate_request = 13, + server_hello_done = 14, + certificate_verify = 15, + client_key_exchange = 16, + finished = 20, + } +} diff --git a/Crypto/src/crypto/tls/ICertificateVerifyer.cs b/Crypto/src/crypto/tls/ICertificateVerifyer.cs new file mode 100644
index 000000000..df5ea51d7 --- /dev/null +++ b/Crypto/src/crypto/tls/ICertificateVerifyer.cs
@@ -0,0 +1,18 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <remarks> + /// This should be implemented by any class which can find out, if a given + /// certificate chain is being accepted by an client. + /// </remarks> + [Obsolete("Perform certificate verification in TlsAuthentication implementation")] + public interface ICertificateVerifyer + { + /// <param name="certs">The certs, which are part of the chain.</param> + /// <returns>True, if the chain is accepted, false otherwise</returns> + bool IsValid(X509CertificateStructure[] certs); + } +} diff --git a/Crypto/src/crypto/tls/KeyExchangeAlgorithm.cs b/Crypto/src/crypto/tls/KeyExchangeAlgorithm.cs new file mode 100644
index 000000000..3fdbeb2a6 --- /dev/null +++ b/Crypto/src/crypto/tls/KeyExchangeAlgorithm.cs
@@ -0,0 +1,36 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public enum KeyExchangeAlgorithm + { + /* + * Note that the values here are implementation-specific and arbitrary. + * It is recommended not to depend on the particular values (e.g. serialization). + */ + NULL, + RSA, + RSA_EXPORT, + DHE_DSS, + DHE_DSS_EXPORT, + DHE_RSA, + DHE_RSA_EXPORT, + DH_DSS, + DH_DSS_EXPORT, + DH_RSA, + DH_RSA_EXPORT, + DH_anon, + DH_anon_export, + PSK, + DHE_PSK, + RSA_PSK, + ECDH_ECDSA, + ECDHE_ECDSA, + ECDH_RSA, + ECDHE_RSA, + ECDH_anon, + SRP, + SRP_DSS, + SRP_RSA, + } +} diff --git a/Crypto/src/crypto/tls/LegacyTlsAuthentication.cs b/Crypto/src/crypto/tls/LegacyTlsAuthentication.cs new file mode 100644
index 000000000..395f94208 --- /dev/null +++ b/Crypto/src/crypto/tls/LegacyTlsAuthentication.cs
@@ -0,0 +1,30 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <summary> + /// A temporary class to wrap old CertificateVerifyer stuff for new TlsAuthentication. + /// </summary> + [Obsolete] + public class LegacyTlsAuthentication + : TlsAuthentication + { + protected ICertificateVerifyer verifyer; + + public LegacyTlsAuthentication(ICertificateVerifyer verifyer) + { + this.verifyer = verifyer; + } + + public virtual void NotifyServerCertificate(Certificate serverCertificate) + { + if (!this.verifyer.IsValid(serverCertificate.GetCerts())) + throw new TlsFatalAlert(AlertDescription.user_canceled); + } + + public virtual TlsCredentials GetClientCredentials(CertificateRequest certificateRequest) + { + return null; + } + } +} diff --git a/Crypto/src/crypto/tls/LegacyTlsClient.cs b/Crypto/src/crypto/tls/LegacyTlsClient.cs new file mode 100644
index 000000000..fbb9a732e --- /dev/null +++ b/Crypto/src/crypto/tls/LegacyTlsClient.cs
@@ -0,0 +1,26 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <summary> + /// A temporary class to use LegacyTlsAuthentication + /// </summary> + [Obsolete] + public class LegacyTlsClient + : DefaultTlsClient + { + [Obsolete] + protected ICertificateVerifyer verifyer; + + [Obsolete] + public LegacyTlsClient(ICertificateVerifyer verifyer) + { + this.verifyer = verifyer; + } + + public override TlsAuthentication GetAuthentication() + { + return new LegacyTlsAuthentication(verifyer); + } + } +} \ No newline at end of file diff --git a/Crypto/src/crypto/tls/NamedCurve.cs b/Crypto/src/crypto/tls/NamedCurve.cs new file mode 100644
index 000000000..c8ee189aa --- /dev/null +++ b/Crypto/src/crypto/tls/NamedCurve.cs
@@ -0,0 +1,72 @@ +using System; + +using Org.BouncyCastle.Asn1.Sec; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <summary> + /// RFC 4492 5.1.1 + /// 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). + /// </summary> + public enum NamedCurve : int + { + sect163k1 = 1, + sect163r1 = 2, + sect163r2 = 3, + sect193r1 = 4, + sect193r2 = 5, + sect233k1 = 6, + sect233r1 = 7, + sect239k1 = 8, + sect283k1 = 9, + sect283r1 = 10, + sect409k1 = 11, + sect409r1 = 12, + sect571k1 = 13, + sect571r1 = 14, + secp160k1 = 15, + secp160r1 = 16, + secp160r2 = 17, + secp192k1 = 18, + secp192r1 = 19, + secp224k1 = 20, + secp224r1 = 21, + secp256k1 = 22, + secp256r1 = 23, + secp384r1 = 24, + secp521r1 = 25, + + /* + * reserved (0xFE00..0xFEFF) + */ + + arbitrary_explicit_prime_curves = 0xFF01, + arbitrary_explicit_char2_curves = 0xFF02, + } + + internal class NamedCurveHelper + { + internal static ECDomainParameters GetECParameters(NamedCurve namedCurve) + { + if (!Enum.IsDefined(typeof(NamedCurve), namedCurve)) + return null; + + string curveName = namedCurve.ToString(); + + // 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.Curve, ecP.G, ecP.N, ecP.H, ecP.GetSeed()); + } + } +} diff --git a/Crypto/src/crypto/tls/PskTlsClient.cs b/Crypto/src/crypto/tls/PskTlsClient.cs new file mode 100644
index 000000000..16975e713 --- /dev/null +++ b/Crypto/src/crypto/tls/PskTlsClient.cs
@@ -0,0 +1,182 @@ +using System; +using System.Collections; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class PskTlsClient + :TlsClient + { + protected TlsCipherFactory cipherFactory; + protected TlsPskIdentity pskIdentity; + + protected TlsClientContext context; + + protected CompressionMethod selectedCompressionMethod; + protected CipherSuite selectedCipherSuite; + + public PskTlsClient(TlsPskIdentity pskIdentity) + : this(new DefaultTlsCipherFactory(), pskIdentity) + { + } + + public PskTlsClient(TlsCipherFactory cipherFactory, TlsPskIdentity pskIdentity) + { + this.cipherFactory = cipherFactory; + this.pskIdentity = pskIdentity; + } + + public virtual void Init(TlsClientContext context) + { + this.context = context; + } + + public virtual CipherSuite[] GetCipherSuites() + { + return new CipherSuite[] { + 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_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_PSK_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA, + }; + } + + public virtual IDictionary GetClientExtensions() + { + return null; + } + + public virtual CompressionMethod[] GetCompressionMethods() + { + return new CompressionMethod[] { CompressionMethod.NULL }; + } + + public virtual void NotifySessionID(byte[] sessionID) + { + // Currently ignored + } + + public virtual void NotifySelectedCipherSuite(CipherSuite selectedCipherSuite) + { + this.selectedCipherSuite = selectedCipherSuite; + } + + public virtual void NotifySelectedCompressionMethod(CompressionMethod selectedCompressionMethod) + { + this.selectedCompressionMethod = selectedCompressionMethod; + } + + public virtual void NotifySecureRenegotiation(bool secureRenegotiation) + { + if (!secureRenegotiation) + { + /* + * RFC 5746 3.4. If the extension is not present, the server does not support + * secure renegotiation; set secure_renegotiation flag to FALSE. 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 virtual void ProcessServerExtensions(IDictionary serverExtensions) + { + } + + public virtual TlsKeyExchange GetKeyExchange() + { + 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: + 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: + 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: + return CreatePskKeyExchange(KeyExchangeAlgorithm.DHE_PSK); + + default: + /* + * Note: internal error here; the TlsProtocolHandler 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 virtual TlsAuthentication GetAuthentication() + { + return null; + } + + public virtual TlsCompression GetCompression() + { + switch (selectedCompressionMethod) + { + case CompressionMethod.NULL: + return new TlsNullCompression(); + + default: + /* + * Note: internal error here; the TlsProtocolHandler 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 virtual TlsCipher GetCipher() + { + 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.cls_3DES_EDE_CBC, + DigestAlgorithm.SHA); + + 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, + DigestAlgorithm.SHA); + + 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, + DigestAlgorithm.SHA); + + default: + /* + * Note: internal error here; the TlsProtocolHandler 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 virtual TlsKeyExchange CreatePskKeyExchange(KeyExchangeAlgorithm keyExchange) + { + return new TlsPskKeyExchange(context, keyExchange, pskIdentity); + } + } +} diff --git a/Crypto/src/crypto/tls/RecordStream.cs b/Crypto/src/crypto/tls/RecordStream.cs new file mode 100644
index 000000000..e18894b4e --- /dev/null +++ b/Crypto/src/crypto/tls/RecordStream.cs
@@ -0,0 +1,166 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <remarks>An implementation of the TLS 1.0 record layer.</remarks> + internal class RecordStream + { + private TlsProtocolHandler handler; + private Stream inStr; + private Stream outStr; + private CombinedHash hash; + private TlsCompression readCompression = null; + private TlsCompression writeCompression = null; + private TlsCipher readCipher = null; + private TlsCipher writeCipher = null; + private MemoryStream buffer = new MemoryStream(); + + internal RecordStream( + TlsProtocolHandler handler, + Stream inStr, + Stream outStr) + { + this.handler = handler; + this.inStr = inStr; + this.outStr = outStr; + this.hash = new CombinedHash(); + this.readCompression = new TlsNullCompression(); + this.writeCompression = this.readCompression; + this.readCipher = new TlsNullCipher(); + this.writeCipher = this.readCipher; + } + + internal void ClientCipherSpecDecided(TlsCompression tlsCompression, TlsCipher tlsCipher) + { + this.writeCompression = tlsCompression; + this.writeCipher = tlsCipher; + } + + internal void ServerClientSpecReceived() + { + this.readCompression = this.writeCompression; + this.readCipher = this.writeCipher; + } + + public void ReadData() + { + ContentType type = (ContentType)TlsUtilities.ReadUint8(inStr); + TlsUtilities.CheckVersion(inStr); + int size = TlsUtilities.ReadUint16(inStr); + byte[] buf = DecodeAndVerify(type, inStr, size); + handler.ProcessData(type, buf, 0, buf.Length); + } + + internal byte[] DecodeAndVerify( + ContentType type, + Stream inStr, + int len) + { + byte[] buf = new byte[len]; + TlsUtilities.ReadFully(buf, inStr); + byte[] decoded = readCipher.DecodeCiphertext(type, buf, 0, buf.Length); + + Stream cOut = readCompression.Decompress(buffer); + + if (cOut == buffer) + { + return decoded; + } + + cOut.Write(decoded, 0, decoded.Length); + cOut.Flush(); + byte[] contents = buffer.ToArray(); + buffer.SetLength(0); + return contents; + } + + internal void WriteMessage( + ContentType type, + byte[] message, + int offset, + int len) + { + if (type == ContentType.handshake) + { + UpdateHandshakeData(message, offset, len); + } + + Stream cOut = writeCompression.Compress(buffer); + + byte[] ciphertext; + if (cOut == buffer) + { + ciphertext = writeCipher.EncodePlaintext(type, message, offset, len); + } + else + { + cOut.Write(message, offset, len); + cOut.Flush(); + ciphertext = writeCipher.EncodePlaintext(type, buffer.ToArray(), 0, (int)buffer.Position); + buffer.SetLength(0); + } + + byte[] writeMessage = new byte[ciphertext.Length + 5]; + TlsUtilities.WriteUint8((byte)type, writeMessage, 0); + TlsUtilities.WriteVersion(writeMessage, 1); + TlsUtilities.WriteUint16(ciphertext.Length, writeMessage, 3); + Array.Copy(ciphertext, 0, writeMessage, 5, ciphertext.Length); + outStr.Write(writeMessage, 0, writeMessage.Length); + outStr.Flush(); + } + + internal void UpdateHandshakeData( + byte[] message, + int offset, + int len) + { + hash.BlockUpdate(message, offset, len); + } + + internal byte[] GetCurrentHash() + { + return DoFinal(new CombinedHash(hash)); + } + + internal void Close() + { + IOException e = null; + try + { + inStr.Dispose(); + } + catch (IOException ex) + { + e = ex; + } + + try + { + // NB: This is harmless if outStr == inStr + outStr.Dispose(); + } + catch (IOException ex) + { + e = ex; + } + + if (e != null) + { + throw e; + } + } + + internal void Flush() + { + outStr.Flush(); + } + + private static byte[] DoFinal(CombinedHash ch) + { + byte[] bs = new byte[ch.GetDigestSize()]; + ch.DoFinal(bs, 0); + return bs; + } + } +} diff --git a/Crypto/src/crypto/tls/SecurityParameters.cs b/Crypto/src/crypto/tls/SecurityParameters.cs new file mode 100644
index 000000000..9ed3969eb --- /dev/null +++ b/Crypto/src/crypto/tls/SecurityParameters.cs
@@ -0,0 +1,26 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class SecurityParameters + { + internal byte[] clientRandom = null; + internal byte[] serverRandom = null; + internal byte[] masterSecret = null; + + public byte[] ClientRandom + { + get { return clientRandom; } + } + + public byte[] ServerRandom + { + get { return serverRandom; } + } + + public byte[] MasterSecret + { + get { return masterSecret; } + } + } +} diff --git a/Crypto/src/crypto/tls/SrpTlsClient.cs b/Crypto/src/crypto/tls/SrpTlsClient.cs new file mode 100644
index 000000000..6c2638bb3 --- /dev/null +++ b/Crypto/src/crypto/tls/SrpTlsClient.cs
@@ -0,0 +1,188 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class SrpTlsClient + : TlsClient + { + protected TlsCipherFactory cipherFactory; + protected byte[] identity; + protected byte[] password; + + protected TlsClientContext context; + + protected CompressionMethod selectedCompressionMethod; + protected CipherSuite selectedCipherSuite; + + public SrpTlsClient(byte[] identity, byte[] password) + : this(new DefaultTlsCipherFactory(), identity, password) + { + } + + public SrpTlsClient(TlsCipherFactory cipherFactory, byte[] identity, byte[] password) + { + this.cipherFactory = cipherFactory; + this.identity = Arrays.Clone(identity); + this.password = Arrays.Clone(password); + } + + public virtual void Init(TlsClientContext context) + { + this.context = context; + } + + public virtual CipherSuite[] GetCipherSuites() + { + return new CipherSuite[] { + CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA, + 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 virtual IDictionary GetClientExtensions() + { + IDictionary clientExtensions = Platform.CreateHashtable(); + + MemoryStream srpData = new MemoryStream(); + TlsUtilities.WriteOpaque8(this.identity, srpData); + clientExtensions[ExtensionType.srp] = srpData.ToArray(); + + return clientExtensions; + } + + public virtual CompressionMethod[] GetCompressionMethods() + { + return new CompressionMethod[] { CompressionMethod.NULL }; + } + + public virtual void NotifySessionID(byte[] sessionID) + { + // Currently ignored + } + + public virtual void NotifySelectedCipherSuite(CipherSuite selectedCipherSuite) + { + this.selectedCipherSuite = selectedCipherSuite; + } + + public virtual void NotifySelectedCompressionMethod(CompressionMethod selectedCompressionMethod) + { + this.selectedCompressionMethod = selectedCompressionMethod; + } + + public virtual void NotifySecureRenegotiation(bool secureRenegotiation) + { + if (!secureRenegotiation) + { + /* + * RFC 5746 3.4. If the extension is not present, the server does not support + * secure renegotiation; set secure_renegotiation flag to FALSE. 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 virtual void ProcessServerExtensions(IDictionary serverExtensions) + { + // There is no server response for the SRP extension + } + + public virtual TlsKeyExchange GetKeyExchange() + { + 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 TlsProtocolHandler 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 abstract TlsAuthentication GetAuthentication(); + + public virtual TlsCompression GetCompression() + { + switch (selectedCompressionMethod) + { + case CompressionMethod.NULL: + return new TlsNullCompression(); + + default: + /* + * Note: internal error here; the TlsProtocolHandler 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 virtual TlsCipher GetCipher() + { + 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.cls_3DES_EDE_CBC, DigestAlgorithm.SHA); + + 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, DigestAlgorithm.SHA); + + 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, DigestAlgorithm.SHA); + + default: + /* + * Note: internal error here; the TlsProtocolHandler 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 virtual TlsKeyExchange CreateSrpKeyExchange(KeyExchangeAlgorithm keyExchange) + { + return new TlsSrpKeyExchange(context, keyExchange, identity, password); + } + } +} diff --git a/Crypto/src/crypto/tls/Ssl3Mac.cs b/Crypto/src/crypto/tls/Ssl3Mac.cs new file mode 100644
index 000000000..b2f3f309e --- /dev/null +++ b/Crypto/src/crypto/tls/Ssl3Mac.cs
@@ -0,0 +1,114 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /** + * HMAC implementation based on original internet draft for HMAC (RFC 2104) + * + * The difference is that padding is concatentated versus XORed with the key + * + * H(K + opad, H(K + ipad, text)) + */ + public class Ssl3Mac + : IMac + { + private const byte IPAD = 0x36; + private const byte OPAD = 0x5C; + + internal static readonly byte[] MD5_IPAD = GenPad(IPAD, 48); + internal static readonly byte[] MD5_OPAD = GenPad(OPAD, 48); + internal static readonly byte[] SHA1_IPAD = GenPad(IPAD, 40); + internal static readonly byte[] SHA1_OPAD = GenPad(OPAD, 40); + + private IDigest digest; + + private byte[] secret; + private byte[] ipad, opad; + + /** + * 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(IDigest digest) + { + this.digest = digest; + + if (digest.GetDigestSize() == 20) + { + this.ipad = SHA1_IPAD; + this.opad = SHA1_OPAD; + } + else + { + this.ipad = MD5_IPAD; + this.opad = MD5_OPAD; + } + } + + public virtual string AlgorithmName + { + get { return digest.AlgorithmName + "/SSL3MAC"; } + } + + public virtual void Init(ICipherParameters parameters) + { + secret = Arrays.Clone(((KeyParameter)parameters).GetKey()); + + Reset(); + } + + public virtual int GetMacSize() + { + return digest.GetDigestSize(); + } + + public virtual void Update(byte input) + { + digest.Update(input); + } + + public virtual void BlockUpdate(byte[] input, int inOff, int len) + { + digest.BlockUpdate(input, inOff, len); + } + + public virtual int DoFinal(byte[] output, int outOff) + { + byte[] tmp = new byte[digest.GetDigestSize()]; + digest.DoFinal(tmp, 0); + + digest.BlockUpdate(secret, 0, secret.Length); + digest.BlockUpdate(opad, 0, opad.Length); + digest.BlockUpdate(tmp, 0, tmp.Length); + + int len = digest.DoFinal(output, outOff); + + Reset(); + + return len; + } + + /** + * Reset the mac generator. + */ + public virtual void Reset() + { + digest.Reset(); + digest.BlockUpdate(secret, 0, secret.Length); + digest.BlockUpdate(ipad, 0, ipad.Length); + } + + private static byte[] GenPad(byte b, int count) + { + byte[] padding = new byte[count]; + Arrays.Fill(padding, b); + return padding; + } + } +} diff --git a/Crypto/src/crypto/tls/TlsAgreementCredentials.cs b/Crypto/src/crypto/tls/TlsAgreementCredentials.cs new file mode 100644
index 000000000..46ee4f90e --- /dev/null +++ b/Crypto/src/crypto/tls/TlsAgreementCredentials.cs
@@ -0,0 +1,11 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsAgreementCredentials : TlsCredentials + { + /// <exception cref="IOException"></exception> + byte[] GenerateAgreement(AsymmetricKeyParameter serverPublicKey); + } +} diff --git a/Crypto/src/crypto/tls/TlsAuthentication.cs b/Crypto/src/crypto/tls/TlsAuthentication.cs new file mode 100644
index 000000000..9aea5e449 --- /dev/null +++ b/Crypto/src/crypto/tls/TlsAuthentication.cs
@@ -0,0 +1,31 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsAuthentication + { + /// <summary> + /// Called by the protocol handler to report the server certificate. + /// </summary> + /// <remarks> + /// This method is responsible for certificate verification and validation + /// </remarks> + /// <param name="serverCertificate">The server <see cref="Certificate"/> received</param> + /// <exception cref="IOException"></exception> + void NotifyServerCertificate(Certificate serverCertificate); + + /// <summary> + /// Return client credentials in response to server's certificate request + /// </summary> + /// <param name="certificateRequest"> + /// A <see cref="CertificateRequest"/> containing server certificate request details + /// </param> + /// <returns> + /// A <see cref="TlsCredentials"/> to be used for client authentication + /// (or <c>null</c> for no client authentication) + /// </returns> + /// <exception cref="IOException"></exception> + TlsCredentials GetClientCredentials(CertificateRequest certificateRequest); + } +} diff --git a/Crypto/src/crypto/tls/TlsBlockCipher.cs b/Crypto/src/crypto/tls/TlsBlockCipher.cs new file mode 100644
index 000000000..ef7be1913 --- /dev/null +++ b/Crypto/src/crypto/tls/TlsBlockCipher.cs
@@ -0,0 +1,248 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <summary> + /// A generic TLS 1.0 block cipher. This can be used for AES or 3DES for example. + /// </summary> + public class TlsBlockCipher + : TlsCipher + { + protected TlsClientContext context; + + protected IBlockCipher encryptCipher; + protected IBlockCipher decryptCipher; + + protected TlsMac wMac; + protected TlsMac rMac; + + public virtual TlsMac WriteMac + { + get { return wMac; } + } + + public virtual TlsMac ReadMac + { + get { return rMac; } + } + + public TlsBlockCipher(TlsClientContext context, IBlockCipher encryptCipher, + IBlockCipher decryptCipher, IDigest writeDigest, IDigest readDigest, int cipherKeySize) + { + this.context = context; + this.encryptCipher = encryptCipher; + this.decryptCipher = decryptCipher; + + int prfSize = (2 * cipherKeySize) + writeDigest.GetDigestSize() + + readDigest.GetDigestSize() + encryptCipher.GetBlockSize() + + decryptCipher.GetBlockSize(); + + SecurityParameters securityParameters = context.SecurityParameters; + + byte[] keyBlock = TlsUtilities.PRF(securityParameters.masterSecret, "key expansion", + TlsUtilities.Concat(securityParameters.serverRandom, securityParameters.clientRandom), + prfSize); + + int offset = 0; + + // Init MACs + wMac = CreateTlsMac(writeDigest, keyBlock, ref offset); + rMac = CreateTlsMac(readDigest, keyBlock, ref offset); + + // Build keys + KeyParameter encryptKey = CreateKeyParameter(keyBlock, ref offset, cipherKeySize); + KeyParameter decryptKey = CreateKeyParameter(keyBlock, ref offset, cipherKeySize); + + // Add IVs + ParametersWithIV encryptParams = CreateParametersWithIV(encryptKey, + keyBlock, ref offset, encryptCipher.GetBlockSize()); + ParametersWithIV decryptParams = CreateParametersWithIV(decryptKey, + keyBlock, ref offset, decryptCipher.GetBlockSize()); + + if (offset != prfSize) + throw new TlsFatalAlert(AlertDescription.internal_error); + + // Init Ciphers + encryptCipher.Init(true, encryptParams); + decryptCipher.Init(false, decryptParams); + } + + protected virtual TlsMac CreateTlsMac(IDigest digest, byte[] buf, ref int off) + { + int len = digest.GetDigestSize(); + TlsMac mac = new TlsMac(digest, buf, off, len); + off += len; + return mac; + } + + protected virtual KeyParameter CreateKeyParameter(byte[] buf, ref int off, int len) + { + KeyParameter key = new KeyParameter(buf, off, len); + off += len; + return key; + } + + protected virtual ParametersWithIV CreateParametersWithIV(KeyParameter key, + byte[] buf, ref int off, int len) + { + ParametersWithIV ivParams = new ParametersWithIV(key, buf, off, len); + off += len; + return ivParams; + } + + public virtual byte[] EncodePlaintext(ContentType type, byte[] plaintext, int offset, int len) + { + int blocksize = encryptCipher.GetBlockSize(); + + // Add a random number of extra blocks worth of padding + int minPaddingSize = blocksize - ((len + wMac.Size + 1) % blocksize); + int maxExtraPadBlocks = (255 - minPaddingSize) / blocksize; + int actualExtraPadBlocks = ChooseExtraPadBlocks(context.SecureRandom, maxExtraPadBlocks); + int paddingsize = minPaddingSize + (actualExtraPadBlocks * blocksize); + + int totalsize = len + wMac.Size + paddingsize + 1; + byte[] outbuf = new byte[totalsize]; + Array.Copy(plaintext, offset, outbuf, 0, len); + byte[] mac = wMac.CalculateMac(type, plaintext, offset, len); + Array.Copy(mac, 0, outbuf, len, mac.Length); + int paddoffset = len + mac.Length; + for (int i = 0; i <= paddingsize; i++) + { + outbuf[i + paddoffset] = (byte)paddingsize; + } + for (int i = 0; i < totalsize; i += blocksize) + { + encryptCipher.ProcessBlock(outbuf, i, outbuf, i); + } + return outbuf; + } + + public virtual byte[] DecodeCiphertext(ContentType type, byte[] ciphertext, int offset, int len) + { + // TODO TLS 1.1 (RFC 4346) introduces an explicit IV + + int minLength = rMac.Size + 1; + int blocksize = decryptCipher.GetBlockSize(); + bool decrypterror = false; + + /* + * ciphertext must be at least (macsize + 1) bytes long + */ + if (len < minLength) + { + throw new TlsFatalAlert(AlertDescription.decode_error); + } + + /* + * ciphertext must be a multiple of blocksize + */ + if (len % blocksize != 0) + { + throw new TlsFatalAlert(AlertDescription.decryption_failed); + } + + /* + * Decrypt all the ciphertext using the blockcipher + */ + for (int i = 0; i < len; i += blocksize) + { + decryptCipher.ProcessBlock(ciphertext, i + offset, ciphertext, i + offset); + } + + /* + * Check if padding is correct + */ + int lastByteOffset = offset + len - 1; + + byte paddingsizebyte = ciphertext[lastByteOffset]; + + int paddingsize = paddingsizebyte; + + int maxPaddingSize = len - minLength; + if (paddingsize > maxPaddingSize) + { + decrypterror = true; + paddingsize = 0; + } + else + { + /* + * Now, check all the padding-bytes (constant-time comparison). + */ + byte diff = 0; + for (int i = lastByteOffset - paddingsize; i < lastByteOffset; ++i) + { + diff |= (byte)(ciphertext[i] ^ paddingsizebyte); + } + if (diff != 0) + { + /* Wrong padding */ + decrypterror = true; + paddingsize = 0; + } + } + + /* + * We now don't care if padding verification has failed or not, we will calculate + * the mac to give an attacker no kind of timing profile he can use to find out if + * mac verification failed or padding verification failed. + */ + int plaintextlength = len - minLength - paddingsize; + byte[] calculatedMac = rMac.CalculateMac(type, ciphertext, offset, plaintextlength); + + /* + * Check all bytes in the mac (constant-time comparison). + */ + byte[] decryptedMac = new byte[calculatedMac.Length]; + Array.Copy(ciphertext, offset + plaintextlength, decryptedMac, 0, calculatedMac.Length); + + if (!Arrays.ConstantTimeAreEqual(calculatedMac, decryptedMac)) + { + decrypterror = true; + } + + /* + * Now, it is safe to fail. + */ + if (decrypterror) + { + throw new TlsFatalAlert(AlertDescription.bad_record_mac); + } + + byte[] plaintext = new byte[plaintextlength]; + Array.Copy(ciphertext, offset, plaintext, 0, plaintextlength); + return plaintext; + } + + protected virtual int ChooseExtraPadBlocks(SecureRandom r, int max) + { +// return r.NextInt(max + 1); + + uint x = (uint)r.NextInt(); + int n = LowestBitSet(x); + return System.Math.Min(n, max); + } + + private int LowestBitSet(uint x) + { + if (x == 0) + { + return 32; + } + + int n = 0; + while ((x & 1) == 0) + { + ++n; + x >>= 1; + } + return n; + } + } +} diff --git a/Crypto/src/crypto/tls/TlsCipher.cs b/Crypto/src/crypto/tls/TlsCipher.cs new file mode 100644
index 000000000..22c769d82 --- /dev/null +++ b/Crypto/src/crypto/tls/TlsCipher.cs
@@ -0,0 +1,14 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsCipher + { + /// <exception cref="IOException"></exception> + byte[] EncodePlaintext(ContentType type, byte[] plaintext, int offset, int len); + + /// <exception cref="IOException"></exception> + byte[] DecodeCiphertext(ContentType type, byte[] ciphertext, int offset, int len); + } +} diff --git a/Crypto/src/crypto/tls/TlsCipherFactory.cs b/Crypto/src/crypto/tls/TlsCipherFactory.cs new file mode 100644
index 000000000..0756603f4 --- /dev/null +++ b/Crypto/src/crypto/tls/TlsCipherFactory.cs
@@ -0,0 +1,12 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsCipherFactory + { + /// <exception cref="IOException"></exception> + TlsCipher CreateCipher(TlsClientContext context, EncryptionAlgorithm encryptionAlgorithm, + DigestAlgorithm digestAlgorithm); + } +} diff --git a/Crypto/src/crypto/tls/TlsClient.cs b/Crypto/src/crypto/tls/TlsClient.cs new file mode 100644
index 000000000..eceaa3cd3 --- /dev/null +++ b/Crypto/src/crypto/tls/TlsClient.cs
@@ -0,0 +1,129 @@ +using System; +using System.Collections; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsClient + { + /// <summary> + /// Called at the start of a new TLS session, before any other methods. + /// </summary> + /// <param name="context"> + /// A <see cref="TlsProtocolHandler"/> + /// </param> + void Init(TlsClientContext context); + + /// <summary> + /// Get the list of cipher suites that this client supports. + /// </summary> + /// <returns> + /// An array of <see cref="CipherSuite"/>, each specifying a supported cipher suite. + /// </returns> + CipherSuite[] GetCipherSuites(); + + /// <summary> + /// Get the list of compression methods that this client supports. + /// </summary> + /// <returns> + /// An array of <see cref="CompressionMethod"/>, each specifying a supported compression method. + /// </returns> + CompressionMethod[] GetCompressionMethods(); + + /// <summary> + /// Get the (optional) table of client extensions to be included in (extended) client hello. + /// </summary> + /// <returns> + /// A <see cref="IDictionary"/> (<see cref="ExtensionType"/> -> byte[]). May be null. + /// </returns> + /// <exception cref="IOException"></exception> + IDictionary GetClientExtensions(); + + /// <summary> + /// Reports the session ID once it has been determined. + /// </summary> + /// <param name="sessionID"> + /// A <see cref="System.Byte"/> + /// </param> + void NotifySessionID(byte[] sessionID); + + /// <summary> + /// Report the cipher suite that was selected by the server. + /// </summary> + /// <remarks> + /// The protocol handler validates this value against the offered cipher suites + /// <seealso cref="GetCipherSuites"/> + /// </remarks> + /// <param name="selectedCipherSuite"> + /// A <see cref="CipherSuite"/> + /// </param> + void NotifySelectedCipherSuite(CipherSuite selectedCipherSuite); + + /// <summary> + /// Report the compression method that was selected by the server. + /// </summary> + /// <remarks> + /// The protocol handler validates this value against the offered compression methods + /// <seealso cref="GetCompressionMethods"/> + /// </remarks> + /// <param name="selectedCompressionMethod"> + /// A <see cref="CompressionMethod"/> + /// </param> + void NotifySelectedCompressionMethod(CompressionMethod selectedCompressionMethod); + + /// <summary> + /// Report whether the server supports secure renegotiation + /// </summary> + /// <remarks> + /// The protocol handler automatically processes the relevant extensions + /// </remarks> + /// <param name="secureRenegotiation"> + /// A <see cref="System.Boolean"/>, true if the server supports secure renegotiation + /// </param> + /// <exception cref="IOException"></exception> + void NotifySecureRenegotiation(bool secureRenegotiation); + + /// <summary> + /// Report the extensions from an extended server hello. + /// </summary> + /// <remarks> + /// Will only be called if we returned a non-null result from <see cref="GetClientExtensions"/>. + /// </remarks> + /// <param name="serverExtensions"> + /// A <see cref="IDictionary"/> (<see cref="ExtensionType"/> -> byte[]) + /// </param> + void ProcessServerExtensions(IDictionary serverExtensions); + + /// <summary> + /// Return an implementation of <see cref="TlsKeyExchange"/> to negotiate the key exchange + /// part of the protocol. + /// </summary> + /// <returns> + /// A <see cref="TlsKeyExchange"/> + /// </returns> + /// <exception cref="IOException"/> + TlsKeyExchange GetKeyExchange(); + + /// <summary> + /// Return an implementation of <see cref="TlsAuthentication"/> to handle authentication + /// part of the protocol. + /// </summary> + /// <exception cref="IOException"/> + TlsAuthentication GetAuthentication(); + + /// <summary> + /// Return an implementation of <see cref="TlsCompression"/> to handle record compression. + /// </summary> + /// <exception cref="IOException"/> + TlsCompression GetCompression(); + + /// <summary> + /// Return an implementation of <see cref="TlsCipher"/> to use for encryption/decryption. + /// </summary> + /// <returns> + /// A <see cref="TlsCipher"/> + /// </returns> + /// <exception cref="IOException"/> + TlsCipher GetCipher(); + } +} diff --git a/Crypto/src/crypto/tls/TlsClientContext.cs b/Crypto/src/crypto/tls/TlsClientContext.cs new file mode 100644
index 000000000..dbb10aa76 --- /dev/null +++ b/Crypto/src/crypto/tls/TlsClientContext.cs
@@ -0,0 +1,15 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsClientContext + { + SecureRandom SecureRandom { get; } + + SecurityParameters SecurityParameters { get; } + + object UserObject { get; set; } + } +} diff --git a/Crypto/src/crypto/tls/TlsClientContextImpl.cs b/Crypto/src/crypto/tls/TlsClientContextImpl.cs new file mode 100644
index 000000000..9d5dee232 --- /dev/null +++ b/Crypto/src/crypto/tls/TlsClientContextImpl.cs
@@ -0,0 +1,37 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Tls +{ + internal class TlsClientContextImpl + : TlsClientContext + { + private readonly SecureRandom secureRandom; + private readonly SecurityParameters securityParameters; + + private object userObject = null; + + internal TlsClientContextImpl(SecureRandom secureRandom, SecurityParameters securityParameters) + { + this.secureRandom = secureRandom; + this.securityParameters = securityParameters; + } + + public virtual SecureRandom SecureRandom + { + get { return secureRandom; } + } + + public virtual SecurityParameters SecurityParameters + { + get { return securityParameters; } + } + + public virtual object UserObject + { + get { return userObject; } + set { this.userObject = value; } + } + } +} diff --git a/Crypto/src/crypto/tls/TlsCompression.cs b/Crypto/src/crypto/tls/TlsCompression.cs new file mode 100644
index 000000000..177d64b7e --- /dev/null +++ b/Crypto/src/crypto/tls/TlsCompression.cs
@@ -0,0 +1,12 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsCompression + { + Stream Compress(Stream output); + + Stream Decompress(Stream output); + } +} diff --git a/Crypto/src/crypto/tls/TlsCredentials.cs b/Crypto/src/crypto/tls/TlsCredentials.cs new file mode 100644
index 000000000..5c5f1c02e --- /dev/null +++ b/Crypto/src/crypto/tls/TlsCredentials.cs
@@ -0,0 +1,9 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsCredentials + { + Certificate Certificate { get; } + } +} diff --git a/Crypto/src/crypto/tls/TlsDHKeyExchange.cs b/Crypto/src/crypto/tls/TlsDHKeyExchange.cs new file mode 100644
index 000000000..40ac416e0 --- /dev/null +++ b/Crypto/src/crypto/tls/TlsDHKeyExchange.cs
@@ -0,0 +1,201 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <summary> + /// TLS 1.0 DH key exchange. + /// </summary> + internal class TlsDHKeyExchange + : TlsKeyExchange + { + protected TlsClientContext context; + protected KeyExchangeAlgorithm keyExchange; + protected TlsSigner tlsSigner; + + protected AsymmetricKeyParameter serverPublicKey = null; + protected DHPublicKeyParameters dhAgreeServerPublicKey = null; + protected TlsAgreementCredentials agreementCredentials; + protected DHPrivateKeyParameters dhAgreeClientPrivateKey = null; + + internal TlsDHKeyExchange(TlsClientContext context, KeyExchangeAlgorithm keyExchange) + { + 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 ArgumentException("unsupported key exchange algorithm", "keyExchange"); + } + + this.context = context; + this.keyExchange = keyExchange; + } + + public virtual void SkipServerCertificate() + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + public virtual void ProcessServerCertificate(Certificate serverCertificate) + { + X509CertificateStructure x509Cert = serverCertificate.certs[0]; + SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo; + + try + { + this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo); + } + catch (Exception) + { + throw new TlsFatalAlert(AlertDescription.unsupported_certificate); + } + + if (tlsSigner == null) + { + try + { + this.dhAgreeServerPublicKey = ValidateDHPublicKey((DHPublicKeyParameters)this.serverPublicKey); + } + catch (InvalidCastException) + { + throw new TlsFatalAlert(AlertDescription.certificate_unknown); + } + + TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.KeyAgreement); + } + else + { + if (!tlsSigner.IsValidPublicKey(this.serverPublicKey)) + { + throw new TlsFatalAlert(AlertDescription.certificate_unknown); + } + + TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature); + } + + // TODO + /* + * Perform various checks per RFC2246 7.4.2: "Unless otherwise specified, the + * signing algorithm for the certificate must be the same as the algorithm for the + * certificate key." + */ + } + + public virtual void SkipServerKeyExchange() + { + // OK + } + + public virtual void ProcessServerKeyExchange(Stream input) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest) + { + ClientCertificateType[] types = certificateRequest.CertificateTypes; + foreach (ClientCertificateType type in types) + { + switch (type) + { + 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 virtual void SkipClientCredentials() + { + this.agreementCredentials = null; + } + + public virtual void ProcessClientCredentials(TlsCredentials clientCredentials) + { + if (clientCredentials is TlsAgreementCredentials) + { + // TODO Validate client cert has matching parameters (see 'areCompatibleParameters')? + + this.agreementCredentials = (TlsAgreementCredentials)clientCredentials; + } + else if (clientCredentials is TlsSignerCredentials) + { + // OK + } + else + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public virtual void GenerateClientKeyExchange(Stream output) + { + /* + * 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) + { + GenerateEphemeralClientKeyExchange(dhAgreeServerPublicKey.Parameters, output); + } + } + + public virtual byte[] GeneratePremasterSecret() + { + if (agreementCredentials != null) + { + return agreementCredentials.GenerateAgreement(dhAgreeServerPublicKey); + } + + return CalculateDHBasicAgreement(dhAgreeServerPublicKey, dhAgreeClientPrivateKey); + } + + protected virtual bool AreCompatibleParameters(DHParameters a, DHParameters b) + { + return a.P.Equals(b.P) && a.G.Equals(b.G); + } + + protected virtual byte[] CalculateDHBasicAgreement(DHPublicKeyParameters publicKey, + DHPrivateKeyParameters privateKey) + { + return TlsDHUtilities.CalculateDHBasicAgreement(publicKey, privateKey); + } + + protected virtual AsymmetricCipherKeyPair GenerateDHKeyPair(DHParameters dhParams) + { + return TlsDHUtilities.GenerateDHKeyPair(context.SecureRandom, dhParams); + } + + protected virtual void GenerateEphemeralClientKeyExchange(DHParameters dhParams, Stream output) + { + this.dhAgreeClientPrivateKey = TlsDHUtilities.GenerateEphemeralClientKeyExchange( + context.SecureRandom, dhParams, output); + } + + protected virtual DHPublicKeyParameters ValidateDHPublicKey(DHPublicKeyParameters key) + { + return TlsDHUtilities.ValidateDHPublicKey(key); + } + } +} diff --git a/Crypto/src/crypto/tls/TlsDHUtilities.cs b/Crypto/src/crypto/tls/TlsDHUtilities.cs new file mode 100644
index 000000000..733749ea1 --- /dev/null +++ b/Crypto/src/crypto/tls/TlsDHUtilities.cs
@@ -0,0 +1,70 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto.Agreement; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class TlsDHUtilities + { + public static byte[] CalculateDHBasicAgreement(DHPublicKeyParameters publicKey, + DHPrivateKeyParameters privateKey) + { + DHBasicAgreement dhAgree = new DHBasicAgreement(); + dhAgree.Init(privateKey); + BigInteger agreement = dhAgree.CalculateAgreement(publicKey); + return BigIntegers.AsUnsignedByteArray(agreement); + } + + 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, Stream output) + { + AsymmetricCipherKeyPair dhAgreeClientKeyPair = GenerateDHKeyPair(random, dhParams); + DHPrivateKeyParameters dhAgreeClientPrivateKey = + (DHPrivateKeyParameters)dhAgreeClientKeyPair.Private; + + BigInteger Yc = ((DHPublicKeyParameters)dhAgreeClientKeyPair.Public).Y; + byte[] keData = BigIntegers.AsUnsignedByteArray(Yc); + TlsUtilities.WriteOpaque16(keData, output); + + return dhAgreeClientPrivateKey; + } + + public static DHPublicKeyParameters ValidateDHPublicKey(DHPublicKeyParameters key) + { + BigInteger Y = key.Y; + DHParameters parameters = key.Parameters; + BigInteger p = parameters.P; + BigInteger g = parameters.G; + + if (!p.IsProbablePrime(2)) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + if (g.CompareTo(BigInteger.Two) < 0 || g.CompareTo(p.Subtract(BigInteger.Two)) > 0) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + if (Y.CompareTo(BigInteger.Two) < 0 || Y.CompareTo(p.Subtract(BigInteger.One)) > 0) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + // TODO See RFC 2631 for more discussion of Diffie-Hellman validation + + return key; + } + } +} \ No newline at end of file diff --git a/Crypto/src/crypto/tls/TlsDeflateCompression.cs b/Crypto/src/crypto/tls/TlsDeflateCompression.cs new file mode 100644
index 000000000..146c961c7 --- /dev/null +++ b/Crypto/src/crypto/tls/TlsDeflateCompression.cs
@@ -0,0 +1,45 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Utilities.Zlib; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class TlsDeflateCompression + : TlsCompression + { + protected ZStream zIn, zOut; + + public TlsDeflateCompression() + { + this.zIn = new ZStream(); + this.zIn.inflateInit(); + + this.zOut = new ZStream(); + // TODO Allow custom setting + this.zOut.deflateInit(JZlib.Z_DEFAULT_COMPRESSION); + } + + public virtual Stream Compress(Stream output) + { + return new DeflateOutputStream(output, zOut, true); + } + + public virtual Stream Decompress(Stream output) + { + return new DeflateOutputStream(output, zIn, false); + } + + protected class DeflateOutputStream : ZOutputStream + { + public DeflateOutputStream(Stream output, ZStream z, bool compress) + : base(output) + { + this.z = z; + this.compress = compress; + // TODO http://www.bolet.org/~pornin/deflate-flush.html says we should use Z_SYNC_FLUSH + this.FlushMode = JZlib.Z_PARTIAL_FLUSH; + } + } + } +} diff --git a/Crypto/src/crypto/tls/TlsDheKeyExchange.cs b/Crypto/src/crypto/tls/TlsDheKeyExchange.cs new file mode 100644
index 000000000..edadaeb38 --- /dev/null +++ b/Crypto/src/crypto/tls/TlsDheKeyExchange.cs
@@ -0,0 +1,56 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Tls +{ + internal class TlsDheKeyExchange + : TlsDHKeyExchange + { + internal TlsDheKeyExchange(TlsClientContext context, KeyExchangeAlgorithm keyExchange) + : base(context, keyExchange) + { + } + + public override void SkipServerKeyExchange() + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + public override void ProcessServerKeyExchange(Stream input) + { + SecurityParameters securityParameters = context.SecurityParameters; + + ISigner signer = InitSigner(tlsSigner, securityParameters); + Stream sigIn = new SignerStream(input, signer, null); + + byte[] pBytes = TlsUtilities.ReadOpaque16(sigIn); + byte[] gBytes = TlsUtilities.ReadOpaque16(sigIn); + byte[] YsBytes = TlsUtilities.ReadOpaque16(sigIn); + + byte[] sigByte = TlsUtilities.ReadOpaque16(input); + if (!signer.VerifySignature(sigByte)) + { + throw new TlsFatalAlert(AlertDescription.bad_certificate); + } + + BigInteger p = new BigInteger(1, pBytes); + BigInteger g = new BigInteger(1, gBytes); + BigInteger Ys = new BigInteger(1, YsBytes); + + this.dhAgreeServerPublicKey = ValidateDHPublicKey( + new DHPublicKeyParameters(Ys, new DHParameters(p, g))); + } + + protected virtual ISigner InitSigner(TlsSigner tlsSigner, SecurityParameters securityParameters) + { + ISigner signer = tlsSigner.CreateVerifyer(this.serverPublicKey); + signer.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length); + signer.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length); + return signer; + } + } +} diff --git a/Crypto/src/crypto/tls/TlsDsaSigner.cs b/Crypto/src/crypto/tls/TlsDsaSigner.cs new file mode 100644
index 000000000..27d7b1f91 --- /dev/null +++ b/Crypto/src/crypto/tls/TlsDsaSigner.cs
@@ -0,0 +1,51 @@ +using System; + +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Signers; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Tls +{ + internal abstract class TlsDsaSigner + : TlsSigner + { + public virtual byte[] CalculateRawSignature(SecureRandom random, + AsymmetricKeyParameter privateKey, byte[] md5andsha1) + { + ISigner s = MakeSigner(new NullDigest(), true, new ParametersWithRandom(privateKey, random)); + // Note: Only use the SHA1 part of the hash + s.BlockUpdate(md5andsha1, 16, 20); + return s.GenerateSignature(); + } + + public bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5andsha1) + { + ISigner s = MakeSigner(new NullDigest(), false, publicKey); + // Note: Only use the SHA1 part of the hash + s.BlockUpdate(md5andsha1, 16, 20); + return s.VerifySignature(sigBytes); + } + + public virtual ISigner CreateSigner(SecureRandom random, AsymmetricKeyParameter privateKey) + { + return MakeSigner(new Sha1Digest(), true, new ParametersWithRandom(privateKey, random)); + } + + public virtual ISigner CreateVerifyer(AsymmetricKeyParameter publicKey) + { + return MakeSigner(new Sha1Digest(), false, publicKey); + } + + public abstract bool IsValidPublicKey(AsymmetricKeyParameter publicKey); + + protected virtual ISigner MakeSigner(IDigest d, bool forSigning, ICipherParameters cp) + { + ISigner s = new DsaDigestSigner(CreateDsaImpl(), d); + s.Init(forSigning, cp); + return s; + } + + protected abstract IDsa CreateDsaImpl(); + } +} diff --git a/Crypto/src/crypto/tls/TlsDssSigner.cs b/Crypto/src/crypto/tls/TlsDssSigner.cs new file mode 100644
index 000000000..c6f1abcec --- /dev/null +++ b/Crypto/src/crypto/tls/TlsDssSigner.cs
@@ -0,0 +1,21 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Signers; + +namespace Org.BouncyCastle.Crypto.Tls +{ + internal class TlsDssSigner + : TlsDsaSigner + { + public override bool IsValidPublicKey(AsymmetricKeyParameter publicKey) + { + return publicKey is DsaPublicKeyParameters; + } + + protected override IDsa CreateDsaImpl() + { + return new DsaSigner(); + } + } +} diff --git a/Crypto/src/crypto/tls/TlsECDHKeyExchange.cs b/Crypto/src/crypto/tls/TlsECDHKeyExchange.cs new file mode 100644
index 000000000..83983ba47 --- /dev/null +++ b/Crypto/src/crypto/tls/TlsECDHKeyExchange.cs
@@ -0,0 +1,230 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto.Agreement; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /** + * ECDH key exchange (see RFC 4492) + */ + internal class TlsECDHKeyExchange + : TlsKeyExchange + { + protected TlsClientContext context; + protected KeyExchangeAlgorithm keyExchange; + protected TlsSigner tlsSigner; + + protected AsymmetricKeyParameter serverPublicKey; + protected ECPublicKeyParameters ecAgreeServerPublicKey; + protected TlsAgreementCredentials agreementCredentials; + protected ECPrivateKeyParameters ecAgreeClientPrivateKey = null; + + internal TlsECDHKeyExchange(TlsClientContext context, KeyExchangeAlgorithm keyExchange) + { + 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 ArgumentException("unsupported key exchange algorithm", "keyExchange"); + } + + this.context = context; + this.keyExchange = keyExchange; + } + + public virtual void SkipServerCertificate() + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + public virtual void ProcessServerCertificate(Certificate serverCertificate) + { + X509CertificateStructure x509Cert = serverCertificate.certs[0]; + SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo; + + try + { + this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo); + } + catch (Exception) + { + throw new TlsFatalAlert(AlertDescription.unsupported_certificate); + } + + if (tlsSigner == null) + { + try + { + this.ecAgreeServerPublicKey = ValidateECPublicKey((ECPublicKeyParameters)this.serverPublicKey); + } + catch (InvalidCastException) + { + throw new TlsFatalAlert(AlertDescription.certificate_unknown); + } + + TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.KeyAgreement); + } + else + { + if (!tlsSigner.IsValidPublicKey(this.serverPublicKey)) + { + throw new TlsFatalAlert(AlertDescription.certificate_unknown); + } + + TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature); + } + + // TODO + /* + * Perform various checks per RFC2246 7.4.2: "Unless otherwise specified, the + * signing algorithm for the certificate must be the same as the algorithm for the + * certificate key." + */ + } + + public virtual void SkipServerKeyExchange() + { + // do nothing + } + + public virtual void ProcessServerKeyExchange(Stream input) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest) + { + /* + * 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. + */ + ClientCertificateType[] types = certificateRequest.CertificateTypes; + foreach (ClientCertificateType type in types) + { + switch (type) + { + 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 virtual void SkipClientCredentials() + { + this.agreementCredentials = null; + } + + public virtual void ProcessClientCredentials(TlsCredentials clientCredentials) + { + if (clientCredentials is TlsAgreementCredentials) + { + // TODO Validate client cert has matching parameters (see 'AreOnSameCurve')? + + this.agreementCredentials = (TlsAgreementCredentials)clientCredentials; + } + else if (clientCredentials is TlsSignerCredentials) + { + // OK + } + else + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public virtual void GenerateClientKeyExchange(Stream output) + { + if (agreementCredentials == null) + { + GenerateEphemeralClientKeyExchange(ecAgreeServerPublicKey.Parameters, output); + } + } + + public virtual byte[] GeneratePremasterSecret() + { + if (agreementCredentials != null) + { + return agreementCredentials.GenerateAgreement(ecAgreeServerPublicKey); + } + + return CalculateECDHBasicAgreement(ecAgreeServerPublicKey, ecAgreeClientPrivateKey); + } + + protected virtual bool AreOnSameCurve(ECDomainParameters a, ECDomainParameters b) + { + // TODO Move to ECDomainParameters.Equals() or other utility method? + return a.Curve.Equals(b.Curve) && a.G.Equals(b.G) && a.N.Equals(b.N) && a.H.Equals(b.H); + } + + protected virtual byte[] ExternalizeKey(ECPublicKeyParameters keyParameters) + { + // TODO Add support for compressed encoding and SPF extension + + /* + * 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. + */ + return keyParameters.Q.GetEncoded(); + } + + protected virtual AsymmetricCipherKeyPair GenerateECKeyPair(ECDomainParameters ecParams) + { + ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator(); + ECKeyGenerationParameters keyGenerationParameters = new ECKeyGenerationParameters(ecParams, + context.SecureRandom); + keyPairGenerator.Init(keyGenerationParameters); + return keyPairGenerator.GenerateKeyPair(); + } + + protected virtual void GenerateEphemeralClientKeyExchange(ECDomainParameters ecParams, Stream output) + { + AsymmetricCipherKeyPair ecAgreeClientKeyPair = GenerateECKeyPair(ecParams); + this.ecAgreeClientPrivateKey = (ECPrivateKeyParameters)ecAgreeClientKeyPair.Private; + + byte[] keData = ExternalizeKey((ECPublicKeyParameters)ecAgreeClientKeyPair.Public); + TlsUtilities.WriteOpaque8(keData, output); + } + + protected virtual byte[] CalculateECDHBasicAgreement(ECPublicKeyParameters publicKey, + ECPrivateKeyParameters privateKey) + { + ECDHBasicAgreement basicAgreement = new ECDHBasicAgreement(); + basicAgreement.Init(privateKey); + BigInteger agreement = basicAgreement.CalculateAgreement(publicKey); + return BigIntegers.AsUnsignedByteArray(agreement); + } + + protected virtual ECPublicKeyParameters ValidateECPublicKey(ECPublicKeyParameters key) + { + // TODO Check RFC 4492 for validation + return key; + } + } +} diff --git a/Crypto/src/crypto/tls/TlsECDheKeyExchange.cs b/Crypto/src/crypto/tls/TlsECDheKeyExchange.cs new file mode 100644
index 000000000..5516154ce --- /dev/null +++ b/Crypto/src/crypto/tls/TlsECDheKeyExchange.cs
@@ -0,0 +1,110 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math.EC; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /** + * ECDHE key exchange (see RFC 4492) + */ + internal class TlsECDheKeyExchange : TlsECDHKeyExchange + { + internal TlsECDheKeyExchange(TlsClientContext context, KeyExchangeAlgorithm keyExchange) + : base(context, keyExchange) + { + } + + public override void SkipServerKeyExchange() + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + public override void ProcessServerKeyExchange(Stream input) + { + SecurityParameters securityParameters = context.SecurityParameters; + + ISigner signer = InitSigner(tlsSigner, securityParameters); + Stream sigIn = new SignerStream(input, signer, null); + + ECCurveType curveType = (ECCurveType)TlsUtilities.ReadUint8(sigIn); + ECDomainParameters curve_params; + + // Currently, we only support named curves + if (curveType == ECCurveType.named_curve) + { + NamedCurve namedCurve = (NamedCurve)TlsUtilities.ReadUint16(sigIn); + + // TODO Check namedCurve is one we offered? + + curve_params = NamedCurveHelper.GetECParameters(namedCurve); + } + else + { + // TODO Add support for explicit curve parameters (read from sigIn) + + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + + byte[] publicBytes = TlsUtilities.ReadOpaque8(sigIn); + + byte[] sigByte = TlsUtilities.ReadOpaque16(input); + if (!signer.VerifySignature(sigByte)) + { + throw new TlsFatalAlert(AlertDescription.bad_certificate); + } + + // TODO Check curve_params not null + + ECPoint Q = curve_params.Curve.DecodePoint(publicBytes); + + this.ecAgreeServerPublicKey = ValidateECPublicKey(new ECPublicKeyParameters(Q, curve_params)); + } + + public override void ValidateCertificateRequest(CertificateRequest certificateRequest) + { + /* + * 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. + */ + ClientCertificateType[] types = certificateRequest.CertificateTypes; + foreach (ClientCertificateType type in types) + { + switch (type) + { + case ClientCertificateType.rsa_sign: + case ClientCertificateType.dss_sign: + case ClientCertificateType.ecdsa_sign: + break; + default: + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + } + + public override void ProcessClientCredentials(TlsCredentials clientCredentials) + { + if (clientCredentials is TlsSignerCredentials) + { + // OK + } + else + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + protected virtual ISigner InitSigner(TlsSigner tlsSigner, SecurityParameters securityParameters) + { + ISigner signer = tlsSigner.CreateVerifyer(this.serverPublicKey); + signer.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length); + signer.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length); + return signer; + } + } +} diff --git a/Crypto/src/crypto/tls/TlsECDsaSigner.cs b/Crypto/src/crypto/tls/TlsECDsaSigner.cs new file mode 100644
index 000000000..3c30fdc0c --- /dev/null +++ b/Crypto/src/crypto/tls/TlsECDsaSigner.cs
@@ -0,0 +1,21 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Signers; + +namespace Org.BouncyCastle.Crypto.Tls +{ + internal class TlsECDsaSigner + : TlsDsaSigner + { + public override bool IsValidPublicKey(AsymmetricKeyParameter publicKey) + { + return publicKey is ECPublicKeyParameters; + } + + protected override IDsa CreateDsaImpl() + { + return new ECDsaSigner(); + } + } +} diff --git a/Crypto/src/crypto/tls/TlsException.cs b/Crypto/src/crypto/tls/TlsException.cs new file mode 100644
index 000000000..fa3e73273 --- /dev/null +++ b/Crypto/src/crypto/tls/TlsException.cs
@@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class TlsException : Exception + { + public TlsException() : base() { } + public TlsException(string message) : base(message) { } + public TlsException(string message, Exception exception) : base(message, exception) { } + } +} diff --git a/Crypto/src/crypto/tls/TlsFatalAlert.cs b/Crypto/src/crypto/tls/TlsFatalAlert.cs new file mode 100644
index 000000000..0a9cc6f3a --- /dev/null +++ b/Crypto/src/crypto/tls/TlsFatalAlert.cs
@@ -0,0 +1,21 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class TlsFatalAlert + : IOException + { + private readonly AlertDescription alertDescription; + + public TlsFatalAlert(AlertDescription alertDescription) + { + this.alertDescription = alertDescription; + } + + public AlertDescription AlertDescription + { + get { return alertDescription; } + } + } +} diff --git a/Crypto/src/crypto/tls/TlsKeyExchange.cs b/Crypto/src/crypto/tls/TlsKeyExchange.cs new file mode 100644
index 000000000..5102edbec --- /dev/null +++ b/Crypto/src/crypto/tls/TlsKeyExchange.cs
@@ -0,0 +1,38 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <summary> + /// A generic interface for key exchange implementations in TLS 1.0. + /// </summary> + public interface TlsKeyExchange + { + /// <exception cref="IOException"/> + void SkipServerCertificate(); + + /// <exception cref="IOException"/> + void ProcessServerCertificate(Certificate serverCertificate); + + /// <exception cref="IOException"/> + void SkipServerKeyExchange(); + + /// <exception cref="IOException"/> + void ProcessServerKeyExchange(Stream input); + + /// <exception cref="IOException"/> + void ValidateCertificateRequest(CertificateRequest certificateRequest); + + /// <exception cref="IOException"/> + void SkipClientCredentials(); + + /// <exception cref="IOException"/> + void ProcessClientCredentials(TlsCredentials clientCredentials); + + /// <exception cref="IOException"/> + void GenerateClientKeyExchange(Stream output); + + /// <exception cref="IOException"/> + byte[] GeneratePremasterSecret(); + } +} diff --git a/Crypto/src/crypto/tls/TlsMac.cs b/Crypto/src/crypto/tls/TlsMac.cs new file mode 100644
index 000000000..0e58b89dc --- /dev/null +++ b/Crypto/src/crypto/tls/TlsMac.cs
@@ -0,0 +1,106 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Macs; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <remarks> + /// A generic TLS MAC implementation, which can be used with any kind of + /// IDigest to act as an HMAC. + /// </remarks> + public class TlsMac + { + protected long seqNo; + protected byte[] secret; + protected HMac mac; + + /** + * Generate a new instance of an TlsMac. + * + * @param digest The digest to use. + * @param key_block A byte-array where the key for this mac is located. + * @param offset The number of bytes to skip, before the key starts in the buffer. + * @param len The length of the key. + */ + public TlsMac( + IDigest digest, + byte[] key_block, + int offset, + int len) + { + this.seqNo = 0; + + KeyParameter param = new KeyParameter(key_block, offset, len); + + this.secret = Arrays.Clone(param.GetKey()); + + this.mac = new HMac(digest); + this.mac.Init(param); + } + + /** + * @return the MAC write secret + */ + public virtual byte[] GetMacSecret() + { + return this.secret; + } + + /** + * @return the current write sequence number + */ + public virtual long SequenceNumber + { + get { return this.seqNo; } + } + + /** + * Increment the current write sequence number + */ + public virtual void IncSequenceNumber() + { + this.seqNo++; + } + + /** + * @return The Keysize of the mac. + */ + public virtual int Size + { + get { return mac.GetMacSize(); } + } + + /** + * Calculate the mac for some given data. + * <p/> + * TlsMac will keep track of the sequence number internally. + * + * @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 len The length of the message. + * @return A new byte-buffer containing the mac value. + */ + public virtual byte[] CalculateMac( + ContentType type, + byte[] message, + int offset, + int len) + { + byte[] macHeader = new byte[13]; + TlsUtilities.WriteUint64(seqNo++, macHeader, 0); + TlsUtilities.WriteUint8((byte)type, macHeader, 8); + TlsUtilities.WriteVersion(macHeader, 9); + TlsUtilities.WriteUint16(len, macHeader, 11); + + mac.BlockUpdate(macHeader, 0, macHeader.Length); + mac.BlockUpdate(message, offset, len); + return MacUtilities.DoFinal(mac); + } + } +} diff --git a/Crypto/src/crypto/tls/TlsNullCipher.cs b/Crypto/src/crypto/tls/TlsNullCipher.cs new file mode 100644
index 000000000..b76f76d9c --- /dev/null +++ b/Crypto/src/crypto/tls/TlsNullCipher.cs
@@ -0,0 +1,28 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <summary> + /// A NULL cipher suite, for use during handshake. + /// </summary> + public class TlsNullCipher + : TlsCipher + { + public virtual byte[] EncodePlaintext(ContentType type, byte[] plaintext, int offset, int len) + { + return CopyData(plaintext, offset, len); + } + + public virtual byte[] DecodeCiphertext(ContentType type, byte[] ciphertext, int offset, int len) + { + return CopyData(ciphertext, offset, len); + } + + protected virtual byte[] CopyData(byte[] text, int offset, int len) + { + byte[] result = new byte[len]; + Array.Copy(text, offset, result, 0, len); + return result; + } + } +} diff --git a/Crypto/src/crypto/tls/TlsNullCompression.cs b/Crypto/src/crypto/tls/TlsNullCompression.cs new file mode 100644
index 000000000..45f8fc708 --- /dev/null +++ b/Crypto/src/crypto/tls/TlsNullCompression.cs
@@ -0,0 +1,19 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class TlsNullCompression + : TlsCompression + { + public virtual Stream Compress(Stream output) + { + return output; + } + + public virtual Stream Decompress(Stream output) + { + return output; + } + } +} diff --git a/Crypto/src/crypto/tls/TlsProtocolHandler.cs b/Crypto/src/crypto/tls/TlsProtocolHandler.cs new file mode 100644
index 000000000..6d2b0b144 --- /dev/null +++ b/Crypto/src/crypto/tls/TlsProtocolHandler.cs
@@ -0,0 +1,1259 @@ +using System; +using System.Collections; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto.Agreement; +using Org.BouncyCastle.Crypto.Agreement.Srp; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Encodings; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Prng; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Date; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <remarks>An implementation of all high level protocols in TLS 1.0.</remarks> + public class TlsProtocolHandler + { + /* + * Our Connection states + */ + private const short CS_CLIENT_HELLO_SEND = 1; + private const short CS_SERVER_HELLO_RECEIVED = 2; + private const short CS_SERVER_CERTIFICATE_RECEIVED = 3; + private const short CS_SERVER_KEY_EXCHANGE_RECEIVED = 4; + private const short CS_CERTIFICATE_REQUEST_RECEIVED = 5; + private const short CS_SERVER_HELLO_DONE_RECEIVED = 6; + private const short CS_CLIENT_KEY_EXCHANGE_SEND = 7; + private const short CS_CERTIFICATE_VERIFY_SEND = 8; + private const short CS_CLIENT_CHANGE_CIPHER_SPEC_SEND = 9; + private const short CS_CLIENT_FINISHED_SEND = 10; + private const short CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED = 11; + private const short CS_DONE = 12; + + private static readonly byte[] emptybuf = new byte[0]; + + private static readonly string TLS_ERROR_MESSAGE = "Internal TLS error, this could be an attack"; + + /* + * 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 + */ + private RecordStream rs; + private SecureRandom random; + + private TlsStream tlsStream = null; + + private bool closed = false; + private bool failedWithError = false; + private bool appDataReady = false; + private IDictionary clientExtensions; + + private SecurityParameters securityParameters = null; + + private TlsClientContextImpl tlsClientContext = null; + private TlsClient tlsClient = null; + private CipherSuite[] offeredCipherSuites = null; + private CompressionMethod[] offeredCompressionMethods = null; + private TlsKeyExchange keyExchange = null; + private TlsAuthentication authentication = null; + private CertificateRequest certificateRequest = null; + + private short connection_state = 0; + + 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. + * + * Hopefully, 20 bytes in fast mode are good enough. + */ + byte[] seed = new ThreadedSeedGenerator().GenerateSeed(20, true); + + return new SecureRandom(seed); + } + + public TlsProtocolHandler( + Stream s) + : this(s, s) + { + } + + public TlsProtocolHandler( + Stream s, + SecureRandom sr) + : this(s, s, sr) + { + } + + /// <remarks>Both streams can be the same object</remarks> + public TlsProtocolHandler( + Stream inStr, + Stream outStr) + : this(inStr, outStr, CreateSecureRandom()) + { + } + + /// <remarks>Both streams can be the same object</remarks> + public TlsProtocolHandler( + Stream inStr, + Stream outStr, + SecureRandom sr) + { + this.rs = new RecordStream(this, inStr, outStr); + this.random = sr; + } + + internal void ProcessData( + ContentType protocol, + byte[] buf, + int offset, + int len) + { + /* + * 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. + */ + break; + } + } + + private void ProcessHandshake() + { + bool read; + do + { + read = false; + + /* + * We need the first 4 bytes, they contain type and length of + * the message. + */ + if (handshakeQueue.Available >= 4) + { + byte[] beginning = new byte[4]; + handshakeQueue.Read(beginning, 0, 4, 0); + MemoryStream bis = new MemoryStream(beginning, false); + HandshakeType type = (HandshakeType)TlsUtilities.ReadUint8(bis); + int len = TlsUtilities.ReadUint24(bis); + + /* + * Check if we have enough bytes in the buffer to read + * the full message. + */ + if (handshakeQueue.Available >= (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: + case HandshakeType.finished: + break; + default: + rs.UpdateHandshakeData(beginning, 0, 4); + rs.UpdateHandshakeData(buf, 0, len); + break; + } + + /* + * Now, parse the message. + */ + ProcessHandshakeMessage(type, buf); + read = true; + } + } + } + while (read); + } + + private void ProcessHandshakeMessage(HandshakeType type, byte[] buf) + { + MemoryStream inStr = new MemoryStream(buf, false); + + /* + * Check the type. + */ + switch (type) + { + case HandshakeType.certificate: + { + switch (connection_state) + { + case CS_SERVER_HELLO_RECEIVED: + { + // Parse the Certificate message and send to cipher suite + + Certificate serverCertificate = Certificate.Parse(inStr); + + AssertEmpty(inStr); + + this.keyExchange.ProcessServerCertificate(serverCertificate); + + this.authentication = tlsClient.GetAuthentication(); + this.authentication.NotifyServerCertificate(serverCertificate); + + break; + } + default: + this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message); + break; + } + + connection_state = CS_SERVER_CERTIFICATE_RECEIVED; + break; + } + case HandshakeType.finished: + switch (connection_state) + { + case CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED: + /* + * Read the checksum from the finished message, it has always 12 bytes. + */ + byte[] serverVerifyData = new byte[12]; + TlsUtilities.ReadFully(serverVerifyData, inStr); + + AssertEmpty(inStr); + + /* + * Calculate our own checksum. + */ + byte[] expectedServerVerifyData = TlsUtilities.PRF( + securityParameters.masterSecret, "server finished", + rs.GetCurrentHash(), 12); + + /* + * Compare both checksums. + */ + if (!Arrays.ConstantTimeAreEqual(expectedServerVerifyData, serverVerifyData)) + { + /* + * Wrong checksum in the finished message. + */ + this.FailWithError(AlertLevel.fatal, AlertDescription.handshake_failure); + } + + connection_state = CS_DONE; + + /* + * We are now ready to receive application data. + */ + this.appDataReady = true; + break; + default: + this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message); + break; + } + break; + case HandshakeType.server_hello: + switch (connection_state) + { + case CS_CLIENT_HELLO_SEND: + /* + * Read the server hello message + */ + TlsUtilities.CheckVersion(inStr); + + /* + * Read the server random + */ + securityParameters.serverRandom = new byte[32]; + TlsUtilities.ReadFully(securityParameters.serverRandom, inStr); + + byte[] sessionID = TlsUtilities.ReadOpaque8(inStr); + 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. + */ + CipherSuite selectedCipherSuite = (CipherSuite)TlsUtilities.ReadUint16(inStr); + if (!ArrayContains(offeredCipherSuites, selectedCipherSuite) + || selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV) + { + this.FailWithError(AlertLevel.fatal, AlertDescription.illegal_parameter); + } + + this.tlsClient.NotifySelectedCipherSuite(selectedCipherSuite); + + /* + * Find out which CompressionMethod the server has chosen and check that + * it was one of the offered ones. + */ + CompressionMethod selectedCompressionMethod = (CompressionMethod)TlsUtilities.ReadUint8(inStr); + 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. + */ + + // ExtensionType -> byte[] + IDictionary serverExtensions = Platform.CreateHashtable(); + + if (inStr.Position < inStr.Length) + { + // Process extensions from extended server hello + byte[] extBytes = TlsUtilities.ReadOpaque16(inStr); + + MemoryStream ext = new MemoryStream(extBytes, false); + while (ext.Position < ext.Length) + { + ExtensionType extType = (ExtensionType)TlsUtilities.ReadUint16(ext); + byte[] extValue = TlsUtilities.ReadOpaque16(ext); + + // Note: RFC 5746 makes a special case for EXT_RenegotiationInfo + if (extType != ExtensionType.renegotiation_info + && !clientExtensions.Contains(extType)) + { + /* + * 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. + */ + this.FailWithError(AlertLevel.fatal, AlertDescription.unsupported_extension); + } + + if (serverExtensions.Contains(extType)) + { + /* + * RFC 3546 2.3 + * Also note that when multiple extensions of different types are + * present in the extended client hello or the extended server hello, + * the extensions may appear in any order. There MUST NOT be more than + * one extension of the same type. + */ + this.FailWithError(AlertLevel.fatal, AlertDescription.illegal_parameter); + } + + serverExtensions.Add(extType, extValue); + } + } + + AssertEmpty(inStr); + + /* + * RFC 5746 3.4. When a ServerHello is received, the client MUST check if it + * includes the "renegotiation_info" extension: + */ + { + bool secure_negotiation = serverExtensions.Contains(ExtensionType.renegotiation_info); + + /* + * 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). + */ + if (secure_negotiation) + { + byte[] renegExtValue = (byte[])serverExtensions[ExtensionType.renegotiation_info]; + + if (!Arrays.ConstantTimeAreEqual(renegExtValue, + CreateRenegotiationInfo(emptybuf))) + { + this.FailWithError(AlertLevel.fatal, AlertDescription.handshake_failure); + } + } + + tlsClient.NotifySecureRenegotiation(secure_negotiation); + } + + if (clientExtensions != null) + { + tlsClient.ProcessServerExtensions(serverExtensions); + } + + this.keyExchange = tlsClient.GetKeyExchange(); + + connection_state = CS_SERVER_HELLO_RECEIVED; + break; + default: + this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message); + break; + } + break; + case HandshakeType.server_hello_done: + switch (connection_state) + { + case CS_SERVER_HELLO_RECEIVED: + case CS_SERVER_CERTIFICATE_RECEIVED: + case CS_SERVER_KEY_EXCHANGE_RECEIVED: + case CS_CERTIFICATE_REQUEST_RECEIVED: + + // NB: Original code used case label fall-through + + if (connection_state == CS_SERVER_HELLO_RECEIVED) + { + // There was no server certificate message; check it's OK + this.keyExchange.SkipServerCertificate(); + this.authentication = null; + + // There was no server key exchange message; check it's OK + this.keyExchange.SkipServerKeyExchange(); + } + else if (connection_state == CS_SERVER_CERTIFICATE_RECEIVED) + { + // There was no server key exchange message; check it's OK + this.keyExchange.SkipServerKeyExchange(); + } + + AssertEmpty(inStr); + + connection_state = CS_SERVER_HELLO_DONE_RECEIVED; + + TlsCredentials clientCreds = null; + if (certificateRequest == null) + { + this.keyExchange.SkipClientCredentials(); + } + else + { + clientCreds = this.authentication.GetClientCredentials(certificateRequest); + + Certificate clientCert; + if (clientCreds == null) + { + this.keyExchange.SkipClientCredentials(); + clientCert = Certificate.EmptyChain; + } + else + { + this.keyExchange.ProcessClientCredentials(clientCreds); + clientCert = clientCreds.Certificate; + } + + SendClientCertificate(clientCert); + } + + /* + * Send the client key exchange message, depending on the key + * exchange we are using in our CipherSuite. + */ + SendClientKeyExchange(); + + connection_state = CS_CLIENT_KEY_EXCHANGE_SEND; + + if (clientCreds != null && clientCreds is TlsSignerCredentials) + { + TlsSignerCredentials signerCreds = (TlsSignerCredentials)clientCreds; + byte[] md5andsha1 = rs.GetCurrentHash(); + byte[] clientCertificateSignature = signerCreds.GenerateCertificateSignature( + md5andsha1); + SendCertificateVerify(clientCertificateSignature); + + connection_state = CS_CERTIFICATE_VERIFY_SEND; + } + + /* + * Now, we send change cipher state + */ + byte[] cmessage = new byte[1]; + cmessage[0] = 1; + rs.WriteMessage(ContentType.change_cipher_spec, cmessage, 0, cmessage.Length); + + connection_state = CS_CLIENT_CHANGE_CIPHER_SPEC_SEND; + + /* + * Calculate the master_secret + */ + byte[] pms = this.keyExchange.GeneratePremasterSecret(); + + securityParameters.masterSecret = TlsUtilities.PRF(pms, "master secret", + TlsUtilities.Concat(securityParameters.clientRandom, securityParameters.serverRandom), + 48); + + // 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. + */ + Array.Clear(pms, 0, pms.Length); + + /* + * Initialize our cipher suite + */ + rs.ClientCipherSpecDecided(tlsClient.GetCompression(), tlsClient.GetCipher()); + + /* + * Send our finished message. + */ + byte[] clientVerifyData = TlsUtilities.PRF(securityParameters.masterSecret, + "client finished", rs.GetCurrentHash(), 12); + + MemoryStream bos = new MemoryStream(); + TlsUtilities.WriteUint8((byte)HandshakeType.finished, bos); + TlsUtilities.WriteOpaque24(clientVerifyData, bos); + byte[] message = bos.ToArray(); + + rs.WriteMessage(ContentType.handshake, message, 0, message.Length); + + this.connection_state = CS_CLIENT_FINISHED_SEND; + break; + default: + this.FailWithError(AlertLevel.fatal, AlertDescription.handshake_failure); + break; + } + break; + case HandshakeType.server_key_exchange: + { + switch (connection_state) + { + case CS_SERVER_HELLO_RECEIVED: + case CS_SERVER_CERTIFICATE_RECEIVED: + { + // NB: Original code used case label fall-through + if (connection_state == CS_SERVER_HELLO_RECEIVED) + { + // There was no server certificate message; check it's OK + this.keyExchange.SkipServerCertificate(); + this.authentication = null; + } + + this.keyExchange.ProcessServerKeyExchange(inStr); + + AssertEmpty(inStr); + break; + } + default: + this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message); + break; + } + + this.connection_state = CS_SERVER_KEY_EXCHANGE_RECEIVED; + break; + } + case HandshakeType.certificate_request: + switch (connection_state) + { + case CS_SERVER_CERTIFICATE_RECEIVED: + case CS_SERVER_KEY_EXCHANGE_RECEIVED: + { + // NB: Original code used case label fall-through + if (connection_state == CS_SERVER_CERTIFICATE_RECEIVED) + { + // There was no server key exchange message; check it's OK + this.keyExchange.SkipServerKeyExchange(); + } + + 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); + } + + int numTypes = TlsUtilities.ReadUint8(inStr); + ClientCertificateType[] certificateTypes = new ClientCertificateType[numTypes]; + for (int i = 0; i < numTypes; ++i) + { + certificateTypes[i] = (ClientCertificateType)TlsUtilities.ReadUint8(inStr); + } + + byte[] authorities = TlsUtilities.ReadOpaque16(inStr); + + AssertEmpty(inStr); + + IList authorityDNs = Platform.CreateArrayList(); + + MemoryStream bis = new MemoryStream(authorities, false); + while (bis.Position < bis.Length) + { + byte[] dnBytes = TlsUtilities.ReadOpaque16(bis); + // TODO Switch to X500Name when available + authorityDNs.Add(X509Name.GetInstance(Asn1Object.FromByteArray(dnBytes))); + } + + this.certificateRequest = new CertificateRequest(certificateTypes, + authorityDNs); + this.keyExchange.ValidateCertificateRequest(this.certificateRequest); + + break; + } + default: + this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message); + break; + } + + this.connection_state = CS_CERTIFICATE_REQUEST_RECEIVED; + break; + case HandshakeType.hello_request: + /* + * 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 (connection_state == CS_DONE) + { + // Renegotiation not supported yet + SendAlert(AlertLevel.warning, AlertDescription.no_renegotiation); + } + break; + case HandshakeType.client_key_exchange: + case HandshakeType.certificate_verify: + case HandshakeType.client_hello: + default: + // We do not support this! + this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message); + break; + } + } + + 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() + { + while (alertQueue.Available >= 2) + { + /* + * An alert is always 2 bytes. Read the alert. + */ + byte[] tmp = new byte[2]; + alertQueue.Read(tmp, 0, 2, 0); + alertQueue.RemoveData(2); + byte level = tmp[0]; + byte description = tmp[1]; + if (level == (byte)AlertLevel.fatal) + { + /* + * This is a fatal error. + */ + this.failedWithError = true; + this.closed = true; + /* + * Now try to Close the stream, ignore errors. + */ + try + { + rs.Close(); + } + catch (Exception) + { + } + throw new IOException(TLS_ERROR_MESSAGE); + } + else + { + /* + * This is just a warning. + */ + if (description == (byte)AlertDescription.close_notify) + { + /* + * Close notify + */ + this.FailWithError(AlertLevel.warning, AlertDescription.close_notify); + } + /* + * If it is just a warning, we continue. + */ + } + } + } + + /** + * 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() + { + while (changeCipherSpecQueue.Available > 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); + } + + /* + * Check if we are in the correct connection state. + */ + if (this.connection_state != CS_CLIENT_FINISHED_SEND) + { + this.FailWithError(AlertLevel.fatal, AlertDescription.handshake_failure); + } + + rs.ServerClientSpecReceived(); + + this.connection_state = CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED; + } + } + + private void SendClientCertificate(Certificate clientCert) + { + MemoryStream bos = new MemoryStream(); + TlsUtilities.WriteUint8((byte)HandshakeType.certificate, bos); + + // Reserve space for length + TlsUtilities.WriteUint24(0, bos); + + clientCert.Encode(bos); + byte[] message = bos.ToArray(); + + // Patch actual length back in + TlsUtilities.WriteUint24(message.Length - 4, message, 1); + + rs.WriteMessage(ContentType.handshake, message, 0, message.Length); + } + + private void SendClientKeyExchange() + { + MemoryStream bos = new MemoryStream(); + TlsUtilities.WriteUint8((byte)HandshakeType.client_key_exchange, bos); + + // Reserve space for length + TlsUtilities.WriteUint24(0, bos); + + this.keyExchange.GenerateClientKeyExchange(bos); + byte[] message = bos.ToArray(); + + // Patch actual length back in + TlsUtilities.WriteUint24(message.Length - 4, message, 1); + + rs.WriteMessage(ContentType.handshake, message, 0, message.Length); + } + + private void SendCertificateVerify(byte[] data) + { + /* + * 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 + */ + MemoryStream bos = new MemoryStream(); + TlsUtilities.WriteUint8((byte)HandshakeType.certificate_verify, bos); + TlsUtilities.WriteUint24(data.Length + 2, bos); + TlsUtilities.WriteOpaque16(data, bos); + byte[] message = bos.ToArray(); + + rs.WriteMessage(ContentType.handshake, message, 0, message.Length); + } + + /// <summary>Connects to the remote system.</summary> + /// <param name="verifyer">Will be used when a certificate is received to verify + /// that this certificate is accepted by the client.</param> + /// <exception cref="IOException">If handshake was not successful</exception> + [Obsolete("Use version taking TlsClient")] + public virtual void Connect( + ICertificateVerifyer verifyer) + { + this.Connect(new LegacyTlsClient(verifyer)); + } + + public virtual void Connect(TlsClient tlsClient) + { + if (tlsClient == null) + throw new ArgumentNullException("tlsClient"); + if (this.tlsClient != null) + throw new InvalidOperationException("Connect can only be called once"); + + /* + * Send Client hello + * + * First, generate some random data. + */ + this.securityParameters = new SecurityParameters(); + this.securityParameters.clientRandom = new byte[32]; + random.NextBytes(securityParameters.clientRandom, 4, 28); + TlsUtilities.WriteGmtUnixTime(securityParameters.clientRandom, 0); + + this.tlsClientContext = new TlsClientContextImpl(random, securityParameters); + this.tlsClient = tlsClient; + this.tlsClient.Init(tlsClientContext); + + MemoryStream outStr = new MemoryStream(); + TlsUtilities.WriteVersion(outStr); + outStr.Write(securityParameters.clientRandom, 0, 32); + + /* + * Length of Session id + */ + TlsUtilities.WriteUint8(0, outStr); + + this.offeredCipherSuites = this.tlsClient.GetCipherSuites(); + + // ExtensionType -> 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. + */ + bool noRenegExt = clientExtensions == null + || !clientExtensions.Contains(ExtensionType.renegotiation_info); + + int count = offeredCipherSuites.Length; + if (noRenegExt) + { + // Note: 1 extra slot for TLS_EMPTY_RENEGOTIATION_INFO_SCSV + ++count; + } + + TlsUtilities.WriteUint16(2 * count, outStr); + + for (int i = 0; i < offeredCipherSuites.Length; ++i) + { + TlsUtilities.WriteUint16((int)offeredCipherSuites[i], outStr); + } + + if (noRenegExt) + { + TlsUtilities.WriteUint16((int)CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV, outStr); + } + } + + /* + * Compression methods, just the null method. + */ + this.offeredCompressionMethods = tlsClient.GetCompressionMethods(); + + { + TlsUtilities.WriteUint8((byte)offeredCompressionMethods.Length, outStr); + for (int i = 0; i < offeredCompressionMethods.Length; ++i) + { + TlsUtilities.WriteUint8((byte)offeredCompressionMethods[i], outStr); + } + } + + // Extensions + if (clientExtensions != null) + { + MemoryStream ext = new MemoryStream(); + + foreach (ExtensionType extType in clientExtensions.Keys) + { + WriteExtension(ext, extType, (byte[])clientExtensions[extType]); + } + + TlsUtilities.WriteOpaque16(ext.ToArray(), outStr); + } + + MemoryStream bos = new MemoryStream(); + TlsUtilities.WriteUint8((byte)HandshakeType.client_hello, bos); + TlsUtilities.WriteUint24((int)outStr.Length, bos); + byte[] outBytes = outStr.ToArray(); + bos.Write(outBytes, 0, outBytes.Length); + byte[] message = bos.ToArray(); + SafeWriteMessage(ContentType.handshake, message, 0, message.Length); + connection_state = CS_CLIENT_HELLO_SEND; + + /* + * We will now read data, until we have completed the handshake. + */ + while (connection_state != CS_DONE) + { + SafeReadData(); + } + + this.tlsStream = new TlsStream(this); + } + + /** + * 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. + */ + internal int ReadApplicationData(byte[] buf, int offset, int len) + { + while (applicationDataQueue.Available == 0) + { + if (this.closed) + { + /* + * We need to read some data. + */ + 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 0; + } + + SafeReadData(); + } + len = System.Math.Min(len, applicationDataQueue.Available); + applicationDataQueue.Read(buf, offset, len, 0); + applicationDataQueue.RemoveData(len); + return len; + } + + private void SafeReadData() + { + try + { + rs.ReadData(); + } + catch (TlsFatalAlert e) + { + if (!this.closed) + { + this.FailWithError(e.AlertDescription, e); + } + throw e; + } + catch (IOException e) + { + if (!this.closed) + { + this.FailWithError(AlertDescription.internal_error, e); + } + throw e; + } + catch (Exception e) + { + if (!this.closed) + { + this.FailWithError(AlertDescription.internal_error, e); + } + throw e; + } + } + + private void SafeWriteMessage(ContentType type, byte[] buf, int offset, int len) + { + try + { + rs.WriteMessage(type, buf, offset, len); + } + catch (TlsFatalAlert e) + { + if (!this.closed) + { + this.FailWithError(e.AlertDescription, e); + } + throw e; + } + catch (IOException e) + { + if (!closed) + { + this.FailWithError(AlertDescription.internal_error, e); + } + throw e; + } + catch (Exception e) + { + if (!closed) + { + this.FailWithError(AlertDescription.internal_error, e); + } + 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. + */ + internal void WriteData(byte[] buf, int offset, int len) + { + 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"); + } + + /* + * Protect against known IV attack! + * + * DO NOT REMOVE THIS LINE, EXCEPT YOU KNOW EXACTLY WHAT + * YOU ARE DOING HERE. + */ + SafeWriteMessage(ContentType.application_data, emptybuf, 0, 0); + + do + { + /* + * We are only allowed to write fragments up to 2^14 bytes. + */ + int toWrite = System.Math.Min(len, 1 << 14); + + SafeWriteMessage(ContentType.application_data, buf, offset, toWrite); + + offset += toWrite; + len -= toWrite; + } + while (len > 0); + } + + /// <summary>A Stream which can be used to send data.</summary> + [Obsolete("Use 'Stream' property instead")] + public virtual Stream OutputStream + { + get { return this.tlsStream; } + } + + /// <summary>A Stream which can be used to read data.</summary> + [Obsolete("Use 'Stream' property instead")] + public virtual Stream InputStream + { + get { return this.tlsStream; } + } + + /// <summary>The secure bidirectional stream for this connection</summary> + public virtual Stream Stream + { + get { return this.tlsStream; } + } + + /** + * 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. + */ + private void FailWithError(AlertLevel alertLevel, AlertDescription alertDescription) + { + this.FailWithError(alertLevel, alertDescription, null); + } + + private void FailWithError(AlertDescription alertDescription, Exception ex) + { + this.FailWithError(AlertLevel.fatal, alertDescription, ex); + } + + private void FailWithError(AlertLevel alertLevel, AlertDescription alertDescription, Exception ex) + { + /* + * 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; + } + SendAlert(alertLevel, alertDescription); + rs.Close(); + if (alertLevel == AlertLevel.fatal) + { + throw new IOException(TLS_ERROR_MESSAGE, ex); + } + } + else + { + throw new IOException(TLS_ERROR_MESSAGE, ex); + } + } + + internal void SendAlert(AlertLevel alertLevel, AlertDescription alertDescription) + { + byte[] error = new byte[2]; + error[0] = (byte)alertLevel; + error[1] = (byte)alertDescription; + + rs.WriteMessage(ContentType.alert, error, 0, 2); + } + + /// <summary>Closes this connection</summary> + /// <exception cref="IOException">If something goes wrong during closing.</exception> + public virtual void Close() + { + if (!closed) + { + this.FailWithError(AlertLevel.warning, AlertDescription.close_notify); + } + } + + /** + * Make sure the Stream is now empty. Fail otherwise. + * + * @param is The Stream to check. + * @throws IOException If is is not empty. + */ + internal void AssertEmpty( + MemoryStream inStr) + { + if (inStr.Position < inStr.Length) + { + throw new TlsFatalAlert(AlertDescription.decode_error); + } + } + + internal void Flush() + { + rs.Flush(); + } + + internal bool IsClosed + { + get { return closed; } + } + + private static bool ArrayContains(CipherSuite[] a, CipherSuite n) + { + for (int i = 0; i < a.Length; ++i) + { + if (a[i] == n) + return true; + } + return false; + } + + private static bool ArrayContains(CompressionMethod[] a, CompressionMethod n) + { + for (int i = 0; i < a.Length; ++i) + { + if (a[i] == n) + return true; + } + return false; + } + + private static byte[] CreateRenegotiationInfo(byte[] renegotiated_connection) + { + MemoryStream buf = new MemoryStream(); + TlsUtilities.WriteOpaque8(renegotiated_connection, buf); + return buf.ToArray(); + } + + private static void WriteExtension(Stream output, ExtensionType extType, byte[] extValue) + { + TlsUtilities.WriteUint16((int)extType, output); + TlsUtilities.WriteOpaque16(extValue, output); + } + } +} diff --git a/Crypto/src/crypto/tls/TlsPskIdentity.cs b/Crypto/src/crypto/tls/TlsPskIdentity.cs new file mode 100644
index 000000000..119064ee7 --- /dev/null +++ b/Crypto/src/crypto/tls/TlsPskIdentity.cs
@@ -0,0 +1,15 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsPskIdentity + { + void SkipIdentityHint(); + + void NotifyIdentityHint(byte[] psk_identity_hint); + + byte[] GetPskIdentity(); + + byte[] GetPsk(); + } +} diff --git a/Crypto/src/crypto/tls/TlsPskKeyExchange.cs b/Crypto/src/crypto/tls/TlsPskKeyExchange.cs new file mode 100644
index 000000000..226153a97 --- /dev/null +++ b/Crypto/src/crypto/tls/TlsPskKeyExchange.cs
@@ -0,0 +1,149 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Tls +{ + internal class TlsPskKeyExchange + : TlsKeyExchange + { + protected TlsClientContext context; + protected KeyExchangeAlgorithm keyExchange; + protected TlsPskIdentity pskIdentity; + + protected byte[] psk_identity_hint = null; + + protected DHPublicKeyParameters dhAgreeServerPublicKey = null; + protected DHPrivateKeyParameters dhAgreeClientPrivateKey = null; + + protected RsaKeyParameters rsaServerPublicKey = null; + protected byte[] premasterSecret; + + internal TlsPskKeyExchange(TlsClientContext context, KeyExchangeAlgorithm keyExchange, + TlsPskIdentity pskIdentity) + { + switch (keyExchange) + { + case KeyExchangeAlgorithm.PSK: + case KeyExchangeAlgorithm.RSA_PSK: + case KeyExchangeAlgorithm.DHE_PSK: + break; + default: + throw new ArgumentException("unsupported key exchange algorithm", "keyExchange"); + } + + this.context = context; + this.keyExchange = keyExchange; + this.pskIdentity = pskIdentity; + } + + public virtual void SkipServerCertificate() + { + // OK + } + + public virtual void ProcessServerCertificate(Certificate serverCertificate) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + public virtual void SkipServerKeyExchange() + { + this.psk_identity_hint = new byte[0]; + } + + public virtual void ProcessServerKeyExchange(Stream input) + { + this.psk_identity_hint = TlsUtilities.ReadOpaque16(input); + + if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK) + { + byte[] pBytes = TlsUtilities.ReadOpaque16(input); + byte[] gBytes = TlsUtilities.ReadOpaque16(input); + byte[] YsBytes = TlsUtilities.ReadOpaque16(input); + + BigInteger p = new BigInteger(1, pBytes); + BigInteger g = new BigInteger(1, gBytes); + BigInteger Ys = new BigInteger(1, YsBytes); + + this.dhAgreeServerPublicKey = TlsDHUtilities.ValidateDHPublicKey( + new DHPublicKeyParameters(Ys, new DHParameters(p, g))); + } + else if (this.psk_identity_hint.Length == 0) + { + // TODO Should we enforce that this message should have been skipped if hint is empty? + //throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + } + + public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + public virtual void SkipClientCredentials() + { + // OK + } + + public virtual void ProcessClientCredentials(TlsCredentials clientCredentials) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + public virtual void GenerateClientKeyExchange(Stream output) + { + if (psk_identity_hint == null || psk_identity_hint.Length == 0) + { + pskIdentity.SkipIdentityHint(); + } + else + { + pskIdentity.NotifyIdentityHint(psk_identity_hint); + } + + byte[] psk_identity = pskIdentity.GetPskIdentity(); + + TlsUtilities.WriteOpaque16(psk_identity, output); + + if (this.keyExchange == KeyExchangeAlgorithm.RSA_PSK) + { + this.premasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret( + context.SecureRandom, this.rsaServerPublicKey, output); + } + else if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK) + { + this.dhAgreeClientPrivateKey = TlsDHUtilities.GenerateEphemeralClientKeyExchange( + context.SecureRandom, this.dhAgreeServerPublicKey.Parameters, output); + } + } + + public virtual byte[] GeneratePremasterSecret() + { + byte[] psk = pskIdentity.GetPsk(); + byte[] other_secret = GenerateOtherSecret(psk.Length); + + MemoryStream buf = new MemoryStream(4 + other_secret.Length + psk.Length); + TlsUtilities.WriteOpaque16(other_secret, buf); + TlsUtilities.WriteOpaque16(psk, buf); + return buf.ToArray(); + } + + protected virtual byte[] GenerateOtherSecret(int pskLength) + { + if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK) + { + return TlsDHUtilities.CalculateDHBasicAgreement(dhAgreeServerPublicKey, dhAgreeClientPrivateKey); + } + + if (this.keyExchange == KeyExchangeAlgorithm.RSA_PSK) + { + return this.premasterSecret; + } + + return new byte[pskLength]; + } + } +} diff --git a/Crypto/src/crypto/tls/TlsRsaKeyExchange.cs b/Crypto/src/crypto/tls/TlsRsaKeyExchange.cs new file mode 100644
index 000000000..4538a2a81 --- /dev/null +++ b/Crypto/src/crypto/tls/TlsRsaKeyExchange.cs
@@ -0,0 +1,165 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Encodings; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <summary> + /// TLS 1.0 RSA key exchange. + /// </summary> + internal class TlsRsaKeyExchange + : TlsKeyExchange + { + protected TlsClientContext context; + + protected AsymmetricKeyParameter serverPublicKey = null; + + protected RsaKeyParameters rsaServerPublicKey = null; + + protected byte[] premasterSecret; + + internal TlsRsaKeyExchange(TlsClientContext context) + { + this.context = context; + } + + public virtual void SkipServerCertificate() + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + public virtual void ProcessServerCertificate(Certificate serverCertificate) + { + X509CertificateStructure x509Cert = serverCertificate.certs[0]; + SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo; + + try + { + this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo); + } +// catch (RuntimeException) + catch (Exception) + { + 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); + + TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.KeyEncipherment); + + // TODO + /* + * Perform various checks per RFC2246 7.4.2: "Unless otherwise specified, the + * signing algorithm for the certificate must be the same as the algorithm for the + * certificate key." + */ + } + + public virtual void SkipServerKeyExchange() + { + // OK + } + + public virtual void ProcessServerKeyExchange(Stream input) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest) + { + ClientCertificateType[] types = certificateRequest.CertificateTypes; + foreach (ClientCertificateType type in types) + { + switch (type) + { + case ClientCertificateType.rsa_sign: + case ClientCertificateType.dss_sign: + case ClientCertificateType.ecdsa_sign: + break; + default: + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + } + + public virtual void SkipClientCredentials() + { + // OK + } + + public virtual void ProcessClientCredentials(TlsCredentials clientCredentials) + { + if (!(clientCredentials is TlsSignerCredentials)) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public virtual void GenerateClientKeyExchange(Stream output) + { + this.premasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret( + context.SecureRandom, this.rsaServerPublicKey, output); + } + + public virtual byte[] GeneratePremasterSecret() + { + byte[] tmp = this.premasterSecret; + this.premasterSecret = null; + return tmp; + } + + // Would be needed to process RSA_EXPORT server key exchange +// protected virtual void ProcessRsaServerKeyExchange(Stream input, ISigner signer) +// { +// Stream sigIn = input; +// if (signer != null) +// { +// sigIn = new SignerStream(input, signer, null); +// } +// +// byte[] modulusBytes = TlsUtilities.ReadOpaque16(sigIn); +// byte[] exponentBytes = TlsUtilities.ReadOpaque16(sigIn); +// +// if (signer != null) +// { +// byte[] sigByte = TlsUtilities.ReadOpaque16(input); +// +// 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 virtual RsaKeyParameters ValidateRsaPublicKey(RsaKeyParameters key) + { + // TODO What is the minimum bit length required? +// key.Modulus.BitLength; + + if (!key.Exponent.IsProbablePrime(2)) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + return key; + } + } +} diff --git a/Crypto/src/crypto/tls/TlsRsaSigner.cs b/Crypto/src/crypto/tls/TlsRsaSigner.cs new file mode 100644
index 000000000..a50ff9558 --- /dev/null +++ b/Crypto/src/crypto/tls/TlsRsaSigner.cs
@@ -0,0 +1,53 @@ +using System; + +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Encodings; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Signers; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Tls +{ + internal class TlsRsaSigner + : TlsSigner + { + public virtual byte[] CalculateRawSignature(SecureRandom random, + AsymmetricKeyParameter privateKey, byte[] md5andsha1) + { + ISigner s = MakeSigner(new NullDigest(), true, new ParametersWithRandom(privateKey, random)); + s.BlockUpdate(md5andsha1, 0, md5andsha1.Length); + return s.GenerateSignature(); + } + + public virtual bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, + byte[] md5andsha1) + { + ISigner s = MakeSigner(new NullDigest(), false, publicKey); + s.BlockUpdate(md5andsha1, 0, md5andsha1.Length); + return s.VerifySignature(sigBytes); + } + + public virtual ISigner CreateSigner(SecureRandom random, AsymmetricKeyParameter privateKey) + { + return MakeSigner(new CombinedHash(), true, new ParametersWithRandom(privateKey, random)); + } + + public virtual ISigner CreateVerifyer(AsymmetricKeyParameter publicKey) + { + return MakeSigner(new CombinedHash(), false, publicKey); + } + + public virtual bool IsValidPublicKey(AsymmetricKeyParameter publicKey) + { + return publicKey is RsaKeyParameters && !publicKey.IsPrivate; + } + + protected virtual ISigner MakeSigner(IDigest d, bool forSigning, ICipherParameters cp) + { + ISigner s = new GenericSigner(new Pkcs1Encoding(new RsaBlindedEngine()), d); + s.Init(forSigning, cp); + return s; + } + } +} diff --git a/Crypto/src/crypto/tls/TlsRsaUtilities.cs b/Crypto/src/crypto/tls/TlsRsaUtilities.cs new file mode 100644
index 000000000..4450ba452 --- /dev/null +++ b/Crypto/src/crypto/tls/TlsRsaUtilities.cs
@@ -0,0 +1,42 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto.Encodings; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class TlsRsaUtilities + { + public static byte[] GenerateEncryptedPreMasterSecret(SecureRandom random, + RsaKeyParameters rsaServerPublicKey, Stream output) + { + /* + * Choose a PremasterSecret and send it encrypted to the server + */ + byte[] premasterSecret = new byte[48]; + random.NextBytes(premasterSecret); + TlsUtilities.WriteVersion(premasterSecret, 0); + + Pkcs1Encoding encoding = new Pkcs1Encoding(new RsaBlindedEngine()); + encoding.Init(true, new ParametersWithRandom(rsaServerPublicKey, random)); + + try + { + byte[] keData = encoding.ProcessBlock(premasterSecret, 0, premasterSecret.Length); + TlsUtilities.WriteOpaque16(keData, output); + } + catch (InvalidCipherTextException) + { + /* + * This should never happen, only during decryption. + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + return premasterSecret; + } + } +} diff --git a/Crypto/src/crypto/tls/TlsSigner.cs b/Crypto/src/crypto/tls/TlsSigner.cs new file mode 100644
index 000000000..e59b90705 --- /dev/null +++ b/Crypto/src/crypto/tls/TlsSigner.cs
@@ -0,0 +1,18 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsSigner + { + byte[] CalculateRawSignature(SecureRandom random, AsymmetricKeyParameter privateKey, + byte[] md5andsha1); + bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5andsha1); + + ISigner CreateSigner(SecureRandom random, AsymmetricKeyParameter privateKey); + ISigner CreateVerifyer(AsymmetricKeyParameter publicKey); + + bool IsValidPublicKey(AsymmetricKeyParameter publicKey); + } +} diff --git a/Crypto/src/crypto/tls/TlsSignerCredentials.cs b/Crypto/src/crypto/tls/TlsSignerCredentials.cs new file mode 100644
index 000000000..2adb06c26 --- /dev/null +++ b/Crypto/src/crypto/tls/TlsSignerCredentials.cs
@@ -0,0 +1,11 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsSignerCredentials : TlsCredentials + { + /// <exception cref="IOException"></exception> + byte[] GenerateCertificateSignature(byte[] md5andsha1); + } +} diff --git a/Crypto/src/crypto/tls/TlsSrpKeyExchange.cs b/Crypto/src/crypto/tls/TlsSrpKeyExchange.cs new file mode 100644
index 000000000..852aace41 --- /dev/null +++ b/Crypto/src/crypto/tls/TlsSrpKeyExchange.cs
@@ -0,0 +1,203 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Agreement; +using Org.BouncyCastle.Crypto.Agreement.Srp; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <summary> + /// TLS 1.1 SRP key exchange. + /// </summary> + internal class TlsSrpKeyExchange + : TlsKeyExchange + { + protected TlsClientContext context; + protected KeyExchangeAlgorithm keyExchange; + 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(); + + internal TlsSrpKeyExchange(TlsClientContext context, KeyExchangeAlgorithm keyExchange, + byte[] identity, byte[] password) + { + 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 ArgumentException("unsupported key exchange algorithm", "keyExchange"); + } + + this.context = context; + this.keyExchange = keyExchange; + this.identity = identity; + this.password = password; + } + + public virtual void SkipServerCertificate() + { + if (tlsSigner != null) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + } + + public virtual void ProcessServerCertificate(Certificate serverCertificate) + { + if (tlsSigner == null) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + X509CertificateStructure x509Cert = serverCertificate.certs[0]; + SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo; + + try + { + this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo); + } +// catch (RuntimeException) + catch (Exception) + { + throw new TlsFatalAlert(AlertDescription.unsupported_certificate); + } + + if (!tlsSigner.IsValidPublicKey(this.serverPublicKey)) + { + throw new TlsFatalAlert(AlertDescription.certificate_unknown); + } + + TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature); + + // TODO + /* + * Perform various checks per RFC2246 7.4.2: "Unless otherwise specified, the + * signing algorithm for the certificate must be the same as the algorithm for the + * certificate key." + */ + } + + public virtual void SkipServerKeyExchange() + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + public virtual void ProcessServerKeyExchange(Stream input) + { + SecurityParameters securityParameters = context.SecurityParameters; + + Stream sigIn = input; + ISigner signer = null; + + if (tlsSigner != null) + { + signer = InitSigner(tlsSigner, securityParameters); + sigIn = new SignerStream(input, signer, null); + } + + byte[] NBytes = TlsUtilities.ReadOpaque16(sigIn); + byte[] gBytes = TlsUtilities.ReadOpaque16(sigIn); + byte[] sBytes = TlsUtilities.ReadOpaque8(sigIn); + byte[] BBytes = TlsUtilities.ReadOpaque16(sigIn); + + if (signer != null) + { + byte[] sigByte = TlsUtilities.ReadOpaque16(input); + + if (!signer.VerifySignature(sigByte)) + { + throw new TlsFatalAlert(AlertDescription.bad_certificate); + } + } + + BigInteger N = new BigInteger(1, NBytes); + BigInteger g = new BigInteger(1, gBytes); + + // TODO Validate group parameters (see RFC 5054) + //throw new TlsFatalAlert(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 = Srp6Utilities.ValidatePublicValue(N, new BigInteger(1, BBytes)); + } + catch (CryptoException) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + this.srpClient.Init(N, g, new Sha1Digest(), context.SecureRandom); + } + + public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + public virtual void SkipClientCredentials() + { + // OK + } + + public virtual void ProcessClientCredentials(TlsCredentials clientCredentials) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + public virtual void GenerateClientKeyExchange(Stream output) + { + byte[] keData = BigIntegers.AsUnsignedByteArray(srpClient.GenerateClientCredentials(s, + this.identity, this.password)); + TlsUtilities.WriteOpaque16(keData, output); + } + + public virtual byte[] GeneratePremasterSecret() + { + try + { + // TODO Check if this needs to be a fixed size + return BigIntegers.AsUnsignedByteArray(srpClient.CalculateSecret(B)); + } + catch (CryptoException) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + + protected virtual ISigner InitSigner(TlsSigner tlsSigner, SecurityParameters securityParameters) + { + ISigner signer = tlsSigner.CreateVerifyer(this.serverPublicKey); + signer.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length); + signer.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length); + return signer; + } + } +} diff --git a/Crypto/src/crypto/tls/TlsStream.cs b/Crypto/src/crypto/tls/TlsStream.cs new file mode 100644
index 000000000..e3d05686b --- /dev/null +++ b/Crypto/src/crypto/tls/TlsStream.cs
@@ -0,0 +1,89 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + internal class TlsStream + : Stream + { + private readonly TlsProtocolHandler handler; + + internal TlsStream( + TlsProtocolHandler handler) + { + this.handler = handler; + } + + public override bool CanRead + { + get { return !handler.IsClosed; } + } + + public override bool CanSeek + { + get { return false; } + } + + public override bool CanWrite + { + get { return !handler.IsClosed; } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + handler.Close(); + } + } + + public override void Flush() + { + handler.Flush(); + } + + public override long Length + { + get { throw new NotSupportedException(); } + } + + public override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + public override int Read(byte[] buf, int off, int len) + { + return this.handler.ReadApplicationData(buf, off, len); + } + + public override int ReadByte() + { + byte[] buf = new byte[1]; + if (this.Read(buf, 0, 1) <= 0) + return -1; + return buf[0]; + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buf, int off, int len) + { + this.handler.WriteData(buf, off, len); + } + + public override void WriteByte(byte b) + { + this.handler.WriteData(new byte[] { b }, 0, 1); + } + } +} diff --git a/Crypto/src/crypto/tls/TlsUtilities.cs b/Crypto/src/crypto/tls/TlsUtilities.cs new file mode 100644
index 000000000..0e2452689 --- /dev/null +++ b/Crypto/src/crypto/tls/TlsUtilities.cs
@@ -0,0 +1,286 @@ +using System; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Macs; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Date; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <remarks>Some helper fuctions for MicroTLS.</remarks> + public class TlsUtilities + { + internal static void WriteUint8(byte i, Stream os) + { + os.WriteByte(i); + } + + internal static void WriteUint8(byte i, byte[] buf, int offset) + { + buf[offset] = i; + } + + internal static void WriteUint16(int i, Stream os) + { + os.WriteByte((byte)(i >> 8)); + os.WriteByte((byte)i); + } + + internal static void WriteUint16(int i, byte[] buf, int offset) + { + buf[offset] = (byte)(i >> 8); + buf[offset + 1] = (byte)i; + } + + internal static void WriteUint24(int i, Stream os) + { + os.WriteByte((byte)(i >> 16)); + os.WriteByte((byte)(i >> 8)); + os.WriteByte((byte)i); + } + + internal 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); + } + + internal static void WriteUint64(long i, Stream os) + { + os.WriteByte((byte)(i >> 56)); + os.WriteByte((byte)(i >> 48)); + os.WriteByte((byte)(i >> 40)); + os.WriteByte((byte)(i >> 32)); + os.WriteByte((byte)(i >> 24)); + os.WriteByte((byte)(i >> 16)); + os.WriteByte((byte)(i >> 8)); + os.WriteByte((byte)i); + } + + internal 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); + } + + internal static void WriteOpaque8(byte[] buf, Stream os) + { + WriteUint8((byte)buf.Length, os); + os.Write(buf, 0, buf.Length); + } + + internal static void WriteOpaque16(byte[] buf, Stream os) + { + WriteUint16(buf.Length, os); + os.Write(buf, 0, buf.Length); + } + + internal static void WriteOpaque24(byte[] buf, Stream os) + { + WriteUint24(buf.Length, os); + os.Write(buf, 0, buf.Length); + } + + internal static void WriteUint8Array(byte[] uints, Stream os) + { + os.Write(uints, 0, uints.Length); + } + + internal static void WriteUint16Array(int[] uints, Stream os) + { + for (int i = 0; i < uints.Length; ++i) + { + WriteUint16(uints[i], os); + } + } + + internal static byte ReadUint8(Stream inStr) + { + int i = inStr.ReadByte(); + if (i < 0) + { + throw new EndOfStreamException(); + } + return (byte)i; + } + + internal static int ReadUint16(Stream inStr) + { + int i1 = inStr.ReadByte(); + int i2 = inStr.ReadByte(); + if ((i1 | i2) < 0) + { + throw new EndOfStreamException(); + } + return i1 << 8 | i2; + } + + internal static int ReadUint24(Stream inStr) + { + int i1 = inStr.ReadByte(); + int i2 = inStr.ReadByte(); + int i3 = inStr.ReadByte(); + if ((i1 | i2 | i3) < 0) + { + throw new EndOfStreamException(); + } + return (i1 << 16) | (i2 << 8) | i3; + } + + internal static void ReadFully(byte[] buf, Stream inStr) + { + if (Streams.ReadFully(inStr, buf, 0, buf.Length) < buf.Length) + throw new EndOfStreamException(); + } + + internal static byte[] ReadOpaque8(Stream inStr) + { + byte length = ReadUint8(inStr); + byte[] bytes = new byte[length]; + ReadFully(bytes, inStr); + return bytes; + } + + internal static byte[] ReadOpaque16(Stream inStr) + { + int length = ReadUint16(inStr); + byte[] bytes = new byte[length]; + ReadFully(bytes, inStr); + return bytes; + } + + internal static void CheckVersion(byte[] readVersion) + { + if ((readVersion[0] != 3) || (readVersion[1] != 1)) + { + throw new TlsFatalAlert(AlertDescription.protocol_version); + } + } + + internal static void CheckVersion(Stream inStr) + { + int i1 = inStr.ReadByte(); + int i2 = inStr.ReadByte(); + if ((i1 != 3) || (i2 != 1)) + { + throw new TlsFatalAlert(AlertDescription.protocol_version); + } + } + + internal static void WriteGmtUnixTime(byte[] buf, int offset) + { + int t = (int)(DateTimeUtilities.CurrentUnixMs() / 1000L); + buf[offset] = (byte)(t >> 24); + buf[offset + 1] = (byte)(t >> 16); + buf[offset + 2] = (byte)(t >> 8); + buf[offset + 3] = (byte)t; + } + + internal static void WriteVersion(Stream os) + { + os.WriteByte(3); + os.WriteByte(1); + } + + internal static void WriteVersion(byte[] buf, int offset) + { + buf[offset] = 3; + buf[offset + 1] = 1; + } + + private static void hmac_hash(IDigest digest, byte[] secret, byte[] seed, byte[] output) + { + HMac mac = new HMac(digest); + KeyParameter param = new KeyParameter(secret); + byte[] a = seed; + int size = digest.GetDigestSize(); + int iterations = (output.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.BlockUpdate(a, 0, a.Length); + mac.DoFinal(buf, 0); + a = buf; + mac.Init(param); + mac.BlockUpdate(a, 0, a.Length); + mac.BlockUpdate(seed, 0, seed.Length); + mac.DoFinal(buf2, 0); + Array.Copy(buf2, 0, output, (size * i), System.Math.Min(size, output.Length - (size * i))); + } + } + + internal static byte[] PRF(byte[] secret, string asciiLabel, byte[] seed, int size) + { + byte[] label = Strings.ToAsciiByteArray(asciiLabel); + + int s_half = (secret.Length + 1) / 2; + byte[] s1 = new byte[s_half]; + byte[] s2 = new byte[s_half]; + Array.Copy(secret, 0, s1, 0, s_half); + Array.Copy(secret, secret.Length - s_half, s2, 0, s_half); + + byte[] ls = Concat(label, seed); + + byte[] buf = new byte[size]; + byte[] prf = new byte[size]; + hmac_hash(new MD5Digest(), s1, ls, prf); + hmac_hash(new Sha1Digest(), s2, ls, buf); + for (int i = 0; i < size; i++) + { + buf[i] ^= prf[i]; + } + return buf; + } + + internal static byte[] PRF_1_2(IDigest digest, byte[] secret, string asciiLabel, byte[] seed, int size) + { + byte[] label = Strings.ToAsciiByteArray(asciiLabel); + byte[] labelSeed = Concat(label, seed); + + byte[] buf = new byte[size]; + hmac_hash(digest, secret, labelSeed, buf); + return buf; + } + + internal static byte[] Concat(byte[] a, byte[] b) + { + byte[] c = new byte[a.Length + b.Length]; + Array.Copy(a, 0, c, 0, a.Length); + Array.Copy(b, 0, c, a.Length, b.Length); + return c; + } + + internal static void ValidateKeyUsage(X509CertificateStructure c, int keyUsageBits) + { + X509Extensions exts = c.TbsCertificate.Extensions; + if (exts != null) + { + X509Extension ext = exts.GetExtension(X509Extensions.KeyUsage); + if (ext != null) + { + DerBitString ku = KeyUsage.GetInstance(ext); + //int bits = ku.GetBytes()[0]; + //if ((bits & keyUsageBits) != keyUsageBits) + //{ + // throw new TlsFatalAlert(AlertDescription.certificate_unknown); + //} + } + } + } + } +} diff --git a/Crypto/src/crypto/util/Pack.cs b/Crypto/src/crypto/util/Pack.cs new file mode 100644
index 000000000..67c939ad5 --- /dev/null +++ b/Crypto/src/crypto/util/Pack.cs
@@ -0,0 +1,219 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Utilities +{ + internal sealed class Pack + { + private Pack() + { + } + + internal static void UInt16_To_BE(ushort n, byte[] bs) + { + bs[0] = (byte)(n >> 8); + bs[1] = (byte)(n ); + } + + internal static void UInt16_To_BE(ushort n, byte[] bs, int off) + { + bs[ off] = (byte)(n >> 8); + bs[++off] = (byte)(n ); + } + + internal static ushort BE_To_UInt16(byte[] bs) + { + uint n = (uint)bs[0] << 8; + n |= (uint)bs[1]; + return (ushort)n; + } + + internal static ushort BE_To_UInt16(byte[] bs, int off) + { + uint n = (uint)bs[off] << 8; + n |= (uint)bs[++off]; + return (ushort)n; + } + + internal static void UInt32_To_BE(uint n, byte[] bs) + { + bs[0] = (byte)(n >> 24); + bs[1] = (byte)(n >> 16); + bs[2] = (byte)(n >> 8); + bs[3] = (byte)(n ); + } + + internal static void UInt32_To_BE(uint n, byte[] bs, int off) + { + bs[ off] = (byte)(n >> 24); + bs[++off] = (byte)(n >> 16); + bs[++off] = (byte)(n >> 8); + bs[++off] = (byte)(n ); + } + + internal static void UInt32_To_BE(uint[] ns, byte[] bs, int off) + { + for (int i = 0; i < ns.Length; ++i) + { + UInt32_To_BE(ns[i], bs, off); + off += 4; + } + } + + internal static uint BE_To_UInt32(byte[] bs) + { + uint n = (uint)bs[0] << 24; + n |= (uint)bs[1] << 16; + n |= (uint)bs[2] << 8; + n |= (uint)bs[3]; + return n; + } + + internal static uint BE_To_UInt32(byte[] bs, int off) + { + uint n = (uint)bs[off] << 24; + n |= (uint)bs[++off] << 16; + n |= (uint)bs[++off] << 8; + n |= (uint)bs[++off]; + return n; + } + + internal static void BE_To_UInt32(byte[] bs, int off, uint[] ns) + { + for (int i = 0; i < ns.Length; ++i) + { + ns[i] = BE_To_UInt32(bs, off); + off += 4; + } + } + + internal static ulong BE_To_UInt64(byte[] bs) + { + uint hi = BE_To_UInt32(bs); + uint lo = BE_To_UInt32(bs, 4); + return ((ulong)hi << 32) | (ulong)lo; + } + + internal static ulong BE_To_UInt64(byte[] bs, int off) + { + uint hi = BE_To_UInt32(bs, off); + uint lo = BE_To_UInt32(bs, off + 4); + return ((ulong)hi << 32) | (ulong)lo; + } + + internal static void UInt64_To_BE(ulong n, byte[] bs) + { + UInt32_To_BE((uint)(n >> 32), bs); + UInt32_To_BE((uint)(n ), bs, 4); + } + + internal static void UInt64_To_BE(ulong n, byte[] bs, int off) + { + UInt32_To_BE((uint)(n >> 32), bs, off); + UInt32_To_BE((uint)(n ), bs, off + 4); + } + + internal static void UInt16_To_LE(ushort n, byte[] bs) + { + bs[0] = (byte)(n ); + bs[1] = (byte)(n >> 8); + } + + internal static void UInt16_To_LE(ushort n, byte[] bs, int off) + { + bs[ off] = (byte)(n ); + bs[++off] = (byte)(n >> 8); + } + + internal static ushort LE_To_UInt16(byte[] bs) + { + uint n = (uint)bs[0]; + n |= (uint)bs[1] << 8; + return (ushort)n; + } + + internal static ushort LE_To_UInt16(byte[] bs, int off) + { + uint n = (uint)bs[off]; + n |= (uint)bs[++off] << 8; + return (ushort)n; + } + + internal static void UInt32_To_LE(uint n, byte[] bs) + { + bs[0] = (byte)(n ); + bs[1] = (byte)(n >> 8); + bs[2] = (byte)(n >> 16); + bs[3] = (byte)(n >> 24); + } + + internal static void UInt32_To_LE(uint n, byte[] bs, int off) + { + bs[ off] = (byte)(n ); + bs[++off] = (byte)(n >> 8); + bs[++off] = (byte)(n >> 16); + bs[++off] = (byte)(n >> 24); + } + + internal static void UInt32_To_LE(uint[] ns, byte[] bs, int off) + { + for (int i = 0; i < ns.Length; ++i) + { + UInt32_To_LE(ns[i], bs, off); + off += 4; + } + } + + internal static uint LE_To_UInt32(byte[] bs) + { + uint n = (uint)bs[0]; + n |= (uint)bs[1] << 8; + n |= (uint)bs[2] << 16; + n |= (uint)bs[3] << 24; + return n; + } + + internal static uint LE_To_UInt32(byte[] bs, int off) + { + uint n = (uint)bs[off]; + n |= (uint)bs[++off] << 8; + n |= (uint)bs[++off] << 16; + n |= (uint)bs[++off] << 24; + return n; + } + + internal static void LE_To_UInt32(byte[] bs, int off, uint[] ns) + { + for (int i = 0; i < ns.Length; ++i) + { + ns[i] = LE_To_UInt32(bs, off); + off += 4; + } + } + + internal static ulong LE_To_UInt64(byte[] bs) + { + uint lo = LE_To_UInt32(bs); + uint hi = LE_To_UInt32(bs, 4); + return ((ulong)hi << 32) | (ulong)lo; + } + + internal static ulong LE_To_UInt64(byte[] bs, int off) + { + uint lo = LE_To_UInt32(bs, off); + uint hi = LE_To_UInt32(bs, off + 4); + return ((ulong)hi << 32) | (ulong)lo; + } + + internal static void UInt64_To_LE(ulong n, byte[] bs) + { + UInt32_To_LE((uint)(n ), bs); + UInt32_To_LE((uint)(n >> 32), bs, 4); + } + + internal static void UInt64_To_LE(ulong n, byte[] bs, int off) + { + UInt32_To_LE((uint)(n ), bs, off); + UInt32_To_LE((uint)(n >> 32), bs, off + 4); + } + } +} diff --git a/Crypto/src/math/BigInteger.cs b/Crypto/src/math/BigInteger.cs new file mode 100644
index 000000000..d52c0f83c --- /dev/null +++ b/Crypto/src/math/BigInteger.cs
@@ -0,0 +1,3141 @@ +using System; +using System.Collections; +using System.Diagnostics; +using System.Globalization; +using System.Text; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Math +{ +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT || PORTABLE) + [Serializable] +#endif + public class BigInteger + { + // The primes b/w 2 and ~2^10 + /* + 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 + */ + + // Each list has a product < 2^31 + private static readonly int[][] primeLists = new int[][] + { + new int[]{ 3, 5, 7, 11, 13, 17, 19, 23 }, + new int[]{ 29, 31, 37, 41, 43 }, + new int[]{ 47, 53, 59, 61, 67 }, + new int[]{ 71, 73, 79, 83 }, + new int[]{ 89, 97, 101, 103 }, + + new int[]{ 107, 109, 113, 127 }, + new int[]{ 131, 137, 139, 149 }, + new int[]{ 151, 157, 163, 167 }, + new int[]{ 173, 179, 181, 191 }, + new int[]{ 193, 197, 199, 211 }, + + new int[]{ 223, 227, 229 }, + new int[]{ 233, 239, 241 }, + new int[]{ 251, 257, 263 }, + new int[]{ 269, 271, 277 }, + new int[]{ 281, 283, 293 }, + + new int[]{ 307, 311, 313 }, + new int[]{ 317, 331, 337 }, + new int[]{ 347, 349, 353 }, + new int[]{ 359, 367, 373 }, + new int[]{ 379, 383, 389 }, + + new int[]{ 397, 401, 409 }, + new int[]{ 419, 421, 431 }, + new int[]{ 433, 439, 443 }, + new int[]{ 449, 457, 461 }, + new int[]{ 463, 467, 479 }, + + new int[]{ 487, 491, 499 }, + new int[]{ 503, 509, 521 }, + new int[]{ 523, 541, 547 }, + new int[]{ 557, 563, 569 }, + new int[]{ 571, 577, 587 }, + + new int[]{ 593, 599, 601 }, + new int[]{ 607, 613, 617 }, + new int[]{ 619, 631, 641 }, + new int[]{ 643, 647, 653 }, + new int[]{ 659, 661, 673 }, + + new int[]{ 677, 683, 691 }, + new int[]{ 701, 709, 719 }, + new int[]{ 727, 733, 739 }, + new int[]{ 743, 751, 757 }, + new int[]{ 761, 769, 773 }, + + new int[]{ 787, 797, 809 }, + new int[]{ 811, 821, 823 }, + new int[]{ 827, 829, 839 }, + new int[]{ 853, 857, 859 }, + new int[]{ 863, 877, 881 }, + + new int[]{ 883, 887, 907 }, + new int[]{ 911, 919, 929 }, + new int[]{ 937, 941, 947 }, + new int[]{ 953, 967, 971 }, + new int[]{ 977, 983, 991 }, + + new int[]{ 997, 1009, 1013 }, + new int[]{ 1019, 1021, 1031 }, + }; + + private static readonly int[] primeProducts; + + private const long IMASK = 0xffffffffL; + private static readonly ulong UIMASK = (ulong)IMASK; + + private static readonly int[] ZeroMagnitude = new int[0]; + private static readonly byte[] ZeroEncoding = new byte[0]; + + public static readonly BigInteger Zero = new BigInteger(0, ZeroMagnitude, false); + public static readonly BigInteger One = createUValueOf(1); + public static readonly BigInteger Two = createUValueOf(2); + public static readonly BigInteger Three = createUValueOf(3); + public static readonly BigInteger Ten = createUValueOf(10); + + private static readonly int chunk2 = 1; // TODO Parse 64 bits at a time + private static readonly BigInteger radix2 = ValueOf(2); + private static readonly BigInteger radix2E = radix2.Pow(chunk2); + + private static readonly int chunk10 = 19; + private static readonly BigInteger radix10 = ValueOf(10); + private static readonly BigInteger radix10E = radix10.Pow(chunk10); + + private static readonly int chunk16 = 16; + private static readonly BigInteger radix16 = ValueOf(16); + private static readonly BigInteger radix16E = radix16.Pow(chunk16); + + private static readonly Random RandomSource = new Random(); + + private const int BitsPerByte = 8; + private const int BitsPerInt = 32; + private const int BytesPerInt = 4; + + static BigInteger() + { + primeProducts = new int[primeLists.Length]; + + for (int i = 0; i < primeLists.Length; ++i) + { + int[] primeList = primeLists[i]; + int product = primeList[0]; + for (int j = 1; j < primeList.Length; ++j) + { + product *= primeList[j]; + } + primeProducts[i] = product; + } + } + + private int sign; // -1 means -ve; +1 means +ve; 0 means 0; + private int[] magnitude; // array of ints with [0] being the most significant + private int nBits = -1; // cache BitCount() value + private int nBitLength = -1; // cache calcBitLength() value + private long mQuote = -1L; // -m^(-1) mod b, b = 2^32 (see Montgomery mult.) + + private static int GetByteLength( + int nBits) + { + return (nBits + BitsPerByte - 1) / BitsPerByte; + } + + private BigInteger( + int signum, + int[] mag, + bool checkMag) + { + if (checkMag) + { + int i = 0; + while (i < mag.Length && mag[i] == 0) + { + ++i; + } + + if (i == mag.Length) + { + this.sign = 0; + this.magnitude = ZeroMagnitude; + } + else + { + this.sign = signum; + + if (i == 0) + { + this.magnitude = mag; + } + else + { + // strip leading 0 words + this.magnitude = new int[mag.Length - i]; + Array.Copy(mag, i, this.magnitude, 0, this.magnitude.Length); + } + } + } + else + { + this.sign = signum; + this.magnitude = mag; + } + } + + public BigInteger( + string value) + : this(value, 10) + { + } + + public BigInteger( + string str, + int radix) + { + if (str.Length == 0) + throw new FormatException("Zero length BigInteger"); + + NumberStyles style; + int chunk; + BigInteger r; + BigInteger rE; + + switch (radix) + { + case 2: + // Is there anyway to restrict to binary digits? + style = NumberStyles.Integer; + chunk = chunk2; + r = radix2; + rE = radix2E; + break; + case 10: + // This style seems to handle spaces and minus sign already (our processing redundant?) + style = NumberStyles.Integer; + chunk = chunk10; + r = radix10; + rE = radix10E; + break; + case 16: + // TODO Should this be HexNumber? + style = NumberStyles.AllowHexSpecifier; + chunk = chunk16; + r = radix16; + rE = radix16E; + break; + default: + throw new FormatException("Only bases 2, 10, or 16 allowed"); + } + + + int index = 0; + sign = 1; + + if (str[0] == '-') + { + if (str.Length == 1) + throw new FormatException("Zero length BigInteger"); + + sign = -1; + index = 1; + } + + // strip leading zeros from the string str + while (index < str.Length && Int32.Parse(str[index].ToString(), style) == 0) + { + index++; + } + + if (index >= str.Length) + { + // zero value - we're done + sign = 0; + magnitude = ZeroMagnitude; + return; + } + + ////// + // could we work out the max number of ints required to store + // str.Length digits in the given base, then allocate that + // storage in one hit?, then Generate the magnitude in one hit too? + ////// + + BigInteger b = Zero; + + + int next = index + chunk; + + if (next <= str.Length) + { + do + { + string s = str.Substring(index, chunk); + ulong i = ulong.Parse(s, style); + BigInteger bi = createUValueOf(i); + + switch (radix) + { + case 2: + // TODO Need this because we are parsing in radix 10 above + if (i > 1) + throw new FormatException("Bad character in radix 2 string: " + s); + + // TODO Parse 64 bits at a time + b = b.ShiftLeft(1); + break; + case 16: + b = b.ShiftLeft(64); + break; + default: + b = b.Multiply(rE); + break; + } + + b = b.Add(bi); + + index = next; + next += chunk; + } + while (next <= str.Length); + } + + if (index < str.Length) + { + string s = str.Substring(index); + ulong i = ulong.Parse(s, style); + BigInteger bi = createUValueOf(i); + + if (b.sign > 0) + { + if (radix == 2) + { + // NB: Can't reach here since we are parsing one char at a time + Debug.Assert(false); + + // TODO Parse all bits at once +// b = b.ShiftLeft(s.Length); + } + else if (radix == 16) + { + b = b.ShiftLeft(s.Length << 2); + } + else + { + b = b.Multiply(r.Pow(s.Length)); + } + + b = b.Add(bi); + } + else + { + b = bi; + } + } + + // Note: This is the previous (slower) algorithm + // while (index < value.Length) + // { + // char c = value[index]; + // string s = c.ToString(); + // int i = Int32.Parse(s, style); + // + // b = b.Multiply(r).Add(ValueOf(i)); + // index++; + // } + + magnitude = b.magnitude; + } + + public BigInteger( + byte[] bytes) + : this(bytes, 0, bytes.Length) + { + } + + public BigInteger( + byte[] bytes, + int offset, + int length) + { + if (length == 0) + throw new FormatException("Zero length BigInteger"); + + // TODO Move this processing into MakeMagnitude (provide sign argument) + if ((sbyte)bytes[offset] < 0) + { + this.sign = -1; + + int end = offset + length; + + int iBval; + // strip leading sign bytes + for (iBval = offset; iBval < end && ((sbyte)bytes[iBval] == -1); iBval++) + { + } + + if (iBval >= end) + { + this.magnitude = One.magnitude; + } + else + { + int numBytes = end - iBval; + byte[] inverse = new byte[numBytes]; + + int index = 0; + while (index < numBytes) + { + inverse[index++] = (byte)~bytes[iBval++]; + } + + Debug.Assert(iBval == end); + + while (inverse[--index] == byte.MaxValue) + { + inverse[index] = byte.MinValue; + } + + inverse[index]++; + + this.magnitude = MakeMagnitude(inverse, 0, inverse.Length); + } + } + else + { + // strip leading zero bytes and return magnitude bytes + this.magnitude = MakeMagnitude(bytes, offset, length); + this.sign = this.magnitude.Length > 0 ? 1 : 0; + } + } + + private static int[] MakeMagnitude( + byte[] bytes, + int offset, + int length) + { + int end = offset + length; + + // strip leading zeros + int firstSignificant; + for (firstSignificant = offset; firstSignificant < end + && bytes[firstSignificant] == 0; firstSignificant++) + { + } + + if (firstSignificant >= end) + { + return ZeroMagnitude; + } + + int nInts = (end - firstSignificant + 3) / BytesPerInt; + int bCount = (end - firstSignificant) % BytesPerInt; + if (bCount == 0) + { + bCount = BytesPerInt; + } + + if (nInts < 1) + { + return ZeroMagnitude; + } + + int[] mag = new int[nInts]; + + int v = 0; + int magnitudeIndex = 0; + for (int i = firstSignificant; i < end; ++i) + { + v <<= 8; + v |= bytes[i] & 0xff; + bCount--; + if (bCount <= 0) + { + mag[magnitudeIndex] = v; + magnitudeIndex++; + bCount = BytesPerInt; + v = 0; + } + } + + if (magnitudeIndex < mag.Length) + { + mag[magnitudeIndex] = v; + } + + return mag; + } + + public BigInteger( + int sign, + byte[] bytes) + : this(sign, bytes, 0, bytes.Length) + { + } + + public BigInteger( + int sign, + byte[] bytes, + int offset, + int length) + { + if (sign < -1 || sign > 1) + throw new FormatException("Invalid sign value"); + + if (sign == 0) + { + this.sign = 0; + this.magnitude = ZeroMagnitude; + } + else + { + // copy bytes + this.magnitude = MakeMagnitude(bytes, offset, length); + this.sign = this.magnitude.Length < 1 ? 0 : sign; + } + } + + public BigInteger( + int sizeInBits, + Random random) + { + if (sizeInBits < 0) + throw new ArgumentException("sizeInBits must be non-negative"); + + this.nBits = -1; + this.nBitLength = -1; + + if (sizeInBits == 0) + { + this.sign = 0; + this.magnitude = ZeroMagnitude; + return; + } + + int nBytes = GetByteLength(sizeInBits); + byte[] b = new byte[nBytes]; + random.NextBytes(b); + + // strip off any excess bits in the MSB + b[0] &= rndMask[BitsPerByte * nBytes - sizeInBits]; + + this.magnitude = MakeMagnitude(b, 0, b.Length); + this.sign = this.magnitude.Length < 1 ? 0 : 1; + } + + private static readonly byte[] rndMask = { 255, 127, 63, 31, 15, 7, 3, 1 }; + + public BigInteger( + int bitLength, + int certainty, + Random random) + { + if (bitLength < 2) + throw new ArithmeticException("bitLength < 2"); + + this.sign = 1; + this.nBitLength = bitLength; + + if (bitLength == 2) + { + this.magnitude = random.Next(2) == 0 + ? Two.magnitude + : Three.magnitude; + return; + } + + int nBytes = GetByteLength(bitLength); + byte[] b = new byte[nBytes]; + + int xBits = BitsPerByte * nBytes - bitLength; + byte mask = rndMask[xBits]; + + for (;;) + { + random.NextBytes(b); + + // strip off any excess bits in the MSB + b[0] &= mask; + + // ensure the leading bit is 1 (to meet the strength requirement) + b[0] |= (byte)(1 << (7 - xBits)); + + // ensure the trailing bit is 1 (i.e. must be odd) + b[nBytes - 1] |= 1; + + this.magnitude = MakeMagnitude(b, 0, b.Length); + this.nBits = -1; + this.mQuote = -1L; + + if (certainty < 1) + break; + + if (CheckProbablePrime(certainty, random)) + break; + + if (bitLength > 32) + { + for (int rep = 0; rep < 10000; ++rep) + { + int n = 33 + random.Next(bitLength - 2); + this.magnitude[this.magnitude.Length - (n >> 5)] ^= (1 << (n & 31)); + this.magnitude[this.magnitude.Length - 1] ^= ((random.Next() + 1) << 1); + this.mQuote = -1L; + + if (CheckProbablePrime(certainty, random)) + return; + } + } + } + } + + public BigInteger Abs() + { + return sign >= 0 ? this : Negate(); + } + + /** + * return a = a + b - b preserved. + */ + private static int[] AddMagnitudes( + int[] a, + int[] b) + { + int tI = a.Length - 1; + int vI = b.Length - 1; + long m = 0; + + while (vI >= 0) + { + m += ((long)(uint)a[tI] + (long)(uint)b[vI--]); + a[tI--] = (int)m; + m = (long)((ulong)m >> 32); + } + + if (m != 0) + { + while (tI >= 0 && ++a[tI--] == 0) + { + } + } + + return a; + } + + public BigInteger Add( + BigInteger value) + { + if (this.sign == 0) + return value; + + if (this.sign != value.sign) + { + if (value.sign == 0) + return this; + + if (value.sign < 0) + return Subtract(value.Negate()); + + return value.Subtract(Negate()); + } + + return AddToMagnitude(value.magnitude); + } + + private BigInteger AddToMagnitude( + int[] magToAdd) + { + int[] big, small; + if (this.magnitude.Length < magToAdd.Length) + { + big = magToAdd; + small = this.magnitude; + } + else + { + big = this.magnitude; + small = magToAdd; + } + + // Conservatively avoid over-allocation when no overflow possible + uint limit = uint.MaxValue; + if (big.Length == small.Length) + limit -= (uint) small[0]; + + bool possibleOverflow = (uint) big[0] >= limit; + + int[] bigCopy; + if (possibleOverflow) + { + bigCopy = new int[big.Length + 1]; + big.CopyTo(bigCopy, 1); + } + else + { + bigCopy = (int[]) big.Clone(); + } + + bigCopy = AddMagnitudes(bigCopy, small); + + return new BigInteger(this.sign, bigCopy, possibleOverflow); + } + + public BigInteger And( + BigInteger value) + { + if (this.sign == 0 || value.sign == 0) + { + return Zero; + } + + int[] aMag = this.sign > 0 + ? this.magnitude + : Add(One).magnitude; + + int[] bMag = value.sign > 0 + ? value.magnitude + : value.Add(One).magnitude; + + bool resultNeg = sign < 0 && value.sign < 0; + int resultLength = System.Math.Max(aMag.Length, bMag.Length); + int[] resultMag = new int[resultLength]; + + int aStart = resultMag.Length - aMag.Length; + int bStart = resultMag.Length - bMag.Length; + + for (int i = 0; i < resultMag.Length; ++i) + { + int aWord = i >= aStart ? aMag[i - aStart] : 0; + int bWord = i >= bStart ? bMag[i - bStart] : 0; + + if (this.sign < 0) + { + aWord = ~aWord; + } + + if (value.sign < 0) + { + bWord = ~bWord; + } + + resultMag[i] = aWord & bWord; + + if (resultNeg) + { + resultMag[i] = ~resultMag[i]; + } + } + + BigInteger result = new BigInteger(1, resultMag, true); + + // TODO Optimise this case + if (resultNeg) + { + result = result.Not(); + } + + return result; + } + + public BigInteger AndNot( + BigInteger val) + { + return And(val.Not()); + } + + public int BitCount + { + get + { + if (nBits == -1) + { + if (sign < 0) + { + // TODO Optimise this case + nBits = Not().BitCount; + } + else + { + int sum = 0; + for (int i = 0; i < magnitude.Length; i++) + { + sum += bitCounts[(byte) magnitude[i]]; + sum += bitCounts[(byte)(magnitude[i] >> 8)]; + sum += bitCounts[(byte)(magnitude[i] >> 16)]; + sum += bitCounts[(byte)(magnitude[i] >> 24)]; + } + nBits = sum; + } + } + + return nBits; + } + } + + private readonly static byte[] bitCounts = + { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, + 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, + 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, + 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, + 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, + 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, + 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, + 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, + 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, + 6, 6, 7, 6, 7, 7, 8 + }; + + private int calcBitLength( + int indx, + int[] mag) + { + for (;;) + { + if (indx >= mag.Length) + return 0; + + if (mag[indx] != 0) + break; + + ++indx; + } + + // bit length for everything after the first int + int bitLength = 32 * ((mag.Length - indx) - 1); + + // and determine bitlength of first int + int firstMag = mag[indx]; + bitLength += BitLen(firstMag); + + // Check for negative powers of two + if (sign < 0 && ((firstMag & -firstMag) == firstMag)) + { + do + { + if (++indx >= mag.Length) + { + --bitLength; + break; + } + } + while (mag[indx] == 0); + } + + return bitLength; + } + + public int BitLength + { + get + { + if (nBitLength == -1) + { + nBitLength = sign == 0 + ? 0 + : calcBitLength(0, magnitude); + } + + return nBitLength; + } + } + + // + // BitLen(value) is the number of bits in value. + // + private static int BitLen( + int w) + { + // Binary search - decision tree (5 tests, rarely 6) + return (w < 1 << 15 ? (w < 1 << 7 + ? (w < 1 << 3 ? (w < 1 << 1 + ? (w < 1 << 0 ? (w < 0 ? 32 : 0) : 1) + : (w < 1 << 2 ? 2 : 3)) : (w < 1 << 5 + ? (w < 1 << 4 ? 4 : 5) + : (w < 1 << 6 ? 6 : 7))) + : (w < 1 << 11 + ? (w < 1 << 9 ? (w < 1 << 8 ? 8 : 9) : (w < 1 << 10 ? 10 : 11)) + : (w < 1 << 13 ? (w < 1 << 12 ? 12 : 13) : (w < 1 << 14 ? 14 : 15)))) : (w < 1 << 23 ? (w < 1 << 19 + ? (w < 1 << 17 ? (w < 1 << 16 ? 16 : 17) : (w < 1 << 18 ? 18 : 19)) + : (w < 1 << 21 ? (w < 1 << 20 ? 20 : 21) : (w < 1 << 22 ? 22 : 23))) : (w < 1 << 27 + ? (w < 1 << 25 ? (w < 1 << 24 ? 24 : 25) : (w < 1 << 26 ? 26 : 27)) + : (w < 1 << 29 ? (w < 1 << 28 ? 28 : 29) : (w < 1 << 30 ? 30 : 31))))); + } + +// private readonly static byte[] bitLengths = +// { +// 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, +// 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, +// 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, +// 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, +// 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, +// 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, +// 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, +// 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, +// 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, +// 8, 8, 8, 8, 8, 8, 8, 8 +// }; + + private bool QuickPow2Check() + { + return sign > 0 && nBits == 1; + } + + public int CompareTo( + object obj) + { + return CompareTo((BigInteger)obj); + } + + /** + * unsigned comparison on two arrays - note the arrays may + * start with leading zeros. + */ + private static int CompareTo( + int xIndx, + int[] x, + int yIndx, + int[] y) + { + while (xIndx != x.Length && x[xIndx] == 0) + { + xIndx++; + } + + while (yIndx != y.Length && y[yIndx] == 0) + { + yIndx++; + } + + return CompareNoLeadingZeroes(xIndx, x, yIndx, y); + } + + private static int CompareNoLeadingZeroes( + int xIndx, + int[] x, + int yIndx, + int[] y) + { + int diff = (x.Length - y.Length) - (xIndx - yIndx); + + if (diff != 0) + { + return diff < 0 ? -1 : 1; + } + + // lengths of magnitudes the same, test the magnitude values + + while (xIndx < x.Length) + { + uint v1 = (uint)x[xIndx++]; + uint v2 = (uint)y[yIndx++]; + + if (v1 != v2) + return v1 < v2 ? -1 : 1; + } + + return 0; + } + + public int CompareTo( + BigInteger value) + { + return sign < value.sign ? -1 + : sign > value.sign ? 1 + : sign == 0 ? 0 + : sign * CompareNoLeadingZeroes(0, magnitude, 0, value.magnitude); + } + + /** + * return z = x / y - done in place (z value preserved, x contains the + * remainder) + */ + private int[] Divide( + int[] x, + int[] y) + { + int xStart = 0; + while (xStart < x.Length && x[xStart] == 0) + { + ++xStart; + } + + int yStart = 0; + while (yStart < y.Length && y[yStart] == 0) + { + ++yStart; + } + + Debug.Assert(yStart < y.Length); + + int xyCmp = CompareNoLeadingZeroes(xStart, x, yStart, y); + int[] count; + + if (xyCmp > 0) + { + int yBitLength = calcBitLength(yStart, y); + int xBitLength = calcBitLength(xStart, x); + int shift = xBitLength - yBitLength; + + int[] iCount; + int iCountStart = 0; + + int[] c; + int cStart = 0; + int cBitLength = yBitLength; + if (shift > 0) + { +// iCount = ShiftLeft(One.magnitude, shift); + iCount = new int[(shift >> 5) + 1]; + iCount[0] = 1 << (shift % 32); + + c = ShiftLeft(y, shift); + cBitLength += shift; + } + else + { + iCount = new int[] { 1 }; + + int len = y.Length - yStart; + c = new int[len]; + Array.Copy(y, yStart, c, 0, len); + } + + count = new int[iCount.Length]; + + for (;;) + { + if (cBitLength < xBitLength + || CompareNoLeadingZeroes(xStart, x, cStart, c) >= 0) + { + Subtract(xStart, x, cStart, c); + AddMagnitudes(count, iCount); + + while (x[xStart] == 0) + { + if (++xStart == x.Length) + return count; + } + + //xBitLength = calcBitLength(xStart, x); + xBitLength = 32 * (x.Length - xStart - 1) + BitLen(x[xStart]); + + if (xBitLength <= yBitLength) + { + if (xBitLength < yBitLength) + return count; + + xyCmp = CompareNoLeadingZeroes(xStart, x, yStart, y); + + if (xyCmp <= 0) + break; + } + } + + shift = cBitLength - xBitLength; + + // NB: The case where c[cStart] is 1-bit is harmless + if (shift == 1) + { + uint firstC = (uint) c[cStart] >> 1; + uint firstX = (uint) x[xStart]; + if (firstC > firstX) + ++shift; + } + + if (shift < 2) + { + ShiftRightOneInPlace(cStart, c); + --cBitLength; + ShiftRightOneInPlace(iCountStart, iCount); + } + else + { + ShiftRightInPlace(cStart, c, shift); + cBitLength -= shift; + ShiftRightInPlace(iCountStart, iCount, shift); + } + + //cStart = c.Length - ((cBitLength + 31) / 32); + while (c[cStart] == 0) + { + ++cStart; + } + + while (iCount[iCountStart] == 0) + { + ++iCountStart; + } + } + } + else + { + count = new int[1]; + } + + if (xyCmp == 0) + { + AddMagnitudes(count, One.magnitude); + Array.Clear(x, xStart, x.Length - xStart); + } + + return count; + } + + public BigInteger Divide( + BigInteger val) + { + if (val.sign == 0) + throw new ArithmeticException("Division by zero error"); + + if (sign == 0) + return Zero; + + if (val.QuickPow2Check()) // val is power of two + { + BigInteger result = this.Abs().ShiftRight(val.Abs().BitLength - 1); + return val.sign == this.sign ? result : result.Negate(); + } + + int[] mag = (int[]) this.magnitude.Clone(); + + return new BigInteger(this.sign * val.sign, Divide(mag, val.magnitude), true); + } + + public BigInteger[] DivideAndRemainder( + BigInteger val) + { + if (val.sign == 0) + throw new ArithmeticException("Division by zero error"); + + BigInteger[] biggies = new BigInteger[2]; + + if (sign == 0) + { + biggies[0] = Zero; + biggies[1] = Zero; + } + else if (val.QuickPow2Check()) // val is power of two + { + int e = val.Abs().BitLength - 1; + BigInteger quotient = this.Abs().ShiftRight(e); + int[] remainder = this.LastNBits(e); + + biggies[0] = val.sign == this.sign ? quotient : quotient.Negate(); + biggies[1] = new BigInteger(this.sign, remainder, true); + } + else + { + int[] remainder = (int[]) this.magnitude.Clone(); + int[] quotient = Divide(remainder, val.magnitude); + + biggies[0] = new BigInteger(this.sign * val.sign, quotient, true); + biggies[1] = new BigInteger(this.sign, remainder, true); + } + + return biggies; + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + BigInteger biggie = obj as BigInteger; + if (biggie == null) + return false; + + if (biggie.sign != sign || biggie.magnitude.Length != magnitude.Length) + return false; + + for (int i = 0; i < magnitude.Length; i++) + { + if (biggie.magnitude[i] != magnitude[i]) + { + return false; + } + } + + return true; + } + + public BigInteger Gcd( + BigInteger value) + { + if (value.sign == 0) + return Abs(); + + if (sign == 0) + return value.Abs(); + + BigInteger r; + BigInteger u = this; + BigInteger v = value; + + while (v.sign != 0) + { + r = u.Mod(v); + u = v; + v = r; + } + + return u; + } + + public override int GetHashCode() + { + int hc = magnitude.Length; + if (magnitude.Length > 0) + { + hc ^= magnitude[0]; + + if (magnitude.Length > 1) + { + hc ^= magnitude[magnitude.Length - 1]; + } + } + + return sign < 0 ? ~hc : hc; + } + + // TODO Make public? + private BigInteger Inc() + { + if (this.sign == 0) + return One; + + if (this.sign < 0) + return new BigInteger(-1, doSubBigLil(this.magnitude, One.magnitude), true); + + return AddToMagnitude(One.magnitude); + } + + public int IntValue + { + get + { + return sign == 0 ? 0 + : sign > 0 ? magnitude[magnitude.Length - 1] + : -magnitude[magnitude.Length - 1]; + } + } + + /** + * return whether or not a BigInteger is probably prime with a + * probability of 1 - (1/2)**certainty. + * <p>From Knuth Vol 2, pg 395.</p> + */ + public bool IsProbablePrime( + int certainty) + { + if (certainty <= 0) + return true; + + BigInteger n = Abs(); + + if (!n.TestBit(0)) + return n.Equals(Two); + + if (n.Equals(One)) + return false; + + return n.CheckProbablePrime(certainty, RandomSource); + } + + private bool CheckProbablePrime( + int certainty, + Random random) + { + Debug.Assert(certainty > 0); + Debug.Assert(CompareTo(Two) > 0); + Debug.Assert(TestBit(0)); + + + // Try to reduce the penalty for really small numbers + int numLists = System.Math.Min(BitLength - 1, primeLists.Length); + + for (int i = 0; i < numLists; ++i) + { + int test = Remainder(primeProducts[i]); + + int[] primeList = primeLists[i]; + for (int j = 0; j < primeList.Length; ++j) + { + int prime = primeList[j]; + int qRem = test % prime; + if (qRem == 0) + { + // We may find small numbers in the list + return BitLength < 16 && IntValue == prime; + } + } + } + + + // TODO Special case for < 10^16 (RabinMiller fixed list) +// if (BitLength < 30) +// { +// RabinMiller against 2, 3, 5, 7, 11, 13, 23 is sufficient +// } + + + // TODO Is it worth trying to create a hybrid of these two? + return RabinMillerTest(certainty, random); +// return SolovayStrassenTest(certainty, random); + +// bool rbTest = RabinMillerTest(certainty, random); +// bool ssTest = SolovayStrassenTest(certainty, random); +// +// Debug.Assert(rbTest == ssTest); +// +// return rbTest; + } + + internal bool RabinMillerTest( + int certainty, + Random random) + { + Debug.Assert(certainty > 0); + Debug.Assert(BitLength > 2); + Debug.Assert(TestBit(0)); + + // let n = 1 + d . 2^s + BigInteger n = this; + BigInteger nMinusOne = n.Subtract(One); + int s = nMinusOne.GetLowestSetBit(); + BigInteger r = nMinusOne.ShiftRight(s); + + Debug.Assert(s >= 1); + + do + { + // TODO Make a method for random BigIntegers in range 0 < x < n) + // - Method can be optimized by only replacing examined bits at each trial + BigInteger a; + do + { + a = new BigInteger(n.BitLength, random); + } + while (a.CompareTo(One) <= 0 || a.CompareTo(nMinusOne) >= 0); + + BigInteger y = a.ModPow(r, n); + + if (!y.Equals(One)) + { + int j = 0; + while (!y.Equals(nMinusOne)) + { + if (++j == s) + return false; + + y = y.ModPow(Two, n); + + if (y.Equals(One)) + return false; + } + } + + certainty -= 2; // composites pass for only 1/4 possible 'a' + } + while (certainty > 0); + + return true; + } + +// private bool SolovayStrassenTest( +// int certainty, +// Random random) +// { +// Debug.Assert(certainty > 0); +// Debug.Assert(CompareTo(Two) > 0); +// Debug.Assert(TestBit(0)); +// +// BigInteger n = this; +// BigInteger nMinusOne = n.Subtract(One); +// BigInteger e = nMinusOne.ShiftRight(1); +// +// do +// { +// BigInteger a; +// do +// { +// a = new BigInteger(nBitLength, random); +// } +// // NB: Spec says 0 < x < n, but 1 is trivial +// while (a.CompareTo(One) <= 0 || a.CompareTo(n) >= 0); +// +// +// // TODO Check this is redundant given the way Jacobi() works? +//// if (!a.Gcd(n).Equals(One)) +//// return false; +// +// int x = Jacobi(a, n); +// +// if (x == 0) +// return false; +// +// BigInteger check = a.ModPow(e, n); +// +// if (x == 1 && !check.Equals(One)) +// return false; +// +// if (x == -1 && !check.Equals(nMinusOne)) +// return false; +// +// --certainty; +// } +// while (certainty > 0); +// +// return true; +// } +// +// private static int Jacobi( +// BigInteger a, +// BigInteger b) +// { +// Debug.Assert(a.sign >= 0); +// Debug.Assert(b.sign > 0); +// Debug.Assert(b.TestBit(0)); +// Debug.Assert(a.CompareTo(b) < 0); +// +// int totalS = 1; +// for (;;) +// { +// if (a.sign == 0) +// return 0; +// +// if (a.Equals(One)) +// break; +// +// int e = a.GetLowestSetBit(); +// +// int bLsw = b.magnitude[b.magnitude.Length - 1]; +// if ((e & 1) != 0 && ((bLsw & 7) == 3 || (bLsw & 7) == 5)) +// totalS = -totalS; +// +// // TODO Confirm this is faster than later a1.Equals(One) test +// if (a.BitLength == e + 1) +// break; +// BigInteger a1 = a.ShiftRight(e); +//// if (a1.Equals(One)) +//// break; +// +// int a1Lsw = a1.magnitude[a1.magnitude.Length - 1]; +// if ((bLsw & 3) == 3 && (a1Lsw & 3) == 3) +// totalS = -totalS; +// +//// a = b.Mod(a1); +// a = b.Remainder(a1); +// b = a1; +// } +// return totalS; +// } + + public long LongValue + { + get + { + if (sign == 0) + return 0; + + long v; + if (magnitude.Length > 1) + { + v = ((long)magnitude[magnitude.Length - 2] << 32) + | (magnitude[magnitude.Length - 1] & IMASK); + } + else + { + v = (magnitude[magnitude.Length - 1] & IMASK); + } + + return sign < 0 ? -v : v; + } + } + + public BigInteger Max( + BigInteger value) + { + return CompareTo(value) > 0 ? this : value; + } + + public BigInteger Min( + BigInteger value) + { + return CompareTo(value) < 0 ? this : value; + } + + public BigInteger Mod( + BigInteger m) + { + if (m.sign < 1) + throw new ArithmeticException("Modulus must be positive"); + + BigInteger biggie = Remainder(m); + + return (biggie.sign >= 0 ? biggie : biggie.Add(m)); + } + + public BigInteger ModInverse( + BigInteger m) + { + if (m.sign < 1) + throw new ArithmeticException("Modulus must be positive"); + + // TODO Too slow at the moment +// // "Fast Key Exchange with Elliptic Curve Systems" R.Schoeppel +// if (m.TestBit(0)) +// { +// //The Almost Inverse Algorithm +// int k = 0; +// BigInteger B = One, C = Zero, F = this, G = m, tmp; +// +// for (;;) +// { +// // While F is even, do F=F/u, C=C*u, k=k+1. +// int zeroes = F.GetLowestSetBit(); +// if (zeroes > 0) +// { +// F = F.ShiftRight(zeroes); +// C = C.ShiftLeft(zeroes); +// k += zeroes; +// } +// +// // If F = 1, then return B,k. +// if (F.Equals(One)) +// { +// BigInteger half = m.Add(One).ShiftRight(1); +// BigInteger halfK = half.ModPow(BigInteger.ValueOf(k), m); +// return B.Multiply(halfK).Mod(m); +// } +// +// if (F.CompareTo(G) < 0) +// { +// tmp = G; G = F; F = tmp; +// tmp = B; B = C; C = tmp; +// } +// +// F = F.Add(G); +// B = B.Add(C); +// } +// } + + BigInteger x; + BigInteger gcd = ExtEuclid(this.Mod(m), m, out x); + + if (!gcd.Equals(One)) + throw new ArithmeticException("Numbers not relatively prime."); + + if (x.sign < 0) + { + x = x.Add(m); + } + + return x; + } + + /** + * Calculate the numbers u1, u2, and u3 such that: + * + * u1 * a + u2 * b = u3 + * + * where u3 is the greatest common divider of a and b. + * a and b using the extended Euclid algorithm (refer p. 323 + * of The Art of Computer Programming vol 2, 2nd ed). + * This also seems to have the side effect of calculating + * some form of multiplicative inverse. + * + * @param a First number to calculate gcd for + * @param b Second number to calculate gcd for + * @param u1Out the return object for the u1 value + * @param u2Out the return object for the u2 value + * @return The greatest common divisor of a and b + */ + private static BigInteger ExtEuclid( + BigInteger a, + BigInteger b, + out BigInteger u1Out) + //BigInteger u2Out) + { + BigInteger u1 = One; + BigInteger u3 = a; + BigInteger v1 = Zero; + BigInteger v3 = b; + + while (v3.sign > 0) + { + BigInteger[] q = u3.DivideAndRemainder(v3); + + BigInteger tmp = v1.Multiply(q[0]); + BigInteger tn = u1.Subtract(tmp); + u1 = v1; + v1 = tn; + + u3 = v3; + v3 = q[1]; + } + + //if (u1Out != null) + //{ + // u1Out.sign = u1.sign; + // u1Out.magnitude = u1.magnitude; + //} + u1Out = u1; + + //if (u2Out != null) + //{ + // BigInteger tmp = u1.Multiply(a); + // tmp = u3.Subtract(tmp); + // BigInteger res = tmp.Divide(b); + // u2Out.sign = res.sign; + // u2Out.magnitude = res.magnitude; + //} + + return u3; + } + + private static void ZeroOut( + int[] x) + { + Array.Clear(x, 0, x.Length); + } + + public BigInteger ModPow( + BigInteger exponent, + BigInteger m) + { + if (m.sign < 1) + throw new ArithmeticException("Modulus must be positive"); + + if (m.Equals(One)) + return Zero; + + if (exponent.sign == 0) + return One; + + if (sign == 0) + return Zero; + + int[] zVal = null; + int[] yAccum = null; + int[] yVal; + + // Montgomery exponentiation is only possible if the modulus is odd, + // but AFAIK, this is always the case for crypto algo's + bool useMonty = ((m.magnitude[m.magnitude.Length - 1] & 1) == 1); + long mQ = 0; + if (useMonty) + { + mQ = m.GetMQuote(); + + // tmp = this * R mod m + BigInteger tmp = ShiftLeft(32 * m.magnitude.Length).Mod(m); + zVal = tmp.magnitude; + + useMonty = (zVal.Length <= m.magnitude.Length); + + if (useMonty) + { + yAccum = new int[m.magnitude.Length + 1]; + if (zVal.Length < m.magnitude.Length) + { + int[] longZ = new int[m.magnitude.Length]; + zVal.CopyTo(longZ, longZ.Length - zVal.Length); + zVal = longZ; + } + } + } + + if (!useMonty) + { + if (magnitude.Length <= m.magnitude.Length) + { + //zAccum = new int[m.magnitude.Length * 2]; + zVal = new int[m.magnitude.Length]; + magnitude.CopyTo(zVal, zVal.Length - magnitude.Length); + } + else + { + // + // in normal practice we'll never see this... + // + BigInteger tmp = Remainder(m); + + //zAccum = new int[m.magnitude.Length * 2]; + zVal = new int[m.magnitude.Length]; + tmp.magnitude.CopyTo(zVal, zVal.Length - tmp.magnitude.Length); + } + + yAccum = new int[m.magnitude.Length * 2]; + } + + yVal = new int[m.magnitude.Length]; + + // + // from LSW to MSW + // + for (int i = 0; i < exponent.magnitude.Length; i++) + { + int v = exponent.magnitude[i]; + int bits = 0; + + if (i == 0) + { + while (v > 0) + { + v <<= 1; + bits++; + } + + // + // first time in initialise y + // + zVal.CopyTo(yVal, 0); + + v <<= 1; + bits++; + } + + while (v != 0) + { + if (useMonty) + { + // Montgomery square algo doesn't exist, and a normal + // square followed by a Montgomery reduction proved to + // be almost as heavy as a Montgomery mulitply. + MultiplyMonty(yAccum, yVal, yVal, m.magnitude, mQ); + } + else + { + Square(yAccum, yVal); + Remainder(yAccum, m.magnitude); + Array.Copy(yAccum, yAccum.Length - yVal.Length, yVal, 0, yVal.Length); + ZeroOut(yAccum); + } + bits++; + + if (v < 0) + { + if (useMonty) + { + MultiplyMonty(yAccum, yVal, zVal, m.magnitude, mQ); + } + else + { + Multiply(yAccum, yVal, zVal); + Remainder(yAccum, m.magnitude); + Array.Copy(yAccum, yAccum.Length - yVal.Length, yVal, 0, + yVal.Length); + ZeroOut(yAccum); + } + } + + v <<= 1; + } + + while (bits < 32) + { + if (useMonty) + { + MultiplyMonty(yAccum, yVal, yVal, m.magnitude, mQ); + } + else + { + Square(yAccum, yVal); + Remainder(yAccum, m.magnitude); + Array.Copy(yAccum, yAccum.Length - yVal.Length, yVal, 0, yVal.Length); + ZeroOut(yAccum); + } + bits++; + } + } + + if (useMonty) + { + // Return y * R^(-1) mod m by doing y * 1 * R^(-1) mod m + ZeroOut(zVal); + zVal[zVal.Length - 1] = 1; + MultiplyMonty(yAccum, yVal, zVal, m.magnitude, mQ); + } + + BigInteger result = new BigInteger(1, yVal, true); + + return exponent.sign > 0 + ? result + : result.ModInverse(m); + } + + /** + * return w with w = x * x - w is assumed to have enough space. + */ + private static int[] Square( + int[] w, + int[] x) + { + // Note: this method allows w to be only (2 * x.Length - 1) words if result will fit +// if (w.Length != 2 * x.Length) +// throw new ArgumentException("no I don't think so..."); + + ulong u1, u2, c; + + int wBase = w.Length - 1; + + for (int i = x.Length - 1; i != 0; i--) + { + ulong v = (ulong)(uint) x[i]; + + u1 = v * v; + u2 = u1 >> 32; + u1 = (uint) u1; + + u1 += (ulong)(uint) w[wBase]; + + w[wBase] = (int)(uint) u1; + c = u2 + (u1 >> 32); + + for (int j = i - 1; j >= 0; j--) + { + --wBase; + u1 = v * (ulong)(uint) x[j]; + u2 = u1 >> 31; // multiply by 2! + u1 = (uint)(u1 << 1); // multiply by 2! + u1 += c + (ulong)(uint) w[wBase]; + + w[wBase] = (int)(uint) u1; + c = u2 + (u1 >> 32); + } + + c += (ulong)(uint) w[--wBase]; + w[wBase] = (int)(uint) c; + + if (--wBase >= 0) + { + w[wBase] = (int)(uint)(c >> 32); + } + else + { + Debug.Assert((uint)(c >> 32) == 0); + } + wBase += i; + } + + u1 = (ulong)(uint) x[0]; + u1 = u1 * u1; + u2 = u1 >> 32; + u1 = u1 & IMASK; + + u1 += (ulong)(uint) w[wBase]; + + w[wBase] = (int)(uint) u1; + if (--wBase >= 0) + { + w[wBase] = (int)(uint)(u2 + (u1 >> 32) + (ulong)(uint) w[wBase]); + } + else + { + Debug.Assert((uint)(u2 + (u1 >> 32)) == 0); + } + + return w; + } + + /** + * return x with x = y * z - x is assumed to have enough space. + */ + private static int[] Multiply( + int[] x, + int[] y, + int[] z) + { + int i = z.Length; + + if (i < 1) + return x; + + int xBase = x.Length - y.Length; + + do + { + long a = z[--i] & IMASK; + long val = 0; + + if (a != 0) + { + for (int j = y.Length - 1; j >= 0; j--) + { + val += a * (y[j] & IMASK) + (x[xBase + j] & IMASK); + + x[xBase + j] = (int)val; + + val = (long)((ulong)val >> 32); + } + } + + --xBase; + + if (xBase >= 0) + { + x[xBase] = (int)val; + } + else + { + Debug.Assert(val == 0); + } + } + while (i > 0); + + return x; + } + + private static long FastExtEuclid( + long a, + long b, + long[] uOut) + { + long u1 = 1; + long u3 = a; + long v1 = 0; + long v3 = b; + + while (v3 > 0) + { + long q, tn; + + q = u3 / v3; + + tn = u1 - (v1 * q); + u1 = v1; + v1 = tn; + + tn = u3 - (v3 * q); + u3 = v3; + v3 = tn; + } + + uOut[0] = u1; + uOut[1] = (u3 - (u1 * a)) / b; + + return u3; + } + + private static long FastModInverse( + long v, + long m) + { + if (m < 1) + throw new ArithmeticException("Modulus must be positive"); + + long[] x = new long[2]; + long gcd = FastExtEuclid(v, m, x); + + if (gcd != 1) + throw new ArithmeticException("Numbers not relatively prime."); + + if (x[0] < 0) + { + x[0] += m; + } + + return x[0]; + } + +// private static BigInteger MQuoteB = One.ShiftLeft(32); +// private static BigInteger MQuoteBSub1 = MQuoteB.Subtract(One); + + /** + * Calculate mQuote = -m^(-1) mod b with b = 2^32 (32 = word size) + */ + private long GetMQuote() + { + Debug.Assert(this.sign > 0); + + if (mQuote != -1) + { + return mQuote; // already calculated + } + + if (magnitude.Length == 0 || (magnitude[magnitude.Length - 1] & 1) == 0) + { + return -1; // not for even numbers + } + + long v = (((~this.magnitude[this.magnitude.Length - 1]) | 1) & 0xffffffffL); + mQuote = FastModInverse(v, 0x100000000L); + + return mQuote; + } + + /** + * Montgomery multiplication: a = x * y * R^(-1) mod m + * <br/> + * Based algorithm 14.36 of Handbook of Applied Cryptography. + * <br/> + * <li> m, x, y should have length n </li> + * <li> a should have length (n + 1) </li> + * <li> b = 2^32, R = b^n </li> + * <br/> + * The result is put in x + * <br/> + * NOTE: the indices of x, y, m, a different in HAC and in Java + */ + private static void MultiplyMonty( + int[] a, + int[] x, + int[] y, + int[] m, + long mQuote) + // mQuote = -m^(-1) mod b + { + if (m.Length == 1) + { + x[0] = (int)MultiplyMontyNIsOne((uint)x[0], (uint)y[0], (uint)m[0], (ulong)mQuote); + return; + } + + int n = m.Length; + int nMinus1 = n - 1; + long y_0 = y[nMinus1] & IMASK; + + // 1. a = 0 (Notation: a = (a_{n} a_{n-1} ... a_{0})_{b} ) + Array.Clear(a, 0, n + 1); + + // 2. for i from 0 to (n - 1) do the following: + for (int i = n; i > 0; i--) + { + long x_i = x[i - 1] & IMASK; + + // 2.1 u = ((a[0] + (x[i] * y[0]) * mQuote) mod b + long u = ((((a[n] & IMASK) + ((x_i * y_0) & IMASK)) & IMASK) * mQuote) & IMASK; + + // 2.2 a = (a + x_i * y + u * m) / b + long prod1 = x_i * y_0; + long prod2 = u * (m[nMinus1] & IMASK); + long tmp = (a[n] & IMASK) + (prod1 & IMASK) + (prod2 & IMASK); + long carry = (long)((ulong)prod1 >> 32) + (long)((ulong)prod2 >> 32) + (long)((ulong)tmp >> 32); + for (int j = nMinus1; j > 0; j--) + { + prod1 = x_i * (y[j - 1] & IMASK); + prod2 = u * (m[j - 1] & IMASK); + tmp = (a[j] & IMASK) + (prod1 & IMASK) + (prod2 & IMASK) + (carry & IMASK); + carry = (long)((ulong)carry >> 32) + (long)((ulong)prod1 >> 32) + + (long)((ulong)prod2 >> 32) + (long)((ulong)tmp >> 32); + a[j + 1] = (int)tmp; // division by b + } + carry += (a[0] & IMASK); + a[1] = (int)carry; + a[0] = (int)((ulong)carry >> 32); // OJO!!!!! + } + + // 3. if x >= m the x = x - m + if (CompareTo(0, a, 0, m) >= 0) + { + Subtract(0, a, 0, m); + } + + // put the result in x + Array.Copy(a, 1, x, 0, n); + } + + private static uint MultiplyMontyNIsOne( + uint x, + uint y, + uint m, + ulong mQuote) + { + ulong um = m; + ulong prod1 = (ulong)x * (ulong)y; + ulong u = (prod1 * mQuote) & UIMASK; + ulong prod2 = u * um; + ulong tmp = (prod1 & UIMASK) + (prod2 & UIMASK); + ulong carry = (prod1 >> 32) + (prod2 >> 32) + (tmp >> 32); + + if (carry > um) + { + carry -= um; + } + + return (uint)(carry & UIMASK); + } + + public BigInteger Multiply( + BigInteger val) + { + if (sign == 0 || val.sign == 0) + return Zero; + + if (val.QuickPow2Check()) // val is power of two + { + BigInteger result = this.ShiftLeft(val.Abs().BitLength - 1); + return val.sign > 0 ? result : result.Negate(); + } + + if (this.QuickPow2Check()) // this is power of two + { + BigInteger result = val.ShiftLeft(this.Abs().BitLength - 1); + return this.sign > 0 ? result : result.Negate(); + } + + int resLength = (this.BitLength + val.BitLength) / BitsPerInt + 1; + int[] res = new int[resLength]; + + if (val == this) + { + Square(res, this.magnitude); + } + else + { + Multiply(res, this.magnitude, val.magnitude); + } + + return new BigInteger(sign * val.sign, res, true); + } + + public BigInteger Negate() + { + if (sign == 0) + return this; + + return new BigInteger(-sign, magnitude, false); + } + + public BigInteger NextProbablePrime() + { + if (sign < 0) + throw new ArithmeticException("Cannot be called on value < 0"); + + if (CompareTo(Two) < 0) + return Two; + + BigInteger n = Inc().SetBit(0); + + while (!n.CheckProbablePrime(100, RandomSource)) + { + n = n.Add(Two); + } + + return n; + } + + public BigInteger Not() + { + return Inc().Negate(); + } + + public BigInteger Pow(int exp) + { + if (exp < 0) + { + throw new ArithmeticException("Negative exponent"); + } + + if (exp == 0) + { + return One; + } + + if (sign == 0 || Equals(One)) + { + return this; + } + + BigInteger y = One; + BigInteger z = this; + + for (;;) + { + if ((exp & 0x1) == 1) + { + y = y.Multiply(z); + } + exp >>= 1; + if (exp == 0) break; + z = z.Multiply(z); + } + + return y; + } + + public static BigInteger ProbablePrime( + int bitLength, + Random random) + { + return new BigInteger(bitLength, 100, random); + } + + private int Remainder( + int m) + { + Debug.Assert(m > 0); + + long acc = 0; + for (int pos = 0; pos < magnitude.Length; ++pos) + { + long posVal = (uint) magnitude[pos]; + acc = (acc << 32 | posVal) % m; + } + + return (int) acc; + } + + /** + * return x = x % y - done in place (y value preserved) + */ + private int[] Remainder( + int[] x, + int[] y) + { + int xStart = 0; + while (xStart < x.Length && x[xStart] == 0) + { + ++xStart; + } + + int yStart = 0; + while (yStart < y.Length && y[yStart] == 0) + { + ++yStart; + } + + Debug.Assert(yStart < y.Length); + + int xyCmp = CompareNoLeadingZeroes(xStart, x, yStart, y); + + if (xyCmp > 0) + { + int yBitLength = calcBitLength(yStart, y); + int xBitLength = calcBitLength(xStart, x); + int shift = xBitLength - yBitLength; + + int[] c; + int cStart = 0; + int cBitLength = yBitLength; + if (shift > 0) + { + c = ShiftLeft(y, shift); + cBitLength += shift; + Debug.Assert(c[0] != 0); + } + else + { + int len = y.Length - yStart; + c = new int[len]; + Array.Copy(y, yStart, c, 0, len); + } + + for (;;) + { + if (cBitLength < xBitLength + || CompareNoLeadingZeroes(xStart, x, cStart, c) >= 0) + { + Subtract(xStart, x, cStart, c); + + while (x[xStart] == 0) + { + if (++xStart == x.Length) + return x; + } + + //xBitLength = calcBitLength(xStart, x); + xBitLength = 32 * (x.Length - xStart - 1) + BitLen(x[xStart]); + + if (xBitLength <= yBitLength) + { + if (xBitLength < yBitLength) + return x; + + xyCmp = CompareNoLeadingZeroes(xStart, x, yStart, y); + + if (xyCmp <= 0) + break; + } + } + + shift = cBitLength - xBitLength; + + // NB: The case where c[cStart] is 1-bit is harmless + if (shift == 1) + { + uint firstC = (uint) c[cStart] >> 1; + uint firstX = (uint) x[xStart]; + if (firstC > firstX) + ++shift; + } + + if (shift < 2) + { + ShiftRightOneInPlace(cStart, c); + --cBitLength; + } + else + { + ShiftRightInPlace(cStart, c, shift); + cBitLength -= shift; + } + + //cStart = c.Length - ((cBitLength + 31) / 32); + while (c[cStart] == 0) + { + ++cStart; + } + } + } + + if (xyCmp == 0) + { + Array.Clear(x, xStart, x.Length - xStart); + } + + return x; + } + + public BigInteger Remainder( + BigInteger n) + { + if (n.sign == 0) + throw new ArithmeticException("Division by zero error"); + + if (this.sign == 0) + return Zero; + + // For small values, use fast remainder method + if (n.magnitude.Length == 1) + { + int val = n.magnitude[0]; + + if (val > 0) + { + if (val == 1) + return Zero; + + // TODO Make this func work on uint, and handle val == 1? + int rem = Remainder(val); + + return rem == 0 + ? Zero + : new BigInteger(sign, new int[]{ rem }, false); + } + } + + if (CompareNoLeadingZeroes(0, magnitude, 0, n.magnitude) < 0) + return this; + + int[] result; + if (n.QuickPow2Check()) // n is power of two + { + // TODO Move before small values branch above? + result = LastNBits(n.Abs().BitLength - 1); + } + else + { + result = (int[]) this.magnitude.Clone(); + result = Remainder(result, n.magnitude); + } + + return new BigInteger(sign, result, true); + } + + private int[] LastNBits( + int n) + { + if (n < 1) + return ZeroMagnitude; + + int numWords = (n + BitsPerInt - 1) / BitsPerInt; + numWords = System.Math.Min(numWords, this.magnitude.Length); + int[] result = new int[numWords]; + + Array.Copy(this.magnitude, this.magnitude.Length - numWords, result, 0, numWords); + + int hiBits = n % 32; + if (hiBits != 0) + { + result[0] &= ~(-1 << hiBits); + } + + return result; + } + + /** + * do a left shift - this returns a new array. + */ + private static int[] ShiftLeft( + int[] mag, + int n) + { + int nInts = (int)((uint)n >> 5); + int nBits = n & 0x1f; + int magLen = mag.Length; + int[] newMag; + + if (nBits == 0) + { + newMag = new int[magLen + nInts]; + mag.CopyTo(newMag, 0); + } + else + { + int i = 0; + int nBits2 = 32 - nBits; + int highBits = (int)((uint)mag[0] >> nBits2); + + if (highBits != 0) + { + newMag = new int[magLen + nInts + 1]; + newMag[i++] = highBits; + } + else + { + newMag = new int[magLen + nInts]; + } + + int m = mag[0]; + for (int j = 0; j < magLen - 1; j++) + { + int next = mag[j + 1]; + + newMag[i++] = (m << nBits) | (int)((uint)next >> nBits2); + m = next; + } + + newMag[i] = mag[magLen - 1] << nBits; + } + + return newMag; + } + + public BigInteger ShiftLeft( + int n) + { + if (sign == 0 || magnitude.Length == 0) + return Zero; + + if (n == 0) + return this; + + if (n < 0) + return ShiftRight(-n); + + BigInteger result = new BigInteger(sign, ShiftLeft(magnitude, n), true); + + if (this.nBits != -1) + { + result.nBits = sign > 0 + ? this.nBits + : this.nBits + n; + } + + if (this.nBitLength != -1) + { + result.nBitLength = this.nBitLength + n; + } + + return result; + } + + /** + * do a right shift - this does it in place. + */ + private static void ShiftRightInPlace( + int start, + int[] mag, + int n) + { + int nInts = (int)((uint)n >> 5) + start; + int nBits = n & 0x1f; + int magEnd = mag.Length - 1; + + if (nInts != start) + { + int delta = (nInts - start); + + for (int i = magEnd; i >= nInts; i--) + { + mag[i] = mag[i - delta]; + } + for (int i = nInts - 1; i >= start; i--) + { + mag[i] = 0; + } + } + + if (nBits != 0) + { + int nBits2 = 32 - nBits; + int m = mag[magEnd]; + + for (int i = magEnd; i > nInts; --i) + { + int next = mag[i - 1]; + + mag[i] = (int)((uint)m >> nBits) | (next << nBits2); + m = next; + } + + mag[nInts] = (int)((uint)mag[nInts] >> nBits); + } + } + + /** + * do a right shift by one - this does it in place. + */ + private static void ShiftRightOneInPlace( + int start, + int[] mag) + { + int i = mag.Length; + int m = mag[i - 1]; + + while (--i > start) + { + int next = mag[i - 1]; + mag[i] = ((int)((uint)m >> 1)) | (next << 31); + m = next; + } + + mag[start] = (int)((uint)mag[start] >> 1); + } + + public BigInteger ShiftRight( + int n) + { + if (n == 0) + return this; + + if (n < 0) + return ShiftLeft(-n); + + if (n >= BitLength) + return (this.sign < 0 ? One.Negate() : Zero); + +// int[] res = (int[]) this.magnitude.Clone(); +// +// ShiftRightInPlace(0, res, n); +// +// return new BigInteger(this.sign, res, true); + + int resultLength = (BitLength - n + 31) >> 5; + int[] res = new int[resultLength]; + + int numInts = n >> 5; + int numBits = n & 31; + + if (numBits == 0) + { + Array.Copy(this.magnitude, 0, res, 0, res.Length); + } + else + { + int numBits2 = 32 - numBits; + + int magPos = this.magnitude.Length - 1 - numInts; + for (int i = resultLength - 1; i >= 0; --i) + { + res[i] = (int)((uint) this.magnitude[magPos--] >> numBits); + + if (magPos >= 0) + { + res[i] |= this.magnitude[magPos] << numBits2; + } + } + } + + Debug.Assert(res[0] != 0); + + return new BigInteger(this.sign, res, false); + } + + public int SignValue + { + get { return sign; } + } + + /** + * returns x = x - y - we assume x is >= y + */ + private static int[] Subtract( + int xStart, + int[] x, + int yStart, + int[] y) + { + Debug.Assert(yStart < y.Length); + Debug.Assert(x.Length - xStart >= y.Length - yStart); + + int iT = x.Length; + int iV = y.Length; + long m; + int borrow = 0; + + do + { + m = (x[--iT] & IMASK) - (y[--iV] & IMASK) + borrow; + x[iT] = (int) m; + +// borrow = (m < 0) ? -1 : 0; + borrow = (int)(m >> 63); + } + while (iV > yStart); + + if (borrow != 0) + { + while (--x[--iT] == -1) + { + } + } + + return x; + } + + public BigInteger Subtract( + BigInteger n) + { + if (n.sign == 0) + return this; + + if (this.sign == 0) + return n.Negate(); + + if (this.sign != n.sign) + return Add(n.Negate()); + + int compare = CompareNoLeadingZeroes(0, magnitude, 0, n.magnitude); + if (compare == 0) + return Zero; + + BigInteger bigun, lilun; + if (compare < 0) + { + bigun = n; + lilun = this; + } + else + { + bigun = this; + lilun = n; + } + + return new BigInteger(this.sign * compare, doSubBigLil(bigun.magnitude, lilun.magnitude), true); + } + + private static int[] doSubBigLil( + int[] bigMag, + int[] lilMag) + { + int[] res = (int[]) bigMag.Clone(); + + return Subtract(0, res, 0, lilMag); + } + + public byte[] ToByteArray() + { + return ToByteArray(false); + } + + public byte[] ToByteArrayUnsigned() + { + return ToByteArray(true); + } + + private byte[] ToByteArray( + bool unsigned) + { + if (sign == 0) + return unsigned ? ZeroEncoding : new byte[1]; + + int nBits = (unsigned && sign > 0) + ? BitLength + : BitLength + 1; + + int nBytes = GetByteLength(nBits); + byte[] bytes = new byte[nBytes]; + + int magIndex = magnitude.Length; + int bytesIndex = bytes.Length; + + if (sign > 0) + { + while (magIndex > 1) + { + uint mag = (uint) magnitude[--magIndex]; + bytes[--bytesIndex] = (byte) mag; + bytes[--bytesIndex] = (byte)(mag >> 8); + bytes[--bytesIndex] = (byte)(mag >> 16); + bytes[--bytesIndex] = (byte)(mag >> 24); + } + + uint lastMag = (uint) magnitude[0]; + while (lastMag > byte.MaxValue) + { + bytes[--bytesIndex] = (byte) lastMag; + lastMag >>= 8; + } + + bytes[--bytesIndex] = (byte) lastMag; + } + else // sign < 0 + { + bool carry = true; + + while (magIndex > 1) + { + uint mag = ~((uint) magnitude[--magIndex]); + + if (carry) + { + carry = (++mag == uint.MinValue); + } + + bytes[--bytesIndex] = (byte) mag; + bytes[--bytesIndex] = (byte)(mag >> 8); + bytes[--bytesIndex] = (byte)(mag >> 16); + bytes[--bytesIndex] = (byte)(mag >> 24); + } + + uint lastMag = (uint) magnitude[0]; + + if (carry) + { + // Never wraps because magnitude[0] != 0 + --lastMag; + } + + while (lastMag > byte.MaxValue) + { + bytes[--bytesIndex] = (byte) ~lastMag; + lastMag >>= 8; + } + + bytes[--bytesIndex] = (byte) ~lastMag; + + if (bytesIndex > 0) + { + bytes[--bytesIndex] = byte.MaxValue; + } + } + + return bytes; + } + + public override string ToString() + { + return ToString(10); + } + + public string ToString( + int radix) + { + // TODO Make this method work for other radices (ideally 2 <= radix <= 16) + + switch (radix) + { + case 2: + case 10: + case 16: + break; + default: + throw new FormatException("Only bases 2, 10, 16 are allowed"); + } + + // NB: Can only happen to internally managed instances + if (magnitude == null) + return "null"; + + if (sign == 0) + return "0"; + + Debug.Assert(magnitude.Length > 0); + + StringBuilder sb = new StringBuilder(); + + if (radix == 16) + { + sb.Append(magnitude[0].ToString("x")); + + for (int i = 1; i < magnitude.Length; i++) + { + sb.Append(magnitude[i].ToString("x8")); + } + } + else if (radix == 2) + { + sb.Append('1'); + + for (int i = BitLength - 2; i >= 0; --i) + { + sb.Append(TestBit(i) ? '1' : '0'); + } + } + else + { + // This is algorithm 1a from chapter 4.4 in Seminumerical Algorithms, slow but it works + IList S = Platform.CreateArrayList(); + BigInteger bs = ValueOf(radix); + + // The sign is handled separatly. + // Notice however that for this to work, radix 16 _MUST_ be a special case, + // unless we want to enter a recursion well. In their infinite wisdom, why did not + // the Sun engineers made a c'tor for BigIntegers taking a BigInteger as parameter? + // (Answer: Becuase Sun's BigIntger is clonable, something bouncycastle's isn't.) +// BigInteger u = new BigInteger(Abs().ToString(16), 16); + BigInteger u = this.Abs(); + BigInteger b; + + while (u.sign != 0) + { + b = u.Mod(bs); + if (b.sign == 0) + { + S.Add("0"); + } + else + { + // see how to interact with different bases + S.Add(b.magnitude[0].ToString("d")); + } + u = u.Divide(bs); + } + + // Then pop the stack + for (int i = S.Count - 1; i >= 0; --i) + { + sb.Append((string)S[i]); + } + } + + string s = sb.ToString(); + + Debug.Assert(s.Length > 0); + + // Strip leading zeros. (We know this number is not all zeroes though) + if (s[0] == '0') + { + int nonZeroPos = 0; + while (s[++nonZeroPos] == '0') {} + + s = s.Substring(nonZeroPos); + } + + if (sign == -1) + { + s = "-" + s; + } + + return s; + } + + private static BigInteger createUValueOf( + ulong value) + { + int msw = (int)(value >> 32); + int lsw = (int)value; + + if (msw != 0) + return new BigInteger(1, new int[] { msw, lsw }, false); + + if (lsw != 0) + { + BigInteger n = new BigInteger(1, new int[] { lsw }, false); + // Check for a power of two + if ((lsw & -lsw) == lsw) + { + n.nBits = 1; + } + return n; + } + + return Zero; + } + + private static BigInteger createValueOf( + long value) + { + if (value < 0) + { + if (value == long.MinValue) + return createValueOf(~value).Not(); + + return createValueOf(-value).Negate(); + } + + return createUValueOf((ulong)value); + +// // store value into a byte array +// byte[] b = new byte[8]; +// for (int i = 0; i < 8; i++) +// { +// b[7 - i] = (byte)value; +// value >>= 8; +// } +// +// return new BigInteger(b); + } + + public static BigInteger ValueOf( + long value) + { + switch (value) + { + case 0: + return Zero; + case 1: + return One; + case 2: + return Two; + case 3: + return Three; + case 10: + return Ten; + } + + return createValueOf(value); + } + + public int GetLowestSetBit() + { + if (this.sign == 0) + return -1; + + int w = magnitude.Length; + + while (--w > 0) + { + if (magnitude[w] != 0) + break; + } + + int word = (int) magnitude[w]; + Debug.Assert(word != 0); + + int b = (word & 0x0000FFFF) == 0 + ? (word & 0x00FF0000) == 0 + ? 7 + : 15 + : (word & 0x000000FF) == 0 + ? 23 + : 31; + + while (b > 0) + { + if ((word << b) == int.MinValue) + break; + + b--; + } + + return ((magnitude.Length - w) * 32 - (b + 1)); + } + + public bool TestBit( + int n) + { + if (n < 0) + throw new ArithmeticException("Bit position must not be negative"); + + if (sign < 0) + return !Not().TestBit(n); + + int wordNum = n / 32; + if (wordNum >= magnitude.Length) + return false; + + int word = magnitude[magnitude.Length - 1 - wordNum]; + return ((word >> (n % 32)) & 1) > 0; + } + + public BigInteger Or( + BigInteger value) + { + if (this.sign == 0) + return value; + + if (value.sign == 0) + return this; + + int[] aMag = this.sign > 0 + ? this.magnitude + : Add(One).magnitude; + + int[] bMag = value.sign > 0 + ? value.magnitude + : value.Add(One).magnitude; + + bool resultNeg = sign < 0 || value.sign < 0; + int resultLength = System.Math.Max(aMag.Length, bMag.Length); + int[] resultMag = new int[resultLength]; + + int aStart = resultMag.Length - aMag.Length; + int bStart = resultMag.Length - bMag.Length; + + for (int i = 0; i < resultMag.Length; ++i) + { + int aWord = i >= aStart ? aMag[i - aStart] : 0; + int bWord = i >= bStart ? bMag[i - bStart] : 0; + + if (this.sign < 0) + { + aWord = ~aWord; + } + + if (value.sign < 0) + { + bWord = ~bWord; + } + + resultMag[i] = aWord | bWord; + + if (resultNeg) + { + resultMag[i] = ~resultMag[i]; + } + } + + BigInteger result = new BigInteger(1, resultMag, true); + + // TODO Optimise this case + if (resultNeg) + { + result = result.Not(); + } + + return result; + } + + public BigInteger Xor( + BigInteger value) + { + if (this.sign == 0) + return value; + + if (value.sign == 0) + return this; + + int[] aMag = this.sign > 0 + ? this.magnitude + : Add(One).magnitude; + + int[] bMag = value.sign > 0 + ? value.magnitude + : value.Add(One).magnitude; + + // TODO Can just replace with sign != value.sign? + bool resultNeg = (sign < 0 && value.sign >= 0) || (sign >= 0 && value.sign < 0); + int resultLength = System.Math.Max(aMag.Length, bMag.Length); + int[] resultMag = new int[resultLength]; + + int aStart = resultMag.Length - aMag.Length; + int bStart = resultMag.Length - bMag.Length; + + for (int i = 0; i < resultMag.Length; ++i) + { + int aWord = i >= aStart ? aMag[i - aStart] : 0; + int bWord = i >= bStart ? bMag[i - bStart] : 0; + + if (this.sign < 0) + { + aWord = ~aWord; + } + + if (value.sign < 0) + { + bWord = ~bWord; + } + + resultMag[i] = aWord ^ bWord; + + if (resultNeg) + { + resultMag[i] = ~resultMag[i]; + } + } + + BigInteger result = new BigInteger(1, resultMag, true); + + // TODO Optimise this case + if (resultNeg) + { + result = result.Not(); + } + + return result; + } + + public BigInteger SetBit( + int n) + { + if (n < 0) + throw new ArithmeticException("Bit address less than zero"); + + if (TestBit(n)) + return this; + + // TODO Handle negative values and zero + if (sign > 0 && n < (BitLength - 1)) + return FlipExistingBit(n); + + return Or(One.ShiftLeft(n)); + } + + public BigInteger ClearBit( + int n) + { + if (n < 0) + throw new ArithmeticException("Bit address less than zero"); + + if (!TestBit(n)) + return this; + + // TODO Handle negative values + if (sign > 0 && n < (BitLength - 1)) + return FlipExistingBit(n); + + return AndNot(One.ShiftLeft(n)); + } + + public BigInteger FlipBit( + int n) + { + if (n < 0) + throw new ArithmeticException("Bit address less than zero"); + + // TODO Handle negative values and zero + if (sign > 0 && n < (BitLength - 1)) + return FlipExistingBit(n); + + return Xor(One.ShiftLeft(n)); + } + + private BigInteger FlipExistingBit( + int n) + { + Debug.Assert(sign > 0); + Debug.Assert(n >= 0); + Debug.Assert(n < BitLength - 1); + + int[] mag = (int[]) this.magnitude.Clone(); + mag[mag.Length - 1 - (n >> 5)] ^= (1 << (n & 31)); // Flip bit + //mag[mag.Length - 1 - (n / 32)] ^= (1 << (n % 32)); + return new BigInteger(this.sign, mag, false); + } + } +} diff --git a/Crypto/src/math/ec/ECAlgorithms.cs b/Crypto/src/math/ec/ECAlgorithms.cs new file mode 100644
index 000000000..be4fd1b14 --- /dev/null +++ b/Crypto/src/math/ec/ECAlgorithms.cs
@@ -0,0 +1,93 @@ +using System; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Math.EC +{ + public class ECAlgorithms + { + public static ECPoint SumOfTwoMultiplies(ECPoint P, BigInteger a, + ECPoint Q, BigInteger b) + { + ECCurve c = P.Curve; + if (!c.Equals(Q.Curve)) + throw new ArgumentException("P and Q must be on same curve"); + + // Point multiplication for Koblitz curves (using WTNAF) beats Shamir's trick + if (c is F2mCurve) + { + F2mCurve f2mCurve = (F2mCurve) 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) + * + * 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 + */ + public static ECPoint ShamirsTrick( + ECPoint P, + BigInteger k, + ECPoint Q, + BigInteger l) + { + if (!P.Curve.Equals(Q.Curve)) + throw new ArgumentException("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 = System.Math.Max(k.BitLength, l.BitLength); + ECPoint Z = P.Add(Q); + ECPoint R = P.Curve.Infinity; + + 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/Crypto/src/math/ec/ECCurve.cs b/Crypto/src/math/ec/ECCurve.cs new file mode 100644
index 000000000..4dd5e74e2 --- /dev/null +++ b/Crypto/src/math/ec/ECCurve.cs
@@ -0,0 +1,661 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Math.EC.Abc; + +namespace Org.BouncyCastle.Math.EC +{ + /// <remarks>Base class for an elliptic curve.</remarks> + public abstract class ECCurve + { + internal ECFieldElement a, b; + + public abstract int FieldSize { get; } + public abstract ECFieldElement FromBigInteger(BigInteger x); + public abstract ECPoint CreatePoint(BigInteger x, BigInteger y, bool withCompression); + public abstract ECPoint DecodePoint(byte[] encoded); + public abstract ECPoint Infinity { get; } + + public ECFieldElement A + { + get { return a; } + } + + public ECFieldElement B + { + get { return b; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + ECCurve other = obj as ECCurve; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + ECCurve other) + { + return a.Equals(other.a) && b.Equals(other.b); + } + + public override int GetHashCode() + { + return a.GetHashCode() ^ b.GetHashCode(); + } + } + + public abstract class ECCurveBase : ECCurve + { + protected internal ECCurveBase() + { + } + + protected internal 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 override ECPoint DecodePoint( + byte[] encoded) + { + ECPoint p = null; + int expectedLength = (FieldSize + 7) / 8; + + switch (encoded[0]) + { + case 0x00: // infinity + { + if (encoded.Length != 1) + throw new ArgumentException("Incorrect length for infinity encoding", "encoded"); + + p = Infinity; + break; + } + + case 0x02: // compressed + case 0x03: // compressed + { + if (encoded.Length != (expectedLength + 1)) + throw new ArgumentException("Incorrect length for compressed encoding", "encoded"); + + int yTilde = encoded[0] & 1; + BigInteger X1 = new BigInteger(1, encoded, 1, encoded.Length - 1); + + p = DecompressPoint(yTilde, X1); + break; + } + + case 0x04: // uncompressed + case 0x06: // hybrid + case 0x07: // hybrid + { + if (encoded.Length != (2 * expectedLength + 1)) + throw new ArgumentException("Incorrect length for uncompressed/hybrid encoding", "encoded"); + + BigInteger X1 = new BigInteger(1, encoded, 1, expectedLength); + BigInteger Y1 = new BigInteger(1, encoded, 1 + expectedLength, expectedLength); + + p = CreatePoint(X1, Y1, false); + break; + } + + default: + throw new FormatException("Invalid point encoding " + encoded[0]); + } + + return p; + } + } + + /** + * Elliptic curve over Fp + */ + public class FpCurve : ECCurveBase + { + private readonly BigInteger q; + private readonly FpPoint infinity; + + public FpCurve(BigInteger q, BigInteger a, BigInteger b) + { + this.q = q; + this.a = FromBigInteger(a); + this.b = FromBigInteger(b); + this.infinity = new FpPoint(this, null, null); + } + + public BigInteger Q + { + get { return q; } + } + + public override ECPoint Infinity + { + get { return infinity; } + } + + public override int FieldSize + { + get { return q.BitLength; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new FpFieldElement(this.q, x); + } + + public override ECPoint CreatePoint( + BigInteger X1, + BigInteger Y1, + bool withCompression) + { + // TODO Validation of X1, Y1? + return new FpPoint( + this, + FromBigInteger(X1), + FromBigInteger(Y1), + withCompression); + } + + protected internal override 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 ArithmeticException("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 FpPoint(this, x, beta, true); + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + FpCurve other = obj as FpCurve; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + FpCurve other) + { + return base.Equals(other) && q.Equals(other.q); + } + + public override int GetHashCode() + { + return base.GetHashCode() ^ q.GetHashCode(); + } + } + + /** + * 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 class F2mCurve : ECCurveBase + { + /** + * The exponent <code>m</code> of <code>F<sub>2<sup>m</sup></sub></code>. + */ + private readonly 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 readonly 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 readonly 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 readonly int k3; + + /** + * The order of the base point of the curve. + */ + private readonly BigInteger n; + + /** + * The cofactor of the curve. + */ + private readonly BigInteger h; + + /** + * The point at infinity on this curve. + */ + private readonly F2mPoint infinity; + + /** + * The parameter <code>&#956;</code> of the elliptic curve if this is + * a Koblitz curve. + */ + private sbyte 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 F2mCurve( + 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 F2mCurve( + 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 F2mCurve( + 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 F2mCurve( + 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; + this.infinity = new F2mPoint(this, null, null); + + if (k1 == 0) + throw new ArgumentException("k1 must be > 0"); + + if (k2 == 0) + { + if (k3 != 0) + throw new ArgumentException("k3 must be 0 if k2 == 0"); + } + else + { + if (k2 <= k1) + throw new ArgumentException("k2 must be > k1"); + + if (k3 <= k2) + throw new ArgumentException("k3 must be > k2"); + } + + this.a = FromBigInteger(a); + this.b = FromBigInteger(b); + } + + public override ECPoint Infinity + { + get { return infinity; } + } + + public override int FieldSize + { + get { return m; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new F2mFieldElement(this.m, this.k1, this.k2, this.k3, x); + } + + /** + * Returns true if this is a Koblitz curve (ABC curve). + * @return true if this is a Koblitz curve (ABC curve), false otherwise + */ + public bool IsKoblitz + { + get + { + return n != null && h != null + && (a.ToBigInteger().Equals(BigInteger.Zero) + || a.ToBigInteger().Equals(BigInteger.One)) + && b.ToBigInteger().Equals(BigInteger.One); + } + } + + /** + * Returns the parameter <code>&#956;</code> of the elliptic curve. + * @return <code>&#956;</code> of the elliptic curve. + * @throws ArgumentException if the given ECCurve is not a + * Koblitz curve. + */ + internal sbyte GetMu() + { + if (mu == 0) + { + lock (this) + { + 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. + */ + internal BigInteger[] GetSi() + { + if (si == null) + { + lock (this) + { + if (si == null) + { + si = Tnaf.GetSi(this); + } + } + } + return si; + } + + public override ECPoint CreatePoint( + BigInteger X1, + BigInteger Y1, + bool withCompression) + { + // TODO Validation of X1, Y1? + return new F2mPoint( + this, + FromBigInteger(X1), + FromBigInteger(Y1), + withCompression); + } + + protected internal override ECPoint DecompressPoint( + int yTilde, + BigInteger X1) + { + ECFieldElement xp = FromBigInteger(X1); + ECFieldElement yp = null; + if (xp.ToBigInteger().SignValue == 0) + { + yp = (F2mFieldElement)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 ArithmeticException("Invalid point compression"); + + int zBit = z.ToBigInteger().TestBit(0) ? 1 : 0; + if (zBit != yTilde) + { + z = z.Add(FromBigInteger(BigInteger.One)); + } + + yp = xp.Multiply(z); + } + + return new F2mPoint(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) + { + if (beta.ToBigInteger().SignValue == 0) + { + return FromBigInteger(BigInteger.Zero); + } + + ECFieldElement z = null; + ECFieldElement gamma = FromBigInteger(BigInteger.Zero); + + while (gamma.ToBigInteger().SignValue == 0) + { + ECFieldElement t = FromBigInteger(new BigInteger(m, new Random())); + z = FromBigInteger(BigInteger.Zero); + + 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().SignValue != 0) + { + return null; + } + gamma = z.Square().Add(z); + } + return z; + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + F2mCurve other = obj as F2mCurve; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + F2mCurve other) + { + return m == other.m + && k1 == other.k1 + && k2 == other.k2 + && k3 == other.k3 + && base.Equals(other); + } + + public override int GetHashCode() + { + return base.GetHashCode() ^ m ^ k1 ^ k2 ^ k3; + } + + public int M + { + get { return m; } + } + + /** + * Return true if curve uses a Trinomial basis. + * + * @return true if curve Trinomial, false otherwise. + */ + public bool IsTrinomial() + { + return k2 == 0 && k3 == 0; + } + + public int K1 + { + get { return k1; } + } + + public int K2 + { + get { return k2; } + } + + public int K3 + { + get { return k3; } + } + + public BigInteger N + { + get { return n; } + } + + public BigInteger H + { + get { return h; } + } + } +} diff --git a/Crypto/src/math/ec/ECFieldElement.cs b/Crypto/src/math/ec/ECFieldElement.cs new file mode 100644
index 000000000..5235c6c0e --- /dev/null +++ b/Crypto/src/math/ec/ECFieldElement.cs
@@ -0,0 +1,1253 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Math.EC +{ + public abstract class ECFieldElement + { + public abstract BigInteger ToBigInteger(); + public abstract string FieldName { get; } + public abstract int FieldSize { get; } + 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 override bool Equals( + object obj) + { + if (obj == this) + return true; + + ECFieldElement other = obj as ECFieldElement; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + ECFieldElement other) + { + return ToBigInteger().Equals(other.ToBigInteger()); + } + + public override int GetHashCode() + { + return ToBigInteger().GetHashCode(); + } + + public override string ToString() + { + return this.ToBigInteger().ToString(2); + } + } + + public class FpFieldElement + : ECFieldElement + { + private readonly BigInteger q, x; + + public FpFieldElement( + BigInteger q, + BigInteger x) + { + if (x.CompareTo(q) >= 0) + throw new ArgumentException("x value too large in field element"); + + this.q = q; + this.x = x; + } + + public override BigInteger ToBigInteger() + { + return x; + } + + /** + * return the field name for this field. + * + * @return the string "Fp". + */ + public override string FieldName + { + get { return "Fp"; } + } + + public override int FieldSize + { + get { return q.BitLength; } + } + + public BigInteger Q + { + get { return q; } + } + + public override ECFieldElement Add( + ECFieldElement b) + { + return new FpFieldElement(q, x.Add(b.ToBigInteger()).Mod(q)); + } + + public override ECFieldElement Subtract( + ECFieldElement b) + { + return new FpFieldElement(q, x.Subtract(b.ToBigInteger()).Mod(q)); + } + + public override ECFieldElement Multiply( + ECFieldElement b) + { + return new FpFieldElement(q, x.Multiply(b.ToBigInteger()).Mod(q)); + } + + public override ECFieldElement Divide( + ECFieldElement b) + { + return new FpFieldElement(q, x.Multiply(b.ToBigInteger().ModInverse(q)).Mod(q)); + } + + public override ECFieldElement Negate() + { + return new FpFieldElement(q, x.Negate().Mod(q)); + } + + public override ECFieldElement Square() + { + return new FpFieldElement(q, x.Multiply(x).Mod(q)); + } + + public override ECFieldElement Invert() + { + return new FpFieldElement(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 override ECFieldElement Sqrt() + { + if (!q.TestBit(0)) + throw Platform.CreateNotImplementedException("even value of q"); + + // p mod 4 == 3 + if (q.TestBit(1)) + { + // TODO Can this be optimised (inline the Square?) + // z = g^(u+1) + p, p = 4u + 3 + ECFieldElement z = new FpFieldElement(q, x.ModPow(q.ShiftRight(2).Add(BigInteger.One), q)); + + return this.Equals(z.Square()) ? z : null; + } + + // p mod 4 == 1 + BigInteger qMinusOne = q.Subtract(BigInteger.One); + + BigInteger legendreExponent = qMinusOne.ShiftRight(1); + if (!(x.ModPow(legendreExponent, q).Equals(BigInteger.One))) + return null; + + BigInteger u = qMinusOne.ShiftRight(2); + BigInteger k = u.ShiftLeft(1).Add(BigInteger.One); + + BigInteger Q = this.x; + BigInteger fourQ = Q.ShiftLeft(2).Mod(q); + + BigInteger U, V; + do + { + Random rand = new Random(); + 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 = fastLucasSequence(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); + + Debug.Assert(V.Multiply(V).Mod(q).Equals(x)); + + return new FpFieldElement(q, V); + } + } + while (U.Equals(BigInteger.One) || U.Equals(qMinusOne)); + + return null; + + +// BigInteger qMinusOne = q.Subtract(BigInteger.One); +// +// BigInteger legendreExponent = qMinusOne.ShiftRight(1); +// if (!(x.ModPow(legendreExponent, q).Equals(BigInteger.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); +// BigInteger n2 = n1.Add(BigInteger.One); +// +// BigInteger wOne = WOne(r, x, q); +// BigInteger wSum = W(n1, wOne, q).Add(W(n2, wOne, q)).Mod(q); +// BigInteger twoR = r.ShiftLeft(1); +// +// BigInteger root = twoR.ModPow(q.Subtract(BigInteger.Two), q) +// .Multiply(x).Mod(q) +// .Multiply(wSum).Mod(q); +// +// return new FpFieldElement(q, root); + } + +// private static BigInteger W(BigInteger n, BigInteger wOne, BigInteger p) +// { +// if (n.Equals(BigInteger.One)) +// return wOne; +// +// bool isEven = !n.TestBit(0); +// n = n.ShiftRight(1); +// if (isEven) +// { +// BigInteger w = W(n, wOne, p); +// return w.Multiply(w).Subtract(BigInteger.Two).Mod(p); +// } +// BigInteger w1 = W(n.Add(BigInteger.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(BigInteger.Two), q)).Subtract(BigInteger.Two).Mod(p); +// } + + private static BigInteger[] fastLucasSequence( + BigInteger p, + BigInteger P, + BigInteger Q, + BigInteger k) + { + // TODO Research and apply "common-multiplicand multiplication here" + + int n = k.BitLength; + int s = k.GetLowestSetBit(); + + Debug.Assert(k.TestBit(s)); + + BigInteger Uh = BigInteger.One; + BigInteger Vl = BigInteger.Two; + BigInteger Vh = P; + BigInteger Ql = BigInteger.One; + BigInteger Qh = BigInteger.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 }; + } + +// private static BigInteger[] verifyLucasSequence( +// BigInteger p, +// BigInteger P, +// BigInteger Q, +// BigInteger k) +// { +// BigInteger[] actual = fastLucasSequence(p, P, Q, k); +// BigInteger[] plus1 = fastLucasSequence(p, P, Q, k.Add(BigInteger.One)); +// BigInteger[] plus2 = fastLucasSequence(p, P, Q, k.Add(BigInteger.Two)); +// +// BigInteger[] check = stepLucasSequence(p, P, Q, actual, plus1); +// +// Debug.Assert(check[0].Equals(plus2[0])); +// Debug.Assert(check[1].Equals(plus2[1])); +// +// return actual; +// } +// +// private static BigInteger[] stepLucasSequence( +// BigInteger p, +// BigInteger P, +// BigInteger Q, +// BigInteger[] backTwo, +// BigInteger[] backOne) +// { +// return new BigInteger[] +// { +// P.Multiply(backOne[0]).Subtract(Q.Multiply(backTwo[0])).Mod(p), +// P.Multiply(backOne[1]).Subtract(Q.Multiply(backTwo[1])).Mod(p) +// }; +// } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + FpFieldElement other = obj as FpFieldElement; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + FpFieldElement other) + { + return q.Equals(other.q) && base.Equals(other); + } + + public override int GetHashCode() + { + return q.GetHashCode() ^ base.GetHashCode(); + } + } + +// /** +// * 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 class F2mFieldElement +// : ECFieldElement +// { +// /** +// * Indicates gaussian normal basis representation (GNB). Number chosen +// * according to X9.62. GNB is not implemented at present. +// */ +// public const int Gnb = 1; +// +// /** +// * Indicates trinomial basis representation (Tpb). Number chosen +// * according to X9.62. +// */ +// public const int Tpb = 2; +// +// /** +// * Indicates pentanomial basis representation (Ppb). Number chosen +// * according to X9.62. +// */ +// public const 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 F2mFieldElement( +// int m, +// int k1, +// int k2, +// int k3, +// BigInteger x) +// : base(x) +// { +// if ((k2 == 0) && (k3 == 0)) +// { +// this.representation = Tpb; +// } +// else +// { +// if (k2 >= k3) +// throw new ArgumentException("k2 must be smaller than k3"); +// if (k2 <= 0) +// throw new ArgumentException("k2 must be larger than 0"); +// +// this.representation = Ppb; +// } +// +// if (x.SignValue < 0) +// throw new ArgumentException("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 F2mFieldElement( +// int m, +// int k, +// BigInteger x) +// : this(m, k, 0, 0, x) +// { +// // Set k1 to k, and set k2 and k3 to 0 +// } +// +// public override string FieldName +// { +// get { return "F2m"; } +// } +// +// /** +// * 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 ArgumentException 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 is F2mFieldElement) || !(b is F2mFieldElement)) +// { +// throw new ArgumentException("Field elements are not " +// + "both instances of F2mFieldElement"); +// } +// +// if ((a.x.SignValue < 0) || (b.x.SignValue < 0)) +// { +// throw new ArgumentException( +// "x value may not be negative"); +// } +// +// F2mFieldElement aF2m = (F2mFieldElement)a; +// F2mFieldElement bF2m = (F2mFieldElement)b; +// +// if ((aF2m.m != bF2m.m) || (aF2m.k1 != bF2m.k1) +// || (aF2m.k2 != bF2m.k2) || (aF2m.k3 != bF2m.k3)) +// { +// throw new ArgumentException("Field elements are not " +// + "elements of the same field F2m"); +// } +// +// if (aF2m.representation != bF2m.representation) +// { +// // Should never occur +// throw new ArgumentException( +// "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( +// 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 override ECFieldElement Add( +// ECFieldElement b) +// { +// // No check performed here for performance reasons. Instead the +// // elements involved are checked in ECPoint.F2m +// // checkFieldElements(this, b); +// if (b.x.SignValue == 0) +// return this; +// +// return new F2mFieldElement(this.m, this.k1, this.k2, this.k3, this.x.Xor(b.x)); +// } +// +// public override ECFieldElement Subtract( +// ECFieldElement b) +// { +// // Addition and subtraction are the same in F2m +// return Add(b); +// } +// +// public override ECFieldElement Multiply( +// 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); +// BigInteger az = this.x; +// BigInteger bz = b.x; +// BigInteger cz; +// +// // Compute c(z) = a(z) * b(z) mod f(z) +// if (az.TestBit(0)) +// { +// cz = bz; +// } +// else +// { +// cz = BigInteger.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 F2mFieldElement(m, this.k1, this.k2, this.k3, cz); +// } +// +// +// public override ECFieldElement Divide( +// ECFieldElement b) +// { +// // There may be more efficient implementations +// ECFieldElement bInv = b.Invert(); +// return Multiply(bInv); +// } +// +// public override ECFieldElement Negate() +// { +// // -x == x holds for all x in F2m +// return this; +// } +// +// public override ECFieldElement Square() +// { +// // Naive implementation, can probably be speeded up using modular +// // reduction +// return Multiply(this); +// } +// +// public override 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.SignValue <= 0) +// { +// throw new ArithmeticException("x is zero or negative, " + +// "inversion is impossible"); +// } +// +// // v(z) := f(z) +// BigInteger vz = BigInteger.One.ShiftLeft(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 = BigInteger.One; +// BigInteger g2z = BigInteger.Zero; +// +// // while u != 1 +// while (uz.SignValue != 0) +// { +// // 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) +// { +// BigInteger uzCopy = uz; +// uz = vz; +// vz = uzCopy; +// +// 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 F2mFieldElement(this.m, this.k1, this.k2, this.k3, g2z); +// } +// +// public override ECFieldElement Sqrt() +// { +// throw new ArithmeticException("Not implemented"); +// } +// +// /** +// * @return the representation of the field +// * <code>F<sub>2<sup>m</sup></sub></code>, either of +// * {@link F2mFieldElement.Tpb} (trinomial +// * basis representation) or +// * {@link F2mFieldElement.Ppb} (pentanomial +// * basis representation). +// */ +// public int Representation +// { +// get { return this.representation; } +// } +// +// /** +// * @return the degree <code>m</code> of the reduction polynomial +// * <code>f(z)</code>. +// */ +// public int M +// { +// get { 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 K1 +// { +// get { 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 K2 +// { +// get { 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 K3 +// { +// get { return this.k3; } +// } +// +// public override bool Equals( +// object obj) +// { +// if (obj == this) +// return true; +// +// F2mFieldElement other = obj as F2mFieldElement; +// +// if (other == null) +// return false; +// +// return Equals(other); +// } +// +// protected bool Equals( +// F2mFieldElement other) +// { +// return m == other.m +// && k1 == other.k1 +// && k2 == other.k2 +// && k3 == other.k3 +// && representation == other.representation +// && base.Equals(other); +// } +// +// public override int GetHashCode() +// { +// return base.GetHashCode() ^ 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 class F2mFieldElement + : ECFieldElement + { + /** + * Indicates gaussian normal basis representation (GNB). Number chosen + * according to X9.62. GNB is not implemented at present. + */ + public const int Gnb = 1; + + /** + * Indicates trinomial basis representation (Tpb). Number chosen + * according to X9.62. + */ + public const int Tpb = 2; + + /** + * Indicates pentanomial basis representation (Ppb). Number chosen + * according to X9.62. + */ + public const 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 readonly 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 F2mFieldElement( + int m, + int k1, + int k2, + int k3, + BigInteger x) + { + // t = m / 32 rounded up to the next integer + this.t = (m + 31) >> 5; + this.x = new IntArray(x, t); + + if ((k2 == 0) && (k3 == 0)) + { + this.representation = Tpb; + } + else + { + if (k2 >= k3) + throw new ArgumentException("k2 must be smaller than k3"); + if (k2 <= 0) + throw new ArgumentException("k2 must be larger than 0"); + + this.representation = Ppb; + } + + if (x.SignValue < 0) + throw new ArgumentException("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 F2mFieldElement( + int m, + int k, + BigInteger x) + : this(m, k, 0, 0, x) + { + // Set k1 to k, and set k2 and k3 to 0 + } + + private F2mFieldElement(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 override BigInteger ToBigInteger() + { + return x.ToBigInteger(); + } + + public override string FieldName + { + get { return "F2m"; } + } + + public override int FieldSize + { + get { 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 ArgumentException 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 is F2mFieldElement) || !(b is F2mFieldElement)) + { + throw new ArgumentException("Field elements are not " + + "both instances of F2mFieldElement"); + } + + F2mFieldElement aF2m = (F2mFieldElement)a; + F2mFieldElement bF2m = (F2mFieldElement)b; + + if ((aF2m.m != bF2m.m) || (aF2m.k1 != bF2m.k1) + || (aF2m.k2 != bF2m.k2) || (aF2m.k3 != bF2m.k3)) + { + throw new ArgumentException("Field elements are not " + + "elements of the same field F2m"); + } + + if (aF2m.representation != bF2m.representation) + { + // Should never occur + throw new ArgumentException( + "One of the field " + + "elements are not elements has incorrect representation"); + } + } + + public override ECFieldElement Add( + 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.Copy(); + F2mFieldElement bF2m = (F2mFieldElement) b; + iarrClone.AddShifted(bF2m.x, 0); + return new F2mFieldElement(m, k1, k2, k3, iarrClone); + } + + public override ECFieldElement Subtract( + ECFieldElement b) + { + // Addition and subtraction are the same in F2m + return Add(b); + } + + public override ECFieldElement Multiply( + 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); + F2mFieldElement bF2m = (F2mFieldElement) b; + IntArray mult = x.Multiply(bF2m.x, m); + mult.Reduce(m, new int[]{k1, k2, k3}); + return new F2mFieldElement(m, k1, k2, k3, mult); + } + + public override ECFieldElement Divide( + ECFieldElement b) + { + // There may be more efficient implementations + ECFieldElement bInv = b.Invert(); + return Multiply(bInv); + } + + public override ECFieldElement Negate() + { + // -x == x holds for all x in F2m + return this; + } + + public override ECFieldElement Square() + { + IntArray squared = x.Square(m); + squared.Reduce(m, new int[]{k1, k2, k3}); + return new F2mFieldElement(m, k1, k2, k3, squared); + } + + public override 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.Copy(); + + // 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.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) + { + IntArray uzCopy = uz; + uz = vz; + vz = uzCopy; + + 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 F2mFieldElement(this.m, this.k1, this.k2, this.k3, g2z); + } + + public override ECFieldElement Sqrt() + { + throw new ArithmeticException("Not implemented"); + } + + /** + * @return the representation of the field + * <code>F<sub>2<sup>m</sup></sub></code>, either of + * {@link F2mFieldElement.Tpb} (trinomial + * basis representation) or + * {@link F2mFieldElement.Ppb} (pentanomial + * basis representation). + */ + public int Representation + { + get { return this.representation; } + } + + /** + * @return the degree <code>m</code> of the reduction polynomial + * <code>f(z)</code>. + */ + public int M + { + get { 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 K1 + { + get { 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 K2 + { + get { 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 K3 + { + get { return this.k3; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + F2mFieldElement other = obj as F2mFieldElement; + + if (other == null) + return false; + + return Equals(other); + } + + protected bool Equals( + F2mFieldElement other) + { + return m == other.m + && k1 == other.k1 + && k2 == other.k2 + && k3 == other.k3 + && representation == other.representation + && base.Equals(other); + } + + public override int GetHashCode() + { + return m.GetHashCode() + ^ k1.GetHashCode() + ^ k2.GetHashCode() + ^ k3.GetHashCode() + ^ representation.GetHashCode() + ^ base.GetHashCode(); + } + } +} diff --git a/Crypto/src/math/ec/ECPoint.cs b/Crypto/src/math/ec/ECPoint.cs new file mode 100644
index 000000000..f95f09974 --- /dev/null +++ b/Crypto/src/math/ec/ECPoint.cs
@@ -0,0 +1,567 @@ +using System; +using System.Collections; +using System.Diagnostics; + +using Org.BouncyCastle.Asn1.X9; + +using Org.BouncyCastle.Math.EC.Multiplier; + +namespace Org.BouncyCastle.Math.EC +{ + /** + * base class for points on elliptic curves. + */ + public abstract class ECPoint + { + internal readonly ECCurve curve; + internal readonly ECFieldElement x, y; + internal readonly bool withCompression; + internal ECMultiplier multiplier = null; + internal PreCompInfo preCompInfo = null; + + protected internal ECPoint( + ECCurve curve, + ECFieldElement x, + ECFieldElement y, + bool withCompression) + { + if (curve == null) + throw new ArgumentNullException("curve"); + + this.curve = curve; + this.x = x; + this.y = y; + this.withCompression = withCompression; + } + + public ECCurve Curve + { + get { return curve; } + } + + public ECFieldElement X + { + get { return x; } + } + + public ECFieldElement Y + { + get { return y; } + } + + public bool IsInfinity + { + get { return x == null && y == null; } + } + + public bool IsCompressed + { + get { return withCompression; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + ECPoint o = obj as ECPoint; + + if (o == null) + return false; + + if (this.IsInfinity) + return o.IsInfinity; + + return x.Equals(o.x) && y.Equals(o.y); + } + + public override int GetHashCode() + { + if (this.IsInfinity) + return 0; + + return x.GetHashCode() ^ y.GetHashCode(); + } + +// /** +// * Mainly for testing. Explicitly set the <code>ECMultiplier</code>. +// * @param multiplier The <code>ECMultiplier</code> to be used to multiply +// * this <code>ECPoint</code>. +// */ +// internal 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>. + */ + internal void SetPreCompInfo( + PreCompInfo preCompInfo) + { + this.preCompInfo = preCompInfo; + } + + public abstract byte[] GetEncoded(); + + public abstract ECPoint Add(ECPoint b); + public abstract ECPoint Subtract(ECPoint b); + public abstract ECPoint Negate(); + public abstract ECPoint Twice(); + public abstract ECPoint Multiply(BigInteger b); + + /** + * Sets the appropriate <code>ECMultiplier</code>, unless already set. + */ + internal virtual void AssertECMultiplier() + { + if (this.multiplier == null) + { + lock (this) + { + if (this.multiplier == null) + { + this.multiplier = new FpNafMultiplier(); + } + } + } + } + } + + public abstract class ECPointBase + : ECPoint + { + protected internal ECPointBase( + ECCurve curve, + ECFieldElement x, + ECFieldElement y, + bool withCompression) + : base(curve, x, y, withCompression) + { + } + + protected internal abstract bool YTilde { get; } + + /** + * return the field element encoded with point compression. (S 4.3.6) + */ + public override byte[] GetEncoded() + { + if (this.IsInfinity) + return new byte[1]; + + // Note: some of the tests rely on calculating byte length from the field element + // (since the test cases use mismatching fields for curve/elements) + int byteLength = X9IntegerConverter.GetByteLength(x); + byte[] X = X9IntegerConverter.IntegerToBytes(this.X.ToBigInteger(), byteLength); + byte[] PO; + + if (withCompression) + { + PO = new byte[1 + X.Length]; + + PO[0] = (byte)(YTilde ? 0x03 : 0x02); + } + else + { + byte[] Y = X9IntegerConverter.IntegerToBytes(this.Y.ToBigInteger(), byteLength); + PO = new byte[1 + X.Length + Y.Length]; + + PO[0] = 0x04; + + Y.CopyTo(PO, 1 + X.Length); + } + + X.CopyTo(PO, 1); + + return PO; + } + + /** + * Multiplies this <code>ECPoint</code> by the given number. + * @param k The multiplicator. + * @return <code>k * this</code>. + */ + public override ECPoint Multiply( + BigInteger k) + { + if (k.SignValue < 0) + throw new ArgumentException("The multiplicator cannot be negative", "k"); + + if (this.IsInfinity) + return this; + + if (k.SignValue == 0) + return this.curve.Infinity; + + AssertECMultiplier(); + return this.multiplier.Multiply(this, k, preCompInfo); + } + } + + /** + * Elliptic curve points over Fp + */ + public class FpPoint + : ECPointBase + { + /** + * 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 FpPoint( + 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 FpPoint( + ECCurve curve, + ECFieldElement x, + ECFieldElement y, + bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x != null && y == null) || (x == null && y != null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + protected internal override bool YTilde + { + get + { + return this.Y.ToBigInteger().TestBit(0); + } + } + + // B.3 pg 62 + public override 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(); + } + + Debug.Assert(this.y.Equals(b.y.Negate())); + + // this = -b, i.e. the result is the point at infinity + return this.curve.Infinity; + } + + 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 FpPoint(curve, x3, y3); + } + + // B.3 pg 62 + public override ECPoint Twice() + { + // Twice identity element (point at infinity) is identity + if (this.IsInfinity) + return this; + + // if y1 == 0, then (x1, y1) == (x1, -y1) + // and hence this = -this and thus 2(x1, y1) == infinity + if (this.y.ToBigInteger().SignValue == 0) + return this.curve.Infinity; + + ECFieldElement TWO = this.curve.FromBigInteger(BigInteger.Two); + ECFieldElement THREE = this.curve.FromBigInteger(BigInteger.Three); + 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 FpPoint(curve, x3, y3, this.withCompression); + } + + // D.3.2 pg 102 (see Note:) + public override ECPoint Subtract( + ECPoint b) + { + if (b.IsInfinity) + return this; + + // Add -b + return Add(b.Negate()); + } + + public override ECPoint Negate() + { + return new FpPoint(this.curve, this.x, this.y.Negate(), this.withCompression); + } + + /** + * Sets the default <code>ECMultiplier</code>, unless already set. + */ + internal override void AssertECMultiplier() + { + if (this.multiplier == null) + { + lock (this) + { + if (this.multiplier == null) + { + this.multiplier = new WNafMultiplier(); + } + } + } + } + } + + /** + * Elliptic curve points over F2m + */ + public class F2mPoint + : ECPointBase + { + /** + * @param curve base curve + * @param x x point + * @param y y point + */ + public F2mPoint( + 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 F2mPoint( + ECCurve curve, + ECFieldElement x, + ECFieldElement y, + bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x != null && y == null) || (x == null && y != null)) + { + throw new ArgumentException("Exactly one of the field elements is null"); + } + + if (x != null) + { + // Check if x and y are elements of the same field + F2mFieldElement.CheckFieldElements(this.x, this.y); + + // Check if x and a are elements of the same field + F2mFieldElement.CheckFieldElements(this.x, this.curve.A); + } + } + + /** + * Constructor for point at infinity + */ + [Obsolete("Use ECCurve.Infinity property")] + public F2mPoint( + ECCurve curve) + : this(curve, null, null) + { + } + + protected internal override bool YTilde + { + get + { + // 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) + return this.X.ToBigInteger().SignValue != 0 + && this.Y.Multiply(this.X.Invert()).ToBigInteger().TestBit(0); + } + } + + /** + * 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 ArgumentException("Only points on the same curve can be added or subtracted"); + +// F2mFieldElement.CheckFieldElements(a.x, b.x); + } + + /* (non-Javadoc) + * @see org.bouncycastle.math.ec.ECPoint#add(org.bouncycastle.math.ec.ECPoint) + */ + public override ECPoint Add(ECPoint b) + { + CheckPoints(this, b); + return AddSimple((F2mPoint) 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> + */ + internal F2mPoint AddSimple(F2mPoint b) + { + if (this.IsInfinity) + return b; + + if (b.IsInfinity) + return this; + + F2mFieldElement x2 = (F2mFieldElement) b.X; + F2mFieldElement y2 = (F2mFieldElement) b.Y; + + // Check if b == this or b == -this + if (this.x.Equals(x2)) + { + // this == b, i.e. this must be doubled + if (this.y.Equals(y2)) + return (F2mPoint) this.Twice(); + + // this = -other, i.e. the result is the point at infinity + return (F2mPoint) this.curve.Infinity; + } + + ECFieldElement xSum = this.x.Add(x2); + + F2mFieldElement lambda + = (F2mFieldElement)(this.y.Add(y2)).Divide(xSum); + + F2mFieldElement x3 + = (F2mFieldElement)lambda.Square().Add(lambda).Add(xSum).Add(this.curve.A); + + F2mFieldElement y3 + = (F2mFieldElement)lambda.Multiply(this.x.Add(x3)).Add(x3).Add(this.y); + + return new F2mPoint(curve, x3, y3, withCompression); + } + + /* (non-Javadoc) + * @see org.bouncycastle.math.ec.ECPoint#subtract(org.bouncycastle.math.ec.ECPoint) + */ + public override ECPoint Subtract( + ECPoint b) + { + CheckPoints(this, b); + return SubtractSimple((F2mPoint) 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> + */ + internal F2mPoint SubtractSimple( + F2mPoint b) + { + if (b.IsInfinity) + return this; + + // Add -b + return AddSimple((F2mPoint) b.Negate()); + } + + /* (non-Javadoc) + * @see Org.BouncyCastle.Math.EC.ECPoint#twice() + */ + public override ECPoint Twice() + { + // Twice identity element (point at infinity) is identity + if (this.IsInfinity) + return this; + + // if x1 == 0, then (x1, y1) == (x1, x1 + y1) + // and hence this = -this and thus 2(x1, y1) == infinity + if (this.x.ToBigInteger().SignValue == 0) + return this.curve.Infinity; + + F2mFieldElement lambda = (F2mFieldElement) this.x.Add(this.y.Divide(this.x)); + F2mFieldElement x2 = (F2mFieldElement)lambda.Square().Add(lambda).Add(this.curve.A); + ECFieldElement ONE = this.curve.FromBigInteger(BigInteger.One); + F2mFieldElement y2 = (F2mFieldElement)this.x.Square().Add( + x2.Multiply(lambda.Add(ONE))); + + return new F2mPoint(this.curve, x2, y2, withCompression); + } + + public override ECPoint Negate() + { + return new F2mPoint(curve, this.x, this.x.Add(this.y), withCompression); + } + + /** + * Sets the appropriate <code>ECMultiplier</code>, unless already set. + */ + internal override void AssertECMultiplier() + { + if (this.multiplier == null) + { + lock (this) + { + if (this.multiplier == null) + { + if (((F2mCurve) this.curve).IsKoblitz) + { + this.multiplier = new WTauNafMultiplier(); + } + else + { + this.multiplier = new WNafMultiplier(); + } + } + } + } + } + } +} diff --git a/Crypto/src/math/ec/IntArray.cs b/Crypto/src/math/ec/IntArray.cs new file mode 100644
index 000000000..1089966a8 --- /dev/null +++ b/Crypto/src/math/ec/IntArray.cs
@@ -0,0 +1,485 @@ +using System; +using System.Text; + +namespace Org.BouncyCastle.Math.EC +{ + internal class IntArray + { + // TODO make m fixed for the IntArray, and hence compute T once and for all + + // TODO Use uint's internally? + private int[] m_ints; + + public IntArray(int intLen) + { + m_ints = new int[intLen]; + } + + private IntArray(int[] ints) + { + m_ints = ints; + } + + public IntArray(BigInteger bigInt) + : this(bigInt, 0) + { + } + + public IntArray(BigInteger bigInt, int minIntLen) + { + if (bigInt.SignValue == -1) + throw new ArgumentException("Only positive Integers allowed", "bigint"); + + if (bigInt.SignValue == 0) + { + m_ints = new int[] { 0 }; + return; + } + + byte[] barr = bigInt.ToByteArrayUnsigned(); + int barrLen = barr.Length; + + int intLen = (barrLen + 3) / 4; + m_ints = new int[System.Math.Max(intLen, minIntLen)]; + + int rem = barrLen % 4; + int barrI = 0; + + if (0 < rem) + { + int temp = (int) barr[barrI++]; + while (barrI < rem) + { + temp = temp << 8 | (int) barr[barrI++]; + } + m_ints[--intLen] = temp; + } + + while (intLen > 0) + { + int temp = (int) barr[barrI++]; + for (int i = 1; i < 4; i++) + { + temp = temp << 8 | (int) barr[barrI++]; + } + m_ints[--intLen] = temp; + } + } + + 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 + { + get + { + // JDK 1.5: see Integer.numberOfLeadingZeros() + int intLen = GetUsedLength(); + if (intLen == 0) + return 0; + + int last = intLen - 1; + uint highest = (uint) m_ints[last]; + int bits = (last << 5) + 1; + + // A couple of binary search steps + if (highest > 0x0000ffff) + { + if (highest > 0x00ffffff) + { + 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; + Array.Copy(m_ints, 0, newInts, 0, copyLen); + return newInts; + } + + public BigInteger ToBigInteger() + { + int usedLen = GetUsedLength(); + if (usedLen == 0) + { + return BigInteger.Zero; + } + + int highestInt = m_ints[usedLen - 1]; + byte[] temp = new byte[4]; + int barrI = 0; + bool trailingZeroBytesDone = false; + for (int j = 3; j >= 0; j--) + { + byte thisByte = (byte)((int)((uint) 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)((int)((uint)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); + } + } + + bool carry = false; + for (int i = 0; i < usedLen; i++) + { + // nextCarry is true if highest bit is set + bool 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 ArgumentException("shiftLeft() for max 31 bits " + + ", " + n + "bit shift is not possible", "n"); + } + + 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) | (int)((uint)m_ints[i - 1] >> nm32); + } + newInts[usedLen] = (int)((uint)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); + //Console.WriteLine("Resize required"); + } + + for (int i = 0; i < usedLenOther; i++) + { + m_ints[i + shift] ^= other.m_ints[i]; + } + } + + public int Length + { + get { return m_ints.Length; } + } + + public bool 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.Length + 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 readonly + 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 = (int)((uint) v0 >> 8); + int u = (int)((uint)m_ints[i] >> (j * 4)) & 0xF; + int w = table[u] << 24; + v0 |= w; + } + c.m_ints[i + i] = v0; + + v0 = 0; + int upper = (int)((uint) m_ints[i] >> 16); + for (int j = 0; j < 4; j++) + { + v0 = (int)((uint) v0 >> 8); + int u = (int)((uint)upper >> (j * 4)) & 0xF; + int w = table[u] << 24; + v0 |= w; + } + c.m_ints[i + i + 1] = v0; + } + return c; + } + + public override bool Equals(object o) + { + if (!(o is 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 override int GetHashCode() + { + int i = GetUsedLength(); + int hc = i; + while (--i >= 0) + { + hc *= 17; + hc ^= m_ints[i]; + } + return hc; + } + + internal IntArray Copy() + { + return new IntArray((int[]) m_ints.Clone()); + } + + public override string ToString() + { + int usedLen = GetUsedLength(); + if (usedLen == 0) + { + return "0"; + } + + StringBuilder sb = new StringBuilder(Convert.ToString(m_ints[usedLen - 1], 2)); + for (int iarrJ = usedLen - 2; iarrJ >= 0; iarrJ--) + { + string hexString = Convert.ToString(m_ints[iarrJ], 2); + + // 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/Crypto/src/math/ec/abc/SimpleBigDecimal.cs b/Crypto/src/math/ec/abc/SimpleBigDecimal.cs new file mode 100644
index 000000000..d5664dbfd --- /dev/null +++ b/Crypto/src/math/ec/abc/SimpleBigDecimal.cs
@@ -0,0 +1,241 @@ +using System; +using System.Text; + +namespace Org.BouncyCastle.Math.EC.Abc +{ + /** + * 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. + */ + internal class SimpleBigDecimal + // : Number + { + // private static final long serialVersionUID = 1L; + + private readonly BigInteger bigInt; + private readonly 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 val, int scale) + { + return new SimpleBigDecimal(val.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 ArgumentException("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 ArgumentException("Only SimpleBigDecimal of same scale allowed in arithmetic operations"); + } + + public SimpleBigDecimal AdjustScale(int newScale) + { + if (newScale < 0) + throw new ArgumentException("scale may not be negative"); + + if (newScale == scale) + return 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(BigInteger.One, 1); + return Add(oneHalf.AdjustScale(scale)).Floor(); + } + + public int IntValue + { + get { return Floor().IntValue; } + } + + public long LongValue + { + get { return Floor().LongValue; } + } + +// public double doubleValue() +// { +// return new Double(ToString()).doubleValue(); +// } +// +// public float floatValue() +// { +// return new Float(ToString()).floatValue(); +// } + + public int Scale + { + get { return scale; } + } + + public override string ToString() + { + if (scale == 0) + return bigInt.ToString(); + + BigInteger floorBigInt = Floor(); + + BigInteger fract = bigInt.Subtract(floorBigInt.ShiftLeft(scale)); + if (bigInt.SignValue < 0) + { + fract = BigInteger.One.ShiftLeft(scale).Subtract(fract); + } + + if ((floorBigInt.SignValue == -1) && (!(fract.Equals(BigInteger.Zero)))) + { + floorBigInt = floorBigInt.Add(BigInteger.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[j]; + } + string rightOfPoint = new string(fractCharArr); + + StringBuilder sb = new StringBuilder(leftOfPoint); + sb.Append("."); + sb.Append(rightOfPoint); + + return sb.ToString(); + } + + public override bool Equals( + object obj) + { + if (this == obj) + return true; + + SimpleBigDecimal other = obj as SimpleBigDecimal; + + if (other == null) + return false; + + return bigInt.Equals(other.bigInt) + && scale == other.scale; + } + + public override int GetHashCode() + { + return bigInt.GetHashCode() ^ scale; + } + + } +} diff --git a/Crypto/src/math/ec/abc/Tnaf.cs b/Crypto/src/math/ec/abc/Tnaf.cs new file mode 100644
index 000000000..225fc3075 --- /dev/null +++ b/Crypto/src/math/ec/abc/Tnaf.cs
@@ -0,0 +1,834 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Abc +{ + /** + * Class holding methods for point multiplication based on the window + * &#964;-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. + */ + internal class Tnaf + { + private static readonly BigInteger MinusOne = BigInteger.One.Negate(); + private static readonly BigInteger MinusTwo = BigInteger.Two.Negate(); + private static readonly BigInteger MinusThree = BigInteger.Three.Negate(); + private static readonly BigInteger Four = BigInteger.ValueOf(4); + + /** + * 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>&#945;<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 const sbyte Width = 4; + + /** + * 2<sup>4</sup> + */ + public const sbyte Pow2Width = 16; + + /** + * The <code>&#945;<sub>u</sub></code>'s for <code>a=0</code> as an array + * of <code>ZTauElement</code>s. + */ + public static readonly ZTauElement[] Alpha0 = + { + null, + new ZTauElement(BigInteger.One, BigInteger.Zero), null, + new ZTauElement(MinusThree, MinusOne), null, + new ZTauElement(MinusOne, MinusOne), null, + new ZTauElement(BigInteger.One, MinusOne), null + }; + + /** + * The <code>&#945;<sub>u</sub></code>'s for <code>a=0</code> as an array + * of TNAFs. + */ + public static readonly sbyte[][] Alpha0Tnaf = + { + null, new sbyte[]{1}, null, new sbyte[]{-1, 0, 1}, null, new sbyte[]{1, 0, 1}, null, new sbyte[]{-1, 0, 0, 1} + }; + + /** + * The <code>&#945;<sub>u</sub></code>'s for <code>a=1</code> as an array + * of <code>ZTauElement</code>s. + */ + public static readonly ZTauElement[] Alpha1 = + { + null, + new ZTauElement(BigInteger.One, BigInteger.Zero), null, + new ZTauElement(MinusThree, BigInteger.One), null, + new ZTauElement(MinusOne, BigInteger.One), null, + new ZTauElement(BigInteger.One, BigInteger.One), null + }; + + /** + * The <code>&#945;<sub>u</sub></code>'s for <code>a=1</code> as an array + * of TNAFs. + */ + public static readonly sbyte[][] Alpha1Tnaf = + { + null, new sbyte[]{1}, null, new sbyte[]{-1, 0, 1}, null, new sbyte[]{1, 0, 1}, null, new sbyte[]{-1, 0, 0, -1} + }; + + /** + * Computes the norm of an element <code>&#955;</code> of + * <code><b>Z</b>[&#964;]</code>. + * @param mu The parameter <code>&#956;</code> of the elliptic curve. + * @param lambda The element <code>&#955;</code> of + * <code><b>Z</b>[&#964;]</code>. + * @return The norm of <code>&#955;</code>. + */ + public static BigInteger Norm(sbyte 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 ArgumentException("mu must be 1 or -1"); + } + + return norm; + } + + /** + * Computes the norm of an element <code>&#955;</code> of + * <code><b>R</b>[&#964;]</code>, where <code>&#955; = u + v&#964;</code> + * and <code>u</code> and <code>u</code> are real numbers (elements of + * <code><b>R</b></code>). + * @param mu The parameter <code>&#956;</code> of the elliptic curve. + * @param u The real part of the element <code>&#955;</code> of + * <code><b>R</b>[&#964;]</code>. + * @param v The <code>&#964;</code>-adic part of the element + * <code>&#955;</code> of <code><b>R</b>[&#964;]</code>. + * @return The norm of <code>&#955;</code>. + */ + public static SimpleBigDecimal Norm(sbyte 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 ArgumentException("mu must be 1 or -1"); + } + + return norm; + } + + /** + * Rounds an element <code>&#955;</code> of <code><b>R</b>[&#964;]</code> + * to an element of <code><b>Z</b>[&#964;]</code>, such that their difference + * has minimal norm. <code>&#955;</code> is given as + * <code>&#955; = &#955;<sub>0</sub> + &#955;<sub>1</sub>&#964;</code>. + * @param lambda0 The component <code>&#955;<sub>0</sub></code>. + * @param lambda1 The component <code>&#955;<sub>1</sub></code>. + * @param mu The parameter <code>&#956;</code> of the elliptic curve. Must + * equal 1 or -1. + * @return The rounded element of <code><b>Z</b>[&#964;]</code>. + * @throws ArgumentException if <code>lambda0</code> and + * <code>lambda1</code> do not have same scale. + */ + public static ZTauElement Round(SimpleBigDecimal lambda0, + SimpleBigDecimal lambda1, sbyte mu) + { + int scale = lambda0.Scale; + if (lambda1.Scale != scale) + throw new ArgumentException("lambda0 and lambda1 do not have same scale"); + + if (!((mu == 1) || (mu == -1))) + throw new ArgumentException("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); + } + + sbyte h0 = 0; + sbyte h1 = 0; + + // if eta >= 1 + if (eta.CompareTo(BigInteger.One) >= 0) + { + if (check1.CompareTo(MinusOne) < 0) + { + h1 = mu; + } + else + { + h0 = 1; + } + } + else + { + // eta < 1 + if (check2.CompareTo(BigInteger.Two) >= 0) + { + h1 = mu; + } + } + + // if eta < -1 + if (eta.CompareTo(MinusOne) < 0) + { + if (check1.CompareTo(BigInteger.One) >= 0) + { + h1 = (sbyte)-mu; + } + else + { + h0 = -1; + } + } + else + { + // eta >= -1 + if (check2.CompareTo(MinusTwo) < 0) + { + h1 = (sbyte)-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>&#955; = 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>&#955; = s k / n</code> computed to + * <code>c</code> bits of accuracy. + */ + public static SimpleBigDecimal ApproximateDivisionByN(BigInteger k, + BigInteger s, BigInteger vm, sbyte 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(BigInteger.One); + } + + return new SimpleBigDecimal(ls, c); + } + + /** + * Computes the <code>&#964;</code>-adic NAF (non-adjacent form) of an + * element <code>&#955;</code> of <code><b>Z</b>[&#964;]</code>. + * @param mu The parameter <code>&#956;</code> of the elliptic curve. + * @param lambda The element <code>&#955;</code> of + * <code><b>Z</b>[&#964;]</code>. + * @return The <code>&#964;</code>-adic NAF of <code>&#955;</code>. + */ + public static sbyte[] TauAdicNaf(sbyte mu, ZTauElement lambda) + { + if (!((mu == 1) || (mu == -1))) + throw new ArgumentException("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 + sbyte[] u = new sbyte[maxLength]; + int i = 0; + + // The actual length of the TNAF + int length = 0; + + BigInteger r0 = lambda.u; + BigInteger r1 = lambda.v; + + while(!((r0.Equals(BigInteger.Zero)) && (r1.Equals(BigInteger.Zero)))) + { + // If r0 is odd + if (r0.TestBit(0)) + { + u[i] = (sbyte) BigInteger.Two.Subtract((r0.Subtract(r1.ShiftLeft(1))).Mod(Four)).IntValue; + + // r0 = r0 - u[i] + if (u[i] == 1) + { + r0 = r0.ClearBit(0); + } + else + { + // u[i] == -1 + r0 = r0.Add(BigInteger.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 + sbyte[] tnaf = new sbyte[length]; + Array.Copy(u, 0, tnaf, 0, length); + return tnaf; + } + + /** + * Applies the operation <code>&#964;()</code> to an + * <code>F2mPoint</code>. + * @param p The F2mPoint to which <code>&#964;()</code> is applied. + * @return <code>&#964;(p)</code> + */ + public static F2mPoint Tau(F2mPoint p) + { + if (p.IsInfinity) + return p; + + ECFieldElement x = p.X; + ECFieldElement y = p.Y; + + return new F2mPoint(p.Curve, x.Square(), y.Square(), p.IsCompressed); + } + + /** + * Returns the parameter <code>&#956;</code> of the elliptic curve. + * @param curve The elliptic curve from which to obtain <code>&#956;</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>&#956;</code> of the elliptic curve. + * @throws ArgumentException if the given ECCurve is not a Koblitz + * curve. + */ + public static sbyte GetMu(F2mCurve curve) + { + BigInteger a = curve.A.ToBigInteger(); + + sbyte mu; + if (a.SignValue == 0) + { + mu = -1; + } + else if (a.Equals(BigInteger.One)) + { + mu = 1; + } + else + { + throw new ArgumentException("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>&#956;</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(sbyte mu, int k, bool doV) + { + if (!(mu == 1 || mu == -1)) + throw new ArgumentException("mu must be 1 or -1"); + + BigInteger u0; + BigInteger u1; + BigInteger u2; + + if (doV) + { + u0 = BigInteger.Two; + u1 = BigInteger.ValueOf(mu); + } + else + { + u0 = BigInteger.Zero; + u1 = BigInteger.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>&#956;</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(sbyte 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 = BigInteger.Zero.SetBit(w); + BigInteger u1invert = us[1].ModInverse(twoToW); + BigInteger tw; + tw = BigInteger.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 ArgumentException if <code>curve</code> is not a + * Koblitz curve (Anomalous Binary Curve, ABC). + */ + public static BigInteger[] GetSi(F2mCurve curve) + { + if (!curve.IsKoblitz) + throw new ArgumentException("si is defined for Koblitz curves only"); + + int m = curve.M; + int a = curve.A.ToBigInteger().IntValue; + sbyte mu = curve.GetMu(); + int h = curve.H.IntValue; + int index = m + 3 - a; + BigInteger[] ui = GetLucas(mu, index, false); + + BigInteger dividend0; + BigInteger dividend1; + if (mu == 1) + { + dividend0 = BigInteger.One.Subtract(ui[1]); + dividend1 = BigInteger.One.Subtract(ui[0]); + } + else if (mu == -1) + { + dividend0 = BigInteger.One.Add(ui[1]); + dividend1 = BigInteger.One.Add(ui[0]); + } + else + { + throw new ArgumentException("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 ArgumentException("h (Cofactor) must be 2 or 4"); + } + + return si; + } + + /** + * Partial modular reduction modulo + * <code>(&#964;<sup>m</sup> - 1)/(&#964; - 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 &#956; of the elliptic curve. + * @param c The precision (number of bits of accuracy) of the partial + * modular reduction. + * @return <code>&#961; := k partmod (&#964;<sup>m</sup> - 1)/(&#964; - 1)</code> + */ + public static ZTauElement PartModReduction(BigInteger k, int m, sbyte a, + BigInteger[] s, sbyte mu, sbyte 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.F2mPoint F2mPoint} + * by a <code>BigInteger</code> using the reduced <code>&#964;</code>-adic + * NAF (RTNAF) method. + * @param p The F2mPoint to Multiply. + * @param k The <code>BigInteger</code> by which to Multiply <code>p</code>. + * @return <code>k * p</code> + */ + public static F2mPoint MultiplyRTnaf(F2mPoint p, BigInteger k) + { + F2mCurve curve = (F2mCurve) p.Curve; + int m = curve.M; + sbyte a = (sbyte) curve.A.ToBigInteger().IntValue; + sbyte mu = curve.GetMu(); + BigInteger[] s = curve.GetSi(); + ZTauElement rho = PartModReduction(k, m, a, s, mu, (sbyte)10); + + return MultiplyTnaf(p, rho); + } + + /** + * Multiplies a {@link org.bouncycastle.math.ec.F2mPoint F2mPoint} + * by an element <code>&#955;</code> of <code><b>Z</b>[&#964;]</code> + * using the <code>&#964;</code>-adic NAF (TNAF) method. + * @param p The F2mPoint to Multiply. + * @param lambda The element <code>&#955;</code> of + * <code><b>Z</b>[&#964;]</code>. + * @return <code>&#955; * p</code> + */ + public static F2mPoint MultiplyTnaf(F2mPoint p, ZTauElement lambda) + { + F2mCurve curve = (F2mCurve)p.Curve; + sbyte mu = curve.GetMu(); + sbyte[] u = TauAdicNaf(mu, lambda); + + F2mPoint q = MultiplyFromTnaf(p, u); + + return q; + } + + /** + * Multiplies a {@link org.bouncycastle.math.ec.F2mPoint F2mPoint} + * by an element <code>&#955;</code> of <code><b>Z</b>[&#964;]</code> + * using the <code>&#964;</code>-adic NAF (TNAF) method, given the TNAF + * of <code>&#955;</code>. + * @param p The F2mPoint to Multiply. + * @param u The the TNAF of <code>&#955;</code>.. + * @return <code>&#955; * p</code> + */ + public static F2mPoint MultiplyFromTnaf(F2mPoint p, sbyte[] u) + { + F2mCurve curve = (F2mCurve)p.Curve; + F2mPoint q = (F2mPoint) curve.Infinity; + for (int i = u.Length - 1; i >= 0; i--) + { + q = Tau(q); + if (u[i] == 1) + { + q = (F2mPoint)q.AddSimple(p); + } + else if (u[i] == -1) + { + q = (F2mPoint)q.SubtractSimple(p); + } + } + return q; + } + + /** + * Computes the <code>[&#964;]</code>-adic window NAF of an element + * <code>&#955;</code> of <code><b>Z</b>[&#964;]</code>. + * @param mu The parameter &#956; of the elliptic curve. + * @param lambda The element <code>&#955;</code> of + * <code><b>Z</b>[&#964;]</code> of which to compute the + * <code>[&#964;]</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>&#945;<sub>u</sub></code>'s for the window width. + * @return The <code>[&#964;]</code>-adic window NAF of + * <code>&#955;</code>. + */ + public static sbyte[] TauAdicWNaf(sbyte mu, ZTauElement lambda, + sbyte width, BigInteger pow2w, BigInteger tw, ZTauElement[] alpha) + { + if (!((mu == 1) || (mu == -1))) + throw new ArgumentException("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 + sbyte[] u = new sbyte[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(BigInteger.Zero))&&(r1.Equals(BigInteger.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); + + sbyte uLocal; + // if uUnMod >= 2^(width - 1) + if (uUnMod.CompareTo(pow2wMin1) >= 0) + { + uLocal = (sbyte) uUnMod.Subtract(pow2w).IntValue; + } + else + { + uLocal = (sbyte) uUnMod.IntValue; + } + // uLocal is now in [-2^(width-1), 2^(width-1)-1] + + u[i] = uLocal; + bool s = true; + if (uLocal < 0) + { + s = false; + uLocal = (sbyte)-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 F2mPoint[] GetPreComp(F2mPoint p, sbyte a) + { + F2mPoint[] pu; + pu = new F2mPoint[16]; + pu[1] = p; + sbyte[][] 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/Crypto/src/math/ec/abc/ZTauElement.cs b/Crypto/src/math/ec/abc/ZTauElement.cs new file mode 100644
index 000000000..4fcbf1bdf --- /dev/null +++ b/Crypto/src/math/ec/abc/ZTauElement.cs
@@ -0,0 +1,36 @@ +namespace Org.BouncyCastle.Math.EC.Abc +{ + /** + * Class representing an element of <code><b>Z</b>[&#964;]</code>. Let + * <code>&#955;</code> be an element of <code><b>Z</b>[&#964;]</code>. Then + * <code>&#955;</code> is given as <code>&#955; = u + v&#964;</code>. The + * components <code>u</code> and <code>v</code> may be used directly, there + * are no accessor methods. + * Immutable class. + */ + internal class ZTauElement + { + /** + * The &quot;real&quot; part of <code>&#955;</code>. + */ + public readonly BigInteger u; + + /** + * The &quot;<code>&#964;</code>-adic&quot; part of <code>&#955;</code>. + */ + public readonly BigInteger v; + + /** + * Constructor for an element <code>&#955;</code> of + * <code><b>Z</b>[&#964;]</code>. + * @param u The &quot;real&quot; part of <code>&#955;</code>. + * @param v The &quot;<code>&#964;</code>-adic&quot; part of + * <code>&#955;</code>. + */ + public ZTauElement(BigInteger u, BigInteger v) + { + this.u = u; + this.v = v; + } + } +} diff --git a/Crypto/src/math/ec/multiplier/ECMultiplier.cs b/Crypto/src/math/ec/multiplier/ECMultiplier.cs new file mode 100644
index 000000000..c6d768ea8 --- /dev/null +++ b/Crypto/src/math/ec/multiplier/ECMultiplier.cs
@@ -0,0 +1,18 @@ +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + /** + * Interface for classes encapsulating a point multiplication algorithm + * for <code>ECPoint</code>s. + */ + internal 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/Crypto/src/math/ec/multiplier/FpNafMultiplier.cs b/Crypto/src/math/ec/multiplier/FpNafMultiplier.cs new file mode 100644
index 000000000..f5a98501a --- /dev/null +++ b/Crypto/src/math/ec/multiplier/FpNafMultiplier.cs
@@ -0,0 +1,39 @@ +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + /** + * Class implementing the NAF (Non-Adjacent Form) multiplication algorithm. + */ + internal class FpNafMultiplier + : ECMultiplier + { + /** + * D.3.2 pg 101 + * @see org.bouncycastle.math.ec.multiplier.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.Three); + + ECPoint neg = p.Negate(); + ECPoint R = p; + + for (int i = h.BitLength - 2; i > 0; --i) + { + R = R.Twice(); + + bool hBit = h.TestBit(i); + bool eBit = e.TestBit(i); + + if (hBit != eBit) + { + R = R.Add(hBit ? p : neg); + } + } + + return R; + } + } +} diff --git a/Crypto/src/math/ec/multiplier/PreCompInfo.cs b/Crypto/src/math/ec/multiplier/PreCompInfo.cs new file mode 100644
index 000000000..d379508c8 --- /dev/null +++ b/Crypto/src/math/ec/multiplier/PreCompInfo.cs
@@ -0,0 +1,11 @@ +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + /** + * Interface for classes storing precomputation data for multiplication + * algorithms. Used as a Memento (see GOF patterns) for + * <code>WNafMultiplier</code>. + */ + internal interface PreCompInfo + { + } +} diff --git a/Crypto/src/math/ec/multiplier/ReferenceMultiplier.cs b/Crypto/src/math/ec/multiplier/ReferenceMultiplier.cs new file mode 100644
index 000000000..cdccffc2d --- /dev/null +++ b/Crypto/src/math/ec/multiplier/ReferenceMultiplier.cs
@@ -0,0 +1,30 @@ +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + internal class ReferenceMultiplier + : 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.Curve.Infinity; + 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/Crypto/src/math/ec/multiplier/WNafMultiplier.cs b/Crypto/src/math/ec/multiplier/WNafMultiplier.cs new file mode 100644
index 000000000..b5cf34ba8 --- /dev/null +++ b/Crypto/src/math/ec/multiplier/WNafMultiplier.cs
@@ -0,0 +1,241 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + /** + * Class implementing the WNAF (Window Non-Adjacent Form) multiplication + * algorithm. + */ + internal class WNafMultiplier + : 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 = &#8722;<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>sbyte[]</code>. + */ + public sbyte[] WindowNaf(sbyte width, BigInteger k) + { + // The window NAF is at most 1 element longer than the binary + // representation of the integer k. sbyte 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. + sbyte[] wnaf = new sbyte[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.SignValue > 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] = (sbyte)(remainder.IntValue - pow2wB); + } + else + { + wnaf[i] = (sbyte)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 + sbyte[] wnafShort = new sbyte[length]; + Array.Copy(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 is 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 + sbyte 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]; + Array.Copy(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 + sbyte[] wnaf = WindowNaf(width, k); + int l = wnaf.Length; + + // Apply the Window NAF to p using the precomputed ECPoint values. + ECPoint q = p.Curve.Infinity; + 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/Crypto/src/math/ec/multiplier/WNafPreCompInfo.cs b/Crypto/src/math/ec/multiplier/WNafPreCompInfo.cs new file mode 100644
index 000000000..d9305dace --- /dev/null +++ b/Crypto/src/math/ec/multiplier/WNafPreCompInfo.cs
@@ -0,0 +1,46 @@ +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + /** + * Class holding precomputation data for the WNAF (Window Non-Adjacent Form) + * algorithm. + */ + internal class WNafPreCompInfo + : 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; + + internal ECPoint[] GetPreComp() + { + return preComp; + } + + internal void SetPreComp(ECPoint[] preComp) + { + this.preComp = preComp; + } + + internal ECPoint GetTwiceP() + { + return twiceP; + } + + internal void SetTwiceP(ECPoint twiceThis) + { + this.twiceP = twiceThis; + } + } +} diff --git a/Crypto/src/math/ec/multiplier/WTauNafMultiplier.cs b/Crypto/src/math/ec/multiplier/WTauNafMultiplier.cs new file mode 100644
index 000000000..f1a605770 --- /dev/null +++ b/Crypto/src/math/ec/multiplier/WTauNafMultiplier.cs
@@ -0,0 +1,120 @@ +using System; + +using Org.BouncyCastle.Math.EC.Abc; + +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + /** + * Class implementing the WTNAF (Window + * <code>&#964;</code>-adic Non-Adjacent Form) algorithm. + */ + internal class WTauNafMultiplier + : ECMultiplier + { + /** + * Multiplies a {@link org.bouncycastle.math.ec.F2mPoint F2mPoint} + * by <code>k</code> using the reduced <code>&#964;</code>-adic NAF (RTNAF) + * method. + * @param p The F2mPoint 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 is F2mPoint)) + throw new ArgumentException("Only F2mPoint can be used in WTauNafMultiplier"); + + F2mPoint p = (F2mPoint)point; + + F2mCurve curve = (F2mCurve) p.Curve; + int m = curve.M; + sbyte a = (sbyte) curve.A.ToBigInteger().IntValue; + sbyte mu = curve.GetMu(); + BigInteger[] s = curve.GetSi(); + + ZTauElement rho = Tnaf.PartModReduction(k, m, a, s, mu, (sbyte)10); + + return MultiplyWTnaf(p, rho, preCompInfo, a, mu); + } + + /** + * Multiplies a {@link org.bouncycastle.math.ec.F2mPoint F2mPoint} + * by an element <code>&#955;</code> of <code><b>Z</b>[&#964;]</code> using + * the <code>&#964;</code>-adic NAF (TNAF) method. + * @param p The F2mPoint to multiply. + * @param lambda The element <code>&#955;</code> of + * <code><b>Z</b>[&#964;]</code> of which to compute the + * <code>[&#964;]</code>-adic NAF. + * @return <code>p</code> multiplied by <code>&#955;</code>. + */ + private F2mPoint MultiplyWTnaf(F2mPoint p, ZTauElement lambda, + PreCompInfo preCompInfo, sbyte a, sbyte mu) + { + ZTauElement[] alpha; + if (a == 0) + { + alpha = Tnaf.Alpha0; + } + else + { + // a == 1 + alpha = Tnaf.Alpha1; + } + + BigInteger tw = Tnaf.GetTw(mu, Tnaf.Width); + + sbyte[]u = Tnaf.TauAdicWNaf(mu, lambda, Tnaf.Width, + BigInteger.ValueOf(Tnaf.Pow2Width), tw, alpha); + + return MultiplyFromWTnaf(p, u, preCompInfo); + } + + /** + * Multiplies a {@link org.bouncycastle.math.ec.F2mPoint F2mPoint} + * by an element <code>&#955;</code> of <code><b>Z</b>[&#964;]</code> + * using the window <code>&#964;</code>-adic NAF (TNAF) method, given the + * WTNAF of <code>&#955;</code>. + * @param p The F2mPoint to multiply. + * @param u The the WTNAF of <code>&#955;</code>.. + * @return <code>&#955; * p</code> + */ + private static F2mPoint MultiplyFromWTnaf(F2mPoint p, sbyte[] u, + PreCompInfo preCompInfo) + { + F2mCurve curve = (F2mCurve)p.Curve; + sbyte a = (sbyte) curve.A.ToBigInteger().IntValue; + + F2mPoint[] pu; + if ((preCompInfo == null) || !(preCompInfo is WTauNafPreCompInfo)) + { + pu = Tnaf.GetPreComp(p, a); + p.SetPreCompInfo(new WTauNafPreCompInfo(pu)); + } + else + { + pu = ((WTauNafPreCompInfo)preCompInfo).GetPreComp(); + } + + // q = infinity + F2mPoint q = (F2mPoint) p.Curve.Infinity; + 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/Crypto/src/math/ec/multiplier/WTauNafPreCompInfo.cs b/Crypto/src/math/ec/multiplier/WTauNafPreCompInfo.cs new file mode 100644
index 000000000..cede4a05d --- /dev/null +++ b/Crypto/src/math/ec/multiplier/WTauNafPreCompInfo.cs
@@ -0,0 +1,41 @@ +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + /** + * Class holding precomputation data for the WTNAF (Window + * <code>&#964;</code>-adic Non-Adjacent Form) algorithm. + */ + internal class WTauNafPreCompInfo + : PreCompInfo + { + /** + * Array holding the precomputed <code>F2mPoint</code>s used for the + * WTNAF multiplication in <code> + * {@link org.bouncycastle.math.ec.multiplier.WTauNafMultiplier.multiply() + * WTauNafMultiplier.multiply()}</code>. + */ + private readonly F2mPoint[] preComp; + + /** + * Constructor for <code>WTauNafPreCompInfo</code> + * @param preComp Array holding the precomputed <code>F2mPoint</code>s + * used for the WTNAF multiplication in <code> + * {@link org.bouncycastle.math.ec.multiplier.WTauNafMultiplier.multiply() + * WTauNafMultiplier.multiply()}</code>. + */ + internal WTauNafPreCompInfo(F2mPoint[] preComp) + { + this.preComp = preComp; + } + + /** + * @return the array holding the precomputed <code>F2mPoint</code>s + * used for the WTNAF multiplication in <code> + * {@link org.bouncycastle.math.ec.multiplier.WTauNafMultiplier.multiply() + * WTauNafMultiplier.multiply()}</code>. + */ + internal F2mPoint[] GetPreComp() + { + return preComp; + } + } +} diff --git a/Crypto/src/ocsp/BasicOCSPResp.cs b/Crypto/src/ocsp/BasicOCSPResp.cs new file mode 100644
index 000000000..4253726bb --- /dev/null +++ b/Crypto/src/ocsp/BasicOCSPResp.cs
@@ -0,0 +1,220 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.Ocsp +{ + /// <remarks> + /// <code> + /// BasicOcspResponse ::= SEQUENCE { + /// tbsResponseData ResponseData, + /// signatureAlgorithm AlgorithmIdentifier, + /// signature BIT STRING, + /// certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL + /// } + /// </code> + /// </remarks> + public class BasicOcspResp + : X509ExtensionBase + { + private readonly BasicOcspResponse resp; + private readonly ResponseData data; +// private readonly X509Certificate[] chain; + + public BasicOcspResp( + BasicOcspResponse resp) + { + this.resp = resp; + this.data = resp.TbsResponseData; + } + + /// <returns>The DER encoding of the tbsResponseData field.</returns> + /// <exception cref="OcspException">In the event of an encoding error.</exception> + public byte[] GetTbsResponseData() + { + try + { + return data.GetDerEncoded(); + } + catch (IOException e) + { + throw new OcspException("problem encoding tbsResponseData", e); + } + } + + public int Version + { + get { return data.Version.Value.IntValue + 1; } + } + + public RespID ResponderId + { + get { return new RespID(data.ResponderID); } + } + + public DateTime ProducedAt + { + get { return data.ProducedAt.ToDateTime(); } + } + + public SingleResp[] Responses + { + get + { + Asn1Sequence s = data.Responses; + SingleResp[] rs = new SingleResp[s.Count]; + + for (int i = 0; i != rs.Length; i++) + { + rs[i] = new SingleResp(SingleResponse.GetInstance(s[i])); + } + + return rs; + } + } + + public X509Extensions ResponseExtensions + { + get { return data.ResponseExtensions; } + } + + protected override X509Extensions GetX509Extensions() + { + return ResponseExtensions; + } + + public string SignatureAlgName + { + get { return OcspUtilities.GetAlgorithmName(resp.SignatureAlgorithm.ObjectID); } + } + + public string SignatureAlgOid + { + get { return resp.SignatureAlgorithm.ObjectID.Id; } + } + + [Obsolete("RespData class is no longer required as all functionality is available on this class")] + public RespData GetResponseData() + { + return new RespData(data); + } + + public byte[] GetSignature() + { + return resp.Signature.GetBytes(); + } + + private IList GetCertList() + { + // load the certificates and revocation lists if we have any + + IList certs = Platform.CreateArrayList(); + Asn1Sequence s = resp.Certs; + + if (s != null) + { + foreach (Asn1Encodable ae in s) + { + try + { + certs.Add(new X509CertificateParser().ReadCertificate(ae.GetEncoded())); + } + catch (IOException ex) + { + throw new OcspException("can't re-encode certificate!", ex); + } + catch (CertificateException ex) + { + throw new OcspException("can't re-encode certificate!", ex); + } + } + } + + return certs; + } + + public X509Certificate[] GetCerts() + { + IList certs = GetCertList(); + X509Certificate[] result = new X509Certificate[certs.Count]; + for (int i = 0; i < certs.Count; ++i) + { + result[i] = (X509Certificate)certs[i]; + } + return result; + } + + /// <returns>The certificates, if any, associated with the response.</returns> + /// <exception cref="OcspException">In the event of an encoding error.</exception> + public IX509Store GetCertificates( + string type) + { + try + { + return X509StoreFactory.Create( + "Certificate/" + type, + new X509CollectionStoreParameters(this.GetCertList())); + } + catch (Exception e) + { + throw new OcspException("can't setup the CertStore", e); + } + } + + /// <summary> + /// Verify the signature against the tbsResponseData object we contain. + /// </summary> + public bool Verify( + AsymmetricKeyParameter publicKey) + { + try + { + ISigner signature = SignerUtilities.GetSigner(this.SignatureAlgName); + signature.Init(false, publicKey); + byte[] bs = data.GetDerEncoded(); + signature.BlockUpdate(bs, 0, bs.Length); + + return signature.VerifySignature(this.GetSignature()); + } + catch (Exception e) + { + throw new OcspException("exception processing sig: " + e, e); + } + } + + /// <returns>The ASN.1 encoded representation of this object.</returns> + public byte[] GetEncoded() + { + return resp.GetEncoded(); + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + BasicOcspResp other = obj as BasicOcspResp; + + if (other == null) + return false; + + return resp.Equals(other.resp); + } + + public override int GetHashCode() + { + return resp.GetHashCode(); + } + } +} diff --git a/Crypto/src/ocsp/BasicOCSPRespGenerator.cs b/Crypto/src/ocsp/BasicOCSPRespGenerator.cs new file mode 100644
index 000000000..5ff4bd9cc --- /dev/null +++ b/Crypto/src/ocsp/BasicOCSPRespGenerator.cs
@@ -0,0 +1,318 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Ocsp +{ + /** + * Generator for basic OCSP response objects. + */ + public class BasicOcspRespGenerator + { + private readonly IList list = Platform.CreateArrayList(); + + private X509Extensions responseExtensions; + private RespID responderID; + + private class ResponseObject + { + internal CertificateID certId; + internal CertStatus certStatus; + internal DerGeneralizedTime thisUpdate; + internal DerGeneralizedTime nextUpdate; + internal X509Extensions extensions; + + public ResponseObject( + CertificateID certId, + CertificateStatus certStatus, + DateTime thisUpdate, + X509Extensions extensions) + : this(certId, certStatus, new DerGeneralizedTime(thisUpdate), null, extensions) + { + } + + public ResponseObject( + CertificateID certId, + CertificateStatus certStatus, + DateTime thisUpdate, + DateTime nextUpdate, + X509Extensions extensions) + : this(certId, certStatus, new DerGeneralizedTime(thisUpdate), new DerGeneralizedTime(nextUpdate), extensions) + { + } + + private ResponseObject( + CertificateID certId, + CertificateStatus certStatus, + DerGeneralizedTime thisUpdate, + DerGeneralizedTime nextUpdate, + X509Extensions extensions) + { + this.certId = certId; + + if (certStatus == null) + { + this.certStatus = new CertStatus(); + } + else if (certStatus is UnknownStatus) + { + this.certStatus = new CertStatus(2, DerNull.Instance); + } + else + { + RevokedStatus rs = (RevokedStatus) certStatus; + CrlReason revocationReason = rs.HasRevocationReason + ? new CrlReason(rs.RevocationReason) + : null; + + this.certStatus = new CertStatus( + new RevokedInfo(new DerGeneralizedTime(rs.RevocationTime), revocationReason)); + } + + this.thisUpdate = thisUpdate; + this.nextUpdate = nextUpdate; + + this.extensions = extensions; + } + + public SingleResponse ToResponse() + { + return new SingleResponse(certId.ToAsn1Object(), certStatus, thisUpdate, nextUpdate, extensions); + } + } + + /** + * basic constructor + */ + public BasicOcspRespGenerator( + RespID responderID) + { + this.responderID = responderID; + } + + /** + * construct with the responderID to be the SHA-1 keyHash of the passed in public key. + */ + public BasicOcspRespGenerator( + AsymmetricKeyParameter publicKey) + { + this.responderID = new RespID(publicKey); + } + + /** + * Add a response for a particular Certificate ID. + * + * @param certID certificate ID details + * @param certStatus status of the certificate - null if okay + */ + public void AddResponse( + CertificateID certID, + CertificateStatus certStatus) + { + list.Add(new ResponseObject(certID, certStatus, DateTime.UtcNow, null)); + } + + /** + * Add a response for a particular Certificate ID. + * + * @param certID certificate ID details + * @param certStatus status of the certificate - null if okay + * @param singleExtensions optional extensions + */ + public void AddResponse( + CertificateID certID, + CertificateStatus certStatus, + X509Extensions singleExtensions) + { + list.Add(new ResponseObject(certID, certStatus, DateTime.UtcNow, singleExtensions)); + } + + /** + * Add a response for a particular Certificate ID. + * + * @param certID certificate ID details + * @param nextUpdate date when next update should be requested + * @param certStatus status of the certificate - null if okay + * @param singleExtensions optional extensions + */ + public void AddResponse( + CertificateID certID, + CertificateStatus certStatus, + DateTime nextUpdate, + X509Extensions singleExtensions) + { + list.Add(new ResponseObject(certID, certStatus, DateTime.UtcNow, nextUpdate, singleExtensions)); + } + + /** + * Add a response for a particular Certificate ID. + * + * @param certID certificate ID details + * @param thisUpdate date this response was valid on + * @param nextUpdate date when next update should be requested + * @param certStatus status of the certificate - null if okay + * @param singleExtensions optional extensions + */ + public void AddResponse( + CertificateID certID, + CertificateStatus certStatus, + DateTime thisUpdate, + DateTime nextUpdate, + X509Extensions singleExtensions) + { + list.Add(new ResponseObject(certID, certStatus, thisUpdate, nextUpdate, singleExtensions)); + } + + /** + * Set the extensions for the response. + * + * @param responseExtensions the extension object to carry. + */ + public void SetResponseExtensions( + X509Extensions responseExtensions) + { + this.responseExtensions = responseExtensions; + } + + private BasicOcspResp GenerateResponse( + string signatureName, + AsymmetricKeyParameter privateKey, + X509Certificate[] chain, + DateTime producedAt, + SecureRandom random) + { + DerObjectIdentifier signingAlgorithm; + try + { + signingAlgorithm = OcspUtilities.GetAlgorithmOid(signatureName); + } + catch (Exception e) + { + throw new ArgumentException("unknown signing algorithm specified", e); + } + + Asn1EncodableVector responses = new Asn1EncodableVector(); + + foreach (ResponseObject respObj in list) + { + try + { + responses.Add(respObj.ToResponse()); + } + catch (Exception e) + { + throw new OcspException("exception creating Request", e); + } + } + + ResponseData tbsResp = new ResponseData(responderID.ToAsn1Object(), new DerGeneralizedTime(producedAt), new DerSequence(responses), responseExtensions); + + ISigner sig = null; + + try + { + sig = SignerUtilities.GetSigner(signatureName); + + if (random != null) + { + sig.Init(true, new ParametersWithRandom(privateKey, random)); + } + else + { + sig.Init(true, privateKey); + } + } + catch (Exception e) + { + throw new OcspException("exception creating signature: " + e, e); + } + + DerBitString bitSig = null; + + try + { + byte[] encoded = tbsResp.GetDerEncoded(); + sig.BlockUpdate(encoded, 0, encoded.Length); + + bitSig = new DerBitString(sig.GenerateSignature()); + } + catch (Exception e) + { + throw new OcspException("exception processing TBSRequest: " + e, e); + } + + AlgorithmIdentifier sigAlgId = OcspUtilities.GetSigAlgID(signingAlgorithm); + + DerSequence chainSeq = null; + if (chain != null && chain.Length > 0) + { + Asn1EncodableVector v = new Asn1EncodableVector(); + try + { + for (int i = 0; i != chain.Length; i++) + { + v.Add( + X509CertificateStructure.GetInstance( + Asn1Object.FromByteArray(chain[i].GetEncoded()))); + } + } + catch (IOException e) + { + throw new OcspException("error processing certs", e); + } + catch (CertificateEncodingException e) + { + throw new OcspException("error encoding certs", e); + } + + chainSeq = new DerSequence(v); + } + + return new BasicOcspResp(new BasicOcspResponse(tbsResp, sigAlgId, bitSig, chainSeq)); + } + + public BasicOcspResp Generate( + string signingAlgorithm, + AsymmetricKeyParameter privateKey, + X509Certificate[] chain, + DateTime thisUpdate) + { + return Generate(signingAlgorithm, privateKey, chain, thisUpdate, null); + } + + public BasicOcspResp Generate( + string signingAlgorithm, + AsymmetricKeyParameter privateKey, + X509Certificate[] chain, + DateTime producedAt, + SecureRandom random) + { + if (signingAlgorithm == null) + { + throw new ArgumentException("no signing algorithm specified"); + } + + return GenerateResponse(signingAlgorithm, privateKey, chain, producedAt, random); + } + + /** + * Return an IEnumerable of the signature names supported by the generator. + * + * @return an IEnumerable containing recognised names. + */ + public IEnumerable SignatureAlgNames + { + get { return OcspUtilities.AlgNames; } + } + } +} diff --git a/Crypto/src/ocsp/CertificateID.cs b/Crypto/src/ocsp/CertificateID.cs new file mode 100644
index 000000000..a8f035759 --- /dev/null +++ b/Crypto/src/ocsp/CertificateID.cs
@@ -0,0 +1,141 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Ocsp +{ + public class CertificateID + { + public const string HashSha1 = "1.3.14.3.2.26"; + + private readonly CertID id; + + public CertificateID( + CertID id) + { + if (id == null) + throw new ArgumentNullException("id"); + + this.id = id; + } + + /** + * create from an issuer certificate and the serial number of the + * certificate it signed. + * @exception OcspException if any problems occur creating the id fields. + */ + public CertificateID( + string hashAlgorithm, + X509Certificate issuerCert, + BigInteger serialNumber) + { + AlgorithmIdentifier hashAlg = new AlgorithmIdentifier( + new DerObjectIdentifier(hashAlgorithm), DerNull.Instance); + + this.id = CreateCertID(hashAlg, issuerCert, new DerInteger(serialNumber)); + } + + public string HashAlgOid + { + get { return id.HashAlgorithm.ObjectID.Id; } + } + + public byte[] GetIssuerNameHash() + { + return id.IssuerNameHash.GetOctets(); + } + + public byte[] GetIssuerKeyHash() + { + return id.IssuerKeyHash.GetOctets(); + } + + /** + * return the serial number for the certificate associated + * with this request. + */ + public BigInteger SerialNumber + { + get { return id.SerialNumber.Value; } + } + + public bool MatchesIssuer( + X509Certificate issuerCert) + { + return CreateCertID(id.HashAlgorithm, issuerCert, id.SerialNumber).Equals(id); + } + + public CertID ToAsn1Object() + { + return id; + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + CertificateID other = obj as CertificateID; + + if (other == null) + return false; + + return id.ToAsn1Object().Equals(other.id.ToAsn1Object()); + } + + public override int GetHashCode() + { + return id.ToAsn1Object().GetHashCode(); + } + + + /** + * Create a new CertificateID for a new serial number derived from a previous one + * calculated for the same CA certificate. + * + * @param original the previously calculated CertificateID for the CA. + * @param newSerialNumber the serial number for the new certificate of interest. + * + * @return a new CertificateID for newSerialNumber + */ + public static CertificateID DeriveCertificateID(CertificateID original, BigInteger newSerialNumber) + { + return new CertificateID(new CertID(original.id.HashAlgorithm, original.id.IssuerNameHash, + original.id.IssuerKeyHash, new DerInteger(newSerialNumber))); + } + + private static CertID CreateCertID( + AlgorithmIdentifier hashAlg, + X509Certificate issuerCert, + DerInteger serialNumber) + { + try + { + String hashAlgorithm = hashAlg.ObjectID.Id; + + X509Name issuerName = PrincipalUtilities.GetSubjectX509Principal(issuerCert); + byte[] issuerNameHash = DigestUtilities.CalculateDigest( + hashAlgorithm, issuerName.GetEncoded()); + + AsymmetricKeyParameter issuerKey = issuerCert.GetPublicKey(); + SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(issuerKey); + byte[] issuerKeyHash = DigestUtilities.CalculateDigest( + hashAlgorithm, info.PublicKeyData.GetBytes()); + + return new CertID(hashAlg, new DerOctetString(issuerNameHash), + new DerOctetString(issuerKeyHash), serialNumber); + } + catch (Exception e) + { + throw new OcspException("problem creating ID: " + e, e); + } + } + } +} diff --git a/Crypto/src/ocsp/CertificateStatus.cs b/Crypto/src/ocsp/CertificateStatus.cs new file mode 100644
index 000000000..edfcc2582 --- /dev/null +++ b/Crypto/src/ocsp/CertificateStatus.cs
@@ -0,0 +1,9 @@ +using System; + +namespace Org.BouncyCastle.Ocsp +{ + public abstract class CertificateStatus + { + public static readonly CertificateStatus Good = null; + } +} diff --git a/Crypto/src/ocsp/OCSPException.cs b/Crypto/src/ocsp/OCSPException.cs new file mode 100644
index 000000000..44eed9f32 --- /dev/null +++ b/Crypto/src/ocsp/OCSPException.cs
@@ -0,0 +1,25 @@ +using System; + +namespace Org.BouncyCastle.Ocsp +{ + public class OcspException + : Exception + { + public OcspException() + { + } + + public OcspException( + string message) + : base(message) + { + } + + public OcspException( + string message, + Exception e) + : base(message, e) + { + } + } +} diff --git a/Crypto/src/ocsp/OCSPReq.cs b/Crypto/src/ocsp/OCSPReq.cs new file mode 100644
index 000000000..84808e50a --- /dev/null +++ b/Crypto/src/ocsp/OCSPReq.cs
@@ -0,0 +1,268 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.Ocsp +{ + /** + * <pre> + * OcspRequest ::= SEQUENCE { + * tbsRequest TBSRequest, + * optionalSignature [0] EXPLICIT Signature OPTIONAL } + * + * TBSRequest ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1, + * requestorName [1] EXPLICIT GeneralName OPTIONAL, + * requestList SEQUENCE OF Request, + * requestExtensions [2] EXPLICIT Extensions OPTIONAL } + * + * Signature ::= SEQUENCE { + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING, + * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL} + * + * Version ::= INTEGER { v1(0) } + * + * Request ::= SEQUENCE { + * reqCert CertID, + * singleRequestExtensions [0] EXPLICIT Extensions OPTIONAL } + * + * CertID ::= SEQUENCE { + * hashAlgorithm AlgorithmIdentifier, + * issuerNameHash OCTET STRING, -- Hash of Issuer's DN + * issuerKeyHash OCTET STRING, -- Hash of Issuers public key + * serialNumber CertificateSerialNumber } + * </pre> + */ + public class OcspReq + : X509ExtensionBase + { + private OcspRequest req; + + public OcspReq( + OcspRequest req) + { + this.req = req; + } + + public OcspReq( + byte[] req) + : this(new Asn1InputStream(req)) + { + } + + public OcspReq( + Stream inStr) + : this(new Asn1InputStream(inStr)) + { + } + + private OcspReq( + Asn1InputStream aIn) + { + try + { + this.req = OcspRequest.GetInstance(aIn.ReadObject()); + } + catch (ArgumentException e) + { + throw new IOException("malformed request: " + e.Message); + } + catch (InvalidCastException e) + { + throw new IOException("malformed request: " + e.Message); + } + } + + /** + * Return the DER encoding of the tbsRequest field. + * @return DER encoding of tbsRequest + * @throws OcspException in the event of an encoding error. + */ + public byte[] GetTbsRequest() + { + try + { + return req.TbsRequest.GetEncoded(); + } + catch (IOException e) + { + throw new OcspException("problem encoding tbsRequest", e); + } + } + + public int Version + { + get { return req.TbsRequest.Version.Value.IntValue + 1; } + } + + public GeneralName RequestorName + { + get { return GeneralName.GetInstance(req.TbsRequest.RequestorName); } + } + + public Req[] GetRequestList() + { + Asn1Sequence seq = req.TbsRequest.RequestList; + Req[] requests = new Req[seq.Count]; + + for (int i = 0; i != requests.Length; i++) + { + requests[i] = new Req(Request.GetInstance(seq[i])); + } + + return requests; + } + + public X509Extensions RequestExtensions + { + get { return X509Extensions.GetInstance(req.TbsRequest.RequestExtensions); } + } + + protected override X509Extensions GetX509Extensions() + { + return RequestExtensions; + } + + /** + * return the object identifier representing the signature algorithm + */ + public string SignatureAlgOid + { + get + { + if (!this.IsSigned) + return null; + + return req.OptionalSignature.SignatureAlgorithm.ObjectID.Id; + } + } + + public byte[] GetSignature() + { + if (!this.IsSigned) + return null; + + return req.OptionalSignature.SignatureValue.GetBytes(); + } + + private IList GetCertList() + { + // load the certificates if we have any + + IList certs = Platform.CreateArrayList(); + Asn1Sequence s = req.OptionalSignature.Certs; + + if (s != null) + { + foreach (Asn1Encodable ae in s) + { + try + { + certs.Add(new X509CertificateParser().ReadCertificate(ae.GetEncoded())); + } + catch (Exception e) + { + throw new OcspException("can't re-encode certificate!", e); + } + } + } + + return certs; + } + + public X509Certificate[] GetCerts() + { + if (!this.IsSigned) + return null; + + IList certs = this.GetCertList(); + X509Certificate[] result = new X509Certificate[certs.Count]; + for (int i = 0; i < certs.Count; ++i) + { + result[i] = (X509Certificate)certs[i]; + } + return result; + } + + /** + * If the request is signed return a possibly empty CertStore containing the certificates in the + * request. If the request is not signed the method returns null. + * + * @return null if not signed, a CertStore otherwise + * @throws OcspException + */ + public IX509Store GetCertificates( + string type) + { + if (!this.IsSigned) + return null; + + try + { + return X509StoreFactory.Create( + "Certificate/" + type, + new X509CollectionStoreParameters(this.GetCertList())); + } + catch (Exception e) + { + throw new OcspException("can't setup the CertStore", e); + } + } + + /** + * Return whether or not this request is signed. + * + * @return true if signed false otherwise. + */ + public bool IsSigned + { + get { return req.OptionalSignature != null; } + } + + /** + * Verify the signature against the TBSRequest object we contain. + */ + public bool Verify( + AsymmetricKeyParameter publicKey) + { + if (!this.IsSigned) + throw new OcspException("attempt to Verify signature on unsigned object"); + + try + { + ISigner signature = SignerUtilities.GetSigner(this.SignatureAlgOid); + + signature.Init(false, publicKey); + + byte[] encoded = req.TbsRequest.GetEncoded(); + + signature.BlockUpdate(encoded, 0, encoded.Length); + + return signature.VerifySignature(this.GetSignature()); + } + catch (Exception e) + { + throw new OcspException("exception processing sig: " + e, e); + } + } + + /** + * return the ASN.1 encoded representation of this object. + */ + public byte[] GetEncoded() + { + return req.GetEncoded(); + } + } +} diff --git a/Crypto/src/ocsp/OCSPReqGenerator.cs b/Crypto/src/ocsp/OCSPReqGenerator.cs new file mode 100644
index 000000000..8032a4598 --- /dev/null +++ b/Crypto/src/ocsp/OCSPReqGenerator.cs
@@ -0,0 +1,243 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Ocsp +{ + public class OcspReqGenerator + { + private IList list = Platform.CreateArrayList(); + private GeneralName requestorName = null; + private X509Extensions requestExtensions = null; + + private class RequestObject + { + internal CertificateID certId; + internal X509Extensions extensions; + + public RequestObject( + CertificateID certId, + X509Extensions extensions) + { + this.certId = certId; + this.extensions = extensions; + } + + public Request ToRequest() + { + return new Request(certId.ToAsn1Object(), extensions); + } + } + + /** + * Add a request for the given CertificateID. + * + * @param certId certificate ID of interest + */ + public void AddRequest( + CertificateID certId) + { + list.Add(new RequestObject(certId, null)); + } + + /** + * Add a request with extensions + * + * @param certId certificate ID of interest + * @param singleRequestExtensions the extensions to attach to the request + */ + public void AddRequest( + CertificateID certId, + X509Extensions singleRequestExtensions) + { + list.Add(new RequestObject(certId, singleRequestExtensions)); + } + + /** + * Set the requestor name to the passed in X509Principal + * + * @param requestorName a X509Principal representing the requestor name. + */ + public void SetRequestorName( + X509Name requestorName) + { + try + { + this.requestorName = new GeneralName(GeneralName.DirectoryName, requestorName); + } + catch (Exception e) + { + throw new ArgumentException("cannot encode principal", e); + } + } + + public void SetRequestorName( + GeneralName requestorName) + { + this.requestorName = requestorName; + } + + public void SetRequestExtensions( + X509Extensions requestExtensions) + { + this.requestExtensions = requestExtensions; + } + + private OcspReq GenerateRequest( + DerObjectIdentifier signingAlgorithm, + AsymmetricKeyParameter privateKey, + X509Certificate[] chain, + SecureRandom random) + { + Asn1EncodableVector requests = new Asn1EncodableVector(); + + foreach (RequestObject reqObj in list) + { + try + { + requests.Add(reqObj.ToRequest()); + } + catch (Exception e) + { + throw new OcspException("exception creating Request", e); + } + } + + TbsRequest tbsReq = new TbsRequest(requestorName, new DerSequence(requests), requestExtensions); + + ISigner sig = null; + Signature signature = null; + + if (signingAlgorithm != null) + { + if (requestorName == null) + { + throw new OcspException("requestorName must be specified if request is signed."); + } + + try + { + sig = SignerUtilities.GetSigner(signingAlgorithm.Id); + if (random != null) + { + sig.Init(true, new ParametersWithRandom(privateKey, random)); + } + else + { + sig.Init(true, privateKey); + } + } + catch (Exception e) + { + throw new OcspException("exception creating signature: " + e, e); + } + + DerBitString bitSig = null; + + try + { + byte[] encoded = tbsReq.GetEncoded(); + sig.BlockUpdate(encoded, 0, encoded.Length); + + bitSig = new DerBitString(sig.GenerateSignature()); + } + catch (Exception e) + { + throw new OcspException("exception processing TBSRequest: " + e, e); + } + + AlgorithmIdentifier sigAlgId = new AlgorithmIdentifier(signingAlgorithm, DerNull.Instance); + + if (chain != null && chain.Length > 0) + { + Asn1EncodableVector v = new Asn1EncodableVector(); + try + { + for (int i = 0; i != chain.Length; i++) + { + v.Add( + X509CertificateStructure.GetInstance( + Asn1Object.FromByteArray(chain[i].GetEncoded()))); + } + } + catch (IOException e) + { + throw new OcspException("error processing certs", e); + } + catch (CertificateEncodingException e) + { + throw new OcspException("error encoding certs", e); + } + + signature = new Signature(sigAlgId, bitSig, new DerSequence(v)); + } + else + { + signature = new Signature(sigAlgId, bitSig); + } + } + + return new OcspReq(new OcspRequest(tbsReq, signature)); + } + + /** + * Generate an unsigned request + * + * @return the OcspReq + * @throws OcspException + */ + public OcspReq Generate() + { + return GenerateRequest(null, null, null, null); + } + + public OcspReq Generate( + string signingAlgorithm, + AsymmetricKeyParameter privateKey, + X509Certificate[] chain) + { + return Generate(signingAlgorithm, privateKey, chain, null); + } + + public OcspReq Generate( + string signingAlgorithm, + AsymmetricKeyParameter privateKey, + X509Certificate[] chain, + SecureRandom random) + { + if (signingAlgorithm == null) + throw new ArgumentException("no signing algorithm specified"); + + try + { + DerObjectIdentifier oid = OcspUtilities.GetAlgorithmOid(signingAlgorithm); + + return GenerateRequest(oid, privateKey, chain, random); + } + catch (ArgumentException) + { + throw new ArgumentException("unknown signing algorithm specified: " + signingAlgorithm); + } + } + + /** + * Return an IEnumerable of the signature names supported by the generator. + * + * @return an IEnumerable containing recognised names. + */ + public IEnumerable SignatureAlgNames + { + get { return OcspUtilities.AlgNames; } + } + } +} diff --git a/Crypto/src/ocsp/OCSPResp.cs b/Crypto/src/ocsp/OCSPResp.cs new file mode 100644
index 000000000..dc99c6a9a --- /dev/null +++ b/Crypto/src/ocsp/OCSPResp.cs
@@ -0,0 +1,100 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; + +namespace Org.BouncyCastle.Ocsp +{ + public class OcspResp + { + private OcspResponse resp; + + public OcspResp( + OcspResponse resp) + { + this.resp = resp; + } + + public OcspResp( + byte[] resp) + : this(new Asn1InputStream(resp)) + { + } + + public OcspResp( + Stream inStr) + : this(new Asn1InputStream(inStr)) + { + } + + private OcspResp( + Asn1InputStream aIn) + { + try + { + this.resp = OcspResponse.GetInstance(aIn.ReadObject()); + } + catch (Exception e) + { + throw new IOException("malformed response: " + e.Message, e); + } + } + + public int Status + { + get { return this.resp.ResponseStatus.Value.IntValue; } + } + + public object GetResponseObject() + { + ResponseBytes rb = this.resp.ResponseBytes; + + if (rb == null) + return null; + + if (rb.ResponseType.Equals(OcspObjectIdentifiers.PkixOcspBasic)) + { + try + { + return new BasicOcspResp( + BasicOcspResponse.GetInstance( + Asn1Object.FromByteArray(rb.Response.GetOctets()))); + } + catch (Exception e) + { + throw new OcspException("problem decoding object: " + e, e); + } + } + + return rb.Response; + } + + /** + * return the ASN.1 encoded representation of this object. + */ + public byte[] GetEncoded() + { + return resp.GetEncoded(); + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + OcspResp other = obj as OcspResp; + + if (other == null) + return false; + + return resp.Equals(other.resp); + } + + public override int GetHashCode() + { + return resp.GetHashCode(); + } + } +} diff --git a/Crypto/src/ocsp/OCSPRespGenerator.cs b/Crypto/src/ocsp/OCSPRespGenerator.cs new file mode 100644
index 000000000..e0eb9ae90 --- /dev/null +++ b/Crypto/src/ocsp/OCSPRespGenerator.cs
@@ -0,0 +1,54 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; + +namespace Org.BouncyCastle.Ocsp +{ + /** + * base generator for an OCSP response - at the moment this only supports the + * generation of responses containing BasicOCSP responses. + */ + public class OCSPRespGenerator + { + public const int Successful = 0; // Response has valid confirmations + public const int MalformedRequest = 1; // Illegal confirmation request + public const int InternalError = 2; // Internal error in issuer + public const int TryLater = 3; // Try again later + // (4) is not used + public const int SigRequired = 5; // Must sign the request + public const int Unauthorized = 6; // Request unauthorized + + public OcspResp Generate( + int status, + object response) + { + if (response == null) + { + return new OcspResp(new OcspResponse(new OcspResponseStatus(status),null)); + } + if (response is BasicOcspResp) + { + BasicOcspResp r = (BasicOcspResp)response; + Asn1OctetString octs; + + try + { + octs = new DerOctetString(r.GetEncoded()); + } + catch (Exception e) + { + throw new OcspException("can't encode object.", e); + } + + ResponseBytes rb = new ResponseBytes( + OcspObjectIdentifiers.PkixOcspBasic, octs); + + return new OcspResp(new OcspResponse( + new OcspResponseStatus(status), rb)); + } + + throw new OcspException("unknown response object"); + } + } +} diff --git a/Crypto/src/ocsp/OCSPRespStatus.cs b/Crypto/src/ocsp/OCSPRespStatus.cs new file mode 100644
index 000000000..9c00c7035 --- /dev/null +++ b/Crypto/src/ocsp/OCSPRespStatus.cs
@@ -0,0 +1,22 @@ +using System; + +namespace Org.BouncyCastle.Ocsp +{ + [Obsolete("Use version with correct spelling 'OcspRespStatus'")] + public abstract class OcscpRespStatus : OcspRespStatus + { + } + + public abstract class OcspRespStatus + { + /** + * note 4 is not used. + */ + public const int Successful = 0; // --Response has valid confirmations + public const int MalformedRequest = 1; // --Illegal confirmation request + public const int InternalError = 2; // --Internal error in issuer + public const int TryLater = 3; // --Try again later + public const int SigRequired = 5; // --Must sign the request + public const int Unauthorized = 6; // --Request unauthorized + } +} diff --git a/Crypto/src/ocsp/OCSPUtil.cs b/Crypto/src/ocsp/OCSPUtil.cs new file mode 100644
index 000000000..dad56dc39 --- /dev/null +++ b/Crypto/src/ocsp/OCSPUtil.cs
@@ -0,0 +1,133 @@ +using System; +using System.Collections; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Ocsp +{ + class OcspUtilities + { + private static readonly IDictionary algorithms = Platform.CreateHashtable(); + private static readonly IDictionary oids = Platform.CreateHashtable(); + private static readonly ISet noParams = new HashSet(); + + static OcspUtilities() + { + algorithms.Add("MD2WITHRSAENCRYPTION", PkcsObjectIdentifiers.MD2WithRsaEncryption); + algorithms.Add("MD2WITHRSA", PkcsObjectIdentifiers.MD2WithRsaEncryption); + algorithms.Add("MD5WITHRSAENCRYPTION", PkcsObjectIdentifiers.MD5WithRsaEncryption); + algorithms.Add("MD5WITHRSA", PkcsObjectIdentifiers.MD5WithRsaEncryption); + algorithms.Add("SHA1WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha1WithRsaEncryption); + algorithms.Add("SHA1WITHRSA", PkcsObjectIdentifiers.Sha1WithRsaEncryption); + algorithms.Add("SHA224WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha224WithRsaEncryption); + algorithms.Add("SHA224WITHRSA", PkcsObjectIdentifiers.Sha224WithRsaEncryption); + algorithms.Add("SHA256WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha256WithRsaEncryption); + algorithms.Add("SHA256WITHRSA", PkcsObjectIdentifiers.Sha256WithRsaEncryption); + algorithms.Add("SHA384WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha384WithRsaEncryption); + algorithms.Add("SHA384WITHRSA", PkcsObjectIdentifiers.Sha384WithRsaEncryption); + algorithms.Add("SHA512WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha512WithRsaEncryption); + algorithms.Add("SHA512WITHRSA", PkcsObjectIdentifiers.Sha512WithRsaEncryption); + algorithms.Add("RIPEMD160WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160); + algorithms.Add("RIPEMD160WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160); + algorithms.Add("RIPEMD128WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128); + algorithms.Add("RIPEMD128WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128); + algorithms.Add("RIPEMD256WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256); + algorithms.Add("RIPEMD256WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256); + algorithms.Add("SHA1WITHDSA", X9ObjectIdentifiers.IdDsaWithSha1); + algorithms.Add("DSAWITHSHA1", X9ObjectIdentifiers.IdDsaWithSha1); + algorithms.Add("SHA224WITHDSA", NistObjectIdentifiers.DsaWithSha224); + algorithms.Add("SHA256WITHDSA", NistObjectIdentifiers.DsaWithSha256); + algorithms.Add("SHA1WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha1); + algorithms.Add("ECDSAWITHSHA1", X9ObjectIdentifiers.ECDsaWithSha1); + algorithms.Add("SHA224WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha224); + algorithms.Add("SHA256WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha256); + algorithms.Add("SHA384WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha384); + algorithms.Add("SHA512WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha512); + algorithms.Add("GOST3411WITHGOST3410", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94); + algorithms.Add("GOST3411WITHGOST3410-94", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94); + + oids.Add(PkcsObjectIdentifiers.MD2WithRsaEncryption, "MD2WITHRSA"); + oids.Add(PkcsObjectIdentifiers.MD5WithRsaEncryption, "MD5WITHRSA"); + oids.Add(PkcsObjectIdentifiers.Sha1WithRsaEncryption, "SHA1WITHRSA"); + oids.Add(PkcsObjectIdentifiers.Sha224WithRsaEncryption, "SHA224WITHRSA"); + oids.Add(PkcsObjectIdentifiers.Sha256WithRsaEncryption, "SHA256WITHRSA"); + oids.Add(PkcsObjectIdentifiers.Sha384WithRsaEncryption, "SHA384WITHRSA"); + oids.Add(PkcsObjectIdentifiers.Sha512WithRsaEncryption, "SHA512WITHRSA"); + oids.Add(TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160, "RIPEMD160WITHRSA"); + oids.Add(TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128, "RIPEMD128WITHRSA"); + oids.Add(TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256, "RIPEMD256WITHRSA"); + oids.Add(X9ObjectIdentifiers.IdDsaWithSha1, "SHA1WITHDSA"); + oids.Add(NistObjectIdentifiers.DsaWithSha224, "SHA224WITHDSA"); + oids.Add(NistObjectIdentifiers.DsaWithSha256, "SHA256WITHDSA"); + oids.Add(X9ObjectIdentifiers.ECDsaWithSha1, "SHA1WITHECDSA"); + oids.Add(X9ObjectIdentifiers.ECDsaWithSha224, "SHA224WITHECDSA"); + oids.Add(X9ObjectIdentifiers.ECDsaWithSha256, "SHA256WITHECDSA"); + oids.Add(X9ObjectIdentifiers.ECDsaWithSha384, "SHA384WITHECDSA"); + oids.Add(X9ObjectIdentifiers.ECDsaWithSha512, "SHA512WITHECDSA"); + oids.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94, "GOST3411WITHGOST3410"); + + // + // According to RFC 3279, the ASN.1 encoding SHALL (id-dsa-with-sha1) or MUST (ecdsa-with-SHA*) omit the parameters field. + // The parameters field SHALL be NULL for RSA based signature algorithms. + // + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha1); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha224); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha256); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha384); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha512); + noParams.Add(X9ObjectIdentifiers.IdDsaWithSha1); + noParams.Add(NistObjectIdentifiers.DsaWithSha224); + noParams.Add(NistObjectIdentifiers.DsaWithSha256); + } + + internal static DerObjectIdentifier GetAlgorithmOid( + string algorithmName) + { + algorithmName = algorithmName.ToUpperInvariant(); + + if (algorithms.Contains(algorithmName)) + { + return (DerObjectIdentifier)algorithms[algorithmName]; + } + + return new DerObjectIdentifier(algorithmName); + } + + + internal static string GetAlgorithmName( + DerObjectIdentifier oid) + { + if (oids.Contains(oid)) + { + return (string)oids[oid]; + } + + return oid.Id; + } + + internal static AlgorithmIdentifier GetSigAlgID( + DerObjectIdentifier sigOid) + { + if (noParams.Contains(sigOid)) + { + return new AlgorithmIdentifier(sigOid); + } + + return new AlgorithmIdentifier(sigOid, DerNull.Instance); + } + + internal static IEnumerable AlgNames + { + get { return new EnumerableProxy(algorithms.Keys); } + } + } +} diff --git a/Crypto/src/ocsp/Req.cs b/Crypto/src/ocsp/Req.cs new file mode 100644
index 000000000..68fd9f12a --- /dev/null +++ b/Crypto/src/ocsp/Req.cs
@@ -0,0 +1,38 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Ocsp +{ + public class Req + : X509ExtensionBase + { + private Request req; + + public Req( + Request req) + { + this.req = req; + } + + public CertificateID GetCertID() + { + return new CertificateID(req.ReqCert); + } + + public X509Extensions SingleRequestExtensions + { + get { return req.SingleRequestExtensions; } + } + + protected override X509Extensions GetX509Extensions() + { + return SingleRequestExtensions; + } + } +} diff --git a/Crypto/src/ocsp/RespData.cs b/Crypto/src/ocsp/RespData.cs new file mode 100644
index 000000000..105726ca7 --- /dev/null +++ b/Crypto/src/ocsp/RespData.cs
@@ -0,0 +1,60 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Ocsp +{ + public class RespData + : X509ExtensionBase + { + internal readonly ResponseData data; + + public RespData( + ResponseData data) + { + this.data = data; + } + + public int Version + { + get { return data.Version.Value.IntValue + 1; } + } + + public RespID GetResponderId() + { + return new RespID(data.ResponderID); + } + + public DateTime ProducedAt + { + get { return data.ProducedAt.ToDateTime(); } + } + + public SingleResp[] GetResponses() + { + Asn1Sequence s = data.Responses; + SingleResp[] rs = new SingleResp[s.Count]; + + for (int i = 0; i != rs.Length; i++) + { + rs[i] = new SingleResp(SingleResponse.GetInstance(s[i])); + } + + return rs; + } + + public X509Extensions ResponseExtensions + { + get { return data.ResponseExtensions; } + } + + protected override X509Extensions GetX509Extensions() + { + return ResponseExtensions; + } + } +} diff --git a/Crypto/src/ocsp/RespID.cs b/Crypto/src/ocsp/RespID.cs new file mode 100644
index 000000000..3238b26da --- /dev/null +++ b/Crypto/src/ocsp/RespID.cs
@@ -0,0 +1,72 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Ocsp +{ + /** + * Carrier for a ResponderID. + */ + public class RespID + { + internal readonly ResponderID id; + + public RespID( + ResponderID id) + { + this.id = id; + } + + public RespID( + X509Name name) + { + this.id = new ResponderID(name); + } + + public RespID( + AsymmetricKeyParameter publicKey) + { + try + { + SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(publicKey); + + byte[] keyHash = DigestUtilities.CalculateDigest("SHA1", info.PublicKeyData.GetBytes()); + + this.id = new ResponderID(new DerOctetString(keyHash)); + } + catch (Exception e) + { + throw new OcspException("problem creating ID: " + e, e); + } + } + + public ResponderID ToAsn1Object() + { + return id; + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + RespID other = obj as RespID; + + if (other == null) + return false; + + return id.Equals(other.id); + } + + public override int GetHashCode() + { + return id.GetHashCode(); + } + } +} diff --git a/Crypto/src/ocsp/RevokedStatus.cs b/Crypto/src/ocsp/RevokedStatus.cs new file mode 100644
index 000000000..6e5ad1b26 --- /dev/null +++ b/Crypto/src/ocsp/RevokedStatus.cs
@@ -0,0 +1,58 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Ocsp +{ + /** + * wrapper for the RevokedInfo object + */ + public class RevokedStatus + : CertificateStatus + { + internal readonly RevokedInfo info; + + public RevokedStatus( + RevokedInfo info) + { + this.info = info; + } + + public RevokedStatus( + DateTime revocationDate, + int reason) + { + this.info = new RevokedInfo(new DerGeneralizedTime(revocationDate), new CrlReason(reason)); + } + + public DateTime RevocationTime + { + get { return info.RevocationTime.ToDateTime(); } + } + + public bool HasRevocationReason + { + get { return (info.RevocationReason != null); } + } + + /** + * return the revocation reason. Note: this field is optional, test for it + * with hasRevocationReason() first. + * @exception InvalidOperationException if a reason is asked for and none is avaliable + */ + public int RevocationReason + { + get + { + if (info.RevocationReason == null) + { + throw new InvalidOperationException("attempt to get a reason where none is available"); + } + + return info.RevocationReason.Value.IntValue; + } + } + } +} diff --git a/Crypto/src/ocsp/SingleResp.cs b/Crypto/src/ocsp/SingleResp.cs new file mode 100644
index 000000000..b8979c538 --- /dev/null +++ b/Crypto/src/ocsp/SingleResp.cs
@@ -0,0 +1,81 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Utilities.Date; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Ocsp +{ + public class SingleResp + : X509ExtensionBase + { + internal readonly SingleResponse resp; + + public SingleResp( + SingleResponse resp) + { + this.resp = resp; + } + + public CertificateID GetCertID() + { + return new CertificateID(resp.CertId); + } + + /** + * Return the status object for the response - null indicates good. + * + * @return the status object for the response, null if it is good. + */ + public object GetCertStatus() + { + CertStatus s = resp.CertStatus; + + if (s.TagNo == 0) + { + return null; // good + } + + if (s.TagNo == 1) + { + return new RevokedStatus(RevokedInfo.GetInstance(s.Status)); + } + + return new UnknownStatus(); + } + + public DateTime ThisUpdate + { + get { return resp.ThisUpdate.ToDateTime(); } + } + + /** + * return the NextUpdate value - note: this is an optional field so may + * be returned as null. + * + * @return nextUpdate, or null if not present. + */ + public DateTimeObject NextUpdate + { + get + { + return resp.NextUpdate == null + ? null + : new DateTimeObject(resp.NextUpdate.ToDateTime()); + } + } + + public X509Extensions SingleExtensions + { + get { return resp.SingleExtensions; } + } + + protected override X509Extensions GetX509Extensions() + { + return SingleExtensions; + } + } +} diff --git a/Crypto/src/ocsp/UnknownStatus.cs b/Crypto/src/ocsp/UnknownStatus.cs new file mode 100644
index 000000000..c0f7a3a64 --- /dev/null +++ b/Crypto/src/ocsp/UnknownStatus.cs
@@ -0,0 +1,15 @@ +using System; + +namespace Org.BouncyCastle.Ocsp +{ + /** + * wrapper for the UnknownInfo object + */ + public class UnknownStatus + : CertificateStatus + { + public UnknownStatus() + { + } + } +} diff --git a/Crypto/src/openpgp/IStreamGenerator.cs b/Crypto/src/openpgp/IStreamGenerator.cs new file mode 100644
index 000000000..379213a66 --- /dev/null +++ b/Crypto/src/openpgp/IStreamGenerator.cs
@@ -0,0 +1,7 @@ +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + public interface IStreamGenerator + { + void Close(); + } +} diff --git a/Crypto/src/openpgp/PGPKeyRing.cs b/Crypto/src/openpgp/PGPKeyRing.cs new file mode 100644
index 000000000..6426f3f25 --- /dev/null +++ b/Crypto/src/openpgp/PGPKeyRing.cs
@@ -0,0 +1,79 @@ +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + public abstract class PgpKeyRing + : PgpObject + { + internal PgpKeyRing() + { + } + + internal static TrustPacket ReadOptionalTrustPacket( + BcpgInputStream bcpgInput) + { + return (bcpgInput.NextPacketTag() == PacketTag.Trust) + ? (TrustPacket) bcpgInput.ReadPacket() + : null; + } + + internal static IList ReadSignaturesAndTrust( + BcpgInputStream bcpgInput) + { + try + { + IList sigList = Platform.CreateArrayList(); + + while (bcpgInput.NextPacketTag() == PacketTag.Signature) + { + SignaturePacket signaturePacket = (SignaturePacket) bcpgInput.ReadPacket(); + TrustPacket trustPacket = ReadOptionalTrustPacket(bcpgInput); + + sigList.Add(new PgpSignature(signaturePacket, trustPacket)); + } + + return sigList; + } + catch (PgpException e) + { + throw new IOException("can't create signature object: " + e.Message, e); + } + } + + internal static void ReadUserIDs( + BcpgInputStream bcpgInput, + out IList ids, + out IList idTrusts, + out IList idSigs) + { + ids = Platform.CreateArrayList(); + idTrusts = Platform.CreateArrayList(); + idSigs = Platform.CreateArrayList(); + + while (bcpgInput.NextPacketTag() == PacketTag.UserId + || bcpgInput.NextPacketTag() == PacketTag.UserAttribute) + { + Packet obj = bcpgInput.ReadPacket(); + if (obj is UserIdPacket) + { + UserIdPacket id = (UserIdPacket)obj; + ids.Add(id.GetId()); + } + else + { + UserAttributePacket user = (UserAttributePacket) obj; + ids.Add(new PgpUserAttributeSubpacketVector(user.GetSubpackets())); + } + + idTrusts.Add( + ReadOptionalTrustPacket(bcpgInput)); + + idSigs.Add( + ReadSignaturesAndTrust(bcpgInput)); + } + } + } +} diff --git a/Crypto/src/openpgp/PGPObject.cs b/Crypto/src/openpgp/PGPObject.cs new file mode 100644
index 000000000..d38276cb6 --- /dev/null +++ b/Crypto/src/openpgp/PGPObject.cs
@@ -0,0 +1,9 @@ +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + public abstract class PgpObject + { + internal PgpObject() + { + } + } +} diff --git a/Crypto/src/openpgp/PGPUserAttributeSubpacketVectorGenerator.cs b/Crypto/src/openpgp/PGPUserAttributeSubpacketVectorGenerator.cs new file mode 100644
index 000000000..9d56c8bc3 --- /dev/null +++ b/Crypto/src/openpgp/PGPUserAttributeSubpacketVectorGenerator.cs
@@ -0,0 +1,33 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Bcpg.Attr; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + public class PgpUserAttributeSubpacketVectorGenerator + { + private IList list = Platform.CreateArrayList(); + + public virtual void SetImageAttribute( + ImageAttrib.Format imageType, + byte[] imageData) + { + if (imageData == null) + throw new ArgumentException("attempt to set null image", "imageData"); + + list.Add(new ImageAttrib(imageType, imageData)); + } + + public virtual PgpUserAttributeSubpacketVector Generate() + { + UserAttributeSubpacket[] a = new UserAttributeSubpacket[list.Count]; + for (int i = 0; i < list.Count; ++i) + { + a[i] = (UserAttributeSubpacket)list[i]; + } + return new PgpUserAttributeSubpacketVector(a); + } + } +} diff --git a/Crypto/src/openpgp/PgpCompressedData.cs b/Crypto/src/openpgp/PgpCompressedData.cs new file mode 100644
index 000000000..e64a17c9c --- /dev/null +++ b/Crypto/src/openpgp/PgpCompressedData.cs
@@ -0,0 +1,50 @@ +using System.IO; + +using Org.BouncyCastle.Apache.Bzip2; +using Org.BouncyCastle.Utilities.Zlib; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks>Compressed data objects</remarks> + public class PgpCompressedData + : PgpObject + { + private readonly CompressedDataPacket data; + + public PgpCompressedData( + BcpgInputStream bcpgInput) + { + data = (CompressedDataPacket) bcpgInput.ReadPacket(); + } + + /// <summary>The algorithm used for compression</summary> + public CompressionAlgorithmTag Algorithm + { + get { return data.Algorithm; } + } + + /// <summary>Get the raw input stream contained in the object.</summary> + public Stream GetInputStream() + { + return data.GetInputStream(); + } + + /// <summary>Return an uncompressed input stream which allows reading of the compressed data.</summary> + public Stream GetDataStream() + { + switch (Algorithm) + { + case CompressionAlgorithmTag.Uncompressed: + return GetInputStream(); + case CompressionAlgorithmTag.Zip: + return new ZInputStream(GetInputStream(), true); + case CompressionAlgorithmTag.ZLib: + return new ZInputStream(GetInputStream()); + case CompressionAlgorithmTag.BZip2: + return new CBZip2InputStream(GetInputStream()); + default: + throw new PgpException("can't recognise compression algorithm: " + Algorithm); + } + } + } +} diff --git a/Crypto/src/openpgp/PgpCompressedDataGenerator.cs b/Crypto/src/openpgp/PgpCompressedDataGenerator.cs new file mode 100644
index 000000000..c758ecb05 --- /dev/null +++ b/Crypto/src/openpgp/PgpCompressedDataGenerator.cs
@@ -0,0 +1,203 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Apache.Bzip2; +using Org.BouncyCastle.Utilities.Zlib; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks>Class for producing compressed data packets.</remarks> + public class PgpCompressedDataGenerator + : IStreamGenerator + { + private readonly CompressionAlgorithmTag algorithm; + private readonly int compression; + + private Stream dOut; + private BcpgOutputStream pkOut; + + public PgpCompressedDataGenerator( + CompressionAlgorithmTag algorithm) + : this(algorithm, JZlib.Z_DEFAULT_COMPRESSION) + { + } + + public PgpCompressedDataGenerator( + CompressionAlgorithmTag algorithm, + int compression) + { + switch (algorithm) + { + case CompressionAlgorithmTag.Uncompressed: + case CompressionAlgorithmTag.Zip: + case CompressionAlgorithmTag.ZLib: + case CompressionAlgorithmTag.BZip2: + break; + default: + throw new ArgumentException("unknown compression algorithm", "algorithm"); + } + + if (compression != JZlib.Z_DEFAULT_COMPRESSION) + { + if ((compression < JZlib.Z_NO_COMPRESSION) || (compression > JZlib.Z_BEST_COMPRESSION)) + { + throw new ArgumentException("unknown compression level: " + compression); + } + } + + this.algorithm = algorithm; + this.compression = compression; + } + + /// <summary> + /// <p> + /// Return an output stream which will save the data being written to + /// the compressed object. + /// </p> + /// <p> + /// The stream created can be closed off by either calling Close() + /// on the stream or Close() on the generator. Closing the returned + /// stream does not close off the Stream parameter <c>outStr</c>. + /// </p> + /// </summary> + /// <param name="outStr">Stream to be used for output.</param> + /// <returns>A Stream for output of the compressed data.</returns> + /// <exception cref="ArgumentNullException"></exception> + /// <exception cref="InvalidOperationException"></exception> + /// <exception cref="IOException"></exception> + public Stream Open( + Stream outStr) + { + if (dOut != null) + throw new InvalidOperationException("generator already in open state"); + if (outStr == null) + throw new ArgumentNullException("outStr"); + + this.pkOut = new BcpgOutputStream(outStr, PacketTag.CompressedData); + + doOpen(); + + return new WrappedGeneratorStream(this, dOut); + } + + /// <summary> + /// <p> + /// Return an output stream which will compress the data as it is written to it. + /// The stream will be written out in chunks according to the size of the passed in buffer. + /// </p> + /// <p> + /// The stream created can be closed off by either calling Close() + /// on the stream or Close() on the generator. Closing the returned + /// stream does not close off the Stream parameter <c>outStr</c>. + /// </p> + /// <p> + /// <b>Note</b>: if the buffer is not a power of 2 in length only the largest power of 2 + /// bytes worth of the buffer will be used. + /// </p> + /// <p> + /// <b>Note</b>: using this may break compatibility with RFC 1991 compliant tools. + /// Only recent OpenPGP implementations are capable of accepting these streams. + /// </p> + /// </summary> + /// <param name="outStr">Stream to be used for output.</param> + /// <param name="buffer">The buffer to use.</param> + /// <returns>A Stream for output of the compressed data.</returns> + /// <exception cref="ArgumentNullException"></exception> + /// <exception cref="InvalidOperationException"></exception> + /// <exception cref="IOException"></exception> + /// <exception cref="PgpException"></exception> + public Stream Open( + Stream outStr, + byte[] buffer) + { + if (dOut != null) + throw new InvalidOperationException("generator already in open state"); + if (outStr == null) + throw new ArgumentNullException("outStr"); + if (buffer == null) + throw new ArgumentNullException("buffer"); + + this.pkOut = new BcpgOutputStream(outStr, PacketTag.CompressedData, buffer); + + doOpen(); + + return new WrappedGeneratorStream(this, dOut); + } + + private void doOpen() + { + pkOut.WriteByte((byte) algorithm); + + switch (algorithm) + { + case CompressionAlgorithmTag.Uncompressed: + dOut = pkOut; + break; + case CompressionAlgorithmTag.Zip: + dOut = new SafeZOutputStream(pkOut, compression, true); + break; + case CompressionAlgorithmTag.ZLib: + dOut = new SafeZOutputStream(pkOut, compression, false); + break; + case CompressionAlgorithmTag.BZip2: + dOut = new SafeCBZip2OutputStream(pkOut); + break; + default: + // Constructor should guard against this possibility + throw new InvalidOperationException(); + } + } + + /// <summary>Close the compressed object.</summary>summary> + public void Close() + { + if (dOut != null) + { + if (dOut != pkOut) + { + dOut.Dispose(); + dOut.Flush(); + } + + dOut = null; + + pkOut.Finish(); + pkOut.Flush(); + pkOut = null; + } + } + + private class SafeCBZip2OutputStream : CBZip2OutputStream + { + public SafeCBZip2OutputStream(Stream output) + : base(output) + { + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + Finish(); + } + } + } + + private class SafeZOutputStream : ZOutputStream + { + public SafeZOutputStream(Stream output, int level, bool nowrap) + : base(output, level, nowrap) + { + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + Finish(); + End(); + } + } + } + } +} diff --git a/Crypto/src/openpgp/PgpDataValidationException.cs b/Crypto/src/openpgp/PgpDataValidationException.cs new file mode 100644
index 000000000..74674da59 --- /dev/null +++ b/Crypto/src/openpgp/PgpDataValidationException.cs
@@ -0,0 +1,15 @@ +using System; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks> + /// Thrown if the IV at the start of a data stream indicates the wrong key is being used. + /// </remarks> + public class PgpDataValidationException + : PgpException + { + public PgpDataValidationException() : base() {} + public PgpDataValidationException(string message) : base(message) {} + public PgpDataValidationException(string message, Exception exception) : base(message, exception) {} + } +} diff --git a/Crypto/src/openpgp/PgpEncryptedData.cs b/Crypto/src/openpgp/PgpEncryptedData.cs new file mode 100644
index 000000000..0d237b56c --- /dev/null +++ b/Crypto/src/openpgp/PgpEncryptedData.cs
@@ -0,0 +1,151 @@ +using System; +using System.Diagnostics; +using System.IO; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + public abstract class PgpEncryptedData + { + internal class TruncatedStream + : BaseInputStream + { + private const int LookAheadSize = 22; + private const int LookAheadBufSize = 512; + private const int LookAheadBufLimit = LookAheadBufSize - LookAheadSize; + + private readonly Stream inStr; + private readonly byte[] lookAhead = new byte[LookAheadBufSize]; + private int bufStart, bufEnd; + + internal TruncatedStream( + Stream inStr) + { + int numRead = Streams.ReadFully(inStr, lookAhead, 0, lookAhead.Length); + + if (numRead < LookAheadSize) + throw new EndOfStreamException(); + + this.inStr = inStr; + this.bufStart = 0; + this.bufEnd = numRead - LookAheadSize; + } + + private int FillBuffer() + { + if (bufEnd < LookAheadBufLimit) + return 0; + + Debug.Assert(bufStart == LookAheadBufLimit); + Debug.Assert(bufEnd == LookAheadBufLimit); + + Array.Copy(lookAhead, LookAheadBufLimit, lookAhead, 0, LookAheadSize); + bufEnd = Streams.ReadFully(inStr, lookAhead, LookAheadSize, LookAheadBufLimit); + bufStart = 0; + return bufEnd; + } + + public override int ReadByte() + { + if (bufStart < bufEnd) + return lookAhead[bufStart++]; + + if (FillBuffer() < 1) + return -1; + + return lookAhead[bufStart++]; + } + + public override int Read(byte[] buf, int off, int len) + { + int avail = bufEnd - bufStart; + + int pos = off; + while (len > avail) + { + Array.Copy(lookAhead, bufStart, buf, pos, avail); + + bufStart += avail; + pos += avail; + len -= avail; + + if ((avail = FillBuffer()) < 1) + return pos - off; + } + + Array.Copy(lookAhead, bufStart, buf, pos, len); + bufStart += len; + + return pos + len - off;; + } + + internal byte[] GetLookAhead() + { + byte[] temp = new byte[LookAheadSize]; + Array.Copy(lookAhead, bufStart, temp, 0, LookAheadSize); + return temp; + } + } + + internal InputStreamPacket encData; + internal Stream encStream; + internal TruncatedStream truncStream; + + internal PgpEncryptedData( + InputStreamPacket encData) + { + this.encData = encData; + } + + /// <summary>Return the raw input stream for the data stream.</summary> + public virtual Stream GetInputStream() + { + return encData.GetInputStream(); + } + + /// <summary>Return true if the message is integrity protected.</summary> + /// <returns>True, if there is a modification detection code namespace associated + /// with this stream.</returns> + public bool IsIntegrityProtected() + { + return encData is SymmetricEncIntegrityPacket; + } + + /// <summary>Note: This can only be called after the message has been read.</summary> + /// <returns>True, if the message verifies, false otherwise</returns> + public bool Verify() + { + if (!IsIntegrityProtected()) + throw new PgpException("data not integrity protected."); + + DigestStream dIn = (DigestStream) encStream; + + // + // make sure we are at the end. + // + while (encStream.ReadByte() >= 0) + { + // do nothing + } + + // + // process the MDC packet + // + byte[] lookAhead = truncStream.GetLookAhead(); + + IDigest hash = dIn.ReadDigest(); + hash.BlockUpdate(lookAhead, 0, 2); + byte[] digest = DigestUtilities.DoFinal(hash); + + byte[] streamDigest = new byte[digest.Length]; + Array.Copy(lookAhead, 2, streamDigest, 0, streamDigest.Length); + + return Arrays.ConstantTimeAreEqual(digest, streamDigest); + } + } +} diff --git a/Crypto/src/openpgp/PgpEncryptedDataGenerator.cs b/Crypto/src/openpgp/PgpEncryptedDataGenerator.cs new file mode 100644
index 000000000..f46f99d37 --- /dev/null +++ b/Crypto/src/openpgp/PgpEncryptedDataGenerator.cs
@@ -0,0 +1,506 @@ +using System; +using System.Collections; +using System.Diagnostics; +using System.IO; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks>Generator for encrypted objects.</remarks> + public class PgpEncryptedDataGenerator + : IStreamGenerator + { + private BcpgOutputStream pOut; + private CipherStream cOut; + private IBufferedCipher c; + private bool withIntegrityPacket; + private bool oldFormat; + private DigestStream digestOut; + + private abstract class EncMethod + : ContainedPacket + { + protected byte[] sessionInfo; + protected SymmetricKeyAlgorithmTag encAlgorithm; + protected KeyParameter key; + + public abstract void AddSessionInfo(byte[] si, SecureRandom random); + } + + private class PbeMethod + : EncMethod + { + private S2k s2k; + + internal PbeMethod( + SymmetricKeyAlgorithmTag encAlgorithm, + S2k s2k, + KeyParameter key) + { + this.encAlgorithm = encAlgorithm; + this.s2k = s2k; + this.key = key; + } + + public KeyParameter GetKey() + { + return key; + } + + public override void AddSessionInfo( + byte[] si, + SecureRandom random) + { + string cName = PgpUtilities.GetSymmetricCipherName(encAlgorithm); + IBufferedCipher c = CipherUtilities.GetCipher(cName + "/CFB/NoPadding"); + + byte[] iv = new byte[c.GetBlockSize()]; + c.Init(true, new ParametersWithRandom(new ParametersWithIV(key, iv), random)); + + this.sessionInfo = c.DoFinal(si, 0, si.Length - 2); + } + + public override void Encode(BcpgOutputStream pOut) + { + SymmetricKeyEncSessionPacket pk = new SymmetricKeyEncSessionPacket( + encAlgorithm, s2k, sessionInfo); + + pOut.WritePacket(pk); + } + } + + private class PubMethod + : EncMethod + { + internal PgpPublicKey pubKey; + internal BigInteger[] data; + + internal PubMethod( + PgpPublicKey pubKey) + { + this.pubKey = pubKey; + } + + public override void AddSessionInfo( + byte[] si, + SecureRandom random) + { + IBufferedCipher c; + + switch (pubKey.Algorithm) + { + case PublicKeyAlgorithmTag.RsaEncrypt: + case PublicKeyAlgorithmTag.RsaGeneral: + c = CipherUtilities.GetCipher("RSA//PKCS1Padding"); + break; + case PublicKeyAlgorithmTag.ElGamalEncrypt: + case PublicKeyAlgorithmTag.ElGamalGeneral: + c = CipherUtilities.GetCipher("ElGamal/ECB/PKCS1Padding"); + break; + case PublicKeyAlgorithmTag.Dsa: + throw new PgpException("Can't use DSA for encryption."); + case PublicKeyAlgorithmTag.ECDsa: + throw new PgpException("Can't use ECDSA for encryption."); + default: + throw new PgpException("unknown asymmetric algorithm: " + pubKey.Algorithm); + } + + AsymmetricKeyParameter akp = pubKey.GetKey(); + + c.Init(true, new ParametersWithRandom(akp, random)); + + byte[] encKey = c.DoFinal(si); + + switch (pubKey.Algorithm) + { + case PublicKeyAlgorithmTag.RsaEncrypt: + case PublicKeyAlgorithmTag.RsaGeneral: + data = new BigInteger[]{ new BigInteger(1, encKey) }; + break; + case PublicKeyAlgorithmTag.ElGamalEncrypt: + case PublicKeyAlgorithmTag.ElGamalGeneral: + int halfLength = encKey.Length / 2; + data = new BigInteger[] + { + new BigInteger(1, encKey, 0, halfLength), + new BigInteger(1, encKey, halfLength, halfLength) + }; + break; + default: + throw new PgpException("unknown asymmetric algorithm: " + encAlgorithm); + } + } + + public override void Encode(BcpgOutputStream pOut) + { + PublicKeyEncSessionPacket pk = new PublicKeyEncSessionPacket( + pubKey.KeyId, pubKey.Algorithm, data); + + pOut.WritePacket(pk); + } + } + + private readonly IList methods = Platform.CreateArrayList(); + private readonly SymmetricKeyAlgorithmTag defAlgorithm; + private readonly SecureRandom rand; + + public PgpEncryptedDataGenerator( + SymmetricKeyAlgorithmTag encAlgorithm) + { + this.defAlgorithm = encAlgorithm; + this.rand = new SecureRandom(); + } + + public PgpEncryptedDataGenerator( + SymmetricKeyAlgorithmTag encAlgorithm, + bool withIntegrityPacket) + { + this.defAlgorithm = encAlgorithm; + this.withIntegrityPacket = withIntegrityPacket; + this.rand = new SecureRandom(); + } + + /// <summary>Existing SecureRandom constructor.</summary> + /// <param name="encAlgorithm">The symmetric algorithm to use.</param> + /// <param name="rand">Source of randomness.</param> + public PgpEncryptedDataGenerator( + SymmetricKeyAlgorithmTag encAlgorithm, + SecureRandom rand) + { + this.defAlgorithm = encAlgorithm; + this.rand = rand; + } + + /// <summary>Creates a cipher stream which will have an integrity packet associated with it.</summary> + public PgpEncryptedDataGenerator( + SymmetricKeyAlgorithmTag encAlgorithm, + bool withIntegrityPacket, + SecureRandom rand) + { + this.defAlgorithm = encAlgorithm; + this.rand = rand; + this.withIntegrityPacket = withIntegrityPacket; + } + + /// <summary>Base constructor.</summary> + /// <param name="encAlgorithm">The symmetric algorithm to use.</param> + /// <param name="rand">Source of randomness.</param> + /// <param name="oldFormat">PGP 2.6.x compatibility required.</param> + public PgpEncryptedDataGenerator( + SymmetricKeyAlgorithmTag encAlgorithm, + SecureRandom rand, + bool oldFormat) + { + this.defAlgorithm = encAlgorithm; + this.rand = rand; + this.oldFormat = oldFormat; + } + + /// <summary> + /// Add a PBE encryption method to the encrypted object using the default algorithm (S2K_SHA1). + /// </summary> + public void AddMethod( + char[] passPhrase) + { + AddMethod(passPhrase, HashAlgorithmTag.Sha1); + } + + /// <summary>Add a PBE encryption method to the encrypted object.</summary> + public void AddMethod( + char[] passPhrase, + HashAlgorithmTag s2kDigest) + { + byte[] iv = new byte[8]; + rand.NextBytes(iv); + + S2k s2k = new S2k(s2kDigest, iv, 0x60); + + methods.Add(new PbeMethod(defAlgorithm, s2k, PgpUtilities.MakeKeyFromPassPhrase(defAlgorithm, s2k, passPhrase))); + } + + /// <summary>Add a public key encrypted session key to the encrypted object.</summary> + public void AddMethod( + PgpPublicKey key) + { + if (!key.IsEncryptionKey) + { + throw new ArgumentException("passed in key not an encryption key!"); + } + + methods.Add(new PubMethod(key)); + } + + private void AddCheckSum( + byte[] sessionInfo) + { + Debug.Assert(sessionInfo != null); + Debug.Assert(sessionInfo.Length >= 3); + + int check = 0; + + for (int i = 1; i < sessionInfo.Length - 2; i++) + { + check += sessionInfo[i]; + } + + sessionInfo[sessionInfo.Length - 2] = (byte)(check >> 8); + sessionInfo[sessionInfo.Length - 1] = (byte)(check); + } + + private byte[] CreateSessionInfo( + SymmetricKeyAlgorithmTag algorithm, + KeyParameter key) + { + byte[] keyBytes = key.GetKey(); + byte[] sessionInfo = new byte[keyBytes.Length + 3]; + sessionInfo[0] = (byte) algorithm; + keyBytes.CopyTo(sessionInfo, 1); + AddCheckSum(sessionInfo); + return sessionInfo; + } + + /// <summary> + /// <p> + /// If buffer is non null stream assumed to be partial, otherwise the length will be used + /// to output a fixed length packet. + /// </p> + /// <p> + /// The stream created can be closed off by either calling Close() + /// on the stream or Close() on the generator. Closing the returned + /// stream does not close off the Stream parameter <c>outStr</c>. + /// </p> + /// </summary> + private Stream Open( + Stream outStr, + long length, + byte[] buffer) + { + if (cOut != null) + throw new InvalidOperationException("generator already in open state"); + if (methods.Count == 0) + throw new InvalidOperationException("No encryption methods specified"); + if (outStr == null) + throw new ArgumentNullException("outStr"); + + pOut = new BcpgOutputStream(outStr); + + KeyParameter key; + + if (methods.Count == 1) + { + if (methods[0] is PbeMethod) + { + PbeMethod m = (PbeMethod)methods[0]; + + key = m.GetKey(); + } + else + { + key = PgpUtilities.MakeRandomKey(defAlgorithm, rand); + + byte[] sessionInfo = CreateSessionInfo(defAlgorithm, key); + PubMethod m = (PubMethod)methods[0]; + + try + { + m.AddSessionInfo(sessionInfo, rand); + } + catch (Exception e) + { + throw new PgpException("exception encrypting session key", e); + } + } + + pOut.WritePacket((ContainedPacket)methods[0]); + } + else // multiple methods + { + key = PgpUtilities.MakeRandomKey(defAlgorithm, rand); + byte[] sessionInfo = CreateSessionInfo(defAlgorithm, key); + + for (int i = 0; i != methods.Count; i++) + { + EncMethod m = (EncMethod)methods[i]; + + try + { + m.AddSessionInfo(sessionInfo, rand); + } + catch (Exception e) + { + throw new PgpException("exception encrypting session key", e); + } + + pOut.WritePacket(m); + } + } + + string cName = PgpUtilities.GetSymmetricCipherName(defAlgorithm); + if (cName == null) + { + throw new PgpException("null cipher specified"); + } + + try + { + if (withIntegrityPacket) + { + cName += "/CFB/NoPadding"; + } + else + { + cName += "/OpenPGPCFB/NoPadding"; + } + + c = CipherUtilities.GetCipher(cName); + + // TODO Confirm the IV should be all zero bytes (not inLineIv - see below) + byte[] iv = new byte[c.GetBlockSize()]; + c.Init(true, new ParametersWithRandom(new ParametersWithIV(key, iv), rand)); + + if (buffer == null) + { + // + // we have to Add block size + 2 for the Generated IV and + 1 + 22 if integrity protected + // + if (withIntegrityPacket) + { + pOut = new BcpgOutputStream(outStr, PacketTag.SymmetricEncryptedIntegrityProtected, length + c.GetBlockSize() + 2 + 1 + 22); + pOut.WriteByte(1); // version number + } + else + { + pOut = new BcpgOutputStream(outStr, PacketTag.SymmetricKeyEncrypted, length + c.GetBlockSize() + 2, oldFormat); + } + } + else + { + if (withIntegrityPacket) + { + pOut = new BcpgOutputStream(outStr, PacketTag.SymmetricEncryptedIntegrityProtected, buffer); + pOut.WriteByte(1); // version number + } + else + { + pOut = new BcpgOutputStream(outStr, PacketTag.SymmetricKeyEncrypted, buffer); + } + } + + int blockSize = c.GetBlockSize(); + byte[] inLineIv = new byte[blockSize + 2]; + rand.NextBytes(inLineIv, 0, blockSize); + Array.Copy(inLineIv, inLineIv.Length - 4, inLineIv, inLineIv.Length - 2, 2); + + Stream myOut = cOut = new CipherStream(pOut, null, c); + + if (withIntegrityPacket) + { + string digestName = PgpUtilities.GetDigestName(HashAlgorithmTag.Sha1); + IDigest digest = DigestUtilities.GetDigest(digestName); + myOut = digestOut = new DigestStream(myOut, null, digest); + } + + myOut.Write(inLineIv, 0, inLineIv.Length); + + return new WrappedGeneratorStream(this, myOut); + } + catch (Exception e) + { + throw new PgpException("Exception creating cipher", e); + } + } + + /// <summary> + /// <p> + /// Return an output stream which will encrypt the data as it is written to it. + /// </p> + /// <p> + /// The stream created can be closed off by either calling Close() + /// on the stream or Close() on the generator. Closing the returned + /// stream does not close off the Stream parameter <c>outStr</c>. + /// </p> + /// </summary> + public Stream Open( + Stream outStr, + long length) + { + return Open(outStr, length, null); + } + + /// <summary> + /// <p> + /// Return an output stream which will encrypt the data as it is written to it. + /// The stream will be written out in chunks according to the size of the passed in buffer. + /// </p> + /// <p> + /// The stream created can be closed off by either calling Close() + /// on the stream or Close() on the generator. Closing the returned + /// stream does not close off the Stream parameter <c>outStr</c>. + /// </p> + /// <p> + /// <b>Note</b>: if the buffer is not a power of 2 in length only the largest power of 2 + /// bytes worth of the buffer will be used. + /// </p> + /// </summary> + public Stream Open( + Stream outStr, + byte[] buffer) + { + return Open(outStr, 0, buffer); + } + + /// <summary> + /// <p> + /// Close off the encrypted object - this is equivalent to calling Close() on the stream + /// returned by the Open() method. + /// </p> + /// <p> + /// <b>Note</b>: This does not close the underlying output stream, only the stream on top of + /// it created by the Open() method. + /// </p> + /// </summary> + public void Close() + { + if (cOut != null) + { + // TODO Should this all be under the try/catch block? + if (digestOut != null) + { + // + // hand code a mod detection packet + // + BcpgOutputStream bOut = new BcpgOutputStream( + digestOut, PacketTag.ModificationDetectionCode, 20); + + bOut.Flush(); + digestOut.Flush(); + + // TODO + byte[] dig = DigestUtilities.DoFinal(digestOut.WriteDigest()); + cOut.Write(dig, 0, dig.Length); + } + + cOut.Flush(); + + try + { + pOut.Write(c.DoFinal()); + pOut.Finish(); + } + catch (Exception e) + { + throw new IOException(e.Message, e); + } + + cOut = null; + pOut = null; + } + } + } +} diff --git a/Crypto/src/openpgp/PgpEncryptedDataList.cs b/Crypto/src/openpgp/PgpEncryptedDataList.cs new file mode 100644
index 000000000..8dded7c05 --- /dev/null +++ b/Crypto/src/openpgp/PgpEncryptedDataList.cs
@@ -0,0 +1,72 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks>A holder for a list of PGP encryption method packets.</remarks> + public class PgpEncryptedDataList + : PgpObject + { + private IList list = Platform.CreateArrayList(); + private InputStreamPacket data; + + public PgpEncryptedDataList( + BcpgInputStream bcpgInput) + { + while (bcpgInput.NextPacketTag() == PacketTag.PublicKeyEncryptedSession + || bcpgInput.NextPacketTag() == PacketTag.SymmetricKeyEncryptedSessionKey) + { + list.Add(bcpgInput.ReadPacket()); + } + + data = (InputStreamPacket)bcpgInput.ReadPacket(); + + for (int i = 0; i != list.Count; i++) + { + if (list[i] is SymmetricKeyEncSessionPacket) + { + list[i] = new PgpPbeEncryptedData((SymmetricKeyEncSessionPacket) list[i], data); + } + else + { + list[i] = new PgpPublicKeyEncryptedData((PublicKeyEncSessionPacket) list[i], data); + } + } + } + + public PgpEncryptedData this[int index] + { + get { return (PgpEncryptedData) list[index]; } + } + + [Obsolete("Use 'object[index]' syntax instead")] + public object Get(int index) + { + return this[index]; + } + + [Obsolete("Use 'Count' property instead")] + public int Size + { + get { return list.Count; } + } + + public int Count + { + get { return list.Count; } + } + + public bool IsEmpty + { + get { return list.Count == 0; } + } + + public IEnumerable GetEncryptedDataObjects() + { + return new EnumerableProxy(list); + } + } +} diff --git a/Crypto/src/openpgp/PgpException.cs b/Crypto/src/openpgp/PgpException.cs new file mode 100644
index 000000000..3048116fa --- /dev/null +++ b/Crypto/src/openpgp/PgpException.cs
@@ -0,0 +1,19 @@ +using System; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks>Generic exception class for PGP encoding/decoding problems.</remarks> + public class PgpException + : Exception + { + public PgpException() : base() {} + public PgpException(string message) : base(message) {} + public PgpException(string message, Exception exception) : base(message, exception) {} + + [Obsolete("Use InnerException property")] + public Exception UnderlyingException + { + get { return InnerException; } + } + } +} diff --git a/Crypto/src/openpgp/PgpExperimental.cs b/Crypto/src/openpgp/PgpExperimental.cs new file mode 100644
index 000000000..8518335a1 --- /dev/null +++ b/Crypto/src/openpgp/PgpExperimental.cs
@@ -0,0 +1,16 @@ +using System; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + public class PgpExperimental + : PgpObject + { + private readonly ExperimentalPacket p; + + public PgpExperimental( + BcpgInputStream bcpgIn) + { + p = (ExperimentalPacket) bcpgIn.ReadPacket(); + } + } +} diff --git a/Crypto/src/openpgp/PgpKeyFlags.cs b/Crypto/src/openpgp/PgpKeyFlags.cs new file mode 100644
index 000000000..ea1800606 --- /dev/null +++ b/Crypto/src/openpgp/PgpKeyFlags.cs
@@ -0,0 +1,13 @@ +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks>Key flag values for the KeyFlags subpacket.</remarks> + public abstract class PgpKeyFlags + { + public const int CanCertify = 0x01; // This key may be used to certify other keys. + public const int CanSign = 0x02; // This key may be used to sign data. + public const int CanEncryptCommunications = 0x04; // This key may be used to encrypt communications. + public const int CanEncryptStorage = 0x08; // This key may be used to encrypt storage. + public const int MaybeSplit = 0x10; // The private component of this key may have been split by a secret-sharing mechanism. + public const int MaybeShared = 0x80; // The private component of this key may be in the possession of more than one person. + } +} diff --git a/Crypto/src/openpgp/PgpKeyPair.cs b/Crypto/src/openpgp/PgpKeyPair.cs new file mode 100644
index 000000000..6efb03a42 --- /dev/null +++ b/Crypto/src/openpgp/PgpKeyPair.cs
@@ -0,0 +1,67 @@ +using System; + +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks> + /// General class to handle JCA key pairs and convert them into OpenPGP ones. + /// <p> + /// A word for the unwary, the KeyId for an OpenPGP public key is calculated from + /// a hash that includes the time of creation, if you pass a different date to the + /// constructor below with the same public private key pair the KeyIs will not be the + /// same as for previous generations of the key, so ideally you only want to do + /// this once. + /// </p> + /// </remarks> + public class PgpKeyPair + { + private readonly PgpPublicKey pub; + private readonly PgpPrivateKey priv; + + public PgpKeyPair( + PublicKeyAlgorithmTag algorithm, + AsymmetricCipherKeyPair keyPair, + DateTime time) + : this(algorithm, keyPair.Public, keyPair.Private, time) + { + } + + public PgpKeyPair( + PublicKeyAlgorithmTag algorithm, + AsymmetricKeyParameter pubKey, + AsymmetricKeyParameter privKey, + DateTime time) + { + this.pub = new PgpPublicKey(algorithm, pubKey, time); + this.priv = new PgpPrivateKey(privKey, pub.KeyId); + } + + /// <summary>Create a key pair from a PgpPrivateKey and a PgpPublicKey.</summary> + /// <param name="pub">The public key.</param> + /// <param name="priv">The private key.</param> + public PgpKeyPair( + PgpPublicKey pub, + PgpPrivateKey priv) + { + this.pub = pub; + this.priv = priv; + } + + /// <summary>The keyId associated with this key pair.</summary> + public long KeyId + { + get { return pub.KeyId; } + } + + public PgpPublicKey PublicKey + { + get { return pub; } + } + + public PgpPrivateKey PrivateKey + { + get { return priv; } + } + } +} diff --git a/Crypto/src/openpgp/PgpKeyRingGenerator.cs b/Crypto/src/openpgp/PgpKeyRingGenerator.cs new file mode 100644
index 000000000..e85fc2eef --- /dev/null +++ b/Crypto/src/openpgp/PgpKeyRingGenerator.cs
@@ -0,0 +1,167 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks> + /// Generator for a PGP master and subkey ring. + /// This class will generate both the secret and public key rings + /// </remarks> + public class PgpKeyRingGenerator + { + private IList keys = Platform.CreateArrayList(); + private string id; + private SymmetricKeyAlgorithmTag encAlgorithm; + private int certificationLevel; + private char[] passPhrase; + private bool useSha1; + private PgpKeyPair masterKey; + private PgpSignatureSubpacketVector hashedPacketVector; + private PgpSignatureSubpacketVector unhashedPacketVector; + private SecureRandom rand; + + /// <summary> + /// Create a new key ring generator using old style checksumming. It is recommended to use + /// SHA1 checksumming where possible. + /// </summary> + /// <param name="certificationLevel">The certification level for keys on this ring.</param> + /// <param name="masterKey">The master key pair.</param> + /// <param name="id">The id to be associated with the ring.</param> + /// <param name="encAlgorithm">The algorithm to be used to protect secret keys.</param> + /// <param name="passPhrase">The passPhrase to be used to protect secret keys.</param> + /// <param name="hashedPackets">Packets to be included in the certification hash.</param> + /// <param name="unhashedPackets">Packets to be attached unhashed to the certification.</param> + /// <param name="rand">input secured random.</param> + public PgpKeyRingGenerator( + int certificationLevel, + PgpKeyPair masterKey, + string id, + SymmetricKeyAlgorithmTag encAlgorithm, + char[] passPhrase, + PgpSignatureSubpacketVector hashedPackets, + PgpSignatureSubpacketVector unhashedPackets, + SecureRandom rand) + : this(certificationLevel, masterKey, id, encAlgorithm, passPhrase, false, hashedPackets, unhashedPackets, rand) + { + } + + /// <summary> + /// Create a new key ring generator. + /// </summary> + /// <param name="certificationLevel">The certification level for keys on this ring.</param> + /// <param name="masterKey">The master key pair.</param> + /// <param name="id">The id to be associated with the ring.</param> + /// <param name="encAlgorithm">The algorithm to be used to protect secret keys.</param> + /// <param name="passPhrase">The passPhrase to be used to protect secret keys.</param> + /// <param name="useSha1">Checksum the secret keys with SHA1 rather than the older 16 bit checksum.</param> + /// <param name="hashedPackets">Packets to be included in the certification hash.</param> + /// <param name="unhashedPackets">Packets to be attached unhashed to the certification.</param> + /// <param name="rand">input secured random.</param> + public PgpKeyRingGenerator( + int certificationLevel, + PgpKeyPair masterKey, + string id, + SymmetricKeyAlgorithmTag encAlgorithm, + char[] passPhrase, + bool useSha1, + PgpSignatureSubpacketVector hashedPackets, + PgpSignatureSubpacketVector unhashedPackets, + SecureRandom rand) + { + this.certificationLevel = certificationLevel; + this.masterKey = masterKey; + this.id = id; + this.encAlgorithm = encAlgorithm; + this.passPhrase = passPhrase; + this.useSha1 = useSha1; + this.hashedPacketVector = hashedPackets; + this.unhashedPacketVector = unhashedPackets; + this.rand = rand; + + keys.Add(new PgpSecretKey(certificationLevel, masterKey, id, encAlgorithm, passPhrase, useSha1, hashedPackets, unhashedPackets, rand)); + } + + /// <summary>Add a subkey to the key ring to be generated with default certification.</summary> + public void AddSubKey( + PgpKeyPair keyPair) + { + AddSubKey(keyPair, this.hashedPacketVector, this.unhashedPacketVector); + } + + /// <summary> + /// Add a subkey with specific hashed and unhashed packets associated with it and + /// default certification. + /// </summary> + /// <param name="keyPair">Public/private key pair.</param> + /// <param name="hashedPackets">Hashed packet values to be included in certification.</param> + /// <param name="unhashedPackets">Unhashed packets values to be included in certification.</param> + /// <exception cref="PgpException"></exception> + public void AddSubKey( + PgpKeyPair keyPair, + PgpSignatureSubpacketVector hashedPackets, + PgpSignatureSubpacketVector unhashedPackets) + { + try + { + PgpSignatureGenerator sGen = new PgpSignatureGenerator( + masterKey.PublicKey.Algorithm, HashAlgorithmTag.Sha1); + + // + // Generate the certification + // + sGen.InitSign(PgpSignature.SubkeyBinding, masterKey.PrivateKey); + + sGen.SetHashedSubpackets(hashedPackets); + sGen.SetUnhashedSubpackets(unhashedPackets); + + IList subSigs = Platform.CreateArrayList(); + + subSigs.Add(sGen.GenerateCertification(masterKey.PublicKey, keyPair.PublicKey)); + + keys.Add(new PgpSecretKey(keyPair.PrivateKey, new PgpPublicKey(keyPair.PublicKey, null, subSigs), encAlgorithm, passPhrase, useSha1, rand)); + } + catch (PgpException e) + { + throw e; + } + catch (Exception e) + { + throw new PgpException("exception adding subkey: ", e); + } + } + + /// <summary>Return the secret key ring.</summary> + public PgpSecretKeyRing GenerateSecretKeyRing() + { + return new PgpSecretKeyRing(keys); + } + + /// <summary>Return the public key ring that corresponds to the secret key ring.</summary> + public PgpPublicKeyRing GeneratePublicKeyRing() + { + IList pubKeys = Platform.CreateArrayList(); + + IEnumerator enumerator = keys.GetEnumerator(); + enumerator.MoveNext(); + + PgpSecretKey pgpSecretKey = (PgpSecretKey) enumerator.Current; + pubKeys.Add(pgpSecretKey.PublicKey); + + while (enumerator.MoveNext()) + { + pgpSecretKey = (PgpSecretKey) enumerator.Current; + + PgpPublicKey k = new PgpPublicKey(pgpSecretKey.PublicKey); + k.publicPk = new PublicSubkeyPacket( + k.Algorithm, k.CreationTime, k.publicPk.Key); + + pubKeys.Add(k); + } + + return new PgpPublicKeyRing(pubKeys); + } + } +} diff --git a/Crypto/src/openpgp/PgpKeyValidationException.cs b/Crypto/src/openpgp/PgpKeyValidationException.cs new file mode 100644
index 000000000..da07f400f --- /dev/null +++ b/Crypto/src/openpgp/PgpKeyValidationException.cs
@@ -0,0 +1,15 @@ +using System; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks> + /// Thrown if the key checksum is invalid. + /// </remarks> + public class PgpKeyValidationException + : PgpException + { + public PgpKeyValidationException() : base() {} + public PgpKeyValidationException(string message) : base(message) {} + public PgpKeyValidationException(string message, Exception exception) : base(message, exception) {} + } +} diff --git a/Crypto/src/openpgp/PgpLiteralData.cs b/Crypto/src/openpgp/PgpLiteralData.cs new file mode 100644
index 000000000..79bbc3984 --- /dev/null +++ b/Crypto/src/openpgp/PgpLiteralData.cs
@@ -0,0 +1,63 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Utilities.Date; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <summary>Class for processing literal data objects.</summary> + public class PgpLiteralData + : PgpObject + { + public const char Binary = 'b'; + public const char Text = 't'; + public const char Utf8 = 'u'; + + /// <summary>The special name indicating a "for your eyes only" packet.</summary> + public const string Console = "_CONSOLE"; + + private LiteralDataPacket data; + + public PgpLiteralData( + BcpgInputStream bcpgInput) + { + data = (LiteralDataPacket) bcpgInput.ReadPacket(); + } + + /// <summary>The format of the data stream - Binary or Text</summary> + public int Format + { + get { return data.Format; } + } + + /// <summary>The file name that's associated with the data stream.</summary> + public string FileName + { + get { return data.FileName; } + } + + /// Return the file name as an unintrepreted byte array. + public byte[] GetRawFileName() + { + return data.GetRawFileName(); + } + + /// <summary>The modification time for the file.</summary> + public DateTime ModificationTime + { + get { return DateTimeUtilities.UnixMsToDateTime(data.ModificationTime); } + } + + /// <summary>The raw input stream for the data stream.</summary> + public Stream GetInputStream() + { + return data.GetInputStream(); + } + + /// <summary>The input stream representing the data stream.</summary> + public Stream GetDataStream() + { + return GetInputStream(); + } + } +} diff --git a/Crypto/src/openpgp/PgpLiteralDataGenerator.cs b/Crypto/src/openpgp/PgpLiteralDataGenerator.cs new file mode 100644
index 000000000..b0337c80d --- /dev/null +++ b/Crypto/src/openpgp/PgpLiteralDataGenerator.cs
@@ -0,0 +1,180 @@ +using System; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Date; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks>Class for producing literal data packets.</remarks> + public class PgpLiteralDataGenerator + : IStreamGenerator + { + public const char Binary = PgpLiteralData.Binary; + public const char Text = PgpLiteralData.Text; + public const char Utf8 = PgpLiteralData.Utf8; + + /// <summary>The special name indicating a "for your eyes only" packet.</summary> + public const string Console = PgpLiteralData.Console; + + private BcpgOutputStream pkOut; + private bool oldFormat; + + public PgpLiteralDataGenerator() + { + } + + /// <summary> + /// Generates literal data objects in the old format. + /// This is important if you need compatibility with PGP 2.6.x. + /// </summary> + /// <param name="oldFormat">If true, uses old format.</param> + public PgpLiteralDataGenerator( + bool oldFormat) + { + this.oldFormat = oldFormat; + } + + private void WriteHeader( + BcpgOutputStream outStr, + char format, + string name, + long modificationTime) + { + byte[] encName = Strings.ToUtf8ByteArray(name); + + outStr.Write( + (byte) format, + (byte) encName.Length); + + outStr.Write(encName); + + long modDate = modificationTime / 1000L; + + outStr.Write( + (byte)(modDate >> 24), + (byte)(modDate >> 16), + (byte)(modDate >> 8), + (byte)modDate); + } + + /// <summary> + /// <p> + /// Open a literal data packet, returning a stream to store the data inside the packet. + /// </p> + /// <p> + /// The stream created can be closed off by either calling Close() + /// on the stream or Close() on the generator. Closing the returned + /// stream does not close off the Stream parameter <c>outStr</c>. + /// </p> + /// </summary> + /// <param name="outStr">The stream we want the packet in.</param> + /// <param name="format">The format we are using.</param> + /// <param name="name">The name of the 'file'.</param> + /// <param name="length">The length of the data we will write.</param> + /// <param name="modificationTime">The time of last modification we want stored.</param> + public Stream Open( + Stream outStr, + char format, + string name, + long length, + DateTime modificationTime) + { + if (pkOut != null) + throw new InvalidOperationException("generator already in open state"); + if (outStr == null) + throw new ArgumentNullException("outStr"); + + // Do this first, since it might throw an exception + long unixMs = DateTimeUtilities.DateTimeToUnixMs(modificationTime); + + pkOut = new BcpgOutputStream(outStr, PacketTag.LiteralData, + length + 2 + name.Length + 4, oldFormat); + + WriteHeader(pkOut, format, name, unixMs); + + return new WrappedGeneratorStream(this, pkOut); + } + + /// <summary> + /// <p> + /// Open a literal data packet, returning a stream to store the data inside the packet, + /// as an indefinite length stream. The stream is written out as a series of partial + /// packets with a chunk size determined by the size of the passed in buffer. + /// </p> + /// <p> + /// The stream created can be closed off by either calling Close() + /// on the stream or Close() on the generator. Closing the returned + /// stream does not close off the Stream parameter <c>outStr</c>. + /// </p> + /// <p> + /// <b>Note</b>: if the buffer is not a power of 2 in length only the largest power of 2 + /// bytes worth of the buffer will be used.</p> + /// </summary> + /// <param name="outStr">The stream we want the packet in.</param> + /// <param name="format">The format we are using.</param> + /// <param name="name">The name of the 'file'.</param> + /// <param name="modificationTime">The time of last modification we want stored.</param> + /// <param name="buffer">The buffer to use for collecting data to put into chunks.</param> + public Stream Open( + Stream outStr, + char format, + string name, + DateTime modificationTime, + byte[] buffer) + { + if (pkOut != null) + throw new InvalidOperationException("generator already in open state"); + if (outStr == null) + throw new ArgumentNullException("outStr"); + + // Do this first, since it might throw an exception + long unixMs = DateTimeUtilities.DateTimeToUnixMs(modificationTime); + + pkOut = new BcpgOutputStream(outStr, PacketTag.LiteralData, buffer); + + WriteHeader(pkOut, format, name, unixMs); + + return new WrappedGeneratorStream(this, pkOut); + } + +#if !PORTABLE + /// <summary> + /// <p> + /// Open a literal data packet for the passed in <c>FileInfo</c> object, returning + /// an output stream for saving the file contents. + /// </p> + /// <p> + /// The stream created can be closed off by either calling Close() + /// on the stream or Close() on the generator. Closing the returned + /// stream does not close off the Stream parameter <c>outStr</c>. + /// </p> + /// </summary> + /// <param name="outStr">The stream we want the packet in.</param> + /// <param name="format">The format we are using.</param> + /// <param name="file">The <c>FileInfo</c> object containg the packet details.</param> + public Stream Open( + Stream outStr, + char format, + FileInfo file) + { + return Open(outStr, format, file.Name, file.Length, file.LastWriteTime); + } +#endif + + /// <summary> + /// Close the literal data packet - this is equivalent to calling Close() + /// on the stream returned by the Open() method. + /// </summary> + public void Close() + { + if (pkOut != null) + { + pkOut.Finish(); + pkOut.Flush(); + pkOut = null; + } + } + } +} diff --git a/Crypto/src/openpgp/PgpMarker.cs b/Crypto/src/openpgp/PgpMarker.cs new file mode 100644
index 000000000..733e4e959 --- /dev/null +++ b/Crypto/src/openpgp/PgpMarker.cs
@@ -0,0 +1,18 @@ +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks> + /// A PGP marker packet - in general these should be ignored other than where + /// the idea is to preserve the original input stream. + /// </remarks> + public class PgpMarker + : PgpObject + { + private readonly MarkerPacket p; + + public PgpMarker( + BcpgInputStream bcpgIn) + { + p = (MarkerPacket) bcpgIn.ReadPacket(); + } + } +} diff --git a/Crypto/src/openpgp/PgpObjectFactory.cs b/Crypto/src/openpgp/PgpObjectFactory.cs new file mode 100644
index 000000000..c5c6fcb68 --- /dev/null +++ b/Crypto/src/openpgp/PgpObjectFactory.cs
@@ -0,0 +1,143 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks> + /// General class for reading a PGP object stream. + /// <p> + /// Note: if this class finds a PgpPublicKey or a PgpSecretKey it + /// will create a PgpPublicKeyRing, or a PgpSecretKeyRing for each + /// key found. If all you are trying to do is read a key ring file use + /// either PgpPublicKeyRingBundle or PgpSecretKeyRingBundle.</p> + /// </remarks> + public class PgpObjectFactory + { + private readonly BcpgInputStream bcpgIn; + + public PgpObjectFactory( + Stream inputStream) + { + this.bcpgIn = BcpgInputStream.Wrap(inputStream); + } + + public PgpObjectFactory( + byte[] bytes) + : this(new MemoryStream(bytes, false)) + { + } + + /// <summary>Return the next object in the stream, or null if the end is reached.</summary> + /// <exception cref="IOException">On a parse error</exception> + public PgpObject NextPgpObject() + { + PacketTag tag = bcpgIn.NextPacketTag(); + + if ((int) tag == -1) return null; + + switch (tag) + { + case PacketTag.Signature: + { + IList l = Platform.CreateArrayList(); + + while (bcpgIn.NextPacketTag() == PacketTag.Signature) + { + try + { + l.Add(new PgpSignature(bcpgIn)); + } + catch (PgpException e) + { + throw new IOException("can't create signature object: " + e); + } + } + + PgpSignature[] sigs = new PgpSignature[l.Count]; + for (int i = 0; i < l.Count; ++i) + { + sigs[i] = (PgpSignature)l[i]; + } + return new PgpSignatureList(sigs); + } + case PacketTag.SecretKey: + try + { + return new PgpSecretKeyRing(bcpgIn); + } + catch (PgpException e) + { + throw new IOException("can't create secret key object: " + e); + } + case PacketTag.PublicKey: + return new PgpPublicKeyRing(bcpgIn); + // TODO Make PgpPublicKey a PgpObject or return a PgpPublicKeyRing +// case PacketTag.PublicSubkey: +// return PgpPublicKeyRing.ReadSubkey(bcpgIn); + case PacketTag.CompressedData: + return new PgpCompressedData(bcpgIn); + case PacketTag.LiteralData: + return new PgpLiteralData(bcpgIn); + case PacketTag.PublicKeyEncryptedSession: + case PacketTag.SymmetricKeyEncryptedSessionKey: + return new PgpEncryptedDataList(bcpgIn); + case PacketTag.OnePassSignature: + { + IList l = Platform.CreateArrayList(); + + while (bcpgIn.NextPacketTag() == PacketTag.OnePassSignature) + { + try + { + l.Add(new PgpOnePassSignature(bcpgIn)); + } + catch (PgpException e) + { + throw new IOException("can't create one pass signature object: " + e); + } + } + + PgpOnePassSignature[] sigs = new PgpOnePassSignature[l.Count]; + for (int i = 0; i < l.Count; ++i) + { + sigs[i] = (PgpOnePassSignature)l[i]; + } + return new PgpOnePassSignatureList(sigs); + } + case PacketTag.Marker: + return new PgpMarker(bcpgIn); + case PacketTag.Experimental1: + case PacketTag.Experimental2: + case PacketTag.Experimental3: + case PacketTag.Experimental4: + return new PgpExperimental(bcpgIn); + } + + throw new IOException("unknown object in stream " + bcpgIn.NextPacketTag()); + } + + [Obsolete("Use NextPgpObject() instead")] + public object NextObject() + { + return NextPgpObject(); + } + + /// <summary> + /// Return all available objects in a list. + /// </summary> + /// <returns>An <c>IList</c> containing all objects from this factory, in order.</returns> + public IList AllPgpObjects() + { + IList result = Platform.CreateArrayList(); + PgpObject pgpObject; + while ((pgpObject = NextPgpObject()) != null) + { + result.Add(pgpObject); + } + return result; + } + } +} diff --git a/Crypto/src/openpgp/PgpOnePassSignature.cs b/Crypto/src/openpgp/PgpOnePassSignature.cs new file mode 100644
index 000000000..68fc5994d --- /dev/null +++ b/Crypto/src/openpgp/PgpOnePassSignature.cs
@@ -0,0 +1,179 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks>A one pass signature object.</remarks> + public class PgpOnePassSignature + { + private OnePassSignaturePacket sigPack; + private int signatureType; + private ISigner sig; + private byte lastb; + + internal PgpOnePassSignature( + BcpgInputStream bcpgInput) + : this((OnePassSignaturePacket) bcpgInput.ReadPacket()) + { + } + + internal PgpOnePassSignature( + OnePassSignaturePacket sigPack) + { + this.sigPack = sigPack; + this.signatureType = sigPack.SignatureType; + } + + /// <summary>Initialise the signature object for verification.</summary> + public void InitVerify( + PgpPublicKey pubKey) + { + lastb = 0; + + try + { + sig = SignerUtilities.GetSigner( + PgpUtilities.GetSignatureName(sigPack.KeyAlgorithm, sigPack.HashAlgorithm)); + } + catch (Exception e) + { + throw new PgpException("can't set up signature object.", e); + } + + try + { + sig.Init(false, pubKey.GetKey()); + } + catch (InvalidKeyException e) + { + throw new PgpException("invalid key.", e); + } + } + + public void Update( + byte b) + { + if (signatureType == PgpSignature.CanonicalTextDocument) + { + doCanonicalUpdateByte(b); + } + else + { + sig.Update(b); + } + } + + private void doCanonicalUpdateByte( + byte b) + { + if (b == '\r') + { + doUpdateCRLF(); + } + else if (b == '\n') + { + if (lastb != '\r') + { + doUpdateCRLF(); + } + } + else + { + sig.Update(b); + } + + lastb = b; + } + + private void doUpdateCRLF() + { + sig.Update((byte)'\r'); + sig.Update((byte)'\n'); + } + + public void Update( + byte[] bytes) + { + if (signatureType == PgpSignature.CanonicalTextDocument) + { + for (int i = 0; i != bytes.Length; i++) + { + doCanonicalUpdateByte(bytes[i]); + } + } + else + { + sig.BlockUpdate(bytes, 0, bytes.Length); + } + } + + public void Update( + byte[] bytes, + int off, + int length) + { + if (signatureType == PgpSignature.CanonicalTextDocument) + { + int finish = off + length; + + for (int i = off; i != finish; i++) + { + doCanonicalUpdateByte(bytes[i]); + } + } + else + { + sig.BlockUpdate(bytes, off, length); + } + } + + /// <summary>Verify the calculated signature against the passed in PgpSignature.</summary> + public bool Verify( + PgpSignature pgpSig) + { + byte[] trailer = pgpSig.GetSignatureTrailer(); + + sig.BlockUpdate(trailer, 0, trailer.Length); + + return sig.VerifySignature(pgpSig.GetSignature()); + } + + public long KeyId + { + get { return sigPack.KeyId; } + } + + public int SignatureType + { + get { return sigPack.SignatureType; } + } + + public HashAlgorithmTag HashAlgorithm + { + get { return sigPack.HashAlgorithm; } + } + + public PublicKeyAlgorithmTag KeyAlgorithm + { + get { return sigPack.KeyAlgorithm; } + } + + public byte[] GetEncoded() + { + MemoryStream bOut = new MemoryStream(); + + Encode(bOut); + + return bOut.ToArray(); + } + + public void Encode( + Stream outStr) + { + BcpgOutputStream.Wrap(outStr).WritePacket(sigPack); + } + } +} diff --git a/Crypto/src/openpgp/PgpOnePassSignatureList.cs b/Crypto/src/openpgp/PgpOnePassSignatureList.cs new file mode 100644
index 000000000..37c4288e3 --- /dev/null +++ b/Crypto/src/openpgp/PgpOnePassSignatureList.cs
@@ -0,0 +1,51 @@ +using System; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks>Holder for a list of PgpOnePassSignature objects.</remarks> + public class PgpOnePassSignatureList + : PgpObject + { + private readonly PgpOnePassSignature[] sigs; + + public PgpOnePassSignatureList( + PgpOnePassSignature[] sigs) + { + this.sigs = (PgpOnePassSignature[]) sigs.Clone(); + } + + public PgpOnePassSignatureList( + PgpOnePassSignature sig) + { + this.sigs = new PgpOnePassSignature[]{ sig }; + } + + public PgpOnePassSignature this[int index] + { + get { return sigs[index]; } + } + + [Obsolete("Use 'object[index]' syntax instead")] + public PgpOnePassSignature Get( + int index) + { + return this[index]; + } + + [Obsolete("Use 'Count' property instead")] + public int Size + { + get { return sigs.Length; } + } + + public int Count + { + get { return sigs.Length; } + } + + public bool IsEmpty + { + get { return (sigs.Length == 0); } + } + } +} diff --git a/Crypto/src/openpgp/PgpPbeEncryptedData.cs b/Crypto/src/openpgp/PgpPbeEncryptedData.cs new file mode 100644
index 000000000..c5fe89407 --- /dev/null +++ b/Crypto/src/openpgp/PgpPbeEncryptedData.cs
@@ -0,0 +1,135 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks>A password based encryption object.</remarks> + public class PgpPbeEncryptedData + : PgpEncryptedData + { + private readonly SymmetricKeyEncSessionPacket keyData; + + internal PgpPbeEncryptedData( + SymmetricKeyEncSessionPacket keyData, + InputStreamPacket encData) + : base(encData) + { + this.keyData = keyData; + } + + /// <summary>Return the raw input stream for the data stream.</summary> + public override Stream GetInputStream() + { + return encData.GetInputStream(); + } + + /// <summary>Return the decrypted input stream, using the passed in passphrase.</summary> + public Stream GetDataStream( + char[] passPhrase) + { + try + { + SymmetricKeyAlgorithmTag keyAlgorithm = keyData.EncAlgorithm; + + KeyParameter key = PgpUtilities.MakeKeyFromPassPhrase( + keyAlgorithm, keyData.S2k, passPhrase); + + + byte[] secKeyData = keyData.GetSecKeyData(); + if (secKeyData != null && secKeyData.Length > 0) + { + IBufferedCipher keyCipher = CipherUtilities.GetCipher( + PgpUtilities.GetSymmetricCipherName(keyAlgorithm) + "/CFB/NoPadding"); + + keyCipher.Init(false, + new ParametersWithIV(key, new byte[keyCipher.GetBlockSize()])); + + byte[] keyBytes = keyCipher.DoFinal(secKeyData); + + keyAlgorithm = (SymmetricKeyAlgorithmTag) keyBytes[0]; + + key = ParameterUtilities.CreateKeyParameter( + PgpUtilities.GetSymmetricCipherName(keyAlgorithm), + keyBytes, 1, keyBytes.Length - 1); + } + + + IBufferedCipher c = CreateStreamCipher(keyAlgorithm); + + byte[] iv = new byte[c.GetBlockSize()]; + + c.Init(false, new ParametersWithIV(key, iv)); + + encStream = BcpgInputStream.Wrap(new CipherStream(encData.GetInputStream(), c, null)); + + if (encData is SymmetricEncIntegrityPacket) + { + truncStream = new TruncatedStream(encStream); + + string digestName = PgpUtilities.GetDigestName(HashAlgorithmTag.Sha1); + IDigest digest = DigestUtilities.GetDigest(digestName); + + encStream = new DigestStream(truncStream, digest, null); + } + + if (Streams.ReadFully(encStream, iv, 0, iv.Length) < iv.Length) + throw new EndOfStreamException("unexpected end of stream."); + + int v1 = encStream.ReadByte(); + int v2 = encStream.ReadByte(); + + if (v1 < 0 || v2 < 0) + throw new EndOfStreamException("unexpected end of stream."); + + + // Note: the oracle attack on the "quick check" bytes is not deemed + // a security risk for PBE (see PgpPublicKeyEncryptedData) + + bool repeatCheckPassed = + iv[iv.Length - 2] == (byte)v1 + && iv[iv.Length - 1] == (byte)v2; + + // Note: some versions of PGP appear to produce 0 for the extra + // bytes rather than repeating the two previous bytes + bool zeroesCheckPassed = + v1 == 0 + && v2 == 0; + + if (!repeatCheckPassed && !zeroesCheckPassed) + { + throw new PgpDataValidationException("quick check failed."); + } + + + return encStream; + } + catch (PgpException e) + { + throw e; + } + catch (Exception e) + { + throw new PgpException("Exception creating cipher", e); + } + } + + private IBufferedCipher CreateStreamCipher( + SymmetricKeyAlgorithmTag keyAlgorithm) + { + string mode = (encData is SymmetricEncIntegrityPacket) + ? "CFB" + : "OpenPGPCFB"; + + string cName = PgpUtilities.GetSymmetricCipherName(keyAlgorithm) + + "/" + mode + "/NoPadding"; + + return CipherUtilities.GetCipher(cName); + } + } +} diff --git a/Crypto/src/openpgp/PgpPrivateKey.cs b/Crypto/src/openpgp/PgpPrivateKey.cs new file mode 100644
index 000000000..154c87cd7 --- /dev/null +++ b/Crypto/src/openpgp/PgpPrivateKey.cs
@@ -0,0 +1,42 @@ +using System; + +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks>General class to contain a private key for use with other OpenPGP objects.</remarks> + public class PgpPrivateKey + { + private readonly long keyId; + private readonly AsymmetricKeyParameter privateKey; + + /// <summary> + /// Create a PgpPrivateKey from a regular private key and the ID of its + /// associated public key. + /// </summary> + /// <param name="privateKey">Private key to use.</param> + /// <param name="keyId">ID of the corresponding public key.</param> + public PgpPrivateKey( + AsymmetricKeyParameter privateKey, + long keyId) + { + if (!privateKey.IsPrivate) + throw new ArgumentException("Expected a private key", "privateKey"); + + this.privateKey = privateKey; + this.keyId = keyId; + } + + /// <summary>The keyId associated with the contained private key.</summary> + public long KeyId + { + get { return keyId; } + } + + /// <summary>The contained private key.</summary> + public AsymmetricKeyParameter Key + { + get { return privateKey; } + } + } +} diff --git a/Crypto/src/openpgp/PgpPublicKey.cs b/Crypto/src/openpgp/PgpPublicKey.cs new file mode 100644
index 000000000..b0720146c --- /dev/null +++ b/Crypto/src/openpgp/PgpPublicKey.cs
@@ -0,0 +1,890 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks>General class to handle a PGP public key object.</remarks> + public class PgpPublicKey + { + private static readonly int[] MasterKeyCertificationTypes = new int[] + { + PgpSignature.PositiveCertification, + PgpSignature.CasualCertification, + PgpSignature.NoCertification, + PgpSignature.DefaultCertification + }; + + private long keyId; + private byte[] fingerprint; + private int keyStrength; + + internal PublicKeyPacket publicPk; + internal TrustPacket trustPk; + internal IList keySigs = Platform.CreateArrayList(); + internal IList ids = Platform.CreateArrayList(); + internal IList idTrusts = Platform.CreateArrayList(); + internal IList idSigs = Platform.CreateArrayList(); + internal IList subSigs; + + private void Init() + { + IBcpgKey key = publicPk.Key; + + if (publicPk.Version <= 3) + { + RsaPublicBcpgKey rK = (RsaPublicBcpgKey) key; + + this.keyId = rK.Modulus.LongValue; + + try + { + IDigest digest = DigestUtilities.GetDigest("MD5"); + + byte[] bytes = rK.Modulus.ToByteArrayUnsigned(); + digest.BlockUpdate(bytes, 0, bytes.Length); + + bytes = rK.PublicExponent.ToByteArrayUnsigned(); + digest.BlockUpdate(bytes, 0, bytes.Length); + + this.fingerprint = DigestUtilities.DoFinal(digest); + } + //catch (NoSuchAlgorithmException) + catch (Exception e) + { + throw new IOException("can't find MD5", e); + } + + this.keyStrength = rK.Modulus.BitLength; + } + else + { + byte[] kBytes = publicPk.GetEncodedContents(); + + try + { + IDigest digest = DigestUtilities.GetDigest("SHA1"); + + digest.Update(0x99); + digest.Update((byte)(kBytes.Length >> 8)); + digest.Update((byte)kBytes.Length); + digest.BlockUpdate(kBytes, 0, kBytes.Length); + this.fingerprint = DigestUtilities.DoFinal(digest); + } + catch (Exception e) + { + throw new IOException("can't find SHA1", e); + } + + this.keyId = (long)(((ulong)fingerprint[fingerprint.Length - 8] << 56) + | ((ulong)fingerprint[fingerprint.Length - 7] << 48) + | ((ulong)fingerprint[fingerprint.Length - 6] << 40) + | ((ulong)fingerprint[fingerprint.Length - 5] << 32) + | ((ulong)fingerprint[fingerprint.Length - 4] << 24) + | ((ulong)fingerprint[fingerprint.Length - 3] << 16) + | ((ulong)fingerprint[fingerprint.Length - 2] << 8) + | (ulong)fingerprint[fingerprint.Length - 1]); + + if (key is RsaPublicBcpgKey) + { + this.keyStrength = ((RsaPublicBcpgKey)key).Modulus.BitLength; + } + else if (key is DsaPublicBcpgKey) + { + this.keyStrength = ((DsaPublicBcpgKey)key).P.BitLength; + } + else if (key is ElGamalPublicBcpgKey) + { + this.keyStrength = ((ElGamalPublicBcpgKey)key).P.BitLength; + } + } + } + + /// <summary> + /// Create a PgpPublicKey from the passed in lightweight one. + /// </summary> + /// <remarks> + /// Note: the time passed in affects the value of the key's keyId, so you probably only want + /// to do this once for a lightweight key, or make sure you keep track of the time you used. + /// </remarks> + /// <param name="algorithm">Asymmetric algorithm type representing the public key.</param> + /// <param name="pubKey">Actual public key to associate.</param> + /// <param name="time">Date of creation.</param> + /// <exception cref="ArgumentException">If <c>pubKey</c> is not public.</exception> + /// <exception cref="PgpException">On key creation problem.</exception> + public PgpPublicKey( + PublicKeyAlgorithmTag algorithm, + AsymmetricKeyParameter pubKey, + DateTime time) + { + if (pubKey.IsPrivate) + throw new ArgumentException("Expected a public key", "pubKey"); + + IBcpgKey bcpgKey; + if (pubKey is RsaKeyParameters) + { + RsaKeyParameters rK = (RsaKeyParameters) pubKey; + + bcpgKey = new RsaPublicBcpgKey(rK.Modulus, rK.Exponent); + } + else if (pubKey is DsaPublicKeyParameters) + { + DsaPublicKeyParameters dK = (DsaPublicKeyParameters) pubKey; + DsaParameters dP = dK.Parameters; + + bcpgKey = new DsaPublicBcpgKey(dP.P, dP.Q, dP.G, dK.Y); + } + else if (pubKey is ElGamalPublicKeyParameters) + { + ElGamalPublicKeyParameters eK = (ElGamalPublicKeyParameters) pubKey; + ElGamalParameters eS = eK.Parameters; + + bcpgKey = new ElGamalPublicBcpgKey(eS.P, eS.G, eK.Y); + } + else + { + throw new PgpException("unknown key class"); + } + + this.publicPk = new PublicKeyPacket(algorithm, time, bcpgKey); + this.ids = Platform.CreateArrayList(); + this.idSigs = Platform.CreateArrayList(); + + try + { + Init(); + } + catch (IOException e) + { + throw new PgpException("exception calculating keyId", e); + } + } + + /// <summary>Constructor for a sub-key.</summary> + internal PgpPublicKey( + PublicKeyPacket publicPk, + TrustPacket trustPk, + IList sigs) + { + this.publicPk = publicPk; + this.trustPk = trustPk; + this.subSigs = sigs; + + Init(); + } + + internal PgpPublicKey( + PgpPublicKey key, + TrustPacket trust, + IList subSigs) + { + this.publicPk = key.publicPk; + this.trustPk = trust; + this.subSigs = subSigs; + + this.fingerprint = key.fingerprint; + this.keyId = key.keyId; + this.keyStrength = key.keyStrength; + } + + /// <summary>Copy constructor.</summary> + /// <param name="pubKey">The public key to copy.</param> + internal PgpPublicKey( + PgpPublicKey pubKey) + { + this.publicPk = pubKey.publicPk; + + this.keySigs = Platform.CreateArrayList(pubKey.keySigs); + this.ids = Platform.CreateArrayList(pubKey.ids); + this.idTrusts = Platform.CreateArrayList(pubKey.idTrusts); + this.idSigs = Platform.CreateArrayList(pubKey.idSigs.Count); + for (int i = 0; i != pubKey.idSigs.Count; i++) + { + this.idSigs.Add(Platform.CreateArrayList((IList)pubKey.idSigs[i])); + } + + if (pubKey.subSigs != null) + { + this.subSigs = Platform.CreateArrayList(pubKey.subSigs.Count); + for (int i = 0; i != pubKey.subSigs.Count; i++) + { + this.subSigs.Add(pubKey.subSigs[i]); + } + } + + this.fingerprint = pubKey.fingerprint; + this.keyId = pubKey.keyId; + this.keyStrength = pubKey.keyStrength; + } + + internal PgpPublicKey( + PublicKeyPacket publicPk, + TrustPacket trustPk, + IList keySigs, + IList ids, + IList idTrusts, + IList idSigs) + { + this.publicPk = publicPk; + this.trustPk = trustPk; + this.keySigs = keySigs; + this.ids = ids; + this.idTrusts = idTrusts; + this.idSigs = idSigs; + + Init(); + } + + internal PgpPublicKey( + PublicKeyPacket publicPk, + IList ids, + IList idSigs) + { + this.publicPk = publicPk; + this.ids = ids; + this.idSigs = idSigs; + Init(); + } + + /// <summary>The version of this key.</summary> + public int Version + { + get { return publicPk.Version; } + } + + /// <summary>The creation time of this key.</summary> + public DateTime CreationTime + { + get { return publicPk.GetTime(); } + } + + /// <summary>The number of valid days from creation time - zero means no expiry.</summary> + public int ValidDays + { + get + { + if (publicPk.Version > 3) + { + return (int)(GetValidSeconds() / (24 * 60 * 60)); + } + + return publicPk.ValidDays; + } + } + + /// <summary>Return the trust data associated with the public key, if present.</summary> + /// <returns>A byte array with trust data, null otherwise.</returns> + public byte[] GetTrustData() + { + if (trustPk == null) + { + return null; + } + + return trustPk.GetLevelAndTrustAmount(); + } + + /// <summary>The number of valid seconds from creation time - zero means no expiry.</summary> + public long GetValidSeconds() + { + if (publicPk.Version > 3) + { + if (IsMasterKey) + { + for (int i = 0; i != MasterKeyCertificationTypes.Length; i++) + { + long seconds = GetExpirationTimeFromSig(true, MasterKeyCertificationTypes[i]); + + if (seconds >= 0) + { + return seconds; + } + } + } + else + { + long seconds = GetExpirationTimeFromSig(false, PgpSignature.SubkeyBinding); + + if (seconds >= 0) + { + return seconds; + } + } + + return 0; + } + + return (long) publicPk.ValidDays * 24 * 60 * 60; + } + + private long GetExpirationTimeFromSig( + bool selfSigned, + int signatureType) + { + foreach (PgpSignature sig in GetSignaturesOfType(signatureType)) + { + if (!selfSigned || sig.KeyId == KeyId) + { + PgpSignatureSubpacketVector hashed = sig.GetHashedSubPackets(); + + if (hashed != null) + { + return hashed.GetKeyExpirationTime(); + } + + return 0; + } + } + + return -1; + } + + /// <summary>The keyId associated with the public key.</summary> + public long KeyId + { + get { return keyId; } + } + + /// <summary>The fingerprint of the key</summary> + public byte[] GetFingerprint() + { + return (byte[]) fingerprint.Clone(); + } + + /// <summary> + /// Check if this key has an algorithm type that makes it suitable to use for encryption. + /// </summary> + /// <remarks> + /// Note: with version 4 keys KeyFlags subpackets should also be considered when present for + /// determining the preferred use of the key. + /// </remarks> + /// <returns> + /// <c>true</c> if this key algorithm is suitable for encryption. + /// </returns> + public bool IsEncryptionKey + { + get + { + switch (publicPk.Algorithm) + { + case PublicKeyAlgorithmTag.ElGamalEncrypt: + case PublicKeyAlgorithmTag.ElGamalGeneral: + case PublicKeyAlgorithmTag.RsaEncrypt: + case PublicKeyAlgorithmTag.RsaGeneral: + return true; + default: + return false; + } + } + } + + /// <summary>True, if this is a master key.</summary> + public bool IsMasterKey + { + get { return subSigs == null; } + } + + /// <summary>The algorithm code associated with the public key.</summary> + public PublicKeyAlgorithmTag Algorithm + { + get { return publicPk.Algorithm; } + } + + /// <summary>The strength of the key in bits.</summary> + public int BitStrength + { + get { return keyStrength; } + } + + /// <summary>The public key contained in the object.</summary> + /// <returns>A lightweight public key.</returns> + /// <exception cref="PgpException">If the key algorithm is not recognised.</exception> + public AsymmetricKeyParameter GetKey() + { + try + { + switch (publicPk.Algorithm) + { + case PublicKeyAlgorithmTag.RsaEncrypt: + case PublicKeyAlgorithmTag.RsaGeneral: + case PublicKeyAlgorithmTag.RsaSign: + RsaPublicBcpgKey rsaK = (RsaPublicBcpgKey) publicPk.Key; + return new RsaKeyParameters(false, rsaK.Modulus, rsaK.PublicExponent); + case PublicKeyAlgorithmTag.Dsa: + DsaPublicBcpgKey dsaK = (DsaPublicBcpgKey) publicPk.Key; + return new DsaPublicKeyParameters(dsaK.Y, new DsaParameters(dsaK.P, dsaK.Q, dsaK.G)); + case PublicKeyAlgorithmTag.ElGamalEncrypt: + case PublicKeyAlgorithmTag.ElGamalGeneral: + ElGamalPublicBcpgKey elK = (ElGamalPublicBcpgKey) publicPk.Key; + return new ElGamalPublicKeyParameters(elK.Y, new ElGamalParameters(elK.P, elK.G)); + default: + throw new PgpException("unknown public key algorithm encountered"); + } + } + catch (PgpException e) + { + throw e; + } + catch (Exception e) + { + throw new PgpException("exception constructing public key", e); + } + } + + /// <summary>Allows enumeration of any user IDs associated with the key.</summary> + /// <returns>An <c>IEnumerable</c> of <c>string</c> objects.</returns> + public IEnumerable GetUserIds() + { + IList temp = Platform.CreateArrayList(); + + foreach (object o in ids) + { + if (o is string) + { + temp.Add(o); + } + } + + return new EnumerableProxy(temp); + } + + /// <summary>Allows enumeration of any user attribute vectors associated with the key.</summary> + /// <returns>An <c>IEnumerable</c> of <c>PgpUserAttributeSubpacketVector</c> objects.</returns> + public IEnumerable GetUserAttributes() + { + IList temp = Platform.CreateArrayList(); + + foreach (object o in ids) + { + if (o is PgpUserAttributeSubpacketVector) + { + temp.Add(o); + } + } + + return new EnumerableProxy(temp); + } + + /// <summary>Allows enumeration of any signatures associated with the passed in id.</summary> + /// <param name="id">The ID to be matched.</param> + /// <returns>An <c>IEnumerable</c> of <c>PgpSignature</c> objects.</returns> + public IEnumerable GetSignaturesForId( + string id) + { + if (id == null) + throw new ArgumentNullException("id"); + + for (int i = 0; i != ids.Count; i++) + { + if (id.Equals(ids[i])) + { + return new EnumerableProxy((IList)idSigs[i]); + } + } + + return null; + } + + /// <summary>Allows enumeration of signatures associated with the passed in user attributes.</summary> + /// <param name="userAttributes">The vector of user attributes to be matched.</param> + /// <returns>An <c>IEnumerable</c> of <c>PgpSignature</c> objects.</returns> + public IEnumerable GetSignaturesForUserAttribute( + PgpUserAttributeSubpacketVector userAttributes) + { + for (int i = 0; i != ids.Count; i++) + { + if (userAttributes.Equals(ids[i])) + { + return new EnumerableProxy((IList) idSigs[i]); + } + } + + return null; + } + + /// <summary>Allows enumeration of signatures of the passed in type that are on this key.</summary> + /// <param name="signatureType">The type of the signature to be returned.</param> + /// <returns>An <c>IEnumerable</c> of <c>PgpSignature</c> objects.</returns> + public IEnumerable GetSignaturesOfType( + int signatureType) + { + IList temp = Platform.CreateArrayList(); + + foreach (PgpSignature sig in GetSignatures()) + { + if (sig.SignatureType == signatureType) + { + temp.Add(sig); + } + } + + return new EnumerableProxy(temp); + } + + /// <summary>Allows enumeration of all signatures/certifications associated with this key.</summary> + /// <returns>An <c>IEnumerable</c> with all signatures/certifications.</returns> + public IEnumerable GetSignatures() + { + IList sigs; + if (subSigs != null) + { + sigs = subSigs; + } + else + { + sigs = Platform.CreateArrayList(keySigs); + + foreach (ICollection extraSigs in idSigs) + { + CollectionUtilities.AddRange(sigs, extraSigs); + } + } + + return new EnumerableProxy(sigs); + } + + public byte[] GetEncoded() + { + MemoryStream bOut = new MemoryStream(); + Encode(bOut); + return bOut.ToArray(); + } + + public void Encode( + Stream outStr) + { + BcpgOutputStream bcpgOut = BcpgOutputStream.Wrap(outStr); + + bcpgOut.WritePacket(publicPk); + if (trustPk != null) + { + bcpgOut.WritePacket(trustPk); + } + + if (subSigs == null) // not a sub-key + { + foreach (PgpSignature keySig in keySigs) + { + keySig.Encode(bcpgOut); + } + + for (int i = 0; i != ids.Count; i++) + { + if (ids[i] is string) + { + string id = (string) ids[i]; + + bcpgOut.WritePacket(new UserIdPacket(id)); + } + else + { + PgpUserAttributeSubpacketVector v = (PgpUserAttributeSubpacketVector)ids[i]; + bcpgOut.WritePacket(new UserAttributePacket(v.ToSubpacketArray())); + } + + if (idTrusts[i] != null) + { + bcpgOut.WritePacket((ContainedPacket)idTrusts[i]); + } + + foreach (PgpSignature sig in (IList) idSigs[i]) + { + sig.Encode(bcpgOut); + } + } + } + else + { + foreach (PgpSignature subSig in subSigs) + { + subSig.Encode(bcpgOut); + } + } + } + + /// <summary>Check whether this (sub)key has a revocation signature on it.</summary> + /// <returns>True, if this (sub)key has been revoked.</returns> + public bool IsRevoked() + { + int ns = 0; + bool revoked = false; + if (IsMasterKey) // Master key + { + while (!revoked && (ns < keySigs.Count)) + { + if (((PgpSignature)keySigs[ns++]).SignatureType == PgpSignature.KeyRevocation) + { + revoked = true; + } + } + } + else // Sub-key + { + while (!revoked && (ns < subSigs.Count)) + { + if (((PgpSignature)subSigs[ns++]).SignatureType == PgpSignature.SubkeyRevocation) + { + revoked = true; + } + } + } + return revoked; + } + + /// <summary>Add a certification for an id to the given public key.</summary> + /// <param name="key">The key the certification is to be added to.</param> + /// <param name="id">The ID the certification is associated with.</param> + /// <param name="certification">The new certification.</param> + /// <returns>The re-certified key.</returns> + public static PgpPublicKey AddCertification( + PgpPublicKey key, + string id, + PgpSignature certification) + { + return AddCert(key, id, certification); + } + + /// <summary>Add a certification for the given UserAttributeSubpackets to the given public key.</summary> + /// <param name="key">The key the certification is to be added to.</param> + /// <param name="userAttributes">The attributes the certification is associated with.</param> + /// <param name="certification">The new certification.</param> + /// <returns>The re-certified key.</returns> + public static PgpPublicKey AddCertification( + PgpPublicKey key, + PgpUserAttributeSubpacketVector userAttributes, + PgpSignature certification) + { + return AddCert(key, userAttributes, certification); + } + + private static PgpPublicKey AddCert( + PgpPublicKey key, + object id, + PgpSignature certification) + { + PgpPublicKey returnKey = new PgpPublicKey(key); + IList sigList = null; + + for (int i = 0; i != returnKey.ids.Count; i++) + { + if (id.Equals(returnKey.ids[i])) + { + sigList = (IList) returnKey.idSigs[i]; + } + } + + if (sigList != null) + { + sigList.Add(certification); + } + else + { + sigList = Platform.CreateArrayList(); + sigList.Add(certification); + returnKey.ids.Add(id); + returnKey.idTrusts.Add(null); + returnKey.idSigs.Add(sigList); + } + + return returnKey; + } + + /// <summary> + /// Remove any certifications associated with a user attribute subpacket on a key. + /// </summary> + /// <param name="key">The key the certifications are to be removed from.</param> + /// <param name="userAttributes">The attributes to be removed.</param> + /// <returns> + /// The re-certified key, or null if the user attribute subpacket was not found on the key. + /// </returns> + public static PgpPublicKey RemoveCertification( + PgpPublicKey key, + PgpUserAttributeSubpacketVector userAttributes) + { + return RemoveCert(key, userAttributes); + } + + /// <summary>Remove any certifications associated with a given ID on a key.</summary> + /// <param name="key">The key the certifications are to be removed from.</param> + /// <param name="id">The ID that is to be removed.</param> + /// <returns>The re-certified key, or null if the ID was not found on the key.</returns> + public static PgpPublicKey RemoveCertification( + PgpPublicKey key, + string id) + { + return RemoveCert(key, id); + } + + private static PgpPublicKey RemoveCert( + PgpPublicKey key, + object id) + { + PgpPublicKey returnKey = new PgpPublicKey(key); + bool found = false; + + for (int i = 0; i < returnKey.ids.Count; i++) + { + if (id.Equals(returnKey.ids[i])) + { + found = true; + returnKey.ids.RemoveAt(i); + returnKey.idTrusts.RemoveAt(i); + returnKey.idSigs.RemoveAt(i); + } + } + + return found ? returnKey : null; + } + + /// <summary>Remove a certification associated with a given ID on a key.</summary> + /// <param name="key">The key the certifications are to be removed from.</param> + /// <param name="id">The ID that the certfication is to be removed from.</param> + /// <param name="certification">The certfication to be removed.</param> + /// <returns>The re-certified key, or null if the certification was not found.</returns> + public static PgpPublicKey RemoveCertification( + PgpPublicKey key, + string id, + PgpSignature certification) + { + return RemoveCert(key, id, certification); + } + + /// <summary>Remove a certification associated with a given user attributes on a key.</summary> + /// <param name="key">The key the certifications are to be removed from.</param> + /// <param name="userAttributes">The user attributes that the certfication is to be removed from.</param> + /// <param name="certification">The certification to be removed.</param> + /// <returns>The re-certified key, or null if the certification was not found.</returns> + public static PgpPublicKey RemoveCertification( + PgpPublicKey key, + PgpUserAttributeSubpacketVector userAttributes, + PgpSignature certification) + { + return RemoveCert(key, userAttributes, certification); + } + + private static PgpPublicKey RemoveCert( + PgpPublicKey key, + object id, + PgpSignature certification) + { + PgpPublicKey returnKey = new PgpPublicKey(key); + bool found = false; + + for (int i = 0; i < returnKey.ids.Count; i++) + { + if (id.Equals(returnKey.ids[i])) + { + IList certs = (IList) returnKey.idSigs[i]; + found = certs.Contains(certification); + + if (found) + { + certs.Remove(certification); + } + } + } + + return found ? returnKey : null; + } + + /// <summary>Add a revocation or some other key certification to a key.</summary> + /// <param name="key">The key the revocation is to be added to.</param> + /// <param name="certification">The key signature to be added.</param> + /// <returns>The new changed public key object.</returns> + public static PgpPublicKey AddCertification( + PgpPublicKey key, + PgpSignature certification) + { + if (key.IsMasterKey) + { + if (certification.SignatureType == PgpSignature.SubkeyRevocation) + { + throw new ArgumentException("signature type incorrect for master key revocation."); + } + } + else + { + if (certification.SignatureType == PgpSignature.KeyRevocation) + { + throw new ArgumentException("signature type incorrect for sub-key revocation."); + } + } + + PgpPublicKey returnKey = new PgpPublicKey(key); + + if (returnKey.subSigs != null) + { + returnKey.subSigs.Add(certification); + } + else + { + returnKey.keySigs.Add(certification); + } + + return returnKey; + } + + /// <summary>Remove a certification from the key.</summary> + /// <param name="key">The key the certifications are to be removed from.</param> + /// <param name="certification">The certfication to be removed.</param> + /// <returns>The modified key, null if the certification was not found.</returns> + public static PgpPublicKey RemoveCertification( + PgpPublicKey key, + PgpSignature certification) + { + PgpPublicKey returnKey = new PgpPublicKey(key); + IList sigs = returnKey.subSigs != null + ? returnKey.subSigs + : returnKey.keySigs; + +// bool found = sigs.Remove(certification); + int pos = sigs.IndexOf(certification); + bool found = pos >= 0; + + if (found) + { + sigs.RemoveAt(pos); + } + else + { + foreach (String id in key.GetUserIds()) + { + foreach (object sig in key.GetSignaturesForId(id)) + { + // TODO Is this the right type of equality test? + if (certification == sig) + { + found = true; + returnKey = PgpPublicKey.RemoveCertification(returnKey, id, certification); + } + } + } + + if (!found) + { + foreach (PgpUserAttributeSubpacketVector id in key.GetUserAttributes()) + { + foreach (object sig in key.GetSignaturesForUserAttribute(id)) + { + // TODO Is this the right type of equality test? + if (certification == sig) + { + found = true; + returnKey = PgpPublicKey.RemoveCertification(returnKey, id, certification); + } + } + } + } + } + + return returnKey; + } + } +} diff --git a/Crypto/src/openpgp/PgpPublicKeyEncryptedData.cs b/Crypto/src/openpgp/PgpPublicKeyEncryptedData.cs new file mode 100644
index 000000000..b6504cbcd --- /dev/null +++ b/Crypto/src/openpgp/PgpPublicKeyEncryptedData.cs
@@ -0,0 +1,252 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks>A public key encrypted data object.</remarks> + public class PgpPublicKeyEncryptedData + : PgpEncryptedData + { + private PublicKeyEncSessionPacket keyData; + + internal PgpPublicKeyEncryptedData( + PublicKeyEncSessionPacket keyData, + InputStreamPacket encData) + : base(encData) + { + this.keyData = keyData; + } + + private static IBufferedCipher GetKeyCipher( + PublicKeyAlgorithmTag algorithm) + { + try + { + switch (algorithm) + { + case PublicKeyAlgorithmTag.RsaEncrypt: + case PublicKeyAlgorithmTag.RsaGeneral: + return CipherUtilities.GetCipher("RSA//PKCS1Padding"); + case PublicKeyAlgorithmTag.ElGamalEncrypt: + case PublicKeyAlgorithmTag.ElGamalGeneral: + return CipherUtilities.GetCipher("ElGamal/ECB/PKCS1Padding"); + default: + throw new PgpException("unknown asymmetric algorithm: " + algorithm); + } + } + catch (PgpException e) + { + throw e; + } + catch (Exception e) + { + throw new PgpException("Exception creating cipher", e); + } + } + + private bool ConfirmCheckSum( + byte[] sessionInfo) + { + int check = 0; + + for (int i = 1; i != sessionInfo.Length - 2; i++) + { + check += sessionInfo[i] & 0xff; + } + + return (sessionInfo[sessionInfo.Length - 2] == (byte)(check >> 8)) + && (sessionInfo[sessionInfo.Length - 1] == (byte)(check)); + } + + /// <summary>The key ID for the key used to encrypt the data.</summary> + public long KeyId + { + get { return keyData.KeyId; } + } + + /// <summary> + /// Return the algorithm code for the symmetric algorithm used to encrypt the data. + /// </summary> + public SymmetricKeyAlgorithmTag GetSymmetricAlgorithm( + PgpPrivateKey privKey) + { + byte[] plain = fetchSymmetricKeyData(privKey); + + return (SymmetricKeyAlgorithmTag) plain[0]; + } + + /// <summary>Return the decrypted data stream for the packet.</summary> + public Stream GetDataStream( + PgpPrivateKey privKey) + { + byte[] plain = fetchSymmetricKeyData(privKey); + + IBufferedCipher c2; + string cipherName = PgpUtilities.GetSymmetricCipherName((SymmetricKeyAlgorithmTag) plain[0]); + string cName = cipherName; + + try + { + if (encData is SymmetricEncIntegrityPacket) + { + cName += "/CFB/NoPadding"; + } + else + { + cName += "/OpenPGPCFB/NoPadding"; + } + + c2 = CipherUtilities.GetCipher(cName); + } + catch (PgpException e) + { + throw e; + } + catch (Exception e) + { + throw new PgpException("exception creating cipher", e); + } + + if (c2 == null) + return encData.GetInputStream(); + + try + { + KeyParameter key = ParameterUtilities.CreateKeyParameter( + cipherName, plain, 1, plain.Length - 3); + + byte[] iv = new byte[c2.GetBlockSize()]; + + c2.Init(false, new ParametersWithIV(key, iv)); + + encStream = BcpgInputStream.Wrap(new CipherStream(encData.GetInputStream(), c2, null)); + + if (encData is SymmetricEncIntegrityPacket) + { + truncStream = new TruncatedStream(encStream); + + string digestName = PgpUtilities.GetDigestName(HashAlgorithmTag.Sha1); + IDigest digest = DigestUtilities.GetDigest(digestName); + + encStream = new DigestStream(truncStream, digest, null); + } + + if (Streams.ReadFully(encStream, iv, 0, iv.Length) < iv.Length) + throw new EndOfStreamException("unexpected end of stream."); + + int v1 = encStream.ReadByte(); + int v2 = encStream.ReadByte(); + + if (v1 < 0 || v2 < 0) + throw new EndOfStreamException("unexpected end of stream."); + + // Note: the oracle attack on the "quick check" bytes is deemed + // a security risk for typical public key encryption usages, + // therefore we do not perform the check. + +// bool repeatCheckPassed = +// iv[iv.Length - 2] == (byte)v1 +// && iv[iv.Length - 1] == (byte)v2; +// +// // Note: some versions of PGP appear to produce 0 for the extra +// // bytes rather than repeating the two previous bytes +// bool zeroesCheckPassed = +// v1 == 0 +// && v2 == 0; +// +// if (!repeatCheckPassed && !zeroesCheckPassed) +// { +// throw new PgpDataValidationException("quick check failed."); +// } + + return encStream; + } + catch (PgpException e) + { + throw e; + } + catch (Exception e) + { + throw new PgpException("Exception starting decryption", e); + } + } + + private byte[] fetchSymmetricKeyData( + PgpPrivateKey privKey) + { + IBufferedCipher c1 = GetKeyCipher(keyData.Algorithm); + + try + { + c1.Init(false, privKey.Key); + } + catch (InvalidKeyException e) + { + throw new PgpException("error setting asymmetric cipher", e); + } + + BigInteger[] keyD = keyData.GetEncSessionKey(); + + if (keyData.Algorithm == PublicKeyAlgorithmTag.RsaEncrypt + || keyData.Algorithm == PublicKeyAlgorithmTag.RsaGeneral) + { + c1.ProcessBytes(keyD[0].ToByteArrayUnsigned()); + } + else + { + ElGamalPrivateKeyParameters k = (ElGamalPrivateKeyParameters)privKey.Key; + int size = (k.Parameters.P.BitLength + 7) / 8; + + byte[] bi = keyD[0].ToByteArray(); + + int diff = bi.Length - size; + if (diff >= 0) + { + c1.ProcessBytes(bi, diff, size); + } + else + { + byte[] zeros = new byte[-diff]; + c1.ProcessBytes(zeros); + c1.ProcessBytes(bi); + } + + bi = keyD[1].ToByteArray(); + + diff = bi.Length - size; + if (diff >= 0) + { + c1.ProcessBytes(bi, diff, size); + } + else + { + byte[] zeros = new byte[-diff]; + c1.ProcessBytes(zeros); + c1.ProcessBytes(bi); + } + } + + byte[] plain; + try + { + plain = c1.DoFinal(); + } + catch (Exception e) + { + throw new PgpException("exception decrypting secret key", e); + } + + if (!ConfirmCheckSum(plain)) + throw new PgpKeyValidationException("key checksum failed"); + + return plain; + } + } +} diff --git a/Crypto/src/openpgp/PgpPublicKeyRing.cs b/Crypto/src/openpgp/PgpPublicKeyRing.cs new file mode 100644
index 000000000..ecb935e4b --- /dev/null +++ b/Crypto/src/openpgp/PgpPublicKeyRing.cs
@@ -0,0 +1,200 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks> + /// Class to hold a single master public key and its subkeys. + /// <p> + /// Often PGP keyring files consist of multiple master keys, if you are trying to process + /// or construct one of these you should use the <c>PgpPublicKeyRingBundle</c> class. + /// </p> + /// </remarks> + public class PgpPublicKeyRing + : PgpKeyRing + { + private readonly IList keys; + + public PgpPublicKeyRing( + byte[] encoding) + : this(new MemoryStream(encoding, false)) + { + } + + internal PgpPublicKeyRing( + IList pubKeys) + { + this.keys = pubKeys; + } + + public PgpPublicKeyRing( + Stream inputStream) + { + this.keys = Platform.CreateArrayList(); + + BcpgInputStream bcpgInput = BcpgInputStream.Wrap(inputStream); + + PacketTag initialTag = bcpgInput.NextPacketTag(); + if (initialTag != PacketTag.PublicKey && initialTag != PacketTag.PublicSubkey) + { + throw new IOException("public key ring doesn't start with public key tag: " + + "tag 0x" + ((int)initialTag).ToString("X")); + } + + PublicKeyPacket pubPk = (PublicKeyPacket) bcpgInput.ReadPacket();; + TrustPacket trustPk = ReadOptionalTrustPacket(bcpgInput); + + // direct signatures and revocations + IList keySigs = ReadSignaturesAndTrust(bcpgInput); + + IList ids, idTrusts, idSigs; + ReadUserIDs(bcpgInput, out ids, out idTrusts, out idSigs); + + keys.Add(new PgpPublicKey(pubPk, trustPk, keySigs, ids, idTrusts, idSigs)); + + + // Read subkeys + while (bcpgInput.NextPacketTag() == PacketTag.PublicSubkey) + { + keys.Add(ReadSubkey(bcpgInput)); + } + } + + /// <summary>Return the first public key in the ring.</summary> + public PgpPublicKey GetPublicKey() + { + return (PgpPublicKey) keys[0]; + } + + /// <summary>Return the public key referred to by the passed in key ID if it is present.</summary> + public PgpPublicKey GetPublicKey( + long keyId) + { + foreach (PgpPublicKey k in keys) + { + if (keyId == k.KeyId) + { + return k; + } + } + + return null; + } + + /// <summary>Allows enumeration of all the public keys.</summary> + /// <returns>An <c>IEnumerable</c> of <c>PgpPublicKey</c> objects.</returns> + public IEnumerable GetPublicKeys() + { + return new EnumerableProxy(keys); + } + + public byte[] GetEncoded() + { + MemoryStream bOut = new MemoryStream(); + + Encode(bOut); + + return bOut.ToArray(); + } + + public void Encode( + Stream outStr) + { + if (outStr == null) + throw new ArgumentNullException("outStr"); + + foreach (PgpPublicKey k in keys) + { + k.Encode(outStr); + } + } + + /// <summary> + /// Returns a new key ring with the public key passed in either added or + /// replacing an existing one. + /// </summary> + /// <param name="pubRing">The public key ring to be modified.</param> + /// <param name="pubKey">The public key to be inserted.</param> + /// <returns>A new <c>PgpPublicKeyRing</c></returns> + public static PgpPublicKeyRing InsertPublicKey( + PgpPublicKeyRing pubRing, + PgpPublicKey pubKey) + { + IList keys = Platform.CreateArrayList(pubRing.keys); + bool found = false; + bool masterFound = false; + + for (int i = 0; i != keys.Count; i++) + { + PgpPublicKey key = (PgpPublicKey) keys[i]; + + if (key.KeyId == pubKey.KeyId) + { + found = true; + keys[i] = pubKey; + } + if (key.IsMasterKey) + { + masterFound = true; + } + } + + if (!found) + { + if (pubKey.IsMasterKey) + { + if (masterFound) + throw new ArgumentException("cannot add a master key to a ring that already has one"); + + keys.Insert(0, pubKey); + } + else + { + keys.Add(pubKey); + } + } + + return new PgpPublicKeyRing(keys); + } + + /// <summary>Returns a new key ring with the public key passed in removed from the key ring.</summary> + /// <param name="pubRing">The public key ring to be modified.</param> + /// <param name="pubKey">The public key to be removed.</param> + /// <returns>A new <c>PgpPublicKeyRing</c>, or null if pubKey is not found.</returns> + public static PgpPublicKeyRing RemovePublicKey( + PgpPublicKeyRing pubRing, + PgpPublicKey pubKey) + { + IList keys = Platform.CreateArrayList(pubRing.keys); + bool found = false; + + for (int i = 0; i < keys.Count; i++) + { + PgpPublicKey key = (PgpPublicKey) keys[i]; + + if (key.KeyId == pubKey.KeyId) + { + found = true; + keys.RemoveAt(i); + } + } + + return found ? new PgpPublicKeyRing(keys) : null; + } + + internal static PgpPublicKey ReadSubkey(BcpgInputStream bcpgInput) + { + PublicKeyPacket pk = (PublicKeyPacket) bcpgInput.ReadPacket(); + TrustPacket kTrust = ReadOptionalTrustPacket(bcpgInput); + + // PGP 8 actually leaves out the signature. + IList sigList = ReadSignaturesAndTrust(bcpgInput); + + return new PgpPublicKey(pk, kTrust, sigList); + } + } +} diff --git a/Crypto/src/openpgp/PgpPublicKeyRingBundle.cs b/Crypto/src/openpgp/PgpPublicKeyRingBundle.cs new file mode 100644
index 000000000..77cede4a1 --- /dev/null +++ b/Crypto/src/openpgp/PgpPublicKeyRingBundle.cs
@@ -0,0 +1,280 @@ +using System; +using System.Collections; +using System.Globalization; +using System.IO; + +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks> + /// Often a PGP key ring file is made up of a succession of master/sub-key key rings. + /// If you want to read an entire public key file in one hit this is the class for you. + /// </remarks> + public class PgpPublicKeyRingBundle + { + private readonly IDictionary pubRings; + private readonly IList order; + + private PgpPublicKeyRingBundle( + IDictionary pubRings, + IList order) + { + this.pubRings = pubRings; + this.order = order; + } + + public PgpPublicKeyRingBundle( + byte[] encoding) + : this(new MemoryStream(encoding, false)) + { + } + + /// <summary>Build a PgpPublicKeyRingBundle from the passed in input stream.</summary> + /// <param name="inputStream">Input stream containing data.</param> + /// <exception cref="IOException">If a problem parsing the stream occurs.</exception> + /// <exception cref="PgpException">If an object is encountered which isn't a PgpPublicKeyRing.</exception> + public PgpPublicKeyRingBundle( + Stream inputStream) + : this(new PgpObjectFactory(inputStream).AllPgpObjects()) + { + } + + public PgpPublicKeyRingBundle( + IEnumerable e) + { + this.pubRings = Platform.CreateHashtable(); + this.order = Platform.CreateArrayList(); + + foreach (object obj in e) + { + PgpPublicKeyRing pgpPub = obj as PgpPublicKeyRing; + + if (pgpPub == null) + { + throw new PgpException(obj.GetType().FullName + " found where PgpPublicKeyRing expected"); + } + + long key = pgpPub.GetPublicKey().KeyId; + pubRings.Add(key, pgpPub); + order.Add(key); + } + } + + [Obsolete("Use 'Count' property instead")] + public int Size + { + get { return order.Count; } + } + + /// <summary>Return the number of key rings in this collection.</summary> + public int Count + { + get { return order.Count; } + } + + /// <summary>Allow enumeration of the public key rings making up this collection.</summary> + public IEnumerable GetKeyRings() + { + return new EnumerableProxy(pubRings.Values); + } + + /// <summary>Allow enumeration of the key rings associated with the passed in userId.</summary> + /// <param name="userId">The user ID to be matched.</param> + /// <returns>An <c>IEnumerable</c> of key rings which matched (possibly none).</returns> + public IEnumerable GetKeyRings( + string userId) + { + return GetKeyRings(userId, false, false); + } + + /// <summary>Allow enumeration of the key rings associated with the passed in userId.</summary> + /// <param name="userId">The user ID to be matched.</param> + /// <param name="matchPartial">If true, userId need only be a substring of an actual ID string to match.</param> + /// <returns>An <c>IEnumerable</c> of key rings which matched (possibly none).</returns> + public IEnumerable GetKeyRings( + string userId, + bool matchPartial) + { + return GetKeyRings(userId, matchPartial, false); + } + + /// <summary>Allow enumeration of the key rings associated with the passed in userId.</summary> + /// <param name="userId">The user ID to be matched.</param> + /// <param name="matchPartial">If true, userId need only be a substring of an actual ID string to match.</param> + /// <param name="ignoreCase">If true, case is ignored in user ID comparisons.</param> + /// <returns>An <c>IEnumerable</c> of key rings which matched (possibly none).</returns> + public IEnumerable GetKeyRings( + string userId, + bool matchPartial, + bool ignoreCase) + { + IList rings = Platform.CreateArrayList(); + + if (ignoreCase) + { + userId = userId.ToLowerInvariant(); + } + + foreach (PgpPublicKeyRing pubRing in GetKeyRings()) + { + foreach (string nextUserID in pubRing.GetPublicKey().GetUserIds()) + { + string next = nextUserID; + if (ignoreCase) + { + next = next.ToLowerInvariant(); + } + + if (matchPartial) + { + if (next.IndexOf(userId) > -1) + { + rings.Add(pubRing); + } + } + else + { + if (next.Equals(userId)) + { + rings.Add(pubRing); + } + } + } + } + + return new EnumerableProxy(rings); + } + + /// <summary>Return the PGP public key associated with the given key id.</summary> + /// <param name="keyId">The ID of the public key to return.</param> + public PgpPublicKey GetPublicKey( + long keyId) + { + foreach (PgpPublicKeyRing pubRing in GetKeyRings()) + { + PgpPublicKey pub = pubRing.GetPublicKey(keyId); + + if (pub != null) + { + return pub; + } + } + + return null; + } + + /// <summary>Return the public key ring which contains the key referred to by keyId</summary> + /// <param name="keyId">key ID to match against</param> + public PgpPublicKeyRing GetPublicKeyRing( + long keyId) + { + if (pubRings.Contains(keyId)) + { + return (PgpPublicKeyRing)pubRings[keyId]; + } + + foreach (PgpPublicKeyRing pubRing in GetKeyRings()) + { + PgpPublicKey pub = pubRing.GetPublicKey(keyId); + + if (pub != null) + { + return pubRing; + } + } + + return null; + } + + /// <summary> + /// Return true if a key matching the passed in key ID is present, false otherwise. + /// </summary> + /// <param name="keyID">key ID to look for.</param> + public bool Contains( + long keyID) + { + return GetPublicKey(keyID) != null; + } + + public byte[] GetEncoded() + { + MemoryStream bOut = new MemoryStream(); + + Encode(bOut); + + return bOut.ToArray(); + } + + public void Encode( + Stream outStr) + { + BcpgOutputStream bcpgOut = BcpgOutputStream.Wrap(outStr); + + foreach (long key in order) + { + PgpPublicKeyRing sec = (PgpPublicKeyRing) pubRings[key]; + + sec.Encode(bcpgOut); + } + } + + /// <summary> + /// Return a new bundle containing the contents of the passed in bundle and + /// the passed in public key ring. + /// </summary> + /// <param name="bundle">The <c>PgpPublicKeyRingBundle</c> the key ring is to be added to.</param> + /// <param name="publicKeyRing">The key ring to be added.</param> + /// <returns>A new <c>PgpPublicKeyRingBundle</c> merging the current one with the passed in key ring.</returns> + /// <exception cref="ArgumentException">If the keyId for the passed in key ring is already present.</exception> + public static PgpPublicKeyRingBundle AddPublicKeyRing( + PgpPublicKeyRingBundle bundle, + PgpPublicKeyRing publicKeyRing) + { + long key = publicKeyRing.GetPublicKey().KeyId; + + if (bundle.pubRings.Contains(key)) + { + throw new ArgumentException("Bundle already contains a key with a keyId for the passed in ring."); + } + + IDictionary newPubRings = Platform.CreateHashtable(bundle.pubRings); + IList newOrder = Platform.CreateArrayList(bundle.order); + + newPubRings[key] = publicKeyRing; + + newOrder.Add(key); + + return new PgpPublicKeyRingBundle(newPubRings, newOrder); + } + + /// <summary> + /// Return a new bundle containing the contents of the passed in bundle with + /// the passed in public key ring removed. + /// </summary> + /// <param name="bundle">The <c>PgpPublicKeyRingBundle</c> the key ring is to be removed from.</param> + /// <param name="publicKeyRing">The key ring to be removed.</param> + /// <returns>A new <c>PgpPublicKeyRingBundle</c> not containing the passed in key ring.</returns> + /// <exception cref="ArgumentException">If the keyId for the passed in key ring is not present.</exception> + public static PgpPublicKeyRingBundle RemovePublicKeyRing( + PgpPublicKeyRingBundle bundle, + PgpPublicKeyRing publicKeyRing) + { + long key = publicKeyRing.GetPublicKey().KeyId; + + if (!bundle.pubRings.Contains(key)) + { + throw new ArgumentException("Bundle does not contain a key with a keyId for the passed in ring."); + } + + IDictionary newPubRings = Platform.CreateHashtable(bundle.pubRings); + IList newOrder = Platform.CreateArrayList(bundle.order); + + newPubRings.Remove(key); + newOrder.Remove(key); + + return new PgpPublicKeyRingBundle(newPubRings, newOrder); + } + } +} diff --git a/Crypto/src/openpgp/PgpSecretKey.cs b/Crypto/src/openpgp/PgpSecretKey.cs new file mode 100644
index 000000000..9d87f49c8 --- /dev/null +++ b/Crypto/src/openpgp/PgpSecretKey.cs
@@ -0,0 +1,666 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks>General class to handle a PGP secret key object.</remarks> + public class PgpSecretKey + { + private readonly SecretKeyPacket secret; + private readonly PgpPublicKey pub; + + internal PgpSecretKey( + SecretKeyPacket secret, + PgpPublicKey pub) + { + this.secret = secret; + this.pub = pub; + } + + internal PgpSecretKey( + PgpPrivateKey privKey, + PgpPublicKey pubKey, + SymmetricKeyAlgorithmTag encAlgorithm, + char[] passPhrase, + bool useSha1, + SecureRandom rand) + : this(privKey, pubKey, encAlgorithm, passPhrase, useSha1, rand, false) + { + } + + internal PgpSecretKey( + PgpPrivateKey privKey, + PgpPublicKey pubKey, + SymmetricKeyAlgorithmTag encAlgorithm, + char[] passPhrase, + bool useSha1, + SecureRandom rand, + bool isMasterKey) + { + BcpgObject secKey; + + this.pub = pubKey; + + switch (pubKey.Algorithm) + { + case PublicKeyAlgorithmTag.RsaEncrypt: + case PublicKeyAlgorithmTag.RsaSign: + case PublicKeyAlgorithmTag.RsaGeneral: + RsaPrivateCrtKeyParameters rsK = (RsaPrivateCrtKeyParameters) privKey.Key; + secKey = new RsaSecretBcpgKey(rsK.Exponent, rsK.P, rsK.Q); + break; + case PublicKeyAlgorithmTag.Dsa: + DsaPrivateKeyParameters dsK = (DsaPrivateKeyParameters) privKey.Key; + secKey = new DsaSecretBcpgKey(dsK.X); + break; + case PublicKeyAlgorithmTag.ElGamalEncrypt: + case PublicKeyAlgorithmTag.ElGamalGeneral: + ElGamalPrivateKeyParameters esK = (ElGamalPrivateKeyParameters) privKey.Key; + secKey = new ElGamalSecretBcpgKey(esK.X); + break; + default: + throw new PgpException("unknown key class"); + } + + try + { + MemoryStream bOut = new MemoryStream(); + BcpgOutputStream pOut = new BcpgOutputStream(bOut); + + pOut.WriteObject(secKey); + + byte[] keyData = bOut.ToArray(); + byte[] checksumBytes = Checksum(useSha1, keyData, keyData.Length); + + pOut.Write(checksumBytes); + + byte[] bOutData = bOut.ToArray(); + + if (encAlgorithm == SymmetricKeyAlgorithmTag.Null) + { + if (isMasterKey) + { + this.secret = new SecretKeyPacket(pub.publicPk, encAlgorithm, null, null, bOutData); + } + else + { + this.secret = new SecretSubkeyPacket(pub.publicPk, encAlgorithm, null, null, bOutData); + } + } + else + { + S2k s2k; + byte[] iv; + byte[] encData = EncryptKeyData(bOutData, encAlgorithm, passPhrase, rand, out s2k, out iv); + + int s2kUsage = useSha1 + ? SecretKeyPacket.UsageSha1 + : SecretKeyPacket.UsageChecksum; + + if (isMasterKey) + { + this.secret = new SecretKeyPacket(pub.publicPk, encAlgorithm, s2kUsage, s2k, iv, encData); + } + else + { + this.secret = new SecretSubkeyPacket(pub.publicPk, encAlgorithm, s2kUsage, s2k, iv, encData); + } + } + } + catch (PgpException e) + { + throw e; + } + catch (Exception e) + { + throw new PgpException("Exception encrypting key", e); + } + } + + public PgpSecretKey( + int certificationLevel, + PgpKeyPair keyPair, + string id, + SymmetricKeyAlgorithmTag encAlgorithm, + char[] passPhrase, + PgpSignatureSubpacketVector hashedPackets, + PgpSignatureSubpacketVector unhashedPackets, + SecureRandom rand) + : this(certificationLevel, keyPair, id, encAlgorithm, passPhrase, false, hashedPackets, unhashedPackets, rand) + { + } + + public PgpSecretKey( + int certificationLevel, + PgpKeyPair keyPair, + string id, + SymmetricKeyAlgorithmTag encAlgorithm, + char[] passPhrase, + bool useSha1, + PgpSignatureSubpacketVector hashedPackets, + PgpSignatureSubpacketVector unhashedPackets, + SecureRandom rand) + : this(keyPair.PrivateKey, certifiedPublicKey(certificationLevel, keyPair, id, hashedPackets, unhashedPackets), encAlgorithm, passPhrase, useSha1, rand, true) + { + } + + private static PgpPublicKey certifiedPublicKey( + int certificationLevel, + PgpKeyPair keyPair, + string id, + PgpSignatureSubpacketVector hashedPackets, + PgpSignatureSubpacketVector unhashedPackets) + { + PgpSignatureGenerator sGen; + try + { + sGen = new PgpSignatureGenerator(keyPair.PublicKey.Algorithm, HashAlgorithmTag.Sha1); + } + catch (Exception e) + { + throw new PgpException("Creating signature generator: " + e.Message, e); + } + + // + // Generate the certification + // + sGen.InitSign(certificationLevel, keyPair.PrivateKey); + + sGen.SetHashedSubpackets(hashedPackets); + sGen.SetUnhashedSubpackets(unhashedPackets); + + try + { + PgpSignature certification = sGen.GenerateCertification(id, keyPair.PublicKey); + return PgpPublicKey.AddCertification(keyPair.PublicKey, id, certification); + } + catch (Exception e) + { + throw new PgpException("Exception doing certification: " + e.Message, e); + } + } + + public PgpSecretKey( + int certificationLevel, + PublicKeyAlgorithmTag algorithm, + AsymmetricKeyParameter pubKey, + AsymmetricKeyParameter privKey, + DateTime time, + string id, + SymmetricKeyAlgorithmTag encAlgorithm, + char[] passPhrase, + PgpSignatureSubpacketVector hashedPackets, + PgpSignatureSubpacketVector unhashedPackets, + SecureRandom rand) + : this(certificationLevel, + new PgpKeyPair(algorithm, pubKey, privKey, time), + id, encAlgorithm, passPhrase, hashedPackets, unhashedPackets, rand) + { + } + + public PgpSecretKey( + int certificationLevel, + PublicKeyAlgorithmTag algorithm, + AsymmetricKeyParameter pubKey, + AsymmetricKeyParameter privKey, + DateTime time, + string id, + SymmetricKeyAlgorithmTag encAlgorithm, + char[] passPhrase, + bool useSha1, + PgpSignatureSubpacketVector hashedPackets, + PgpSignatureSubpacketVector unhashedPackets, + SecureRandom rand) + : this(certificationLevel, new PgpKeyPair(algorithm, pubKey, privKey, time), id, encAlgorithm, passPhrase, useSha1, hashedPackets, unhashedPackets, rand) + { + } + + /// <summary> + /// Check if this key has an algorithm type that makes it suitable to use for signing. + /// </summary> + /// <remarks> + /// Note: with version 4 keys KeyFlags subpackets should also be considered when present for + /// determining the preferred use of the key. + /// </remarks> + /// <returns> + /// <c>true</c> if this key algorithm is suitable for use with signing. + /// </returns> + public bool IsSigningKey + { + get + { + switch (pub.Algorithm) + { + case PublicKeyAlgorithmTag.RsaGeneral: + case PublicKeyAlgorithmTag.RsaSign: + case PublicKeyAlgorithmTag.Dsa: + case PublicKeyAlgorithmTag.ECDsa: + case PublicKeyAlgorithmTag.ElGamalGeneral: + return true; + default: + return false; + } + } + } + + /// <summary>True, if this is a master key.</summary> + public bool IsMasterKey + { + get { return pub.IsMasterKey; } + } + + /// <summary>The algorithm the key is encrypted with.</summary> + public SymmetricKeyAlgorithmTag KeyEncryptionAlgorithm + { + get { return secret.EncAlgorithm; } + } + + /// <summary>The key ID of the public key associated with this key.</summary> + public long KeyId + { + get { return pub.KeyId; } + } + + /// <summary>The public key associated with this key.</summary> + public PgpPublicKey PublicKey + { + get { return pub; } + } + + /// <summary>Allows enumeration of any user IDs associated with the key.</summary> + /// <returns>An <c>IEnumerable</c> of <c>string</c> objects.</returns> + public IEnumerable UserIds + { + get { return pub.GetUserIds(); } + } + + /// <summary>Allows enumeration of any user attribute vectors associated with the key.</summary> + /// <returns>An <c>IEnumerable</c> of <c>string</c> objects.</returns> + public IEnumerable UserAttributes + { + get { return pub.GetUserAttributes(); } + } + + private byte[] ExtractKeyData( + char[] passPhrase) + { + SymmetricKeyAlgorithmTag alg = secret.EncAlgorithm; + byte[] encData = secret.GetSecretKeyData(); + + if (alg == SymmetricKeyAlgorithmTag.Null) + return encData; + + byte[] data; + IBufferedCipher c = null; + try + { + string cName = PgpUtilities.GetSymmetricCipherName(alg); + c = CipherUtilities.GetCipher(cName + "/CFB/NoPadding"); + } + catch (Exception e) + { + throw new PgpException("Exception creating cipher", e); + } + + // TODO Factor this block out as 'encryptData' + try + { + KeyParameter key = PgpUtilities.MakeKeyFromPassPhrase(secret.EncAlgorithm, secret.S2k, passPhrase); + byte[] iv = secret.GetIV(); + + if (secret.PublicKeyPacket.Version == 4) + { + c.Init(false, new ParametersWithIV(key, iv)); + + data = c.DoFinal(encData); + + bool useSha1 = secret.S2kUsage == SecretKeyPacket.UsageSha1; + byte[] check = Checksum(useSha1, data, (useSha1) ? data.Length - 20 : data.Length - 2); + + for (int i = 0; i != check.Length; i++) + { + if (check[i] != data[data.Length - check.Length + i]) + { + throw new PgpException("Checksum mismatch at " + i + " of " + check.Length); + } + } + } + else // version 2 or 3, RSA only. + { + data = new byte[encData.Length]; + + // + // read in the four numbers + // + int pos = 0; + + for (int i = 0; i != 4; i++) + { + c.Init(false, new ParametersWithIV(key, iv)); + + int encLen = (((encData[pos] << 8) | (encData[pos + 1] & 0xff)) + 7) / 8; + + data[pos] = encData[pos]; + data[pos + 1] = encData[pos + 1]; + pos += 2; + + c.DoFinal(encData, pos, encLen, data, pos); + pos += encLen; + + if (i != 3) + { + Array.Copy(encData, pos - iv.Length, iv, 0, iv.Length); + } + } + + // + // verify Checksum + // + int cs = ((encData[pos] << 8) & 0xff00) | (encData[pos + 1] & 0xff); + int calcCs = 0; + for (int j=0; j < data.Length-2; j++) + { + calcCs += data[j] & 0xff; + } + + calcCs &= 0xffff; + if (calcCs != cs) + { + throw new PgpException("Checksum mismatch: passphrase wrong, expected " + + cs.ToString("X") + + " found " + calcCs.ToString("X")); + } + } + + return data; + } + catch (PgpException e) + { + throw e; + } + catch (Exception e) + { + throw new PgpException("Exception decrypting key", e); + } + } + + /// <summary>Extract a <c>PgpPrivateKey</c> from this secret key's encrypted contents.</summary> + public PgpPrivateKey ExtractPrivateKey( + char[] passPhrase) + { + byte[] secKeyData = secret.GetSecretKeyData(); + if (secKeyData == null || secKeyData.Length < 1) + return null; + + PublicKeyPacket pubPk = secret.PublicKeyPacket; + try + { + byte[] data = ExtractKeyData(passPhrase); + BcpgInputStream bcpgIn = BcpgInputStream.Wrap(new MemoryStream(data, false)); + AsymmetricKeyParameter privateKey; + switch (pubPk.Algorithm) + { + case PublicKeyAlgorithmTag.RsaEncrypt: + case PublicKeyAlgorithmTag.RsaGeneral: + case PublicKeyAlgorithmTag.RsaSign: + RsaPublicBcpgKey rsaPub = (RsaPublicBcpgKey)pubPk.Key; + RsaSecretBcpgKey rsaPriv = new RsaSecretBcpgKey(bcpgIn); + RsaPrivateCrtKeyParameters rsaPrivSpec = new RsaPrivateCrtKeyParameters( + rsaPriv.Modulus, + rsaPub.PublicExponent, + rsaPriv.PrivateExponent, + rsaPriv.PrimeP, + rsaPriv.PrimeQ, + rsaPriv.PrimeExponentP, + rsaPriv.PrimeExponentQ, + rsaPriv.CrtCoefficient); + privateKey = rsaPrivSpec; + break; + case PublicKeyAlgorithmTag.Dsa: + DsaPublicBcpgKey dsaPub = (DsaPublicBcpgKey)pubPk.Key; + DsaSecretBcpgKey dsaPriv = new DsaSecretBcpgKey(bcpgIn); + DsaParameters dsaParams = new DsaParameters(dsaPub.P, dsaPub.Q, dsaPub.G); + privateKey = new DsaPrivateKeyParameters(dsaPriv.X, dsaParams); + break; + case PublicKeyAlgorithmTag.ElGamalEncrypt: + case PublicKeyAlgorithmTag.ElGamalGeneral: + ElGamalPublicBcpgKey elPub = (ElGamalPublicBcpgKey)pubPk.Key; + ElGamalSecretBcpgKey elPriv = new ElGamalSecretBcpgKey(bcpgIn); + ElGamalParameters elParams = new ElGamalParameters(elPub.P, elPub.G); + privateKey = new ElGamalPrivateKeyParameters(elPriv.X, elParams); + break; + default: + throw new PgpException("unknown public key algorithm encountered"); + } + + return new PgpPrivateKey(privateKey, KeyId); + } + catch (PgpException e) + { + throw e; + } + catch (Exception e) + { + throw new PgpException("Exception constructing key", e); + } + } + + private static byte[] Checksum( + bool useSha1, + byte[] bytes, + int length) + { + if (useSha1) + { + try + { + IDigest dig = DigestUtilities.GetDigest("SHA1"); + dig.BlockUpdate(bytes, 0, length); + return DigestUtilities.DoFinal(dig); + } + //catch (NoSuchAlgorithmException e) + catch (Exception e) + { + throw new PgpException("Can't find SHA-1", e); + } + } + else + { + int Checksum = 0; + for (int i = 0; i != length; i++) + { + Checksum += bytes[i]; + } + + return new byte[] { (byte)(Checksum >> 8), (byte)Checksum }; + } + } + + public byte[] GetEncoded() + { + MemoryStream bOut = new MemoryStream(); + Encode(bOut); + return bOut.ToArray(); + } + + public void Encode( + Stream outStr) + { + BcpgOutputStream bcpgOut = BcpgOutputStream.Wrap(outStr); + + bcpgOut.WritePacket(secret); + if (pub.trustPk != null) + { + bcpgOut.WritePacket(pub.trustPk); + } + + if (pub.subSigs == null) // is not a sub key + { + foreach (PgpSignature keySig in pub.keySigs) + { + keySig.Encode(bcpgOut); + } + + for (int i = 0; i != pub.ids.Count; i++) + { + object pubID = pub.ids[i]; + if (pubID is string) + { + string id = (string) pubID; + bcpgOut.WritePacket(new UserIdPacket(id)); + } + else + { + PgpUserAttributeSubpacketVector v = (PgpUserAttributeSubpacketVector) pubID; + bcpgOut.WritePacket(new UserAttributePacket(v.ToSubpacketArray())); + } + + if (pub.idTrusts[i] != null) + { + bcpgOut.WritePacket((ContainedPacket)pub.idTrusts[i]); + } + + foreach (PgpSignature sig in (IList) pub.idSigs[i]) + { + sig.Encode(bcpgOut); + } + } + } + else + { + foreach (PgpSignature subSig in pub.subSigs) + { + subSig.Encode(bcpgOut); + } + } + + // TODO Check that this is right/necessary + //bcpgOut.Finish(); + } + + /// <summary> + /// Return a copy of the passed in secret key, encrypted using a new password + /// and the passed in algorithm. + /// </summary> + /// <param name="key">The PgpSecretKey to be copied.</param> + /// <param name="oldPassPhrase">The current password for the key.</param> + /// <param name="newPassPhrase">The new password for the key.</param> + /// <param name="newEncAlgorithm">The algorithm to be used for the encryption.</param> + /// <param name="rand">Source of randomness.</param> + public static PgpSecretKey CopyWithNewPassword( + PgpSecretKey key, + char[] oldPassPhrase, + char[] newPassPhrase, + SymmetricKeyAlgorithmTag newEncAlgorithm, + SecureRandom rand) + { + byte[] rawKeyData = key.ExtractKeyData(oldPassPhrase); + int s2kUsage = key.secret.S2kUsage; + byte[] iv = null; + S2k s2k = null; + byte[] keyData; + + if (newEncAlgorithm == SymmetricKeyAlgorithmTag.Null) + { + s2kUsage = SecretKeyPacket.UsageNone; + if (key.secret.S2kUsage == SecretKeyPacket.UsageSha1) // SHA-1 hash, need to rewrite Checksum + { + keyData = new byte[rawKeyData.Length - 18]; + + Array.Copy(rawKeyData, 0, keyData, 0, keyData.Length - 2); + + byte[] check = Checksum(false, keyData, keyData.Length - 2); + + keyData[keyData.Length - 2] = check[0]; + keyData[keyData.Length - 1] = check[1]; + } + else + { + keyData = rawKeyData; + } + } + else + { + try + { + keyData = EncryptKeyData(rawKeyData, newEncAlgorithm, newPassPhrase, rand, out s2k, out iv); + } + catch (PgpException e) + { + throw e; + } + catch (Exception e) + { + throw new PgpException("Exception encrypting key", e); + } + } + + SecretKeyPacket secret; + if (key.secret is SecretSubkeyPacket) + { + secret = new SecretSubkeyPacket(key.secret.PublicKeyPacket, + newEncAlgorithm, s2kUsage, s2k, iv, keyData); + } + else + { + secret = new SecretKeyPacket(key.secret.PublicKeyPacket, + newEncAlgorithm, s2kUsage, s2k, iv, keyData); + } + + return new PgpSecretKey(secret, key.pub); + } + + /// <summary>Replace the passed the public key on the passed in secret key.</summary> + /// <param name="secretKey">Secret key to change.</param> + /// <param name="publicKey">New public key.</param> + /// <returns>A new secret key.</returns> + /// <exception cref="ArgumentException">If KeyId's do not match.</exception> + public static PgpSecretKey ReplacePublicKey( + PgpSecretKey secretKey, + PgpPublicKey publicKey) + { + if (publicKey.KeyId != secretKey.KeyId) + throw new ArgumentException("KeyId's do not match"); + + return new PgpSecretKey(secretKey.secret, publicKey); + } + + private static byte[] EncryptKeyData( + byte[] rawKeyData, + SymmetricKeyAlgorithmTag encAlgorithm, + char[] passPhrase, + SecureRandom random, + out S2k s2k, + out byte[] iv) + { + IBufferedCipher c; + try + { + string cName = PgpUtilities.GetSymmetricCipherName(encAlgorithm); + c = CipherUtilities.GetCipher(cName + "/CFB/NoPadding"); + } + catch (Exception e) + { + throw new PgpException("Exception creating cipher", e); + } + + byte[] s2kIV = new byte[8]; + random.NextBytes(s2kIV); + s2k = new S2k(HashAlgorithmTag.Sha1, s2kIV, 0x60); + + KeyParameter kp = PgpUtilities.MakeKeyFromPassPhrase(encAlgorithm, s2k, passPhrase); + + iv = new byte[c.GetBlockSize()]; + random.NextBytes(iv); + + c.Init(true, new ParametersWithRandom(new ParametersWithIV(kp, iv), random)); + + return c.DoFinal(rawKeyData); + } + } +} diff --git a/Crypto/src/openpgp/PgpSecretKeyRing.cs b/Crypto/src/openpgp/PgpSecretKeyRing.cs new file mode 100644
index 000000000..3e646eaa1 --- /dev/null +++ b/Crypto/src/openpgp/PgpSecretKeyRing.cs
@@ -0,0 +1,301 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks> + /// Class to hold a single master secret key and its subkeys. + /// <p> + /// Often PGP keyring files consist of multiple master keys, if you are trying to process + /// or construct one of these you should use the <c>PgpSecretKeyRingBundle</c> class. + /// </p> + /// </remarks> + public class PgpSecretKeyRing + : PgpKeyRing + { + private readonly IList keys; + private readonly IList extraPubKeys; + + internal PgpSecretKeyRing( + IList keys) + : this(keys, Platform.CreateArrayList()) + { + } + + private PgpSecretKeyRing( + IList keys, + IList extraPubKeys) + { + this.keys = keys; + this.extraPubKeys = extraPubKeys; + } + + public PgpSecretKeyRing( + byte[] encoding) + : this(new MemoryStream(encoding)) + { + } + + public PgpSecretKeyRing( + Stream inputStream) + { + this.keys = Platform.CreateArrayList(); + this.extraPubKeys = Platform.CreateArrayList(); + + BcpgInputStream bcpgInput = BcpgInputStream.Wrap(inputStream); + + PacketTag initialTag = bcpgInput.NextPacketTag(); + if (initialTag != PacketTag.SecretKey && initialTag != PacketTag.SecretSubkey) + { + throw new IOException("secret key ring doesn't start with secret key tag: " + + "tag 0x" + ((int)initialTag).ToString("X")); + } + + SecretKeyPacket secret = (SecretKeyPacket) bcpgInput.ReadPacket(); + + // + // ignore GPG comment packets if found. + // + while (bcpgInput.NextPacketTag() == PacketTag.Experimental2) + { + bcpgInput.ReadPacket(); + } + + TrustPacket trust = ReadOptionalTrustPacket(bcpgInput); + + // revocation and direct signatures + IList keySigs = ReadSignaturesAndTrust(bcpgInput); + + IList ids, idTrusts, idSigs; + ReadUserIDs(bcpgInput, out ids, out idTrusts, out idSigs); + + keys.Add(new PgpSecretKey(secret, new PgpPublicKey(secret.PublicKeyPacket, trust, keySigs, ids, idTrusts, idSigs))); + + + // Read subkeys + while (bcpgInput.NextPacketTag() == PacketTag.SecretSubkey + || bcpgInput.NextPacketTag() == PacketTag.PublicSubkey) + { + if (bcpgInput.NextPacketTag() == PacketTag.SecretSubkey) + { + SecretSubkeyPacket sub = (SecretSubkeyPacket) bcpgInput.ReadPacket(); + + // + // ignore GPG comment packets if found. + // + while (bcpgInput.NextPacketTag() == PacketTag.Experimental2) + { + bcpgInput.ReadPacket(); + } + + TrustPacket subTrust = ReadOptionalTrustPacket(bcpgInput); + IList sigList = ReadSignaturesAndTrust(bcpgInput); + + keys.Add(new PgpSecretKey(sub, new PgpPublicKey(sub.PublicKeyPacket, subTrust, sigList))); + } + else + { + PublicSubkeyPacket sub = (PublicSubkeyPacket) bcpgInput.ReadPacket(); + + TrustPacket subTrust = ReadOptionalTrustPacket(bcpgInput); + IList sigList = ReadSignaturesAndTrust(bcpgInput); + + extraPubKeys.Add(new PgpPublicKey(sub, subTrust, sigList)); + } + } + } + + /// <summary>Return the public key for the master key.</summary> + public PgpPublicKey GetPublicKey() + { + return ((PgpSecretKey) keys[0]).PublicKey; + } + + /// <summary>Return the master private key.</summary> + public PgpSecretKey GetSecretKey() + { + return (PgpSecretKey) keys[0]; + } + + /// <summary>Allows enumeration of the secret keys.</summary> + /// <returns>An <c>IEnumerable</c> of <c>PgpSecretKey</c> objects.</returns> + public IEnumerable GetSecretKeys() + { + return new EnumerableProxy(keys); + } + + public PgpSecretKey GetSecretKey( + long keyId) + { + foreach (PgpSecretKey k in keys) + { + if (keyId == k.KeyId) + { + return k; + } + } + + return null; + } + + /// <summary> + /// Return an iterator of the public keys in the secret key ring that + /// have no matching private key. At the moment only personal certificate data + /// appears in this fashion. + /// </summary> + /// <returns>An <c>IEnumerable</c> of unattached, or extra, public keys.</returns> + public IEnumerable GetExtraPublicKeys() + { + return new EnumerableProxy(extraPubKeys); + } + + public byte[] GetEncoded() + { + MemoryStream bOut = new MemoryStream(); + + Encode(bOut); + + return bOut.ToArray(); + } + + public void Encode( + Stream outStr) + { + if (outStr == null) + throw new ArgumentNullException("outStr"); + + foreach (PgpSecretKey key in keys) + { + key.Encode(outStr); + } + foreach (PgpPublicKey extraPubKey in extraPubKeys) + { + extraPubKey.Encode(outStr); + } + } + + /// <summary> + /// Replace the public key set on the secret ring with the corresponding key off the public ring. + /// </summary> + /// <param name="secretRing">Secret ring to be changed.</param> + /// <param name="publicRing">Public ring containing the new public key set.</param> + public static PgpSecretKeyRing ReplacePublicKeys( + PgpSecretKeyRing secretRing, + PgpPublicKeyRing publicRing) + { + IList newList = Platform.CreateArrayList(secretRing.keys.Count); + + foreach (PgpSecretKey sk in secretRing.keys) + { + PgpPublicKey pk = publicRing.GetPublicKey(sk.KeyId); + + newList.Add(PgpSecretKey.ReplacePublicKey(sk, pk)); + } + + return new PgpSecretKeyRing(newList); + } + + /// <summary> + /// Return a copy of the passed in secret key ring, with the master key and sub keys encrypted + /// using a new password and the passed in algorithm. + /// </summary> + /// <param name="ring">The <c>PgpSecretKeyRing</c> to be copied.</param> + /// <param name="oldPassPhrase">The current password for key.</param> + /// <param name="newPassPhrase">The new password for the key.</param> + /// <param name="newEncAlgorithm">The algorithm to be used for the encryption.</param> + /// <param name="rand">Source of randomness.</param> + public static PgpSecretKeyRing CopyWithNewPassword( + PgpSecretKeyRing ring, + char[] oldPassPhrase, + char[] newPassPhrase, + SymmetricKeyAlgorithmTag newEncAlgorithm, + SecureRandom rand) + { + IList newKeys = Platform.CreateArrayList(ring.keys.Count); + foreach (PgpSecretKey secretKey in ring.GetSecretKeys()) + { + newKeys.Add(PgpSecretKey.CopyWithNewPassword(secretKey, oldPassPhrase, newPassPhrase, newEncAlgorithm, rand)); + } + + return new PgpSecretKeyRing(newKeys, ring.extraPubKeys); + } + + /// <summary> + /// Returns a new key ring with the secret key passed in either added or + /// replacing an existing one with the same key ID. + /// </summary> + /// <param name="secRing">The secret key ring to be modified.</param> + /// <param name="secKey">The secret key to be inserted.</param> + /// <returns>A new <c>PgpSecretKeyRing</c></returns> + public static PgpSecretKeyRing InsertSecretKey( + PgpSecretKeyRing secRing, + PgpSecretKey secKey) + { + IList keys = Platform.CreateArrayList(secRing.keys); + bool found = false; + bool masterFound = false; + + for (int i = 0; i != keys.Count; i++) + { + PgpSecretKey key = (PgpSecretKey) keys[i]; + + if (key.KeyId == secKey.KeyId) + { + found = true; + keys[i] = secKey; + } + if (key.IsMasterKey) + { + masterFound = true; + } + } + + if (!found) + { + if (secKey.IsMasterKey) + { + if (masterFound) + throw new ArgumentException("cannot add a master key to a ring that already has one"); + + keys.Insert(0, secKey); + } + else + { + keys.Add(secKey); + } + } + + return new PgpSecretKeyRing(keys, secRing.extraPubKeys); + } + + /// <summary>Returns a new key ring with the secret key passed in removed from the key ring.</summary> + /// <param name="secRing">The secret key ring to be modified.</param> + /// <param name="secKey">The secret key to be removed.</param> + /// <returns>A new <c>PgpSecretKeyRing</c>, or null if secKey is not found.</returns> + public static PgpSecretKeyRing RemoveSecretKey( + PgpSecretKeyRing secRing, + PgpSecretKey secKey) + { + IList keys = Platform.CreateArrayList(secRing.keys); + bool found = false; + + for (int i = 0; i < keys.Count; i++) + { + PgpSecretKey key = (PgpSecretKey)keys[i]; + + if (key.KeyId == secKey.KeyId) + { + found = true; + keys.RemoveAt(i); + } + } + + return found ? new PgpSecretKeyRing(keys, secRing.extraPubKeys) : null; + } + } +} diff --git a/Crypto/src/openpgp/PgpSecretKeyRingBundle.cs b/Crypto/src/openpgp/PgpSecretKeyRingBundle.cs new file mode 100644
index 000000000..18636dd65 --- /dev/null +++ b/Crypto/src/openpgp/PgpSecretKeyRingBundle.cs
@@ -0,0 +1,281 @@ +using System; +using System.Collections; +using System.Globalization; +using System.IO; + +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks> + /// Often a PGP key ring file is made up of a succession of master/sub-key key rings. + /// If you want to read an entire secret key file in one hit this is the class for you. + /// </remarks> + public class PgpSecretKeyRingBundle + { + private readonly IDictionary secretRings; + private readonly IList order; + + private PgpSecretKeyRingBundle( + IDictionary secretRings, + IList order) + { + this.secretRings = secretRings; + this.order = order; + } + + public PgpSecretKeyRingBundle( + byte[] encoding) + : this(new MemoryStream(encoding, false)) + { + } + + /// <summary>Build a PgpSecretKeyRingBundle from the passed in input stream.</summary> + /// <param name="inputStream">Input stream containing data.</param> + /// <exception cref="IOException">If a problem parsing the stream occurs.</exception> + /// <exception cref="PgpException">If an object is encountered which isn't a PgpSecretKeyRing.</exception> + public PgpSecretKeyRingBundle( + Stream inputStream) + : this(new PgpObjectFactory(inputStream).AllPgpObjects()) + { + } + + public PgpSecretKeyRingBundle( + IEnumerable e) + { + this.secretRings = Platform.CreateHashtable(); + this.order = Platform.CreateArrayList(); + + foreach (object obj in e) + { + PgpSecretKeyRing pgpSecret = obj as PgpSecretKeyRing; + + if (pgpSecret == null) + { + throw new PgpException(obj.GetType().FullName + " found where PgpSecretKeyRing expected"); + } + + long key = pgpSecret.GetPublicKey().KeyId; + secretRings.Add(key, pgpSecret); + order.Add(key); + } + } + + [Obsolete("Use 'Count' property instead")] + public int Size + { + get { return order.Count; } + } + + /// <summary>Return the number of rings in this collection.</summary> + public int Count + { + get { return order.Count; } + } + + /// <summary>Allow enumeration of the secret key rings making up this collection.</summary> + public IEnumerable GetKeyRings() + { + return new EnumerableProxy(secretRings.Values); + } + + /// <summary>Allow enumeration of the key rings associated with the passed in userId.</summary> + /// <param name="userId">The user ID to be matched.</param> + /// <returns>An <c>IEnumerable</c> of key rings which matched (possibly none).</returns> + public IEnumerable GetKeyRings( + string userId) + { + return GetKeyRings(userId, false, false); + } + + /// <summary>Allow enumeration of the key rings associated with the passed in userId.</summary> + /// <param name="userId">The user ID to be matched.</param> + /// <param name="matchPartial">If true, userId need only be a substring of an actual ID string to match.</param> + /// <returns>An <c>IEnumerable</c> of key rings which matched (possibly none).</returns> + public IEnumerable GetKeyRings( + string userId, + bool matchPartial) + { + return GetKeyRings(userId, matchPartial, false); + } + + /// <summary>Allow enumeration of the key rings associated with the passed in userId.</summary> + /// <param name="userId">The user ID to be matched.</param> + /// <param name="matchPartial">If true, userId need only be a substring of an actual ID string to match.</param> + /// <param name="ignoreCase">If true, case is ignored in user ID comparisons.</param> + /// <returns>An <c>IEnumerable</c> of key rings which matched (possibly none).</returns> + public IEnumerable GetKeyRings( + string userId, + bool matchPartial, + bool ignoreCase) + { + IList rings = Platform.CreateArrayList(); + + if (ignoreCase) + { + userId = userId.ToLowerInvariant(); + } + + foreach (PgpSecretKeyRing secRing in GetKeyRings()) + { + foreach (string nextUserID in secRing.GetSecretKey().UserIds) + { + string next = nextUserID; + if (ignoreCase) + { + next = next.ToLowerInvariant(); + } + + if (matchPartial) + { + if (next.IndexOf(userId) > -1) + { + rings.Add(secRing); + } + } + else + { + if (next.Equals(userId)) + { + rings.Add(secRing); + } + } + } + } + + return new EnumerableProxy(rings); + } + + /// <summary>Return the PGP secret key associated with the given key id.</summary> + /// <param name="keyId">The ID of the secret key to return.</param> + public PgpSecretKey GetSecretKey( + long keyId) + { + foreach (PgpSecretKeyRing secRing in GetKeyRings()) + { + PgpSecretKey sec = secRing.GetSecretKey(keyId); + + if (sec != null) + { + return sec; + } + } + + return null; + } + + /// <summary>Return the secret key ring which contains the key referred to by keyId</summary> + /// <param name="keyId">The ID of the secret key</param> + public PgpSecretKeyRing GetSecretKeyRing( + long keyId) + { + long id = keyId; + + if (secretRings.Contains(id)) + { + return (PgpSecretKeyRing) secretRings[id]; + } + + foreach (PgpSecretKeyRing secretRing in GetKeyRings()) + { + PgpSecretKey secret = secretRing.GetSecretKey(keyId); + + if (secret != null) + { + return secretRing; + } + } + + return null; + } + + /// <summary> + /// Return true if a key matching the passed in key ID is present, false otherwise. + /// </summary> + /// <param name="keyID">key ID to look for.</param> + public bool Contains( + long keyID) + { + return GetSecretKey(keyID) != null; + } + + public byte[] GetEncoded() + { + MemoryStream bOut = new MemoryStream(); + + Encode(bOut); + + return bOut.ToArray(); + } + + public void Encode( + Stream outStr) + { + BcpgOutputStream bcpgOut = BcpgOutputStream.Wrap(outStr); + + foreach (long key in order) + { + PgpSecretKeyRing pub = (PgpSecretKeyRing) secretRings[key]; + + pub.Encode(bcpgOut); + } + } + + /// <summary> + /// Return a new bundle containing the contents of the passed in bundle and + /// the passed in secret key ring. + /// </summary> + /// <param name="bundle">The <c>PgpSecretKeyRingBundle</c> the key ring is to be added to.</param> + /// <param name="secretKeyRing">The key ring to be added.</param> + /// <returns>A new <c>PgpSecretKeyRingBundle</c> merging the current one with the passed in key ring.</returns> + /// <exception cref="ArgumentException">If the keyId for the passed in key ring is already present.</exception> + public static PgpSecretKeyRingBundle AddSecretKeyRing( + PgpSecretKeyRingBundle bundle, + PgpSecretKeyRing secretKeyRing) + { + long key = secretKeyRing.GetPublicKey().KeyId; + + if (bundle.secretRings.Contains(key)) + { + throw new ArgumentException("Collection already contains a key with a keyId for the passed in ring."); + } + + IDictionary newSecretRings = Platform.CreateHashtable(bundle.secretRings); + IList newOrder = Platform.CreateArrayList(bundle.order); + + newSecretRings[key] = secretKeyRing; + newOrder.Add(key); + + return new PgpSecretKeyRingBundle(newSecretRings, newOrder); + } + + /// <summary> + /// Return a new bundle containing the contents of the passed in bundle with + /// the passed in secret key ring removed. + /// </summary> + /// <param name="bundle">The <c>PgpSecretKeyRingBundle</c> the key ring is to be removed from.</param> + /// <param name="secretKeyRing">The key ring to be removed.</param> + /// <returns>A new <c>PgpSecretKeyRingBundle</c> not containing the passed in key ring.</returns> + /// <exception cref="ArgumentException">If the keyId for the passed in key ring is not present.</exception> + public static PgpSecretKeyRingBundle RemoveSecretKeyRing( + PgpSecretKeyRingBundle bundle, + PgpSecretKeyRing secretKeyRing) + { + long key = secretKeyRing.GetPublicKey().KeyId; + + if (!bundle.secretRings.Contains(key)) + { + throw new ArgumentException("Collection does not contain a key with a keyId for the passed in ring."); + } + + IDictionary newSecretRings = Platform.CreateHashtable(bundle.secretRings); + IList newOrder = Platform.CreateArrayList(bundle.order); + + newSecretRings.Remove(key); + newOrder.Remove(key); + + return new PgpSecretKeyRingBundle(newSecretRings, newOrder); + } + } +} diff --git a/Crypto/src/openpgp/PgpSignature.cs b/Crypto/src/openpgp/PgpSignature.cs new file mode 100644
index 000000000..cbe0d83b4 --- /dev/null +++ b/Crypto/src/openpgp/PgpSignature.cs
@@ -0,0 +1,422 @@ +using System; +using System.IO; +using Org.BouncyCastle.Asn1; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Date; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks>A PGP signature object.</remarks> + public class PgpSignature + { + public const int BinaryDocument = 0x00; + public const int CanonicalTextDocument = 0x01; + public const int StandAlone = 0x02; + + public const int DefaultCertification = 0x10; + public const int NoCertification = 0x11; + public const int CasualCertification = 0x12; + public const int PositiveCertification = 0x13; + + public const int SubkeyBinding = 0x18; + public const int PrimaryKeyBinding = 0x19; + public const int DirectKey = 0x1f; + public const int KeyRevocation = 0x20; + public const int SubkeyRevocation = 0x28; + public const int CertificationRevocation = 0x30; + public const int Timestamp = 0x40; + + private readonly SignaturePacket sigPck; + private readonly int signatureType; + private readonly TrustPacket trustPck; + + private ISigner sig; + private byte lastb; // Initial value anything but '\r' + + internal PgpSignature( + BcpgInputStream bcpgInput) + : this((SignaturePacket)bcpgInput.ReadPacket()) + { + } + + internal PgpSignature( + SignaturePacket sigPacket) + : this(sigPacket, null) + { + } + + internal PgpSignature( + SignaturePacket sigPacket, + TrustPacket trustPacket) + { + if (sigPacket == null) + throw new ArgumentNullException("sigPacket"); + + this.sigPck = sigPacket; + this.signatureType = sigPck.SignatureType; + this.trustPck = trustPacket; + } + + private void GetSig() + { + this.sig = SignerUtilities.GetSigner( + PgpUtilities.GetSignatureName(sigPck.KeyAlgorithm, sigPck.HashAlgorithm)); + } + + /// <summary>The OpenPGP version number for this signature.</summary> + public int Version + { + get { return sigPck.Version; } + } + + /// <summary>The key algorithm associated with this signature.</summary> + public PublicKeyAlgorithmTag KeyAlgorithm + { + get { return sigPck.KeyAlgorithm; } + } + + /// <summary>The hash algorithm associated with this signature.</summary> + public HashAlgorithmTag HashAlgorithm + { + get { return sigPck.HashAlgorithm; } + } + + public void InitVerify( + PgpPublicKey pubKey) + { + lastb = 0; + if (sig == null) + { + GetSig(); + } + try + { + sig.Init(false, pubKey.GetKey()); + } + catch (InvalidKeyException e) + { + throw new PgpException("invalid key.", e); + } + } + + public void Update( + byte b) + { + if (signatureType == CanonicalTextDocument) + { + doCanonicalUpdateByte(b); + } + else + { + sig.Update(b); + } + } + + private void doCanonicalUpdateByte( + byte b) + { + if (b == '\r') + { + doUpdateCRLF(); + } + else if (b == '\n') + { + if (lastb != '\r') + { + doUpdateCRLF(); + } + } + else + { + sig.Update(b); + } + + lastb = b; + } + + private void doUpdateCRLF() + { + sig.Update((byte)'\r'); + sig.Update((byte)'\n'); + } + + public void Update( + params byte[] bytes) + { + Update(bytes, 0, bytes.Length); + } + + public void Update( + byte[] bytes, + int off, + int length) + { + if (signatureType == CanonicalTextDocument) + { + int finish = off + length; + + for (int i = off; i != finish; i++) + { + doCanonicalUpdateByte(bytes[i]); + } + } + else + { + sig.BlockUpdate(bytes, off, length); + } + } + + public bool Verify() + { + byte[] trailer = GetSignatureTrailer(); + sig.BlockUpdate(trailer, 0, trailer.Length); + + return sig.VerifySignature(GetSignature()); + } + + private void UpdateWithIdData( + int header, + byte[] idBytes) + { + this.Update( + (byte) header, + (byte)(idBytes.Length >> 24), + (byte)(idBytes.Length >> 16), + (byte)(idBytes.Length >> 8), + (byte)(idBytes.Length)); + this.Update(idBytes); + } + + private void UpdateWithPublicKey( + PgpPublicKey key) + { + byte[] keyBytes = GetEncodedPublicKey(key); + + this.Update( + (byte) 0x99, + (byte)(keyBytes.Length >> 8), + (byte)(keyBytes.Length)); + this.Update(keyBytes); + } + + /// <summary> + /// Verify the signature as certifying the passed in public key as associated + /// with the passed in user attributes. + /// </summary> + /// <param name="userAttributes">User attributes the key was stored under.</param> + /// <param name="key">The key to be verified.</param> + /// <returns>True, if the signature matches, false otherwise.</returns> + public bool VerifyCertification( + PgpUserAttributeSubpacketVector userAttributes, + PgpPublicKey key) + { + UpdateWithPublicKey(key); + + // + // hash in the userAttributes + // + try + { + MemoryStream bOut = new MemoryStream(); + foreach (UserAttributeSubpacket packet in userAttributes.ToSubpacketArray()) + { + packet.Encode(bOut); + } + UpdateWithIdData(0xd1, bOut.ToArray()); + } + catch (IOException e) + { + throw new PgpException("cannot encode subpacket array", e); + } + + this.Update(sigPck.GetSignatureTrailer()); + + return sig.VerifySignature(this.GetSignature()); + } + + /// <summary> + /// Verify the signature as certifying the passed in public key as associated + /// with the passed in ID. + /// </summary> + /// <param name="id">ID the key was stored under.</param> + /// <param name="key">The key to be verified.</param> + /// <returns>True, if the signature matches, false otherwise.</returns> + public bool VerifyCertification( + string id, + PgpPublicKey key) + { + UpdateWithPublicKey(key); + + // + // hash in the id + // + UpdateWithIdData(0xb4, Strings.ToByteArray(id)); + + Update(sigPck.GetSignatureTrailer()); + + return sig.VerifySignature(GetSignature()); + } + + /// <summary>Verify a certification for the passed in key against the passed in master key.</summary> + /// <param name="masterKey">The key we are verifying against.</param> + /// <param name="pubKey">The key we are verifying.</param> + /// <returns>True, if the certification is valid, false otherwise.</returns> + public bool VerifyCertification( + PgpPublicKey masterKey, + PgpPublicKey pubKey) + { + UpdateWithPublicKey(masterKey); + UpdateWithPublicKey(pubKey); + + Update(sigPck.GetSignatureTrailer()); + + return sig.VerifySignature(GetSignature()); + } + + /// <summary>Verify a key certification, such as revocation, for the passed in key.</summary> + /// <param name="pubKey">The key we are checking.</param> + /// <returns>True, if the certification is valid, false otherwise.</returns> + public bool VerifyCertification( + PgpPublicKey pubKey) + { + if (SignatureType != KeyRevocation + && SignatureType != SubkeyRevocation) + { + throw new InvalidOperationException("signature is not a key signature"); + } + + UpdateWithPublicKey(pubKey); + + Update(sigPck.GetSignatureTrailer()); + + return sig.VerifySignature(GetSignature()); + } + + public int SignatureType + { + get { return sigPck.SignatureType; } + } + + /// <summary>The ID of the key that created the signature.</summary> + public long KeyId + { + get { return sigPck.KeyId; } + } + + [Obsolete("Use 'CreationTime' property instead")] + public DateTime GetCreationTime() + { + return CreationTime; + } + + /// <summary>The creation time of this signature.</summary> + public DateTime CreationTime + { + get { return DateTimeUtilities.UnixMsToDateTime(sigPck.CreationTime); } + } + + public byte[] GetSignatureTrailer() + { + return sigPck.GetSignatureTrailer(); + } + + /// <summary> + /// Return true if the signature has either hashed or unhashed subpackets. + /// </summary> + public bool HasSubpackets + { + get + { + return sigPck.GetHashedSubPackets() != null + || sigPck.GetUnhashedSubPackets() != null; + } + } + + public PgpSignatureSubpacketVector GetHashedSubPackets() + { + return createSubpacketVector(sigPck.GetHashedSubPackets()); + } + + public PgpSignatureSubpacketVector GetUnhashedSubPackets() + { + return createSubpacketVector(sigPck.GetUnhashedSubPackets()); + } + + private PgpSignatureSubpacketVector createSubpacketVector(SignatureSubpacket[] pcks) + { + return pcks == null ? null : new PgpSignatureSubpacketVector(pcks); + } + + public byte[] GetSignature() + { + MPInteger[] sigValues = sigPck.GetSignature(); + byte[] signature; + + if (sigValues != null) + { + if (sigValues.Length == 1) // an RSA signature + { + signature = sigValues[0].Value.ToByteArrayUnsigned(); + } + else + { + try + { + signature = new DerSequence( + new DerInteger(sigValues[0].Value), + new DerInteger(sigValues[1].Value)).GetEncoded(); + } + catch (IOException e) + { + throw new PgpException("exception encoding DSA sig.", e); + } + } + } + else + { + signature = sigPck.GetSignatureBytes(); + } + + return signature; + } + + // TODO Handle the encoding stuff by subclassing BcpgObject? + public byte[] GetEncoded() + { + MemoryStream bOut = new MemoryStream(); + + Encode(bOut); + + return bOut.ToArray(); + } + + public void Encode( + Stream outStream) + { + BcpgOutputStream bcpgOut = BcpgOutputStream.Wrap(outStream); + + bcpgOut.WritePacket(sigPck); + + if (trustPck != null) + { + bcpgOut.WritePacket(trustPck); + } + } + + private byte[] GetEncodedPublicKey( + PgpPublicKey pubKey) + { + try + { + return pubKey.publicPk.GetEncodedContents(); + } + catch (IOException e) + { + throw new PgpException("exception preparing key.", e); + } + } + } +} diff --git a/Crypto/src/openpgp/PgpSignatureGenerator.cs b/Crypto/src/openpgp/PgpSignatureGenerator.cs new file mode 100644
index 000000000..891397267 --- /dev/null +++ b/Crypto/src/openpgp/PgpSignatureGenerator.cs
@@ -0,0 +1,393 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Bcpg.Sig; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks>Generator for PGP signatures.</remarks> + // TODO Should be able to implement ISigner? + public class PgpSignatureGenerator + { + private static readonly SignatureSubpacket[] EmptySignatureSubpackets = new SignatureSubpacket[0]; + + private PublicKeyAlgorithmTag keyAlgorithm; + private HashAlgorithmTag hashAlgorithm; + private PgpPrivateKey privKey; + private ISigner sig; + private IDigest dig; + private int signatureType; + private byte lastb; + + private SignatureSubpacket[] unhashed = EmptySignatureSubpackets; + private SignatureSubpacket[] hashed = EmptySignatureSubpackets; + + /// <summary>Create a generator for the passed in keyAlgorithm and hashAlgorithm codes.</summary> + public PgpSignatureGenerator( + PublicKeyAlgorithmTag keyAlgorithm, + HashAlgorithmTag hashAlgorithm) + { + this.keyAlgorithm = keyAlgorithm; + this.hashAlgorithm = hashAlgorithm; + + dig = DigestUtilities.GetDigest(PgpUtilities.GetDigestName(hashAlgorithm)); + sig = SignerUtilities.GetSigner(PgpUtilities.GetSignatureName(keyAlgorithm, hashAlgorithm)); + } + + /// <summary>Initialise the generator for signing.</summary> + public void InitSign( + int sigType, + PgpPrivateKey key) + { + InitSign(sigType, key, null); + } + + /// <summary>Initialise the generator for signing.</summary> + public void InitSign( + int sigType, + PgpPrivateKey key, + SecureRandom random) + { + this.privKey = key; + this.signatureType = sigType; + + try + { + ICipherParameters cp = key.Key; + if (random != null) + { + cp = new ParametersWithRandom(key.Key, random); + } + + sig.Init(true, cp); + } + catch (InvalidKeyException e) + { + throw new PgpException("invalid key.", e); + } + + dig.Reset(); + lastb = 0; + } + + public void Update( + byte b) + { + if (signatureType == PgpSignature.CanonicalTextDocument) + { + doCanonicalUpdateByte(b); + } + else + { + doUpdateByte(b); + } + } + + private void doCanonicalUpdateByte( + byte b) + { + if (b == '\r') + { + doUpdateCRLF(); + } + else if (b == '\n') + { + if (lastb != '\r') + { + doUpdateCRLF(); + } + } + else + { + doUpdateByte(b); + } + + lastb = b; + } + + private void doUpdateCRLF() + { + doUpdateByte((byte)'\r'); + doUpdateByte((byte)'\n'); + } + + private void doUpdateByte( + byte b) + { + sig.Update(b); + dig.Update(b); + } + + public void Update( + params byte[] b) + { + Update(b, 0, b.Length); + } + + public void Update( + byte[] b, + int off, + int len) + { + if (signatureType == PgpSignature.CanonicalTextDocument) + { + int finish = off + len; + + for (int i = off; i != finish; i++) + { + doCanonicalUpdateByte(b[i]); + } + } + else + { + sig.BlockUpdate(b, off, len); + dig.BlockUpdate(b, off, len); + } + } + + public void SetHashedSubpackets( + PgpSignatureSubpacketVector hashedPackets) + { + hashed = hashedPackets == null + ? EmptySignatureSubpackets + : hashedPackets.ToSubpacketArray(); + } + + public void SetUnhashedSubpackets( + PgpSignatureSubpacketVector unhashedPackets) + { + unhashed = unhashedPackets == null + ? EmptySignatureSubpackets + : unhashedPackets.ToSubpacketArray(); + } + + /// <summary>Return the one pass header associated with the current signature.</summary> + public PgpOnePassSignature GenerateOnePassVersion( + bool isNested) + { + return new PgpOnePassSignature( + new OnePassSignaturePacket( + signatureType, hashAlgorithm, keyAlgorithm, privKey.KeyId, isNested)); + } + + /// <summary>Return a signature object containing the current signature state.</summary> + public PgpSignature Generate() + { + SignatureSubpacket[] hPkts = hashed, unhPkts = unhashed; + + if (!packetPresent(hashed, SignatureSubpacketTag.CreationTime)) + { + hPkts = insertSubpacket(hPkts, new SignatureCreationTime(false, DateTime.UtcNow)); + } + + if (!packetPresent(hashed, SignatureSubpacketTag.IssuerKeyId) + && !packetPresent(unhashed, SignatureSubpacketTag.IssuerKeyId)) + { + unhPkts = insertSubpacket(unhPkts, new IssuerKeyId(false, privKey.KeyId)); + } + + int version = 4; + byte[] hData; + + try + { + MemoryStream hOut = new MemoryStream(); + + for (int i = 0; i != hPkts.Length; i++) + { + hPkts[i].Encode(hOut); + } + + byte[] data = hOut.ToArray(); + + MemoryStream sOut = new MemoryStream(data.Length + 6); + sOut.WriteByte((byte)version); + sOut.WriteByte((byte)signatureType); + sOut.WriteByte((byte)keyAlgorithm); + sOut.WriteByte((byte)hashAlgorithm); + sOut.WriteByte((byte)(data.Length >> 8)); + sOut.WriteByte((byte)data.Length); + sOut.Write(data, 0, data.Length); + + hData = sOut.ToArray(); + } + catch (IOException e) + { + throw new PgpException("exception encoding hashed data.", e); + } + + sig.BlockUpdate(hData, 0, hData.Length); + dig.BlockUpdate(hData, 0, hData.Length); + + hData = new byte[] + { + (byte) version, + 0xff, + (byte)(hData.Length >> 24), + (byte)(hData.Length >> 16), + (byte)(hData.Length >> 8), + (byte) hData.Length + }; + + sig.BlockUpdate(hData, 0, hData.Length); + dig.BlockUpdate(hData, 0, hData.Length); + + byte[] sigBytes = sig.GenerateSignature(); + byte[] digest = DigestUtilities.DoFinal(dig); + byte[] fingerPrint = new byte[] { digest[0], digest[1] }; + + // an RSA signature + bool isRsa = keyAlgorithm == PublicKeyAlgorithmTag.RsaSign + || keyAlgorithm == PublicKeyAlgorithmTag.RsaGeneral; + + MPInteger[] sigValues = isRsa + ? PgpUtilities.RsaSigToMpi(sigBytes) + : PgpUtilities.DsaSigToMpi(sigBytes); + + return new PgpSignature( + new SignaturePacket(signatureType, privKey.KeyId, keyAlgorithm, + hashAlgorithm, hPkts, unhPkts, fingerPrint, sigValues)); + } + + /// <summary>Generate a certification for the passed in ID and key.</summary> + /// <param name="id">The ID we are certifying against the public key.</param> + /// <param name="pubKey">The key we are certifying against the ID.</param> + /// <returns>The certification.</returns> + public PgpSignature GenerateCertification( + string id, + PgpPublicKey pubKey) + { + UpdateWithPublicKey(pubKey); + + // + // hash in the id + // + UpdateWithIdData(0xb4, Strings.ToByteArray(id)); + + return Generate(); + } + + /// <summary>Generate a certification for the passed in userAttributes.</summary> + /// <param name="userAttributes">The ID we are certifying against the public key.</param> + /// <param name="pubKey">The key we are certifying against the ID.</param> + /// <returns>The certification.</returns> + public PgpSignature GenerateCertification( + PgpUserAttributeSubpacketVector userAttributes, + PgpPublicKey pubKey) + { + UpdateWithPublicKey(pubKey); + + // + // hash in the attributes + // + try + { + MemoryStream bOut = new MemoryStream(); + foreach (UserAttributeSubpacket packet in userAttributes.ToSubpacketArray()) + { + packet.Encode(bOut); + } + UpdateWithIdData(0xd1, bOut.ToArray()); + } + catch (IOException e) + { + throw new PgpException("cannot encode subpacket array", e); + } + + return this.Generate(); + } + + /// <summary>Generate a certification for the passed in key against the passed in master key.</summary> + /// <param name="masterKey">The key we are certifying against.</param> + /// <param name="pubKey">The key we are certifying.</param> + /// <returns>The certification.</returns> + public PgpSignature GenerateCertification( + PgpPublicKey masterKey, + PgpPublicKey pubKey) + { + UpdateWithPublicKey(masterKey); + UpdateWithPublicKey(pubKey); + + return Generate(); + } + + /// <summary>Generate a certification, such as a revocation, for the passed in key.</summary> + /// <param name="pubKey">The key we are certifying.</param> + /// <returns>The certification.</returns> + public PgpSignature GenerateCertification( + PgpPublicKey pubKey) + { + UpdateWithPublicKey(pubKey); + + return Generate(); + } + + private byte[] GetEncodedPublicKey( + PgpPublicKey pubKey) + { + try + { + return pubKey.publicPk.GetEncodedContents(); + } + catch (IOException e) + { + throw new PgpException("exception preparing key.", e); + } + } + + private bool packetPresent( + SignatureSubpacket[] packets, + SignatureSubpacketTag type) + { + for (int i = 0; i != packets.Length; i++) + { + if (packets[i].SubpacketType == type) + { + return true; + } + } + + return false; + } + + private SignatureSubpacket[] insertSubpacket( + SignatureSubpacket[] packets, + SignatureSubpacket subpacket) + { + SignatureSubpacket[] tmp = new SignatureSubpacket[packets.Length + 1]; + tmp[0] = subpacket; + packets.CopyTo(tmp, 1); + return tmp; + } + + private void UpdateWithIdData( + int header, + byte[] idBytes) + { + this.Update( + (byte) header, + (byte)(idBytes.Length >> 24), + (byte)(idBytes.Length >> 16), + (byte)(idBytes.Length >> 8), + (byte)(idBytes.Length)); + this.Update(idBytes); + } + + private void UpdateWithPublicKey( + PgpPublicKey key) + { + byte[] keyBytes = GetEncodedPublicKey(key); + + this.Update( + (byte) 0x99, + (byte)(keyBytes.Length >> 8), + (byte)(keyBytes.Length)); + this.Update(keyBytes); + } + } +} diff --git a/Crypto/src/openpgp/PgpSignatureList.cs b/Crypto/src/openpgp/PgpSignatureList.cs new file mode 100644
index 000000000..61976fc4f --- /dev/null +++ b/Crypto/src/openpgp/PgpSignatureList.cs
@@ -0,0 +1,51 @@ +using System; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks>A list of PGP signatures - normally in the signature block after literal data.</remarks> + public class PgpSignatureList + : PgpObject + { + private PgpSignature[] sigs; + + public PgpSignatureList( + PgpSignature[] sigs) + { + this.sigs = (PgpSignature[]) sigs.Clone(); + } + + public PgpSignatureList( + PgpSignature sig) + { + this.sigs = new PgpSignature[]{ sig }; + } + + public PgpSignature this[int index] + { + get { return sigs[index]; } + } + + [Obsolete("Use 'object[index]' syntax instead")] + public PgpSignature Get( + int index) + { + return this[index]; + } + + [Obsolete("Use 'Count' property instead")] + public int Size + { + get { return sigs.Length; } + } + + public int Count + { + get { return sigs.Length; } + } + + public bool IsEmpty + { + get { return (sigs.Length == 0); } + } + } +} diff --git a/Crypto/src/openpgp/PgpSignatureSubpacketGenerator.cs b/Crypto/src/openpgp/PgpSignatureSubpacketGenerator.cs new file mode 100644
index 000000000..4adf64012 --- /dev/null +++ b/Crypto/src/openpgp/PgpSignatureSubpacketGenerator.cs
@@ -0,0 +1,193 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Bcpg.Sig; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks>Generator for signature subpackets.</remarks> + public class PgpSignatureSubpacketGenerator + { + private IList list = Platform.CreateArrayList(); + + public void SetRevocable( + bool isCritical, + bool isRevocable) + { + list.Add(new Revocable(isCritical, isRevocable)); + } + + public void SetExportable( + bool isCritical, + bool isExportable) + { + list.Add(new Exportable(isCritical, isExportable)); + } + + /// <summary> + /// Add a TrustSignature packet to the signature. The values for depth and trust are largely + /// installation dependent but there are some guidelines in RFC 4880 - 5.2.3.13. + /// </summary> + /// <param name="isCritical">true if the packet is critical.</param> + /// <param name="depth">depth level.</param> + /// <param name="trustAmount">trust amount.</param> + public void SetTrust( + bool isCritical, + int depth, + int trustAmount) + { + list.Add(new TrustSignature(isCritical, depth, trustAmount)); + } + + /// <summary> + /// Set the number of seconds a key is valid for after the time of its creation. + /// A value of zero means the key never expires. + /// </summary> + /// <param name="isCritical">True, if should be treated as critical, false otherwise.</param> + /// <param name="seconds">The number of seconds the key is valid, or zero if no expiry.</param> + public void SetKeyExpirationTime( + bool isCritical, + long seconds) + { + list.Add(new KeyExpirationTime(isCritical, seconds)); + } + + /// <summary> + /// Set the number of seconds a signature is valid for after the time of its creation. + /// A value of zero means the signature never expires. + /// </summary> + /// <param name="isCritical">True, if should be treated as critical, false otherwise.</param> + /// <param name="seconds">The number of seconds the signature is valid, or zero if no expiry.</param> + public void SetSignatureExpirationTime( + bool isCritical, + long seconds) + { + list.Add(new SignatureExpirationTime(isCritical, seconds)); + } + + /// <summary> + /// Set the creation time for the signature. + /// <p> + /// Note: this overrides the generation of a creation time when the signature + /// is generated.</p> + /// </summary> + public void SetSignatureCreationTime( + bool isCritical, + DateTime date) + { + list.Add(new SignatureCreationTime(isCritical, date)); + } + + public void SetPreferredHashAlgorithms( + bool isCritical, + int[] algorithms) + { + list.Add(new PreferredAlgorithms(SignatureSubpacketTag.PreferredHashAlgorithms, isCritical, algorithms)); + } + + public void SetPreferredSymmetricAlgorithms( + bool isCritical, + int[] algorithms) + { + list.Add(new PreferredAlgorithms(SignatureSubpacketTag.PreferredSymmetricAlgorithms, isCritical, algorithms)); + } + + public void SetPreferredCompressionAlgorithms( + bool isCritical, + int[] algorithms) + { + list.Add(new PreferredAlgorithms(SignatureSubpacketTag.PreferredCompressionAlgorithms, isCritical, algorithms)); + } + + public void SetKeyFlags( + bool isCritical, + int flags) + { + list.Add(new KeyFlags(isCritical, flags)); + } + + public void SetSignerUserId( + bool isCritical, + string userId) + { + if (userId == null) + throw new ArgumentNullException("userId"); + + list.Add(new SignerUserId(isCritical, userId)); + } + + public void SetEmbeddedSignature( + bool isCritical, + PgpSignature pgpSignature) + { + byte[] sig = pgpSignature.GetEncoded(); + byte[] data; + + // TODO Should be >= ? + if (sig.Length - 1 > 256) + { + data = new byte[sig.Length - 3]; + } + else + { + data = new byte[sig.Length - 2]; + } + + Array.Copy(sig, sig.Length - data.Length, data, 0, data.Length); + + list.Add(new EmbeddedSignature(isCritical, data)); + } + + public void SetPrimaryUserId( + bool isCritical, + bool isPrimaryUserId) + { + list.Add(new PrimaryUserId(isCritical, isPrimaryUserId)); + } + + public void SetNotationData( + bool isCritical, + bool isHumanReadable, + string notationName, + string notationValue) + { + list.Add(new NotationData(isCritical, isHumanReadable, notationName, notationValue)); + } + + /// <summary> + /// Sets revocation reason sub packet + /// </summary> + public void SetRevocationReason(bool isCritical, RevocationReasonTag reason, + string description) + { + list.Add(new RevocationReason(isCritical, reason, description)); + } + + /// <summary> + /// Sets revocation key sub packet + /// </summary> + public void SetRevocationKey(bool isCritical, PublicKeyAlgorithmTag keyAlgorithm, byte[] fingerprint) + { + list.Add(new RevocationKey(isCritical, RevocationKeyTag.ClassDefault, keyAlgorithm, fingerprint)); + } + + /// <summary> + /// Sets issuer key sub packet + /// </summary> + public void SetIssuerKeyID(bool isCritical, long keyID) + { + list.Add(new IssuerKeyId(isCritical, keyID)); + } + + public PgpSignatureSubpacketVector Generate() + { + SignatureSubpacket[] a = new SignatureSubpacket[list.Count]; + for (int i = 0; i < list.Count; ++i) + { + a[i] = (SignatureSubpacket)list[i]; + } + return new PgpSignatureSubpacketVector(a); + } + } +} diff --git a/Crypto/src/openpgp/PgpSignatureSubpacketVector.cs b/Crypto/src/openpgp/PgpSignatureSubpacketVector.cs new file mode 100644
index 000000000..68fe4b594 --- /dev/null +++ b/Crypto/src/openpgp/PgpSignatureSubpacketVector.cs
@@ -0,0 +1,229 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Bcpg.Sig; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks>Container for a list of signature subpackets.</remarks> + public class PgpSignatureSubpacketVector + { + private readonly SignatureSubpacket[] packets; + + internal PgpSignatureSubpacketVector( + SignatureSubpacket[] packets) + { + this.packets = packets; + } + + public SignatureSubpacket GetSubpacket( + SignatureSubpacketTag type) + { + for (int i = 0; i != packets.Length; i++) + { + if (packets[i].SubpacketType == type) + { + return packets[i]; + } + } + + return null; + } + + /** + * Return true if a particular subpacket type exists. + * + * @param type type to look for. + * @return true if present, false otherwise. + */ + public bool HasSubpacket( + SignatureSubpacketTag type) + { + return GetSubpacket(type) != null; + } + + /** + * Return all signature subpackets of the passed in type. + * @param type subpacket type code + * @return an array of zero or more matching subpackets. + */ + public SignatureSubpacket[] GetSubpackets( + SignatureSubpacketTag type) + { + int count = 0; + for (int i = 0; i < packets.Length; ++i) + { + if (packets[i].SubpacketType == type) + { + ++count; + } + } + + SignatureSubpacket[] result = new SignatureSubpacket[count]; + + int pos = 0; + for (int i = 0; i < packets.Length; ++i) + { + if (packets[i].SubpacketType == type) + { + result[pos++] = packets[i]; + } + } + + return result; + } + + public NotationData[] GetNotationDataOccurences() + { + SignatureSubpacket[] notations = GetSubpackets(SignatureSubpacketTag.NotationData); + NotationData[] vals = new NotationData[notations.Length]; + + for (int i = 0; i < notations.Length; i++) + { + vals[i] = (NotationData) notations[i]; + } + + return vals; + } + + public long GetIssuerKeyId() + { + SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.IssuerKeyId); + + return p == null ? 0 : ((IssuerKeyId) p).KeyId; + } + + public bool HasSignatureCreationTime() + { + return GetSubpacket(SignatureSubpacketTag.CreationTime) != null; + } + + public DateTime GetSignatureCreationTime() + { + SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.CreationTime); + + if (p == null) + { + throw new PgpException("SignatureCreationTime not available"); + } + + return ((SignatureCreationTime)p).GetTime(); + } + + /// <summary> + /// Return the number of seconds a signature is valid for after its creation date. + /// A value of zero means the signature never expires. + /// </summary> + /// <returns>Seconds a signature is valid for.</returns> + public long GetSignatureExpirationTime() + { + SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.ExpireTime); + + return p == null ? 0 : ((SignatureExpirationTime) p).Time; + } + + /// <summary> + /// Return the number of seconds a key is valid for after its creation date. + /// A value of zero means the key never expires. + /// </summary> + /// <returns>Seconds a signature is valid for.</returns> + public long GetKeyExpirationTime() + { + SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.KeyExpireTime); + + return p == null ? 0 : ((KeyExpirationTime) p).Time; + } + + public int[] GetPreferredHashAlgorithms() + { + SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.PreferredHashAlgorithms); + + return p == null ? null : ((PreferredAlgorithms) p).GetPreferences(); + } + + public int[] GetPreferredSymmetricAlgorithms() + { + SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.PreferredSymmetricAlgorithms); + + return p == null ? null : ((PreferredAlgorithms) p).GetPreferences(); + } + + public int[] GetPreferredCompressionAlgorithms() + { + SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.PreferredCompressionAlgorithms); + + return p == null ? null : ((PreferredAlgorithms) p).GetPreferences(); + } + + public int GetKeyFlags() + { + SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.KeyFlags); + + return p == null ? 0 : ((KeyFlags) p).Flags; + } + + public string GetSignerUserId() + { + SignatureSubpacket p = GetSubpacket(SignatureSubpacketTag.SignerUserId); + + return p == null ? null : ((SignerUserId) p).GetId(); + } + + public bool IsPrimaryUserId() + { + PrimaryUserId primaryId = (PrimaryUserId) + this.GetSubpacket(SignatureSubpacketTag.PrimaryUserId); + + if (primaryId != null) + { + return primaryId.IsPrimaryUserId(); + } + + return false; + } + + public SignatureSubpacketTag[] GetCriticalTags() + { + int count = 0; + for (int i = 0; i != packets.Length; i++) + { + if (packets[i].IsCritical()) + { + count++; + } + } + + SignatureSubpacketTag[] list = new SignatureSubpacketTag[count]; + + count = 0; + + for (int i = 0; i != packets.Length; i++) + { + if (packets[i].IsCritical()) + { + list[count++] = packets[i].SubpacketType; + } + } + + return list; + } + + [Obsolete("Use 'Count' property instead")] + public int Size + { + get { return packets.Length; } + } + + /// <summary>Return the number of packets this vector contains.</summary> + public int Count + { + get { return packets.Length; } + } + + internal SignatureSubpacket[] ToSubpacketArray() + { + return packets; + } + } +} diff --git a/Crypto/src/openpgp/PgpUserAttributeSubpacketVector.cs b/Crypto/src/openpgp/PgpUserAttributeSubpacketVector.cs new file mode 100644
index 000000000..4cdbeda54 --- /dev/null +++ b/Crypto/src/openpgp/PgpUserAttributeSubpacketVector.cs
@@ -0,0 +1,81 @@ +using Org.BouncyCastle.Bcpg.Attr; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks>Container for a list of user attribute subpackets.</remarks> + public class PgpUserAttributeSubpacketVector + { + private readonly UserAttributeSubpacket[] packets; + + internal PgpUserAttributeSubpacketVector( + UserAttributeSubpacket[] packets) + { + this.packets = packets; + } + + public UserAttributeSubpacket GetSubpacket( + UserAttributeSubpacketTag type) + { + for (int i = 0; i != packets.Length; i++) + { + if (packets[i].SubpacketType == type) + { + return packets[i]; + } + } + + return null; + } + + public ImageAttrib GetImageAttribute() + { + UserAttributeSubpacket p = GetSubpacket(UserAttributeSubpacketTag.ImageAttribute); + + return p == null ? null : (ImageAttrib) p; + } + + internal UserAttributeSubpacket[] ToSubpacketArray() + { + return packets; + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + PgpUserAttributeSubpacketVector other = obj as PgpUserAttributeSubpacketVector; + + if (other == null) + return false; + + if (other.packets.Length != packets.Length) + { + return false; + } + + for (int i = 0; i != packets.Length; i++) + { + if (!other.packets[i].Equals(packets[i])) + { + return false; + } + } + + return true; + } + + public override int GetHashCode() + { + int code = 0; + + foreach (object o in packets) + { + code ^= o.GetHashCode(); + } + + return code; + } + } +} diff --git a/Crypto/src/openpgp/PgpUtilities.cs b/Crypto/src/openpgp/PgpUtilities.cs new file mode 100644
index 000000000..e22381bb1 --- /dev/null +++ b/Crypto/src/openpgp/PgpUtilities.cs
@@ -0,0 +1,428 @@ +using System; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks>Basic utility class.</remarks> + public sealed class PgpUtilities + { + private PgpUtilities() + { + } + + public static MPInteger[] DsaSigToMpi( + byte[] encoding) + { + DerInteger i1, i2; + + try + { + Asn1Sequence s = (Asn1Sequence) Asn1Object.FromByteArray(encoding); + + i1 = (DerInteger) s[0]; + i2 = (DerInteger) s[1]; + } + catch (IOException e) + { + throw new PgpException("exception encoding signature", e); + } + + return new MPInteger[]{ new MPInteger(i1.Value), new MPInteger(i2.Value) }; + } + + public static MPInteger[] RsaSigToMpi( + byte[] encoding) + { + return new MPInteger[]{ new MPInteger(new BigInteger(1, encoding)) }; + } + + public static string GetDigestName( + HashAlgorithmTag hashAlgorithm) + { + switch (hashAlgorithm) + { + case HashAlgorithmTag.Sha1: + return "SHA1"; + case HashAlgorithmTag.MD2: + return "MD2"; + case HashAlgorithmTag.MD5: + return "MD5"; + case HashAlgorithmTag.RipeMD160: + return "RIPEMD160"; + case HashAlgorithmTag.Sha224: + return "SHA224"; + case HashAlgorithmTag.Sha256: + return "SHA256"; + case HashAlgorithmTag.Sha384: + return "SHA384"; + case HashAlgorithmTag.Sha512: + return "SHA512"; + default: + throw new PgpException("unknown hash algorithm tag in GetDigestName: " + hashAlgorithm); + } + } + + public static string GetSignatureName( + PublicKeyAlgorithmTag keyAlgorithm, + HashAlgorithmTag hashAlgorithm) + { + string encAlg; + switch (keyAlgorithm) + { + case PublicKeyAlgorithmTag.RsaGeneral: + case PublicKeyAlgorithmTag.RsaSign: + encAlg = "RSA"; + break; + case PublicKeyAlgorithmTag.Dsa: + encAlg = "DSA"; + break; + case PublicKeyAlgorithmTag.ElGamalEncrypt: // in some malformed cases. + case PublicKeyAlgorithmTag.ElGamalGeneral: + encAlg = "ElGamal"; + break; + default: + throw new PgpException("unknown algorithm tag in signature:" + keyAlgorithm); + } + + return GetDigestName(hashAlgorithm) + "with" + encAlg; + } + + public static string GetSymmetricCipherName( + SymmetricKeyAlgorithmTag algorithm) + { + switch (algorithm) + { + case SymmetricKeyAlgorithmTag.Null: + return null; + case SymmetricKeyAlgorithmTag.TripleDes: + return "DESEDE"; + case SymmetricKeyAlgorithmTag.Idea: + return "IDEA"; + case SymmetricKeyAlgorithmTag.Cast5: + return "CAST5"; + case SymmetricKeyAlgorithmTag.Blowfish: + return "Blowfish"; + case SymmetricKeyAlgorithmTag.Safer: + return "SAFER"; + case SymmetricKeyAlgorithmTag.Des: + return "DES"; + case SymmetricKeyAlgorithmTag.Aes128: + return "AES"; + case SymmetricKeyAlgorithmTag.Aes192: + return "AES"; + case SymmetricKeyAlgorithmTag.Aes256: + return "AES"; + case SymmetricKeyAlgorithmTag.Twofish: + return "Twofish"; + default: + throw new PgpException("unknown symmetric algorithm: " + algorithm); + } + } + + public static int GetKeySize(SymmetricKeyAlgorithmTag algorithm) + { + int keySize; + switch (algorithm) + { + case SymmetricKeyAlgorithmTag.Des: + keySize = 64; + break; + case SymmetricKeyAlgorithmTag.Idea: + case SymmetricKeyAlgorithmTag.Cast5: + case SymmetricKeyAlgorithmTag.Blowfish: + case SymmetricKeyAlgorithmTag.Safer: + case SymmetricKeyAlgorithmTag.Aes128: + keySize = 128; + break; + case SymmetricKeyAlgorithmTag.TripleDes: + case SymmetricKeyAlgorithmTag.Aes192: + keySize = 192; + break; + case SymmetricKeyAlgorithmTag.Aes256: + case SymmetricKeyAlgorithmTag.Twofish: + keySize = 256; + break; + default: + throw new PgpException("unknown symmetric algorithm: " + algorithm); + } + + return keySize; + } + + public static KeyParameter MakeKey( + SymmetricKeyAlgorithmTag algorithm, + byte[] keyBytes) + { + string algName = GetSymmetricCipherName(algorithm); + + return ParameterUtilities.CreateKeyParameter(algName, keyBytes); + } + + public static KeyParameter MakeRandomKey( + SymmetricKeyAlgorithmTag algorithm, + SecureRandom random) + { + int keySize = GetKeySize(algorithm); + byte[] keyBytes = new byte[(keySize + 7) / 8]; + random.NextBytes(keyBytes); + return MakeKey(algorithm, keyBytes); + } + + public static KeyParameter MakeKeyFromPassPhrase( + SymmetricKeyAlgorithmTag algorithm, + S2k s2k, + char[] passPhrase) + { + int keySize = GetKeySize(algorithm); + byte[] pBytes = Strings.ToByteArray(new string(passPhrase)); + byte[] keyBytes = new byte[(keySize + 7) / 8]; + + int generatedBytes = 0; + int loopCount = 0; + + while (generatedBytes < keyBytes.Length) + { + IDigest digest; + if (s2k != null) + { + string digestName = GetDigestName(s2k.HashAlgorithm); + + try + { + digest = DigestUtilities.GetDigest(digestName); + } + catch (Exception e) + { + throw new PgpException("can't find S2k digest", e); + } + + for (int i = 0; i != loopCount; i++) + { + digest.Update(0); + } + + byte[] iv = s2k.GetIV(); + + switch (s2k.Type) + { + case S2k.Simple: + digest.BlockUpdate(pBytes, 0, pBytes.Length); + break; + case S2k.Salted: + digest.BlockUpdate(iv, 0, iv.Length); + digest.BlockUpdate(pBytes, 0, pBytes.Length); + break; + case S2k.SaltedAndIterated: + long count = s2k.IterationCount; + digest.BlockUpdate(iv, 0, iv.Length); + digest.BlockUpdate(pBytes, 0, pBytes.Length); + + count -= iv.Length + pBytes.Length; + + while (count > 0) + { + if (count < iv.Length) + { + digest.BlockUpdate(iv, 0, (int)count); + break; + } + else + { + digest.BlockUpdate(iv, 0, iv.Length); + count -= iv.Length; + } + + if (count < pBytes.Length) + { + digest.BlockUpdate(pBytes, 0, (int)count); + count = 0; + } + else + { + digest.BlockUpdate(pBytes, 0, pBytes.Length); + count -= pBytes.Length; + } + } + break; + default: + throw new PgpException("unknown S2k type: " + s2k.Type); + } + } + else + { + try + { + digest = DigestUtilities.GetDigest("MD5"); + + for (int i = 0; i != loopCount; i++) + { + digest.Update(0); + } + + digest.BlockUpdate(pBytes, 0, pBytes.Length); + } + catch (Exception e) + { + throw new PgpException("can't find MD5 digest", e); + } + } + + byte[] dig = DigestUtilities.DoFinal(digest); + + if (dig.Length > (keyBytes.Length - generatedBytes)) + { + Array.Copy(dig, 0, keyBytes, generatedBytes, keyBytes.Length - generatedBytes); + } + else + { + Array.Copy(dig, 0, keyBytes, generatedBytes, dig.Length); + } + + generatedBytes += dig.Length; + + loopCount++; + } + + Array.Clear(pBytes, 0, pBytes.Length); + + return MakeKey(algorithm, keyBytes); + } + +#if !PORTABLE + /// <summary>Write out the passed in file as a literal data packet.</summary> + public static void WriteFileToLiteralData( + Stream output, + char fileType, + FileInfo file) + { + PgpLiteralDataGenerator lData = new PgpLiteralDataGenerator(); + Stream pOut = lData.Open(output, fileType, file.Name, file.Length, file.LastWriteTime); + PipeFileContents(file, pOut, 4096); + } + + /// <summary>Write out the passed in file as a literal data packet in partial packet format.</summary> + public static void WriteFileToLiteralData( + Stream output, + char fileType, + FileInfo file, + byte[] buffer) + { + PgpLiteralDataGenerator lData = new PgpLiteralDataGenerator(); + Stream pOut = lData.Open(output, fileType, file.Name, file.LastWriteTime, buffer); + PipeFileContents(file, pOut, buffer.Length); + } + + private static void PipeFileContents(FileInfo file, Stream pOut, int bufSize) + { + FileStream inputStream = file.OpenRead(); + byte[] buf = new byte[bufSize]; + + int len; + while ((len = inputStream.Read(buf, 0, buf.Length)) > 0) + { + pOut.Write(buf, 0, len); + } + + pOut.Close(); + inputStream.Close(); + } +#endif + + private const int ReadAhead = 60; + + private static bool IsPossiblyBase64( + int ch) + { + return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') + || (ch >= '0' && ch <= '9') || (ch == '+') || (ch == '/') + || (ch == '\r') || (ch == '\n'); + } + + /// <summary> + /// Return either an ArmoredInputStream or a BcpgInputStream based on whether + /// the initial characters of the stream are binary PGP encodings or not. + /// </summary> + public static Stream GetDecoderStream( + Stream inputStream) + { + // TODO Remove this restriction? + if (!inputStream.CanSeek) + throw new ArgumentException("inputStream must be seek-able", "inputStream"); + + long markedPos = inputStream.Position; + + int ch = inputStream.ReadByte(); + if ((ch & 0x80) != 0) + { + inputStream.Position = markedPos; + + return inputStream; + } + else + { + if (!IsPossiblyBase64(ch)) + { + inputStream.Position = markedPos; + + return new ArmoredInputStream(inputStream); + } + + byte[] buf = new byte[ReadAhead]; + int count = 1; + int index = 1; + + buf[0] = (byte)ch; + while (count != ReadAhead && (ch = inputStream.ReadByte()) >= 0) + { + if (!IsPossiblyBase64(ch)) + { + inputStream.Position = markedPos; + + return new ArmoredInputStream(inputStream); + } + + if (ch != '\n' && ch != '\r') + { + buf[index++] = (byte)ch; + } + + count++; + } + + inputStream.Position = markedPos; + + // + // nothing but new lines, little else, assume regular armoring + // + if (count < 4) + { + return new ArmoredInputStream(inputStream); + } + + // + // test our non-blank data + // + byte[] firstBlock = new byte[8]; + Array.Copy(buf, 0, firstBlock, 0, firstBlock.Length); + byte[] decoded = Base64.Decode(firstBlock); + + // + // it's a base64 PGP block. + // + bool hasHeaders = (decoded[0] & 0x80) == 0; + + return new ArmoredInputStream(inputStream, hasHeaders); + } + } + } +} diff --git a/Crypto/src/openpgp/PgpV3SignatureGenerator.cs b/Crypto/src/openpgp/PgpV3SignatureGenerator.cs new file mode 100644
index 000000000..fc8b42df2 --- /dev/null +++ b/Crypto/src/openpgp/PgpV3SignatureGenerator.cs
@@ -0,0 +1,199 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities.Date; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + /// <remarks>Generator for old style PGP V3 Signatures.</remarks> + // TODO Should be able to implement ISigner? + public class PgpV3SignatureGenerator + { + private PublicKeyAlgorithmTag keyAlgorithm; + private HashAlgorithmTag hashAlgorithm; + private PgpPrivateKey privKey; + private ISigner sig; + private IDigest dig; + private int signatureType; + private byte lastb; + + /// <summary>Create a generator for the passed in keyAlgorithm and hashAlgorithm codes.</summary> + public PgpV3SignatureGenerator( + PublicKeyAlgorithmTag keyAlgorithm, + HashAlgorithmTag hashAlgorithm) + { + this.keyAlgorithm = keyAlgorithm; + this.hashAlgorithm = hashAlgorithm; + + dig = DigestUtilities.GetDigest(PgpUtilities.GetDigestName(hashAlgorithm)); + sig = SignerUtilities.GetSigner(PgpUtilities.GetSignatureName(keyAlgorithm, hashAlgorithm)); + } + + /// <summary>Initialise the generator for signing.</summary> + public void InitSign( + int sigType, + PgpPrivateKey key) + { + InitSign(sigType, key, null); + } + + /// <summary>Initialise the generator for signing.</summary> + public void InitSign( + int sigType, + PgpPrivateKey key, + SecureRandom random) + { + this.privKey = key; + this.signatureType = sigType; + + try + { + ICipherParameters cp = key.Key; + if (random != null) + { + cp = new ParametersWithRandom(key.Key, random); + } + + sig.Init(true, cp); + } + catch (InvalidKeyException e) + { + throw new PgpException("invalid key.", e); + } + + dig.Reset(); + lastb = 0; + } + + public void Update( + byte b) + { + if (signatureType == PgpSignature.CanonicalTextDocument) + { + doCanonicalUpdateByte(b); + } + else + { + doUpdateByte(b); + } + } + + private void doCanonicalUpdateByte( + byte b) + { + if (b == '\r') + { + doUpdateCRLF(); + } + else if (b == '\n') + { + if (lastb != '\r') + { + doUpdateCRLF(); + } + } + else + { + doUpdateByte(b); + } + + lastb = b; + } + + private void doUpdateCRLF() + { + doUpdateByte((byte)'\r'); + doUpdateByte((byte)'\n'); + } + + private void doUpdateByte( + byte b) + { + sig.Update(b); + dig.Update(b); + } + + public void Update( + byte[] b) + { + if (signatureType == PgpSignature.CanonicalTextDocument) + { + for (int i = 0; i != b.Length; i++) + { + doCanonicalUpdateByte(b[i]); + } + } + else + { + sig.BlockUpdate(b, 0, b.Length); + dig.BlockUpdate(b, 0, b.Length); + } + } + + public void Update( + byte[] b, + int off, + int len) + { + if (signatureType == PgpSignature.CanonicalTextDocument) + { + int finish = off + len; + + for (int i = off; i != finish; i++) + { + doCanonicalUpdateByte(b[i]); + } + } + else + { + sig.BlockUpdate(b, off, len); + dig.BlockUpdate(b, off, len); + } + } + + /// <summary>Return the one pass header associated with the current signature.</summary> + public PgpOnePassSignature GenerateOnePassVersion( + bool isNested) + { + return new PgpOnePassSignature( + new OnePassSignaturePacket(signatureType, hashAlgorithm, keyAlgorithm, privKey.KeyId, isNested)); + } + + /// <summary>Return a V3 signature object containing the current signature state.</summary> + public PgpSignature Generate() + { + long creationTime = DateTimeUtilities.CurrentUnixMs() / 1000L; + + byte[] hData = new byte[] + { + (byte) signatureType, + (byte)(creationTime >> 24), + (byte)(creationTime >> 16), + (byte)(creationTime >> 8), + (byte) creationTime + }; + + sig.BlockUpdate(hData, 0, hData.Length); + dig.BlockUpdate(hData, 0, hData.Length); + + byte[] sigBytes = sig.GenerateSignature(); + byte[] digest = DigestUtilities.DoFinal(dig); + byte[] fingerPrint = new byte[]{ digest[0], digest[1] }; + + // an RSA signature + bool isRsa = keyAlgorithm == PublicKeyAlgorithmTag.RsaSign + || keyAlgorithm == PublicKeyAlgorithmTag.RsaGeneral; + + MPInteger[] sigValues = isRsa + ? PgpUtilities.RsaSigToMpi(sigBytes) + : PgpUtilities.DsaSigToMpi(sigBytes); + + return new PgpSignature( + new SignaturePacket(3, signatureType, privKey.KeyId, keyAlgorithm, + hashAlgorithm, creationTime * 1000L, fingerPrint, sigValues)); + } + } +} diff --git a/Crypto/src/openpgp/WrappedGeneratorStream.cs b/Crypto/src/openpgp/WrappedGeneratorStream.cs new file mode 100644
index 000000000..baad0d429 --- /dev/null +++ b/Crypto/src/openpgp/WrappedGeneratorStream.cs
@@ -0,0 +1,28 @@ +using System.IO; + +using Org.BouncyCastle.Asn1.Utilities; + +namespace Org.BouncyCastle.Bcpg.OpenPgp +{ + public class WrappedGeneratorStream + : FilterStream + { + private readonly IStreamGenerator gen; + + public WrappedGeneratorStream( + IStreamGenerator gen, + Stream str) + : base(str) + { + this.gen = gen; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + gen.Close(); + } + } + } +} diff --git a/Crypto/src/openssl/EncryptionException.cs b/Crypto/src/openssl/EncryptionException.cs new file mode 100644
index 000000000..043e8f899 --- /dev/null +++ b/Crypto/src/openssl/EncryptionException.cs
@@ -0,0 +1,22 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Security +{ + public class EncryptionException + : IOException + { + public EncryptionException( + string message) + : base(message) + { + } + + public EncryptionException( + string message, + Exception exception) + : base(message, exception) + { + } + } +} diff --git a/Crypto/src/openssl/IPasswordFinder.cs b/Crypto/src/openssl/IPasswordFinder.cs new file mode 100644
index 000000000..4fcef1bd7 --- /dev/null +++ b/Crypto/src/openssl/IPasswordFinder.cs
@@ -0,0 +1,9 @@ +using System; + +namespace Org.BouncyCastle.OpenSsl +{ + public interface IPasswordFinder + { + char[] GetPassword(); + } +} diff --git a/Crypto/src/openssl/MiscPemGenerator.cs b/Crypto/src/openssl/MiscPemGenerator.cs new file mode 100644
index 000000000..a1f64498d --- /dev/null +++ b/Crypto/src/openssl/MiscPemGenerator.cs
@@ -0,0 +1,277 @@ +using System; +using System.Collections; +using System.IO; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Pkcs; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.Utilities.IO.Pem; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.OpenSsl +{ + /** + * PEM generator for the original set of PEM objects used in Open SSL. + */ + public class MiscPemGenerator + : PemObjectGenerator + { + private object obj; + private string algorithm; + private char[] password; + private SecureRandom random; + + public MiscPemGenerator(object obj) + { + this.obj = obj; + } + + public MiscPemGenerator( + object obj, + string algorithm, + char[] password, + SecureRandom random) + { + this.obj = obj; + this.algorithm = algorithm; + this.password = password; + this.random = random; + } + + private static PemObject CreatePemObject(object obj) + { + if (obj == null) + throw new ArgumentNullException("obj"); + + if (obj is AsymmetricCipherKeyPair) + { + return CreatePemObject(((AsymmetricCipherKeyPair)obj).Private); + } + + string type; + byte[] encoding; + + if (obj is PemObject) + return (PemObject)obj; + + if (obj is PemObjectGenerator) + return ((PemObjectGenerator)obj).Generate(); + + if (obj is X509Certificate) + { + // TODO Should we prefer "X509 CERTIFICATE" here? + type = "CERTIFICATE"; + try + { + encoding = ((X509Certificate)obj).GetEncoded(); + } + catch (CertificateEncodingException e) + { + throw new IOException("Cannot Encode object: " + e.ToString()); + } + } + else if (obj is X509Crl) + { + type = "X509 CRL"; + try + { + encoding = ((X509Crl)obj).GetEncoded(); + } + catch (CrlException e) + { + throw new IOException("Cannot Encode object: " + e.ToString()); + } + } + else if (obj is AsymmetricKeyParameter) + { + AsymmetricKeyParameter akp = (AsymmetricKeyParameter) obj; + if (akp.IsPrivate) + { + string keyType; + encoding = EncodePrivateKey(akp, out keyType); + + type = keyType + " PRIVATE KEY"; + } + else + { + type = "PUBLIC KEY"; + + encoding = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(akp).GetDerEncoded(); + } + } + else if (obj is IX509AttributeCertificate) + { + type = "ATTRIBUTE CERTIFICATE"; + encoding = ((X509V2AttributeCertificate)obj).GetEncoded(); + } + else if (obj is Pkcs10CertificationRequest) + { + type = "CERTIFICATE REQUEST"; + encoding = ((Pkcs10CertificationRequest)obj).GetEncoded(); + } + else if (obj is Asn1.Cms.ContentInfo) + { + type = "PKCS7"; + encoding = ((Asn1.Cms.ContentInfo)obj).GetEncoded(); + } + else + { + throw new PemGenerationException("Object type not supported: " + obj.GetType().FullName); + } + + return new PemObject(type, encoding); + } + +// private string GetHexEncoded(byte[] bytes) +// { +// bytes = Hex.Encode(bytes); +// +// char[] chars = new char[bytes.Length]; +// +// for (int i = 0; i != bytes.Length; i++) +// { +// chars[i] = (char)bytes[i]; +// } +// +// return new string(chars); +// } + + private static PemObject CreatePemObject( + object obj, + string algorithm, + char[] password, + SecureRandom random) + { + if (obj == null) + throw new ArgumentNullException("obj"); + if (algorithm == null) + throw new ArgumentNullException("algorithm"); + if (password == null) + throw new ArgumentNullException("password"); + if (random == null) + throw new ArgumentNullException("random"); + + if (obj is AsymmetricCipherKeyPair) + { + return CreatePemObject(((AsymmetricCipherKeyPair)obj).Private, algorithm, password, random); + } + + string type = null; + byte[] keyData = null; + + if (obj is AsymmetricKeyParameter) + { + AsymmetricKeyParameter akp = (AsymmetricKeyParameter) obj; + if (akp.IsPrivate) + { + string keyType; + keyData = EncodePrivateKey(akp, out keyType); + + type = keyType + " PRIVATE KEY"; + } + } + + if (type == null || keyData == null) + { + // TODO Support other types? + throw new PemGenerationException("Object type not supported: " + obj.GetType().FullName); + } + + + string dekAlgName = algorithm.ToUpperInvariant(); + + // Note: For backward compatibility + if (dekAlgName == "DESEDE") + { + dekAlgName = "DES-EDE3-CBC"; + } + + int ivLength = dekAlgName.StartsWith("AES-") ? 16 : 8; + + byte[] iv = new byte[ivLength]; + random.NextBytes(iv); + + byte[] encData = PemUtilities.Crypt(true, keyData, password, dekAlgName, iv); + + IList headers = Platform.CreateArrayList(2); + + headers.Add(new PemHeader("Proc-Type", "4,ENCRYPTED")); + headers.Add(new PemHeader("DEK-Info", dekAlgName + "," + Hex.ToHexString(iv))); + + return new PemObject(type, headers, encData); + } + + private static byte[] EncodePrivateKey( + AsymmetricKeyParameter akp, + out string keyType) + { + PrivateKeyInfo info = PrivateKeyInfoFactory.CreatePrivateKeyInfo(akp); + + DerObjectIdentifier oid = info.AlgorithmID.ObjectID; + + if (oid.Equals(X9ObjectIdentifiers.IdDsa)) + { + keyType = "DSA"; + + DsaParameter p = DsaParameter.GetInstance(info.AlgorithmID.Parameters); + + BigInteger x = ((DsaPrivateKeyParameters) akp).X; + BigInteger y = p.G.ModPow(x, p.P); + + // TODO Create an ASN1 object somewhere for this? + return new DerSequence( + new DerInteger(0), + new DerInteger(p.P), + new DerInteger(p.Q), + new DerInteger(p.G), + new DerInteger(y), + new DerInteger(x)).GetEncoded(); + } + + if (oid.Equals(PkcsObjectIdentifiers.RsaEncryption)) + { + keyType = "RSA"; + } + else if (oid.Equals(CryptoProObjectIdentifiers.GostR3410x2001) + || oid.Equals(X9ObjectIdentifiers.IdECPublicKey)) + { + keyType = "EC"; + } + else + { + throw new ArgumentException("Cannot handle private key of type: " + akp.GetType().FullName, "akp"); + } + + return info.PrivateKey.GetEncoded(); + } + + public PemObject Generate() + { + try + { + if (algorithm != null) + { + return CreatePemObject(obj, algorithm, password, random); + } + + return CreatePemObject(obj); + } + catch (IOException e) + { + throw new PemGenerationException("encoding exception", e); + } + } + } +} diff --git a/Crypto/src/openssl/PEMException.cs b/Crypto/src/openssl/PEMException.cs new file mode 100644
index 000000000..df0eebc69 --- /dev/null +++ b/Crypto/src/openssl/PEMException.cs
@@ -0,0 +1,22 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.OpenSsl +{ + public class PemException + : IOException + { + public PemException( + string message) + : base(message) + { + } + + public PemException( + string message, + Exception exception) + : base(message, exception) + { + } + } +} diff --git a/Crypto/src/openssl/PEMReader.cs b/Crypto/src/openssl/PEMReader.cs new file mode 100644
index 000000000..a2fedab96 --- /dev/null +++ b/Crypto/src/openssl/PEMReader.cs
@@ -0,0 +1,407 @@ +using System; +using System.Collections; +using System.Diagnostics; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Sec; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Pkcs; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.Utilities.IO.Pem; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.OpenSsl +{ + /** + * Class for reading OpenSSL PEM encoded streams containing + * X509 certificates, PKCS8 encoded keys and PKCS7 objects. + * <p> + * In the case of PKCS7 objects the reader will return a CMS ContentInfo object. Keys and + * Certificates will be returned using the appropriate java.security type.</p> + */ + public class PemReader + : Org.BouncyCastle.Utilities.IO.Pem.PemReader + { +// private static readonly IDictionary parsers = new Hashtable(); + + static PemReader() + { +// parsers.Add("CERTIFICATE REQUEST", new PKCS10CertificationRequestParser()); +// parsers.Add("NEW CERTIFICATE REQUEST", new PKCS10CertificationRequestParser()); +// parsers.Add("CERTIFICATE", new X509CertificateParser(provider)); +// parsers.Add("X509 CERTIFICATE", new X509CertificateParser(provider)); +// parsers.Add("X509 CRL", new X509CRLParser(provider)); +// parsers.Add("PKCS7", new PKCS7Parser()); +// parsers.Add("ATTRIBUTE CERTIFICATE", new X509AttributeCertificateParser()); +// parsers.Add("EC PARAMETERS", new ECNamedCurveSpecParser()); +// parsers.Add("PUBLIC KEY", new PublicKeyParser(provider)); +// parsers.Add("RSA PUBLIC KEY", new RSAPublicKeyParser(provider)); +// parsers.Add("RSA PRIVATE KEY", new RSAKeyPairParser(provider)); +// parsers.Add("DSA PRIVATE KEY", new DSAKeyPairParser(provider)); +// parsers.Add("EC PRIVATE KEY", new ECDSAKeyPairParser(provider)); +// parsers.Add("ENCRYPTED PRIVATE KEY", new EncryptedPrivateKeyParser(provider)); +// parsers.Add("PRIVATE KEY", new PrivateKeyParser(provider)); + } + + private readonly IPasswordFinder pFinder; + + /** + * Create a new PemReader + * + * @param reader the Reader + */ + public PemReader( + TextReader reader) + : this(reader, null) + { + } + + /** + * Create a new PemReader with a password finder + * + * @param reader the Reader + * @param pFinder the password finder + */ + public PemReader( + TextReader reader, + IPasswordFinder pFinder) + : base(reader) + { + this.pFinder = pFinder; + } + + public object ReadObject() + { + PemObject obj = ReadPemObject(); + + if (obj == null) + return null; + + // TODO Follow Java build and map to parser objects? +// if (parsers.Contains(obj.Type)) +// return ((PemObjectParser)parsers[obj.Type]).ParseObject(obj); + + if (obj.Type.EndsWith("PRIVATE KEY")) + return ReadPrivateKey(obj); + + switch (obj.Type) + { + case "PUBLIC KEY": + return ReadPublicKey(obj); + case "RSA PUBLIC KEY": + return ReadRsaPublicKey(obj); + case "CERTIFICATE REQUEST": + case "NEW CERTIFICATE REQUEST": + return ReadCertificateRequest(obj); + case "CERTIFICATE": + case "X509 CERTIFICATE": + return ReadCertificate(obj); + case "PKCS7": + return ReadPkcs7(obj); + case "X509 CRL": + return ReadCrl(obj); + case "ATTRIBUTE CERTIFICATE": + return ReadAttributeCertificate(obj); + // TODO Add back in when tests done, and return type issue resolved + //case "EC PARAMETERS": + // return ReadECParameters(obj); + default: + throw new IOException("unrecognised object: " + obj.Type); + } + } + + private AsymmetricKeyParameter ReadRsaPublicKey(PemObject pemObject) + { + RsaPublicKeyStructure rsaPubStructure = RsaPublicKeyStructure.GetInstance( + Asn1Object.FromByteArray(pemObject.Content)); + + return new RsaKeyParameters( + false, // not private + rsaPubStructure.Modulus, + rsaPubStructure.PublicExponent); + } + + private AsymmetricKeyParameter ReadPublicKey(PemObject pemObject) + { + return PublicKeyFactory.CreateKey(pemObject.Content); + } + + /** + * Reads in a X509Certificate. + * + * @return the X509Certificate + * @throws IOException if an I/O error occured + */ + private X509Certificate ReadCertificate(PemObject pemObject) + { + try + { + return new X509CertificateParser().ReadCertificate(pemObject.Content); + } + catch (Exception e) + { + throw new PemException("problem parsing cert: " + e.ToString()); + } + } + + /** + * Reads in a X509CRL. + * + * @return the X509Certificate + * @throws IOException if an I/O error occured + */ + private X509Crl ReadCrl(PemObject pemObject) + { + try + { + return new X509CrlParser().ReadCrl(pemObject.Content); + } + catch (Exception e) + { + throw new PemException("problem parsing cert: " + e.ToString()); + } + } + + /** + * Reads in a PKCS10 certification request. + * + * @return the certificate request. + * @throws IOException if an I/O error occured + */ + private Pkcs10CertificationRequest ReadCertificateRequest(PemObject pemObject) + { + try + { + return new Pkcs10CertificationRequest(pemObject.Content); + } + catch (Exception e) + { + throw new PemException("problem parsing cert: " + e.ToString()); + } + } + + /** + * Reads in a X509 Attribute Certificate. + * + * @return the X509 Attribute Certificate + * @throws IOException if an I/O error occured + */ + private IX509AttributeCertificate ReadAttributeCertificate(PemObject pemObject) + { + return new X509V2AttributeCertificate(pemObject.Content); + } + + /** + * Reads in a PKCS7 object. This returns a ContentInfo object suitable for use with the CMS + * API. + * + * @return the X509Certificate + * @throws IOException if an I/O error occured + */ + // TODO Consider returning Asn1.Pkcs.ContentInfo + private Asn1.Cms.ContentInfo ReadPkcs7(PemObject pemObject) + { + try + { + return Asn1.Cms.ContentInfo.GetInstance( + Asn1Object.FromByteArray(pemObject.Content)); + } + catch (Exception e) + { + throw new PemException("problem parsing PKCS7 object: " + e.ToString()); + } + } + + /** + * Read a Key Pair + */ + private object ReadPrivateKey(PemObject pemObject) + { + // + // extract the key + // + Debug.Assert(pemObject.Type.EndsWith("PRIVATE KEY")); + + string type = pemObject.Type.Substring(0, pemObject.Type.Length - "PRIVATE KEY".Length).Trim(); + byte[] keyBytes = pemObject.Content; + + IDictionary fields = Platform.CreateHashtable(); + foreach (PemHeader header in pemObject.Headers) + { + fields[header.Name] = header.Value; + } + + string procType = (string) fields["Proc-Type"]; + + if (procType == "4,ENCRYPTED") + { + if (pFinder == null) + throw new PasswordException("No password finder specified, but a password is required"); + + char[] password = pFinder.GetPassword(); + + if (password == null) + throw new PasswordException("Password is null, but a password is required"); + + string dekInfo = (string) fields["DEK-Info"]; + string[] tknz = dekInfo.Split(','); + + string dekAlgName = tknz[0].Trim(); + byte[] iv = Hex.Decode(tknz[1].Trim()); + + keyBytes = PemUtilities.Crypt(false, keyBytes, password, dekAlgName, iv); + } + + try + { + AsymmetricKeyParameter pubSpec, privSpec; + Asn1Sequence seq = (Asn1Sequence) Asn1Object.FromByteArray(keyBytes); + + switch (type) + { + case "RSA": + { + if (seq.Count != 9) + throw new PemException("malformed sequence in RSA private key"); + + RsaPrivateKeyStructure rsa = new RsaPrivateKeyStructure(seq); + + pubSpec = new RsaKeyParameters(false, rsa.Modulus, rsa.PublicExponent); + privSpec = new RsaPrivateCrtKeyParameters( + rsa.Modulus, rsa.PublicExponent, rsa.PrivateExponent, + rsa.Prime1, rsa.Prime2, rsa.Exponent1, rsa.Exponent2, + rsa.Coefficient); + + break; + } + + case "DSA": + { + if (seq.Count != 6) + throw new PemException("malformed sequence in DSA private key"); + + // TODO Create an ASN1 object somewhere for this? + //DerInteger v = (DerInteger)seq[0]; + DerInteger p = (DerInteger)seq[1]; + DerInteger q = (DerInteger)seq[2]; + DerInteger g = (DerInteger)seq[3]; + DerInteger y = (DerInteger)seq[4]; + DerInteger x = (DerInteger)seq[5]; + + DsaParameters parameters = new DsaParameters(p.Value, q.Value, g.Value); + + privSpec = new DsaPrivateKeyParameters(x.Value, parameters); + pubSpec = new DsaPublicKeyParameters(y.Value, parameters); + + break; + } + + case "EC": + { + ECPrivateKeyStructure pKey = new ECPrivateKeyStructure(seq); + AlgorithmIdentifier algId = new AlgorithmIdentifier( + X9ObjectIdentifiers.IdECPublicKey, pKey.GetParameters()); + + PrivateKeyInfo privInfo = new PrivateKeyInfo(algId, pKey.ToAsn1Object()); + + // TODO Are the keys returned here ECDSA, as Java version forces? + privSpec = PrivateKeyFactory.CreateKey(privInfo); + + DerBitString pubKey = pKey.GetPublicKey(); + if (pubKey != null) + { + SubjectPublicKeyInfo pubInfo = new SubjectPublicKeyInfo(algId, pubKey.GetBytes()); + + // TODO Are the keys returned here ECDSA, as Java version forces? + pubSpec = PublicKeyFactory.CreateKey(pubInfo); + } + else + { + pubSpec = ECKeyPairGenerator.GetCorrespondingPublicKey( + (ECPrivateKeyParameters)privSpec); + } + + break; + } + + case "ENCRYPTED": + { + char[] password = pFinder.GetPassword(); + + if (password == null) + throw new PasswordException("Password is null, but a password is required"); + + return PrivateKeyFactory.DecryptKey(password, EncryptedPrivateKeyInfo.GetInstance(seq)); + } + + case "": + { + return PrivateKeyFactory.CreateKey(PrivateKeyInfo.GetInstance(seq)); + } + + default: + throw new ArgumentException("Unknown key type: " + type, "type"); + } + + return new AsymmetricCipherKeyPair(pubSpec, privSpec); + } + catch (IOException e) + { + throw e; + } + catch (Exception e) + { + throw new PemException( + "problem creating " + type + " private key: " + e.ToString()); + } + } + + // TODO Add an equivalent class for ECNamedCurveParameterSpec? + //private ECNamedCurveParameterSpec ReadECParameters( +// private X9ECParameters ReadECParameters(PemObject pemObject) +// { +// DerObjectIdentifier oid = (DerObjectIdentifier)Asn1Object.FromByteArray(pemObject.Content); +// +// //return ECNamedCurveTable.getParameterSpec(oid.Id); +// return GetCurveParameters(oid.Id); +// } + + //private static ECDomainParameters GetCurveParameters( + private static X9ECParameters GetCurveParameters( + string name) + { + // TODO ECGost3410NamedCurves support (returns ECDomainParameters though) + X9ECParameters ecP = X962NamedCurves.GetByName(name); + + if (ecP == null) + { + ecP = SecNamedCurves.GetByName(name); + if (ecP == null) + { + ecP = NistNamedCurves.GetByName(name); + if (ecP == null) + { + ecP = TeleTrusTNamedCurves.GetByName(name); + + if (ecP == null) + throw new Exception("unknown curve name: " + name); + } + } + } + + //return new ECDomainParameters(ecP.Curve, ecP.G, ecP.N, ecP.H, ecP.GetSeed()); + return ecP; + } + } +} diff --git a/Crypto/src/openssl/PEMUtilities.cs b/Crypto/src/openssl/PEMUtilities.cs new file mode 100644
index 000000000..b58e5e765 --- /dev/null +++ b/Crypto/src/openssl/PEMUtilities.cs
@@ -0,0 +1,158 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.OpenSsl +{ + internal sealed class PemUtilities + { + private enum PemBaseAlg { AES_128, AES_192, AES_256, BF, DES, DES_EDE, DES_EDE3, RC2, RC2_40, RC2_64 }; + private enum PemMode { CBC, CFB, ECB, OFB }; + + static PemUtilities() + { + // Signal to obfuscation tools not to change enum constants + ((PemBaseAlg)Enums.GetArbitraryValue(typeof(PemBaseAlg))).ToString(); + ((PemMode)Enums.GetArbitraryValue(typeof(PemMode))).ToString(); + } + + private static void ParseDekAlgName( + string dekAlgName, + out PemBaseAlg baseAlg, + out PemMode mode) + { + try + { + mode = PemMode.ECB; + + if (dekAlgName == "DES-EDE" || dekAlgName == "DES-EDE3") + { + baseAlg = (PemBaseAlg)Enums.GetEnumValue(typeof(PemBaseAlg), dekAlgName); + return; + } + + int pos = dekAlgName.LastIndexOf('-'); + if (pos >= 0) + { + baseAlg = (PemBaseAlg)Enums.GetEnumValue(typeof(PemBaseAlg), dekAlgName.Substring(0, pos)); + mode = (PemMode)Enums.GetEnumValue(typeof(PemMode), dekAlgName.Substring(pos + 1)); + return; + } + } + catch (ArgumentException) + { + } + + throw new EncryptionException("Unknown DEK algorithm: " + dekAlgName); + } + + internal static byte[] Crypt( + bool encrypt, + byte[] bytes, + char[] password, + string dekAlgName, + byte[] iv) + { + PemBaseAlg baseAlg; + PemMode mode; + ParseDekAlgName(dekAlgName, out baseAlg, out mode); + + string padding; + switch (mode) + { + case PemMode.CBC: + case PemMode.ECB: + padding = "PKCS5Padding"; + break; + case PemMode.CFB: + case PemMode.OFB: + padding = "NoPadding"; + break; + default: + throw new EncryptionException("Unknown DEK algorithm: " + dekAlgName); + } + + string algorithm; + + byte[] salt = iv; + switch (baseAlg) + { + case PemBaseAlg.AES_128: + case PemBaseAlg.AES_192: + case PemBaseAlg.AES_256: + algorithm = "AES"; + if (salt.Length > 8) + { + salt = new byte[8]; + Array.Copy(iv, 0, salt, 0, salt.Length); + } + break; + case PemBaseAlg.BF: + algorithm = "BLOWFISH"; + break; + case PemBaseAlg.DES: + algorithm = "DES"; + break; + case PemBaseAlg.DES_EDE: + case PemBaseAlg.DES_EDE3: + algorithm = "DESede"; + break; + case PemBaseAlg.RC2: + case PemBaseAlg.RC2_40: + case PemBaseAlg.RC2_64: + algorithm = "RC2"; + break; + default: + throw new EncryptionException("Unknown DEK algorithm: " + dekAlgName); + } + + string cipherName = algorithm + "/" + mode + "/" + padding; + IBufferedCipher cipher = CipherUtilities.GetCipher(cipherName); + + ICipherParameters cParams = GetCipherParameters(password, baseAlg, salt); + + if (mode != PemMode.ECB) + { + cParams = new ParametersWithIV(cParams, iv); + } + + cipher.Init(encrypt, cParams); + + return cipher.DoFinal(bytes); + } + + private static ICipherParameters GetCipherParameters( + char[] password, + PemBaseAlg baseAlg, + byte[] salt) + { + string algorithm; + int keyBits; + switch (baseAlg) + { + case PemBaseAlg.AES_128: keyBits = 128; algorithm = "AES128"; break; + case PemBaseAlg.AES_192: keyBits = 192; algorithm = "AES192"; break; + case PemBaseAlg.AES_256: keyBits = 256; algorithm = "AES256"; break; + case PemBaseAlg.BF: keyBits = 128; algorithm = "BLOWFISH"; break; + case PemBaseAlg.DES: keyBits = 64; algorithm = "DES"; break; + case PemBaseAlg.DES_EDE: keyBits = 128; algorithm = "DESEDE"; break; + case PemBaseAlg.DES_EDE3: keyBits = 192; algorithm = "DESEDE3"; break; + case PemBaseAlg.RC2: keyBits = 128; algorithm = "RC2"; break; + case PemBaseAlg.RC2_40: keyBits = 40; algorithm = "RC2"; break; + case PemBaseAlg.RC2_64: keyBits = 64; algorithm = "RC2"; break; + default: + return null; + } + + OpenSslPbeParametersGenerator pGen = new OpenSslPbeParametersGenerator(); + + pGen.Init(PbeParametersGenerator.Pkcs5PasswordToBytes(password), salt); + + return pGen.GenerateDerivedParameters(algorithm, keyBits); + } + } +} diff --git a/Crypto/src/openssl/PEMWriter.cs b/Crypto/src/openssl/PEMWriter.cs new file mode 100644
index 000000000..aefb018f3 --- /dev/null +++ b/Crypto/src/openssl/PEMWriter.cs
@@ -0,0 +1,61 @@ +using System; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Pkcs; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.Utilities.IO.Pem; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.OpenSsl +{ + /// <remarks>General purpose writer for OpenSSL PEM objects.</remarks> + public class PemWriter + : Org.BouncyCastle.Utilities.IO.Pem.PemWriter + { + /// <param name="writer">The TextWriter object to write the output to.</param> + public PemWriter( + TextWriter writer) + : base(writer) + { + } + + public void WriteObject( + object obj) + { + try + { + base.WriteObject(new MiscPemGenerator(obj)); + } + catch (PemGenerationException e) + { + if (e.InnerException is IOException) + throw (IOException)e.InnerException; + + throw e; + } + } + + public void WriteObject( + object obj, + string algorithm, + char[] password, + SecureRandom random) + { + base.WriteObject(new MiscPemGenerator(obj, algorithm, password, random)); + } + } +} diff --git a/Crypto/src/openssl/PasswordException.cs b/Crypto/src/openssl/PasswordException.cs new file mode 100644
index 000000000..b21dee231 --- /dev/null +++ b/Crypto/src/openssl/PasswordException.cs
@@ -0,0 +1,22 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Security +{ + public class PasswordException + : IOException + { + public PasswordException( + string message) + : base(message) + { + } + + public PasswordException( + string message, + Exception exception) + : base(message, exception) + { + } + } +} diff --git a/Crypto/src/openssl/Pkcs8Generator.cs b/Crypto/src/openssl/Pkcs8Generator.cs new file mode 100644
index 000000000..d03ea08d2 --- /dev/null +++ b/Crypto/src/openssl/Pkcs8Generator.cs
@@ -0,0 +1,111 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Pkcs; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities.IO.Pem; + +namespace Org.BouncyCastle.OpenSsl +{ + public class Pkcs8Generator + : PemObjectGenerator + { + // FIXME See PbeUtilities static constructor +// public static readonly string Aes128Cbc = NistObjectIdentifiers.IdAes128Cbc.Id; +// public static readonly string Aes192Cbc = NistObjectIdentifiers.IdAes192Cbc.Id; +// public static readonly string Aes256Cbc = NistObjectIdentifiers.IdAes256Cbc.Id; +// +// public static readonly string Des3Cbc = PkcsObjectIdentifiers.DesEde3Cbc.Id; + + public static readonly string PbeSha1_RC4_128 = PkcsObjectIdentifiers.PbeWithShaAnd128BitRC4.Id; + public static readonly string PbeSha1_RC4_40 = PkcsObjectIdentifiers.PbeWithShaAnd40BitRC4.Id; + public static readonly string PbeSha1_3DES = PkcsObjectIdentifiers.PbeWithShaAnd3KeyTripleDesCbc.Id; + public static readonly string PbeSha1_2DES = PkcsObjectIdentifiers.PbeWithShaAnd2KeyTripleDesCbc.Id; + public static readonly string PbeSha1_RC2_128 = PkcsObjectIdentifiers.PbeWithShaAnd128BitRC2Cbc.Id; + public static readonly string PbeSha1_RC2_40 = PkcsObjectIdentifiers.PbewithShaAnd40BitRC2Cbc.Id; + + private char[] password; + private string algorithm; + private int iterationCount; + private AsymmetricKeyParameter privKey; + private SecureRandom random; + + /** + * Constructor for an unencrypted private key PEM object. + * + * @param key private key to be encoded. + */ + public Pkcs8Generator(AsymmetricKeyParameter privKey) + { + this.privKey = privKey; + } + + /** + * Constructor for an encrypted private key PEM object. + * + * @param key private key to be encoded + * @param algorithm encryption algorithm to use + * @param provider provider to use + * @throws NoSuchAlgorithmException if algorithm/mode cannot be found + */ + public Pkcs8Generator(AsymmetricKeyParameter privKey, string algorithm) + { + // TODO Check privKey.IsPrivate + this.privKey = privKey; + this.algorithm = algorithm; + this.iterationCount = 2048; + } + + public SecureRandom SecureRandom + { + set { this.random = value; } + } + + public char[] Password + { + set { this.password = value; } + } + + public int IterationCount + { + set { this.iterationCount = value; } + } + + public PemObject Generate() + { + if (algorithm == null) + { + PrivateKeyInfo pki = PrivateKeyInfoFactory.CreatePrivateKeyInfo(privKey); + + return new PemObject("PRIVATE KEY", pki.GetEncoded()); + } + + // TODO Theoretically, the amount of salt needed depends on the algorithm + byte[] salt = new byte[20]; + if (random == null) + { + random = new SecureRandom(); + } + random.NextBytes(salt); + + try + { + EncryptedPrivateKeyInfo epki = EncryptedPrivateKeyInfoFactory.CreateEncryptedPrivateKeyInfo( + algorithm, password, salt, iterationCount, privKey); + + return new PemObject("ENCRYPTED PRIVATE KEY", epki.GetEncoded()); + } + catch (Exception e) + { + throw new PemGenerationException("Couldn't encrypt private key", e); + } + } + } +} diff --git a/Crypto/src/pkcs/AsymmetricKeyEntry.cs b/Crypto/src/pkcs/AsymmetricKeyEntry.cs new file mode 100644
index 000000000..6da3ade3e --- /dev/null +++ b/Crypto/src/pkcs/AsymmetricKeyEntry.cs
@@ -0,0 +1,60 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Pkcs +{ + public class AsymmetricKeyEntry + : Pkcs12Entry + { + private readonly AsymmetricKeyParameter key; + + public AsymmetricKeyEntry( + AsymmetricKeyParameter key) + : base(Platform.CreateHashtable()) + { + this.key = key; + } + +#if !(SILVERLIGHT || PORTABLE) + [Obsolete] + public AsymmetricKeyEntry( + AsymmetricKeyParameter key, + Hashtable attributes) + : base(attributes) + { + this.key = key; + } +#endif + + public AsymmetricKeyEntry( + AsymmetricKeyParameter key, + IDictionary attributes) + : base(attributes) + { + this.key = key; + } + + public AsymmetricKeyParameter Key + { + get { return this.key; } + } + + public override bool Equals(object obj) + { + AsymmetricKeyEntry other = obj as AsymmetricKeyEntry; + + if (other == null) + return false; + + return key.Equals(other.key); + } + + public override int GetHashCode() + { + return ~key.GetHashCode(); + } + } +} diff --git a/Crypto/src/pkcs/EncryptedPrivateKeyInfoFactory.cs b/Crypto/src/pkcs/EncryptedPrivateKeyInfoFactory.cs new file mode 100644
index 000000000..b69693490 --- /dev/null +++ b/Crypto/src/pkcs/EncryptedPrivateKeyInfoFactory.cs
@@ -0,0 +1,75 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Pkcs +{ + public sealed class EncryptedPrivateKeyInfoFactory + { + private EncryptedPrivateKeyInfoFactory() + { + } + + public static EncryptedPrivateKeyInfo CreateEncryptedPrivateKeyInfo( + DerObjectIdentifier algorithm, + char[] passPhrase, + byte[] salt, + int iterationCount, + AsymmetricKeyParameter key) + { + return CreateEncryptedPrivateKeyInfo( + algorithm.Id, passPhrase, salt, iterationCount, + PrivateKeyInfoFactory.CreatePrivateKeyInfo(key)); + } + + public static EncryptedPrivateKeyInfo CreateEncryptedPrivateKeyInfo( + string algorithm, + char[] passPhrase, + byte[] salt, + int iterationCount, + AsymmetricKeyParameter key) + { + return CreateEncryptedPrivateKeyInfo( + algorithm, passPhrase, salt, iterationCount, + PrivateKeyInfoFactory.CreatePrivateKeyInfo(key)); + } + + public static EncryptedPrivateKeyInfo CreateEncryptedPrivateKeyInfo( + string algorithm, + char[] passPhrase, + byte[] salt, + int iterationCount, + PrivateKeyInfo keyInfo) + { + if (!PbeUtilities.IsPbeAlgorithm(algorithm)) + throw new ArgumentException("attempt to use non-PBE algorithm with PBE EncryptedPrivateKeyInfo generation"); + + IBufferedCipher cipher = PbeUtilities.CreateEngine(algorithm) as IBufferedCipher; + + if (cipher == null) + { + // TODO Throw exception? + } + + Asn1Encodable parameters = PbeUtilities.GenerateAlgorithmParameters( + algorithm, salt, iterationCount); + + ICipherParameters keyParameters = PbeUtilities.GenerateCipherParameters( + algorithm, passPhrase, parameters); + + cipher.Init(true, keyParameters); + + byte[] keyBytes = keyInfo.GetEncoded(); + byte[] encoding = cipher.DoFinal(keyBytes); + + DerObjectIdentifier oid = PbeUtilities.GetObjectIdentifier(algorithm); + AlgorithmIdentifier algID = new AlgorithmIdentifier(oid, parameters); + + return new EncryptedPrivateKeyInfo(algID, encoding); + } + } +} diff --git a/Crypto/src/pkcs/PKCS12StoreBuilder.cs b/Crypto/src/pkcs/PKCS12StoreBuilder.cs new file mode 100644
index 000000000..c8fa0f603 --- /dev/null +++ b/Crypto/src/pkcs/PKCS12StoreBuilder.cs
@@ -0,0 +1,41 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Pkcs; + +namespace Org.BouncyCastle.Pkcs +{ + public class Pkcs12StoreBuilder + { + private DerObjectIdentifier keyAlgorithm = PkcsObjectIdentifiers.PbeWithShaAnd3KeyTripleDesCbc; + private DerObjectIdentifier certAlgorithm = PkcsObjectIdentifiers.PbewithShaAnd40BitRC2Cbc; + private bool useDerEncoding = false; + + public Pkcs12StoreBuilder() + { + } + + public Pkcs12Store Build() + { + return new Pkcs12Store(keyAlgorithm, certAlgorithm, useDerEncoding); + } + + public Pkcs12StoreBuilder SetCertAlgorithm(DerObjectIdentifier certAlgorithm) + { + this.certAlgorithm = certAlgorithm; + return this; + } + + public Pkcs12StoreBuilder SetKeyAlgorithm(DerObjectIdentifier keyAlgorithm) + { + this.keyAlgorithm = keyAlgorithm; + return this; + } + + public Pkcs12StoreBuilder SetUseDerEncoding(bool useDerEncoding) + { + this.useDerEncoding = useDerEncoding; + return this; + } + } +} diff --git a/Crypto/src/pkcs/Pkcs10CertificationRequest.cs b/Crypto/src/pkcs/Pkcs10CertificationRequest.cs new file mode 100644
index 000000000..2c343c0b4 --- /dev/null +++ b/Crypto/src/pkcs/Pkcs10CertificationRequest.cs
@@ -0,0 +1,466 @@ +using System; +using System.Collections; +using System.Globalization; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Pkcs +{ + /// <remarks> + /// A class for verifying and creating Pkcs10 Certification requests. + /// </remarks> + /// <code> + /// CertificationRequest ::= Sequence { + /// certificationRequestInfo CertificationRequestInfo, + /// signatureAlgorithm AlgorithmIdentifier{{ SignatureAlgorithms }}, + /// signature BIT STRING + /// } + /// + /// CertificationRequestInfo ::= Sequence { + /// version Integer { v1(0) } (v1,...), + /// subject Name, + /// subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }}, + /// attributes [0] Attributes{{ CRIAttributes }} + /// } + /// + /// Attributes { ATTRIBUTE:IOSet } ::= Set OF Attr{{ IOSet }} + /// + /// Attr { ATTRIBUTE:IOSet } ::= Sequence { + /// type ATTRIBUTE.&amp;id({IOSet}), + /// values Set SIZE(1..MAX) OF ATTRIBUTE.&amp;Type({IOSet}{\@type}) + /// } + /// </code> + /// see <a href="http://www.rsasecurity.com/rsalabs/node.asp?id=2132"/> + public class Pkcs10CertificationRequest + : CertificationRequest + { + protected static readonly IDictionary algorithms = Platform.CreateHashtable(); + protected static readonly IDictionary exParams = Platform.CreateHashtable(); + protected static readonly IDictionary keyAlgorithms = Platform.CreateHashtable(); + protected static readonly IDictionary oids = Platform.CreateHashtable(); + protected static readonly ISet noParams = new HashSet(); + + static Pkcs10CertificationRequest() + { + algorithms.Add("MD2WITHRSAENCRYPTION", new DerObjectIdentifier("1.2.840.113549.1.1.2")); + algorithms.Add("MD2WITHRSA", new DerObjectIdentifier("1.2.840.113549.1.1.2")); + algorithms.Add("MD5WITHRSAENCRYPTION", new DerObjectIdentifier("1.2.840.113549.1.1.4")); + algorithms.Add("MD5WITHRSA", new DerObjectIdentifier("1.2.840.113549.1.1.4")); + algorithms.Add("RSAWITHMD5", new DerObjectIdentifier("1.2.840.113549.1.1.4")); + algorithms.Add("SHA1WITHRSAENCRYPTION", new DerObjectIdentifier("1.2.840.113549.1.1.5")); + algorithms.Add("SHA1WITHRSA", new DerObjectIdentifier("1.2.840.113549.1.1.5")); + algorithms.Add("SHA224WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha224WithRsaEncryption); + algorithms.Add("SHA224WITHRSA", PkcsObjectIdentifiers.Sha224WithRsaEncryption); + algorithms.Add("SHA256WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha256WithRsaEncryption); + algorithms.Add("SHA256WITHRSA", PkcsObjectIdentifiers.Sha256WithRsaEncryption); + algorithms.Add("SHA384WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha384WithRsaEncryption); + algorithms.Add("SHA384WITHRSA", PkcsObjectIdentifiers.Sha384WithRsaEncryption); + algorithms.Add("SHA512WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha512WithRsaEncryption); + algorithms.Add("SHA512WITHRSA", PkcsObjectIdentifiers.Sha512WithRsaEncryption); + algorithms.Add("SHA1WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss); + algorithms.Add("SHA224WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss); + algorithms.Add("SHA256WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss); + algorithms.Add("SHA384WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss); + algorithms.Add("SHA512WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss); + algorithms.Add("RSAWITHSHA1", new DerObjectIdentifier("1.2.840.113549.1.1.5")); + algorithms.Add("RIPEMD128WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128); + algorithms.Add("RIPEMD128WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128); + algorithms.Add("RIPEMD160WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160); + algorithms.Add("RIPEMD160WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160); + algorithms.Add("RIPEMD256WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256); + algorithms.Add("RIPEMD256WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256); + algorithms.Add("SHA1WITHDSA", new DerObjectIdentifier("1.2.840.10040.4.3")); + algorithms.Add("DSAWITHSHA1", new DerObjectIdentifier("1.2.840.10040.4.3")); + algorithms.Add("SHA224WITHDSA", NistObjectIdentifiers.DsaWithSha224); + algorithms.Add("SHA256WITHDSA", NistObjectIdentifiers.DsaWithSha256); + algorithms.Add("SHA384WITHDSA", NistObjectIdentifiers.DsaWithSha384); + algorithms.Add("SHA512WITHDSA", NistObjectIdentifiers.DsaWithSha512); + algorithms.Add("SHA1WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha1); + algorithms.Add("SHA224WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha224); + algorithms.Add("SHA256WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha256); + algorithms.Add("SHA384WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha384); + algorithms.Add("SHA512WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha512); + algorithms.Add("ECDSAWITHSHA1", X9ObjectIdentifiers.ECDsaWithSha1); + algorithms.Add("GOST3411WITHGOST3410", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94); + algorithms.Add("GOST3410WITHGOST3411", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94); + algorithms.Add("GOST3411WITHECGOST3410", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001); + algorithms.Add("GOST3411WITHECGOST3410-2001", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001); + algorithms.Add("GOST3411WITHGOST3410-2001", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001); + + // + // reverse mappings + // + oids.Add(new DerObjectIdentifier("1.2.840.113549.1.1.5"), "SHA1WITHRSA"); + oids.Add(PkcsObjectIdentifiers.Sha224WithRsaEncryption, "SHA224WITHRSA"); + oids.Add(PkcsObjectIdentifiers.Sha256WithRsaEncryption, "SHA256WITHRSA"); + oids.Add(PkcsObjectIdentifiers.Sha384WithRsaEncryption, "SHA384WITHRSA"); + oids.Add(PkcsObjectIdentifiers.Sha512WithRsaEncryption, "SHA512WITHRSA"); + oids.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94, "GOST3411WITHGOST3410"); + oids.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001, "GOST3411WITHECGOST3410"); + + oids.Add(new DerObjectIdentifier("1.2.840.113549.1.1.4"), "MD5WITHRSA"); + oids.Add(new DerObjectIdentifier("1.2.840.113549.1.1.2"), "MD2WITHRSA"); + oids.Add(new DerObjectIdentifier("1.2.840.10040.4.3"), "SHA1WITHDSA"); + oids.Add(X9ObjectIdentifiers.ECDsaWithSha1, "SHA1WITHECDSA"); + oids.Add(X9ObjectIdentifiers.ECDsaWithSha224, "SHA224WITHECDSA"); + oids.Add(X9ObjectIdentifiers.ECDsaWithSha256, "SHA256WITHECDSA"); + oids.Add(X9ObjectIdentifiers.ECDsaWithSha384, "SHA384WITHECDSA"); + oids.Add(X9ObjectIdentifiers.ECDsaWithSha512, "SHA512WITHECDSA"); + oids.Add(OiwObjectIdentifiers.Sha1WithRsa, "SHA1WITHRSA"); + oids.Add(OiwObjectIdentifiers.DsaWithSha1, "SHA1WITHDSA"); + oids.Add(NistObjectIdentifiers.DsaWithSha224, "SHA224WITHDSA"); + oids.Add(NistObjectIdentifiers.DsaWithSha256, "SHA256WITHDSA"); + + // + // key types + // + keyAlgorithms.Add(PkcsObjectIdentifiers.RsaEncryption, "RSA"); + keyAlgorithms.Add(X9ObjectIdentifiers.IdDsa, "DSA"); + + // + // According to RFC 3279, the ASN.1 encoding SHALL (id-dsa-with-sha1) or MUST (ecdsa-with-SHA*) omit the parameters field. + // The parameters field SHALL be NULL for RSA based signature algorithms. + // + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha1); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha224); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha256); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha384); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha512); + noParams.Add(X9ObjectIdentifiers.IdDsaWithSha1); + noParams.Add(NistObjectIdentifiers.DsaWithSha224); + noParams.Add(NistObjectIdentifiers.DsaWithSha256); + + // + // RFC 4491 + // + noParams.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94); + noParams.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001); + + // + // explicit params + // + AlgorithmIdentifier sha1AlgId = new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1, DerNull.Instance); + exParams.Add("SHA1WITHRSAANDMGF1", CreatePssParams(sha1AlgId, 20)); + + AlgorithmIdentifier sha224AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha224, DerNull.Instance); + exParams.Add("SHA224WITHRSAANDMGF1", CreatePssParams(sha224AlgId, 28)); + + AlgorithmIdentifier sha256AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha256, DerNull.Instance); + exParams.Add("SHA256WITHRSAANDMGF1", CreatePssParams(sha256AlgId, 32)); + + AlgorithmIdentifier sha384AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha384, DerNull.Instance); + exParams.Add("SHA384WITHRSAANDMGF1", CreatePssParams(sha384AlgId, 48)); + + AlgorithmIdentifier sha512AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha512, DerNull.Instance); + exParams.Add("SHA512WITHRSAANDMGF1", CreatePssParams(sha512AlgId, 64)); + } + + private static RsassaPssParameters CreatePssParams( + AlgorithmIdentifier hashAlgId, + int saltSize) + { + return new RsassaPssParameters( + hashAlgId, + new AlgorithmIdentifier(PkcsObjectIdentifiers.IdMgf1, hashAlgId), + new DerInteger(saltSize), + new DerInteger(1)); + } + + protected Pkcs10CertificationRequest() + { + } + + public Pkcs10CertificationRequest( + byte[] encoded) + : base((Asn1Sequence) Asn1Object.FromByteArray(encoded)) + { + } + + public Pkcs10CertificationRequest( + Asn1Sequence seq) + : base(seq) + { + } + + public Pkcs10CertificationRequest( + Stream input) + : base((Asn1Sequence) Asn1Object.FromStream(input)) + { + } + + /// <summary> + /// Instantiate a Pkcs10CertificationRequest object with the necessary credentials. + /// </summary> + ///<param name="signatureAlgorithm">Name of Sig Alg.</param> + /// <param name="subject">X509Name of subject eg OU="My unit." O="My Organisatioin" C="au" </param> + /// <param name="publicKey">Public Key to be included in cert reqest.</param> + /// <param name="attributes">ASN1Set of Attributes.</param> + /// <param name="signingKey">Matching Private key for nominated (above) public key to be used to sign the request.</param> + public Pkcs10CertificationRequest( + string signatureAlgorithm, + X509Name subject, + AsymmetricKeyParameter publicKey, + Asn1Set attributes, + AsymmetricKeyParameter signingKey) + { + if (signatureAlgorithm == null) + throw new ArgumentNullException("signatureAlgorithm"); + if (subject == null) + throw new ArgumentNullException("subject"); + if (publicKey == null) + throw new ArgumentNullException("publicKey"); + if (publicKey.IsPrivate) + throw new ArgumentException("expected public key", "publicKey"); + if (!signingKey.IsPrivate) + throw new ArgumentException("key for signing must be private", "signingKey"); + +// DerObjectIdentifier sigOid = SignerUtilities.GetObjectIdentifier(signatureAlgorithm); + string algorithmName = signatureAlgorithm.ToUpperInvariant(); + DerObjectIdentifier sigOid = (DerObjectIdentifier) algorithms[algorithmName]; + + if (sigOid == null) + { + try + { + sigOid = new DerObjectIdentifier(algorithmName); + } + catch (Exception e) + { + throw new ArgumentException("Unknown signature type requested", e); + } + } + + if (noParams.Contains(sigOid)) + { + this.sigAlgId = new AlgorithmIdentifier(sigOid); + } + else if (exParams.Contains(algorithmName)) + { + this.sigAlgId = new AlgorithmIdentifier(sigOid, (Asn1Encodable) exParams[algorithmName]); + } + else + { + this.sigAlgId = new AlgorithmIdentifier(sigOid, DerNull.Instance); + } + + SubjectPublicKeyInfo pubInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(publicKey); + + this.reqInfo = new CertificationRequestInfo(subject, pubInfo, attributes); + + ISigner sig = SignerUtilities.GetSigner(signatureAlgorithm); + + sig.Init(true, signingKey); + + try + { + // Encode. + byte[] b = reqInfo.GetDerEncoded(); + sig.BlockUpdate(b, 0, b.Length); + } + catch (Exception e) + { + throw new ArgumentException("exception encoding TBS cert request", e); + } + + // Generate Signature. + sigBits = new DerBitString(sig.GenerateSignature()); + } + +// internal Pkcs10CertificationRequest( +// Asn1InputStream seqStream) +// { +// Asn1Sequence seq = (Asn1Sequence) seqStream.ReadObject(); +// try +// { +// this.reqInfo = CertificationRequestInfo.GetInstance(seq[0]); +// this.sigAlgId = AlgorithmIdentifier.GetInstance(seq[1]); +// this.sigBits = (DerBitString) seq[2]; +// } +// catch (Exception ex) +// { +// throw new ArgumentException("Create From Asn1Sequence: " + ex.Message); +// } +// } + + /// <summary> + /// Get the public key. + /// </summary> + /// <returns>The public key.</returns> + public AsymmetricKeyParameter GetPublicKey() + { + return PublicKeyFactory.CreateKey(reqInfo.SubjectPublicKeyInfo); + } + + /// <summary> + /// Verify Pkcs10 Cert Request is valid. + /// </summary> + /// <returns>true = valid.</returns> + public bool Verify() + { + return Verify(this.GetPublicKey()); + } + + public bool Verify( + AsymmetricKeyParameter publicKey) + { + ISigner sig; + + try + { + sig = SignerUtilities.GetSigner(GetSignatureName(sigAlgId)); + } + catch (Exception e) + { + // try an alternate + string alt = (string) oids[sigAlgId.ObjectID]; + + if (alt != null) + { + sig = SignerUtilities.GetSigner(alt); + } + else + { + throw e; + } + } + + SetSignatureParameters(sig, sigAlgId.Parameters); + + sig.Init(false, publicKey); + + try + { + byte[] b = reqInfo.GetDerEncoded(); + sig.BlockUpdate(b, 0, b.Length); + } + catch (Exception e) + { + throw new SignatureException("exception encoding TBS cert request", e); + } + + return sig.VerifySignature(sigBits.GetBytes()); + } + +// /// <summary> +// /// Get the Der Encoded Pkcs10 Certification Request. +// /// </summary> +// /// <returns>A byte array.</returns> +// public byte[] GetEncoded() +// { +// return new CertificationRequest(reqInfo, sigAlgId, sigBits).GetDerEncoded(); +// } + + // TODO Figure out how to set parameters on an ISigner + private void SetSignatureParameters( + ISigner signature, + Asn1Encodable asn1Params) + { + if (asn1Params != null && !(asn1Params is Asn1Null)) + { +// AlgorithmParameters sigParams = AlgorithmParameters.GetInstance(signature.getAlgorithm()); +// +// try +// { +// sigParams.init(asn1Params.ToAsn1Object().GetDerEncoded()); +// } +// catch (IOException e) +// { +// throw new SignatureException("IOException decoding parameters: " + e.Message); +// } + + if (signature.AlgorithmName.EndsWith("MGF1")) + { + throw Platform.CreateNotImplementedException("signature algorithm with MGF1"); + +// try +// { +// signature.setParameter(sigParams.getParameterSpec(PSSParameterSpec.class)); +// } +// catch (GeneralSecurityException e) +// { +// throw new SignatureException("Exception extracting parameters: " + e.getMessage()); +// } + } + } + } + + internal static string GetSignatureName( + AlgorithmIdentifier sigAlgId) + { + Asn1Encodable asn1Params = sigAlgId.Parameters; + + if (asn1Params != null && !(asn1Params is Asn1Null)) + { + if (sigAlgId.ObjectID.Equals(PkcsObjectIdentifiers.IdRsassaPss)) + { + RsassaPssParameters rsaParams = RsassaPssParameters.GetInstance(asn1Params); + return GetDigestAlgName(rsaParams.HashAlgorithm.ObjectID) + "withRSAandMGF1"; + } + } + + return sigAlgId.ObjectID.Id; + } + + private static string GetDigestAlgName( + DerObjectIdentifier digestAlgOID) + { + if (PkcsObjectIdentifiers.MD5.Equals(digestAlgOID)) + { + return "MD5"; + } + else if (OiwObjectIdentifiers.IdSha1.Equals(digestAlgOID)) + { + return "SHA1"; + } + else if (NistObjectIdentifiers.IdSha224.Equals(digestAlgOID)) + { + return "SHA224"; + } + else if (NistObjectIdentifiers.IdSha256.Equals(digestAlgOID)) + { + return "SHA256"; + } + else if (NistObjectIdentifiers.IdSha384.Equals(digestAlgOID)) + { + return "SHA384"; + } + else if (NistObjectIdentifiers.IdSha512.Equals(digestAlgOID)) + { + return "SHA512"; + } + else if (TeleTrusTObjectIdentifiers.RipeMD128.Equals(digestAlgOID)) + { + return "RIPEMD128"; + } + else if (TeleTrusTObjectIdentifiers.RipeMD160.Equals(digestAlgOID)) + { + return "RIPEMD160"; + } + else if (TeleTrusTObjectIdentifiers.RipeMD256.Equals(digestAlgOID)) + { + return "RIPEMD256"; + } + else if (CryptoProObjectIdentifiers.GostR3411.Equals(digestAlgOID)) + { + return "GOST3411"; + } + else + { + return digestAlgOID.Id; + } + } + } +} diff --git a/Crypto/src/pkcs/Pkcs10CertificationRequestDelaySigned.cs b/Crypto/src/pkcs/Pkcs10CertificationRequestDelaySigned.cs new file mode 100644
index 000000000..f649b47a2 --- /dev/null +++ b/Crypto/src/pkcs/Pkcs10CertificationRequestDelaySigned.cs
@@ -0,0 +1,150 @@ +using System; +using System.Collections; +using System.Globalization; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Pkcs +{ + /// <remarks> + /// A class for creating and verifying Pkcs10 Certification requests (this is an extension on <see cref="Pkcs10CertificationRequest"/>). + /// The requests are made using delay signing. This is useful for situations where + /// the private key is in another environment and not directly accessible (e.g. HSM) + /// So the first step creates the request, then the signing is done outside this + /// object and the signature is then used to complete the request. + /// </remarks> + /// <code> + /// CertificationRequest ::= Sequence { + /// certificationRequestInfo CertificationRequestInfo, + /// signatureAlgorithm AlgorithmIdentifier{{ SignatureAlgorithms }}, + /// signature BIT STRING + /// } + /// + /// CertificationRequestInfo ::= Sequence { + /// version Integer { v1(0) } (v1,...), + /// subject Name, + /// subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }}, + /// attributes [0] Attributes{{ CRIAttributes }} + /// } + /// + /// Attributes { ATTRIBUTE:IOSet } ::= Set OF Attr{{ IOSet }} + /// + /// Attr { ATTRIBUTE:IOSet } ::= Sequence { + /// type ATTRIBUTE.&amp;id({IOSet}), + /// values Set SIZE(1..MAX) OF ATTRIBUTE.&amp;Type({IOSet}{\@type}) + /// } + /// </code> + /// see <a href="http://www.rsasecurity.com/rsalabs/node.asp?id=2132"/> + public class Pkcs10CertificationRequestDelaySigned : Pkcs10CertificationRequest + { + protected Pkcs10CertificationRequestDelaySigned() + : base() + { + } + public Pkcs10CertificationRequestDelaySigned( + byte[] encoded) + : base(encoded) + { + } + public Pkcs10CertificationRequestDelaySigned( + Asn1Sequence seq) + : base(seq) + { + } + public Pkcs10CertificationRequestDelaySigned( + Stream input) + : base(input) + { + } + public Pkcs10CertificationRequestDelaySigned( + string signatureAlgorithm, + X509Name subject, + AsymmetricKeyParameter publicKey, + Asn1Set attributes, + AsymmetricKeyParameter signingKey) + : base(signatureAlgorithm, subject, publicKey, attributes, signingKey) + { + } + /// <summary> + /// Instantiate a Pkcs10CertificationRequest object with the necessary credentials. + /// </summary> + /// <param name="signatureAlgorithm">Name of Sig Alg.</param> + /// <param name="subject">X509Name of subject eg OU="My unit." O="My Organisatioin" C="au" </param> + /// <param name="publicKey">Public Key to be included in cert reqest.</param> + /// <param name="attributes">ASN1Set of Attributes.</param> + /// <remarks> + /// After the object is constructed use the <see cref="GetDataToSign"/> and finally the + /// SignRequest methods to finalize the request. + /// </remarks> + public Pkcs10CertificationRequestDelaySigned( + string signatureAlgorithm, + X509Name subject, + AsymmetricKeyParameter publicKey, + Asn1Set attributes) + { + if (signatureAlgorithm == null) + throw new ArgumentNullException("signatureAlgorithm"); + if (subject == null) + throw new ArgumentNullException("subject"); + if (publicKey == null) + throw new ArgumentNullException("publicKey"); + if (publicKey.IsPrivate) + throw new ArgumentException("expected public key", "publicKey"); +// DerObjectIdentifier sigOid = SignerUtilities.GetObjectIdentifier(signatureAlgorithm); + string algorithmName = signatureAlgorithm.ToUpperInvariant(); + DerObjectIdentifier sigOid = (DerObjectIdentifier) algorithms[algorithmName]; + if (sigOid == null) + { + try + { + sigOid = new DerObjectIdentifier(algorithmName); + } + catch (Exception e) + { + throw new ArgumentException("Unknown signature type requested", e); + } + } + if (noParams.Contains(sigOid)) + { + this.sigAlgId = new AlgorithmIdentifier(sigOid); + } + else if (exParams.Contains(algorithmName)) + { + this.sigAlgId = new AlgorithmIdentifier(sigOid, (Asn1Encodable) exParams[algorithmName]); + } + else + { + this.sigAlgId = new AlgorithmIdentifier(sigOid, DerNull.Instance); + } + SubjectPublicKeyInfo pubInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(publicKey); + this.reqInfo = new CertificationRequestInfo(subject, pubInfo, attributes); + } + public byte[] GetDataToSign() + { + return reqInfo.GetDerEncoded(); + } + public void SignRequest(byte[] signedData) + { + //build the signature from the signed data + sigBits = new DerBitString(signedData); + } + public void SignRequest(DerBitString signedData) + { + //build the signature from the signed data + sigBits = signedData; + } + } +} diff --git a/Crypto/src/pkcs/Pkcs12Entry.cs b/Crypto/src/pkcs/Pkcs12Entry.cs new file mode 100644
index 000000000..5dcc94e88 --- /dev/null +++ b/Crypto/src/pkcs/Pkcs12Entry.cs
@@ -0,0 +1,64 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Pkcs +{ + public abstract class Pkcs12Entry + { + private readonly IDictionary attributes; + + protected internal Pkcs12Entry( + IDictionary attributes) + { + this.attributes = attributes; + + foreach (DictionaryEntry entry in attributes) + { + if (!(entry.Key is string)) + throw new ArgumentException("Attribute keys must be of type: " + typeof(string).FullName, "attributes"); + if (!(entry.Value is Asn1Encodable)) + throw new ArgumentException("Attribute values must be of type: " + typeof(Asn1Encodable).FullName, "attributes"); + } + } + + [Obsolete("Use 'object[index]' syntax instead")] + public Asn1Encodable GetBagAttribute( + DerObjectIdentifier oid) + { + return (Asn1Encodable)this.attributes[oid.Id]; + } + + [Obsolete("Use 'object[index]' syntax instead")] + public Asn1Encodable GetBagAttribute( + string oid) + { + return (Asn1Encodable)this.attributes[oid]; + } + + [Obsolete("Use 'BagAttributeKeys' property")] + public IEnumerator GetBagAttributeKeys() + { + return this.attributes.Keys.GetEnumerator(); + } + + public Asn1Encodable this[ + DerObjectIdentifier oid] + { + get { return (Asn1Encodable) this.attributes[oid.Id]; } + } + + public Asn1Encodable this[ + string oid] + { + get { return (Asn1Encodable) this.attributes[oid]; } + } + + public IEnumerable BagAttributeKeys + { + get { return new EnumerableProxy(this.attributes.Keys); } + } + } +} diff --git a/Crypto/src/pkcs/Pkcs12Store.cs b/Crypto/src/pkcs/Pkcs12Store.cs new file mode 100644
index 000000000..2e7f2295e --- /dev/null +++ b/Crypto/src/pkcs/Pkcs12Store.cs
@@ -0,0 +1,1231 @@ +using System; +using System.Collections; +using System.Globalization; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.Utilities; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Pkcs +{ + public class Pkcs12Store + { + private readonly IgnoresCaseHashtable keys = new IgnoresCaseHashtable(); + private readonly IDictionary localIds = Platform.CreateHashtable(); + private readonly IgnoresCaseHashtable certs = new IgnoresCaseHashtable(); + private readonly IDictionary chainCerts = Platform.CreateHashtable(); + private readonly IDictionary keyCerts = Platform.CreateHashtable(); + private readonly DerObjectIdentifier keyAlgorithm; + private readonly DerObjectIdentifier certAlgorithm; + private readonly bool useDerEncoding; + + private const int MinIterations = 1024; + private const int SaltSize = 20; + + private static SubjectKeyIdentifier CreateSubjectKeyID( + AsymmetricKeyParameter pubKey) + { + return new SubjectKeyIdentifier( + SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pubKey)); + } + + internal class CertId + { + private readonly byte[] id; + + internal CertId( + AsymmetricKeyParameter pubKey) + { + this.id = CreateSubjectKeyID(pubKey).GetKeyIdentifier(); + } + + internal CertId( + byte[] id) + { + this.id = id; + } + + internal byte[] Id + { + get { return id; } + } + + public override int GetHashCode() + { + return Arrays.GetHashCode(id); + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + CertId other = obj as CertId; + + if (other == null) + return false; + + return Arrays.AreEqual(id, other.id); + } + } + + internal Pkcs12Store( + DerObjectIdentifier keyAlgorithm, + DerObjectIdentifier certAlgorithm, + bool useDerEncoding) + { + this.keyAlgorithm = keyAlgorithm; + this.certAlgorithm = certAlgorithm; + this.useDerEncoding = useDerEncoding; + } + + // TODO Consider making obsolete +// [Obsolete("Use 'Pkcs12StoreBuilder' instead")] + public Pkcs12Store() + : this(PkcsObjectIdentifiers.PbeWithShaAnd3KeyTripleDesCbc, + PkcsObjectIdentifiers.PbewithShaAnd40BitRC2Cbc, false) + { + } + + // TODO Consider making obsolete +// [Obsolete("Use 'Pkcs12StoreBuilder' and 'Load' method instead")] + public Pkcs12Store( + Stream input, + char[] password) + : this() + { + Load(input, password); + } + + public void Load( + Stream input, + char[] password) + { + if (input == null) + throw new ArgumentNullException("input"); + if (password == null) + throw new ArgumentNullException("password"); + + Asn1Sequence obj = (Asn1Sequence) Asn1Object.FromStream(input); + Pfx bag = new Pfx(obj); + ContentInfo info = bag.AuthSafe; + bool unmarkedKey = false; + bool wrongPkcs12Zero = false; + + if (bag.MacData != null) // check the mac code + { + MacData mData = bag.MacData; + DigestInfo dInfo = mData.Mac; + AlgorithmIdentifier algId = dInfo.AlgorithmID; + byte[] salt = mData.GetSalt(); + int itCount = mData.IterationCount.IntValue; + + byte[] data = ((Asn1OctetString) info.Content).GetOctets(); + + byte[] mac = CalculatePbeMac(algId.ObjectID, salt, itCount, password, false, data); + byte[] dig = dInfo.GetDigest(); + + if (!Arrays.ConstantTimeAreEqual(mac, dig)) + { + if (password.Length > 0) + throw new IOException("PKCS12 key store MAC invalid - wrong password or corrupted file."); + + // Try with incorrect zero length password + mac = CalculatePbeMac(algId.ObjectID, salt, itCount, password, true, data); + + if (!Arrays.ConstantTimeAreEqual(mac, dig)) + throw new IOException("PKCS12 key store MAC invalid - wrong password or corrupted file."); + + wrongPkcs12Zero = true; + } + } + + keys.Clear(); + localIds.Clear(); + + IList chain = Platform.CreateArrayList(); + + if (info.ContentType.Equals(PkcsObjectIdentifiers.Data)) + { + byte[] octs = ((Asn1OctetString)info.Content).GetOctets(); + AuthenticatedSafe authSafe = new AuthenticatedSafe( + (Asn1Sequence) Asn1OctetString.FromByteArray(octs)); + ContentInfo[] cis = authSafe.GetContentInfo(); + + foreach (ContentInfo ci in cis) + { + DerObjectIdentifier oid = ci.ContentType; + + if (oid.Equals(PkcsObjectIdentifiers.Data)) + { + byte[] octets = ((Asn1OctetString)ci.Content).GetOctets(); + Asn1Sequence seq = (Asn1Sequence) Asn1Object.FromByteArray(octets); + + foreach (Asn1Sequence subSeq in seq) + { + SafeBag b = new SafeBag(subSeq); + + if (b.BagID.Equals(PkcsObjectIdentifiers.Pkcs8ShroudedKeyBag)) + { + EncryptedPrivateKeyInfo eIn = EncryptedPrivateKeyInfo.GetInstance(b.BagValue); + PrivateKeyInfo privInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo( + password, wrongPkcs12Zero, eIn); + AsymmetricKeyParameter privKey = PrivateKeyFactory.CreateKey(privInfo); + + // + // set the attributes on the key + // + IDictionary attributes = Platform.CreateHashtable(); + AsymmetricKeyEntry pkcs12Key = new AsymmetricKeyEntry(privKey, attributes); + string alias = null; + Asn1OctetString localId = null; + + if (b.BagAttributes != null) + { + foreach (Asn1Sequence sq in b.BagAttributes) + { + DerObjectIdentifier aOid = (DerObjectIdentifier) sq[0]; + Asn1Set attrSet = (Asn1Set) sq[1]; + Asn1Encodable attr = null; + + if (attrSet.Count > 0) + { + // TODO We should be adding all attributes in the set + attr = attrSet[0]; + + // TODO We might want to "merge" attribute sets with + // the same OID - currently, differing values give an error + if (attributes.Contains(aOid.Id)) + { + // OK, but the value has to be the same + if (!attributes[aOid.Id].Equals(attr)) + { + throw new IOException("attempt to add existing attribute with different value"); + } + } + else + { + attributes.Add(aOid.Id, attr); + } + + if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName)) + { + alias = ((DerBmpString)attr).GetString(); + // TODO Do these in a separate loop, just collect aliases here + keys[alias] = pkcs12Key; + } + else if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID)) + { + localId = (Asn1OctetString)attr; + } + } + } + } + + if (localId != null) + { + string name = Hex.ToHexString(localId.GetOctets()); + + if (alias == null) + { + keys[name] = pkcs12Key; + } + else + { + // TODO There may have been more than one alias + localIds[alias] = name; + } + } + else + { + unmarkedKey = true; + keys["unmarked"] = pkcs12Key; + } + } + else if (b.BagID.Equals(PkcsObjectIdentifiers.CertBag)) + { + chain.Add(b); + } + else + { + System.Diagnostics.Debug.WriteLine("extra " + b.BagID); + System.Diagnostics.Debug.WriteLine("extra " + Asn1Dump.DumpAsString(b)); + } + } + } + else if (oid.Equals(PkcsObjectIdentifiers.EncryptedData)) + { + EncryptedData d = EncryptedData.GetInstance(ci.Content); + byte[] octets = CryptPbeData(false, d.EncryptionAlgorithm, + password, wrongPkcs12Zero, d.Content.GetOctets()); + Asn1Sequence seq = (Asn1Sequence) Asn1Object.FromByteArray(octets); + + foreach (Asn1Sequence subSeq in seq) + { + SafeBag b = new SafeBag(subSeq); + + if (b.BagID.Equals(PkcsObjectIdentifiers.CertBag)) + { + chain.Add(b); + } + else if (b.BagID.Equals(PkcsObjectIdentifiers.Pkcs8ShroudedKeyBag)) + { + EncryptedPrivateKeyInfo eIn = EncryptedPrivateKeyInfo.GetInstance(b.BagValue); + PrivateKeyInfo privInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo( + password, wrongPkcs12Zero, eIn); + AsymmetricKeyParameter privKey = PrivateKeyFactory.CreateKey(privInfo); + + // + // set the attributes on the key + // + IDictionary attributes = Platform.CreateHashtable(); + AsymmetricKeyEntry pkcs12Key = new AsymmetricKeyEntry(privKey, attributes); + string alias = null; + Asn1OctetString localId = null; + + foreach (Asn1Sequence sq in b.BagAttributes) + { + DerObjectIdentifier aOid = (DerObjectIdentifier) sq[0]; + Asn1Set attrSet = (Asn1Set) sq[1]; + Asn1Encodable attr = null; + + if (attrSet.Count > 0) + { + // TODO We should be adding all attributes in the set + attr = attrSet[0]; + + // TODO We might want to "merge" attribute sets with + // the same OID - currently, differing values give an error + if (attributes.Contains(aOid.Id)) + { + // OK, but the value has to be the same + if (!attributes[aOid.Id].Equals(attr)) + { + throw new IOException("attempt to add existing attribute with different value"); + } + } + else + { + attributes.Add(aOid.Id, attr); + } + + if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName)) + { + alias = ((DerBmpString)attr).GetString(); + // TODO Do these in a separate loop, just collect aliases here + keys[alias] = pkcs12Key; + } + else if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID)) + { + localId = (Asn1OctetString)attr; + } + } + } + + // TODO Should we be checking localIds != null here + // as for PkcsObjectIdentifiers.Data version above? + + string name = Hex.ToHexString(localId.GetOctets()); + + if (alias == null) + { + keys[name] = pkcs12Key; + } + else + { + // TODO There may have been more than one alias + localIds[alias] = name; + } + } + else if (b.BagID.Equals(PkcsObjectIdentifiers.KeyBag)) + { + PrivateKeyInfo privKeyInfo = PrivateKeyInfo.GetInstance(b.BagValue); + AsymmetricKeyParameter privKey = PrivateKeyFactory.CreateKey(privKeyInfo); + + // + // set the attributes on the key + // + string alias = null; + Asn1OctetString localId = null; + IDictionary attributes = Platform.CreateHashtable(); + AsymmetricKeyEntry pkcs12Key = new AsymmetricKeyEntry(privKey, attributes); + + foreach (Asn1Sequence sq in b.BagAttributes) + { + DerObjectIdentifier aOid = (DerObjectIdentifier) sq[0]; + Asn1Set attrSet = (Asn1Set) sq[1]; + Asn1Encodable attr = null; + + if (attrSet.Count > 0) + { + // TODO We should be adding all attributes in the set + attr = attrSet[0]; + + // TODO We might want to "merge" attribute sets with + // the same OID - currently, differing values give an error + if (attributes.Contains(aOid.Id)) + { + // OK, but the value has to be the same + if (!attributes[aOid.Id].Equals(attr)) + { + throw new IOException("attempt to add existing attribute with different value"); + } + } + else + { + attributes.Add(aOid.Id, attr); + } + + if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName)) + { + alias = ((DerBmpString)attr).GetString(); + // TODO Do these in a separate loop, just collect aliases here + keys[alias] = pkcs12Key; + } + else if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID)) + { + localId = (Asn1OctetString)attr; + } + } + } + + // TODO Should we be checking localIds != null here + // as for PkcsObjectIdentifiers.Data version above? + + string name = Hex.ToHexString(localId.GetOctets()); + + if (alias == null) + { + keys[name] = pkcs12Key; + } + else + { + // TODO There may have been more than one alias + localIds[alias] = name; + } + } + else + { + System.Diagnostics.Debug.WriteLine("extra " + b.BagID); + System.Diagnostics.Debug.WriteLine("extra " + Asn1Dump.DumpAsString(b)); + } + } + } + else + { + System.Diagnostics.Debug.WriteLine("extra " + oid); + System.Diagnostics.Debug.WriteLine("extra " + Asn1Dump.DumpAsString(ci.Content)); + } + } + } + + certs.Clear(); + chainCerts.Clear(); + keyCerts.Clear(); + + foreach (SafeBag b in chain) + { + CertBag cb = new CertBag((Asn1Sequence)b.BagValue); + byte[] octets = ((Asn1OctetString) cb.CertValue).GetOctets(); + X509Certificate cert = new X509CertificateParser().ReadCertificate(octets); + + // + // set the attributes + // + IDictionary attributes = Platform.CreateHashtable(); + Asn1OctetString localId = null; + string alias = null; + + if (b.BagAttributes != null) + { + foreach (Asn1Sequence sq in b.BagAttributes) + { + DerObjectIdentifier aOid = (DerObjectIdentifier) sq[0]; + Asn1Set attrSet = (Asn1Set) sq[1]; + + if (attrSet.Count > 0) + { + // TODO We should be adding all attributes in the set + Asn1Encodable attr = attrSet[0]; + + // TODO We might want to "merge" attribute sets with + // the same OID - currently, differing values give an error + if (attributes.Contains(aOid.Id)) + { + // OK, but the value has to be the same + if (!attributes[aOid.Id].Equals(attr)) + { + throw new IOException("attempt to add existing attribute with different value"); + } + } + else + { + attributes.Add(aOid.Id, attr); + } + + if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName)) + { + alias = ((DerBmpString)attr).GetString(); + } + else if (aOid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID)) + { + localId = (Asn1OctetString)attr; + } + } + } + } + + CertId certId = new CertId(cert.GetPublicKey()); + X509CertificateEntry pkcs12Cert = new X509CertificateEntry(cert, attributes); + + chainCerts[certId] = pkcs12Cert; + + if (unmarkedKey) + { + if (keyCerts.Count == 0) + { + string name = Hex.ToHexString(certId.Id); + + keyCerts[name] = pkcs12Cert; + + object temp = keys["unmarked"]; + keys.Remove("unmarked"); + keys[name] = temp; + } + } + else + { + if (localId != null) + { + string name = Hex.ToHexString(localId.GetOctets()); + + keyCerts[name] = pkcs12Cert; + } + + if (alias != null) + { + // TODO There may have been more than one alias + certs[alias] = pkcs12Cert; + } + } + } + } + + public AsymmetricKeyEntry GetKey( + string alias) + { + if (alias == null) + throw new ArgumentNullException("alias"); + + return (AsymmetricKeyEntry)keys[alias]; + } + + public bool IsCertificateEntry( + string alias) + { + if (alias == null) + throw new ArgumentNullException("alias"); + + return (certs[alias] != null && keys[alias] == null); + } + + public bool IsKeyEntry( + string alias) + { + if (alias == null) + throw new ArgumentNullException("alias"); + + return (keys[alias] != null); + } + + private IDictionary GetAliasesTable() + { + IDictionary tab = Platform.CreateHashtable(); + + foreach (string key in certs.Keys) + { + tab[key] = "cert"; + } + + foreach (string a in keys.Keys) + { + if (tab[a] == null) + { + tab[a] = "key"; + } + } + + return tab; + } + + public IEnumerable Aliases + { + get { return new EnumerableProxy(GetAliasesTable().Keys); } + } + + public bool ContainsAlias( + string alias) + { + return certs[alias] != null || keys[alias] != null; + } + + /** + * simply return the cert entry for the private key + */ + public X509CertificateEntry GetCertificate( + string alias) + { + if (alias == null) + throw new ArgumentNullException("alias"); + + X509CertificateEntry c = (X509CertificateEntry) certs[alias]; + + // + // look up the key table - and try the local key id + // + if (c == null) + { + string id = (string)localIds[alias]; + if (id != null) + { + c = (X509CertificateEntry)keyCerts[id]; + } + else + { + c = (X509CertificateEntry)keyCerts[alias]; + } + } + + return c; + } + + public string GetCertificateAlias( + X509Certificate cert) + { + if (cert == null) + throw new ArgumentNullException("cert"); + + foreach (DictionaryEntry entry in certs) + { + X509CertificateEntry entryValue = (X509CertificateEntry) entry.Value; + if (entryValue.Certificate.Equals(cert)) + { + return (string) entry.Key; + } + } + + foreach (DictionaryEntry entry in keyCerts) + { + X509CertificateEntry entryValue = (X509CertificateEntry) entry.Value; + if (entryValue.Certificate.Equals(cert)) + { + return (string) entry.Key; + } + } + + return null; + } + + public X509CertificateEntry[] GetCertificateChain( + string alias) + { + if (alias == null) + throw new ArgumentNullException("alias"); + + if (!IsKeyEntry(alias)) + { + return null; + } + + X509CertificateEntry c = GetCertificate(alias); + + if (c != null) + { + IList cs = Platform.CreateArrayList(); + + while (c != null) + { + X509Certificate x509c = c.Certificate; + X509CertificateEntry nextC = null; + + Asn1OctetString ext = x509c.GetExtensionValue(X509Extensions.AuthorityKeyIdentifier); + if (ext != null) + { + AuthorityKeyIdentifier id = AuthorityKeyIdentifier.GetInstance( + Asn1Object.FromByteArray(ext.GetOctets())); + + if (id.GetKeyIdentifier() != null) + { + nextC = (X509CertificateEntry) chainCerts[new CertId(id.GetKeyIdentifier())]; + } + } + + if (nextC == null) + { + // + // no authority key id, try the Issuer DN + // + X509Name i = x509c.IssuerDN; + X509Name s = x509c.SubjectDN; + + if (!i.Equivalent(s)) + { + foreach (CertId certId in chainCerts.Keys) + { + X509CertificateEntry x509CertEntry = (X509CertificateEntry) chainCerts[certId]; + + X509Certificate crt = x509CertEntry.Certificate; + + X509Name sub = crt.SubjectDN; + if (sub.Equivalent(i)) + { + try + { + x509c.Verify(crt.GetPublicKey()); + + nextC = x509CertEntry; + break; + } + catch (InvalidKeyException) + { + // TODO What if it doesn't verify? + } + } + } + } + } + + cs.Add(c); + if (nextC != c) // self signed - end of the chain + { + c = nextC; + } + else + { + c = null; + } + } + + X509CertificateEntry[] result = new X509CertificateEntry[cs.Count]; + for (int i = 0; i < cs.Count; ++i) + { + result[i] = (X509CertificateEntry)cs[i]; + } + return result; + } + + return null; + } + + public void SetCertificateEntry( + string alias, + X509CertificateEntry certEntry) + { + if (alias == null) + throw new ArgumentNullException("alias"); + if (certEntry == null) + throw new ArgumentNullException("certEntry"); + if (keys[alias] != null) + throw new ArgumentException("There is a key entry with the name " + alias + "."); + + certs[alias] = certEntry; + chainCerts[new CertId(certEntry.Certificate.GetPublicKey())] = certEntry; + } + + public void SetKeyEntry( + string alias, + AsymmetricKeyEntry keyEntry, + X509CertificateEntry[] chain) + { + if (alias == null) + throw new ArgumentNullException("alias"); + if (keyEntry == null) + throw new ArgumentNullException("keyEntry"); + if (keyEntry.Key.IsPrivate && (chain == null)) + throw new ArgumentException("No certificate chain for private key"); + + if (keys[alias] != null) + { + DeleteEntry(alias); + } + + keys[alias] = keyEntry; + certs[alias] = chain[0]; + + for (int i = 0; i != chain.Length; i++) + { + chainCerts[new CertId(chain[i].Certificate.GetPublicKey())] = chain[i]; + } + } + + public void DeleteEntry( + string alias) + { + if (alias == null) + throw new ArgumentNullException("alias"); + + AsymmetricKeyEntry k = (AsymmetricKeyEntry)keys[alias]; + if (k != null) + { + keys.Remove(alias); + } + + X509CertificateEntry c = (X509CertificateEntry)certs[alias]; + + if (c != null) + { + certs.Remove(alias); + chainCerts.Remove(new CertId(c.Certificate.GetPublicKey())); + } + + if (k != null) + { + string id = (string)localIds[alias]; + if (id != null) + { + localIds.Remove(alias); + c = (X509CertificateEntry)keyCerts[id]; + } + if (c != null) + { + keyCerts.Remove(id); + chainCerts.Remove(new CertId(c.Certificate.GetPublicKey())); + } + } + + if (c == null && k == null) + { + throw new ArgumentException("no such entry as " + alias); + } + } + + public bool IsEntryOfType( + string alias, + Type entryType) + { + if (entryType == typeof(X509CertificateEntry)) + return IsCertificateEntry(alias); + + if (entryType == typeof(AsymmetricKeyEntry)) + return IsKeyEntry(alias) && GetCertificate(alias) != null; + + return false; + } + + [Obsolete("Use 'Count' property instead")] + public int Size() + { + return Count; + } + + public int Count + { + // TODO Seems a little inefficient + get { return GetAliasesTable().Count; } + } + + public void Save( + Stream stream, + char[] password, + SecureRandom random) + { + if (stream == null) + throw new ArgumentNullException("stream"); + if (password == null) + throw new ArgumentNullException("password"); + if (random == null) + throw new ArgumentNullException("random"); + + // + // handle the key + // + Asn1EncodableVector keyS = new Asn1EncodableVector(); + foreach (string name in keys.Keys) + { + byte[] kSalt = new byte[SaltSize]; + random.NextBytes(kSalt); + + AsymmetricKeyEntry privKey = (AsymmetricKeyEntry) keys[name]; + EncryptedPrivateKeyInfo kInfo = + EncryptedPrivateKeyInfoFactory.CreateEncryptedPrivateKeyInfo( + keyAlgorithm, password, kSalt, MinIterations, privKey.Key); + + Asn1EncodableVector kName = new Asn1EncodableVector(); + + foreach (string oid in privKey.BagAttributeKeys) + { + Asn1Encodable entry = privKey[oid]; + + // NB: Ignore any existing FriendlyName + if (oid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName.Id)) + continue; + + kName.Add( + new DerSequence( + new DerObjectIdentifier(oid), + new DerSet(entry))); + } + + // + // make sure we are using the local alias on store + // + // NB: We always set the FriendlyName based on 'name' + //if (privKey[PkcsObjectIdentifiers.Pkcs9AtFriendlyName] == null) + { + kName.Add( + new DerSequence( + PkcsObjectIdentifiers.Pkcs9AtFriendlyName, + new DerSet(new DerBmpString(name)))); + } + + // + // make sure we have a local key-id + // + if (privKey[PkcsObjectIdentifiers.Pkcs9AtLocalKeyID] == null) + { + X509CertificateEntry ct = GetCertificate(name); + AsymmetricKeyParameter pubKey = ct.Certificate.GetPublicKey(); + SubjectKeyIdentifier subjectKeyID = CreateSubjectKeyID(pubKey); + + kName.Add( + new DerSequence( + PkcsObjectIdentifiers.Pkcs9AtLocalKeyID, + new DerSet(subjectKeyID))); + } + + SafeBag kBag = new SafeBag(PkcsObjectIdentifiers.Pkcs8ShroudedKeyBag, kInfo.ToAsn1Object(), new DerSet(kName)); + keyS.Add(kBag); + } + + byte[] derEncodedBytes = new DerSequence(keyS).GetDerEncoded(); + + BerOctetString keyString = new BerOctetString(derEncodedBytes); + + // + // certificate processing + // + byte[] cSalt = new byte[SaltSize]; + + random.NextBytes(cSalt); + + Asn1EncodableVector certSeq = new Asn1EncodableVector(); + Pkcs12PbeParams cParams = new Pkcs12PbeParams(cSalt, MinIterations); + AlgorithmIdentifier cAlgId = new AlgorithmIdentifier(certAlgorithm, cParams.ToAsn1Object()); + ISet doneCerts = new HashSet(); + + foreach (string name in keys.Keys) + { + X509CertificateEntry certEntry = GetCertificate(name); + CertBag cBag = new CertBag( + PkcsObjectIdentifiers.X509Certificate, + new DerOctetString(certEntry.Certificate.GetEncoded())); + + Asn1EncodableVector fName = new Asn1EncodableVector(); + + foreach (string oid in certEntry.BagAttributeKeys) + { + Asn1Encodable entry = certEntry[oid]; + + // NB: Ignore any existing FriendlyName + if (oid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName.Id)) + continue; + + fName.Add( + new DerSequence( + new DerObjectIdentifier(oid), + new DerSet(entry))); + } + + // + // make sure we are using the local alias on store + // + // NB: We always set the FriendlyName based on 'name' + //if (certEntry[PkcsObjectIdentifiers.Pkcs9AtFriendlyName] == null) + { + fName.Add( + new DerSequence( + PkcsObjectIdentifiers.Pkcs9AtFriendlyName, + new DerSet(new DerBmpString(name)))); + } + + // + // make sure we have a local key-id + // + if (certEntry[PkcsObjectIdentifiers.Pkcs9AtLocalKeyID] == null) + { + AsymmetricKeyParameter pubKey = certEntry.Certificate.GetPublicKey(); + SubjectKeyIdentifier subjectKeyID = CreateSubjectKeyID(pubKey); + + fName.Add( + new DerSequence( + PkcsObjectIdentifiers.Pkcs9AtLocalKeyID, + new DerSet(subjectKeyID))); + } + + SafeBag sBag = new SafeBag( + PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName)); + + certSeq.Add(sBag); + + doneCerts.Add(certEntry.Certificate); + } + + foreach (string certId in certs.Keys) + { + X509CertificateEntry cert = (X509CertificateEntry)certs[certId]; + + if (keys[certId] != null) + continue; + + CertBag cBag = new CertBag( + PkcsObjectIdentifiers.X509Certificate, + new DerOctetString(cert.Certificate.GetEncoded())); + + Asn1EncodableVector fName = new Asn1EncodableVector(); + + foreach (string oid in cert.BagAttributeKeys) + { + // a certificate not immediately linked to a key doesn't require + // a localKeyID and will confuse some PKCS12 implementations. + // + // If we find one, we'll prune it out. + if (oid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID.Id)) + continue; + + Asn1Encodable entry = cert[oid]; + + // NB: Ignore any existing FriendlyName + if (oid.Equals(PkcsObjectIdentifiers.Pkcs9AtFriendlyName.Id)) + continue; + + fName.Add( + new DerSequence( + new DerObjectIdentifier(oid), + new DerSet(entry))); + } + + // + // make sure we are using the local alias on store + // + // NB: We always set the FriendlyName based on 'certId' + //if (cert[PkcsObjectIdentifiers.Pkcs9AtFriendlyName] == null) + { + fName.Add( + new DerSequence( + PkcsObjectIdentifiers.Pkcs9AtFriendlyName, + new DerSet(new DerBmpString(certId)))); + } + + SafeBag sBag = new SafeBag(PkcsObjectIdentifiers.CertBag, + cBag.ToAsn1Object(), new DerSet(fName)); + + certSeq.Add(sBag); + + doneCerts.Add(cert.Certificate); + } + + foreach (CertId certId in chainCerts.Keys) + { + X509CertificateEntry cert = (X509CertificateEntry)chainCerts[certId]; + + if (doneCerts.Contains(cert.Certificate)) + continue; + + CertBag cBag = new CertBag( + PkcsObjectIdentifiers.X509Certificate, + new DerOctetString(cert.Certificate.GetEncoded())); + + Asn1EncodableVector fName = new Asn1EncodableVector(); + + foreach (string oid in cert.BagAttributeKeys) + { + // a certificate not immediately linked to a key doesn't require + // a localKeyID and will confuse some PKCS12 implementations. + // + // If we find one, we'll prune it out. + if (oid.Equals(PkcsObjectIdentifiers.Pkcs9AtLocalKeyID.Id)) + continue; + + fName.Add( + new DerSequence( + new DerObjectIdentifier(oid), + new DerSet(cert[oid]))); + } + + SafeBag sBag = new SafeBag(PkcsObjectIdentifiers.CertBag, cBag.ToAsn1Object(), new DerSet(fName)); + + certSeq.Add(sBag); + } + + derEncodedBytes = new DerSequence(certSeq).GetDerEncoded(); + + byte[] certBytes = CryptPbeData(true, cAlgId, password, false, derEncodedBytes); + + EncryptedData cInfo = new EncryptedData(PkcsObjectIdentifiers.Data, cAlgId, new BerOctetString(certBytes)); + + ContentInfo[] info = new ContentInfo[] + { + new ContentInfo(PkcsObjectIdentifiers.Data, keyString), + new ContentInfo(PkcsObjectIdentifiers.EncryptedData, cInfo.ToAsn1Object()) + }; + + byte[] data = new AuthenticatedSafe(info).GetEncoded( + useDerEncoding ? Asn1Encodable.Der : Asn1Encodable.Ber); + + ContentInfo mainInfo = new ContentInfo(PkcsObjectIdentifiers.Data, new BerOctetString(data)); + + // + // create the mac + // + byte[] mSalt = new byte[20]; + random.NextBytes(mSalt); + + byte[] mac = CalculatePbeMac(OiwObjectIdentifiers.IdSha1, + mSalt, MinIterations, password, false, data); + + AlgorithmIdentifier algId = new AlgorithmIdentifier( + OiwObjectIdentifiers.IdSha1, DerNull.Instance); + DigestInfo dInfo = new DigestInfo(algId, mac); + + MacData mData = new MacData(dInfo, mSalt, MinIterations); + + // + // output the Pfx + // + Pfx pfx = new Pfx(mainInfo, mData); + + DerOutputStream derOut; + if (useDerEncoding) + { + derOut = new DerOutputStream(stream); + } + else + { + derOut = new BerOutputStream(stream); + } + + derOut.WriteObject(pfx); + } + + internal static byte[] CalculatePbeMac( + DerObjectIdentifier oid, + byte[] salt, + int itCount, + char[] password, + bool wrongPkcs12Zero, + byte[] data) + { + Asn1Encodable asn1Params = PbeUtilities.GenerateAlgorithmParameters( + oid, salt, itCount); + ICipherParameters cipherParams = PbeUtilities.GenerateCipherParameters( + oid, password, wrongPkcs12Zero, asn1Params); + + IMac mac = (IMac) PbeUtilities.CreateEngine(oid); + mac.Init(cipherParams); + mac.BlockUpdate(data, 0, data.Length); + return MacUtilities.DoFinal(mac); + } + + private static byte[] CryptPbeData( + bool forEncryption, + AlgorithmIdentifier algId, + char[] password, + bool wrongPkcs12Zero, + byte[] data) + { + Pkcs12PbeParams pbeParams = Pkcs12PbeParams.GetInstance(algId.Parameters); + ICipherParameters cipherParams = PbeUtilities.GenerateCipherParameters( + algId.ObjectID, password, wrongPkcs12Zero, pbeParams); + + IBufferedCipher cipher = PbeUtilities.CreateEngine(algId.ObjectID) as IBufferedCipher; + + if (cipher == null) + throw new Exception("Unknown encryption algorithm: " + algId.ObjectID); + + cipher.Init(forEncryption, cipherParams); + + return cipher.DoFinal(data); + } + + private class IgnoresCaseHashtable + : IEnumerable + { + private readonly IDictionary orig = Platform.CreateHashtable(); + private readonly IDictionary keys = Platform.CreateHashtable(); + + public void Clear() + { + orig.Clear(); + keys.Clear(); + } + + public IEnumerator GetEnumerator() + { + return orig.GetEnumerator(); + } + + public ICollection Keys + { + get { return orig.Keys; } + } + + public object Remove( + string alias) + { + string lower = alias.ToLowerInvariant(); + string k = (string) keys[lower]; + + if (k == null) + return null; + + keys.Remove(lower); + + object o = orig[k]; + orig.Remove(k); + return o; + } + + public object this[ + string alias] + { + get + { + string lower = alias.ToLowerInvariant(); + string k = (string) keys[lower]; + + if (k == null) + return null; + + return orig[k]; + } + set + { + string lower = alias.ToLowerInvariant(); + string k = (string) keys[lower]; + if (k != null) + { + orig.Remove(k); + } + keys[lower] = alias; + orig[alias] = value; + } + } + + public ICollection Values + { + get { return orig.Values; } + } + } + } +} diff --git a/Crypto/src/pkcs/Pkcs12Utilities.cs b/Crypto/src/pkcs/Pkcs12Utilities.cs new file mode 100644
index 000000000..d35c8b6a2 --- /dev/null +++ b/Crypto/src/pkcs/Pkcs12Utilities.cs
@@ -0,0 +1,77 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Pkcs +{ + /** + * Utility class for reencoding PKCS#12 files to definite length. + */ + public class Pkcs12Utilities + { + /** + * Just re-encode the outer layer of the PKCS#12 file to definite length encoding. + * + * @param berPKCS12File - original PKCS#12 file + * @return a byte array representing the DER encoding of the PFX structure + * @throws IOException + */ + public static byte[] ConvertToDefiniteLength( + byte[] berPkcs12File) + { + Pfx pfx = new Pfx(Asn1Sequence.GetInstance(Asn1Object.FromByteArray(berPkcs12File))); + + return pfx.GetEncoded(Asn1Encodable.Der); + } + + /** + * Re-encode the PKCS#12 structure to definite length encoding at the inner layer + * as well, recomputing the MAC accordingly. + * + * @param berPKCS12File - original PKCS12 file. + * @param provider - provider to use for MAC calculation. + * @return a byte array representing the DER encoding of the PFX structure. + * @throws IOException on parsing, encoding errors. + */ + public static byte[] ConvertToDefiniteLength( + byte[] berPkcs12File, + char[] passwd) + { + Pfx pfx = new Pfx(Asn1Sequence.GetInstance(Asn1Object.FromByteArray(berPkcs12File))); + + ContentInfo info = pfx.AuthSafe; + + Asn1OctetString content = Asn1OctetString.GetInstance(info.Content); + Asn1Object obj = Asn1Object.FromByteArray(content.GetOctets()); + + info = new ContentInfo(info.ContentType, new DerOctetString(obj.GetEncoded(Asn1Encodable.Der))); + + MacData mData = pfx.MacData; + + try + { + int itCount = mData.IterationCount.IntValue; + byte[] data = Asn1OctetString.GetInstance(info.Content).GetOctets(); + byte[] res = Pkcs12Store.CalculatePbeMac( + mData.Mac.AlgorithmID.ObjectID, mData.GetSalt(), itCount, passwd, false, data); + + AlgorithmIdentifier algId = new AlgorithmIdentifier( + mData.Mac.AlgorithmID.ObjectID, DerNull.Instance); + DigestInfo dInfo = new DigestInfo(algId, res); + + mData = new MacData(dInfo, mData.GetSalt(), itCount); + } + catch (Exception e) + { + throw new IOException("error constructing MAC: " + e.ToString()); + } + + pfx = new Pfx(info, mData); + + return pfx.GetEncoded(Asn1Encodable.Der); + } + } +} \ No newline at end of file diff --git a/Crypto/src/pkcs/PrivateKeyInfoFactory.cs b/Crypto/src/pkcs/PrivateKeyInfoFactory.cs new file mode 100644
index 000000000..ed566cae9 --- /dev/null +++ b/Crypto/src/pkcs/PrivateKeyInfoFactory.cs
@@ -0,0 +1,214 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Sec; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Pkcs +{ + public sealed class PrivateKeyInfoFactory + { + private PrivateKeyInfoFactory() + { + } + + public static PrivateKeyInfo CreatePrivateKeyInfo( + AsymmetricKeyParameter key) + { + if (key == null) + throw new ArgumentNullException("key"); + if (!key.IsPrivate) + throw new ArgumentException("Public key passed - private key expected", "key"); + + if (key is ElGamalPrivateKeyParameters) + { + ElGamalPrivateKeyParameters _key = (ElGamalPrivateKeyParameters)key; + return new PrivateKeyInfo( + new AlgorithmIdentifier( + OiwObjectIdentifiers.ElGamalAlgorithm, + new ElGamalParameter( + _key.Parameters.P, + _key.Parameters.G).ToAsn1Object()), + new DerInteger(_key.X)); + } + + if (key is DsaPrivateKeyParameters) + { + DsaPrivateKeyParameters _key = (DsaPrivateKeyParameters)key; + return new PrivateKeyInfo( + new AlgorithmIdentifier( + X9ObjectIdentifiers.IdDsa, + new DsaParameter( + _key.Parameters.P, + _key.Parameters.Q, + _key.Parameters.G).ToAsn1Object()), + new DerInteger(_key.X)); + } + + if (key is DHPrivateKeyParameters) + { + DHPrivateKeyParameters _key = (DHPrivateKeyParameters)key; + + DHParameter p = new DHParameter( + _key.Parameters.P, _key.Parameters.G, _key.Parameters.L); + + return new PrivateKeyInfo( + new AlgorithmIdentifier(_key.AlgorithmOid, p.ToAsn1Object()), + new DerInteger(_key.X)); + } + + if (key is RsaKeyParameters) + { + AlgorithmIdentifier algID = new AlgorithmIdentifier( + PkcsObjectIdentifiers.RsaEncryption, DerNull.Instance); + + RsaPrivateKeyStructure keyStruct; + if (key is RsaPrivateCrtKeyParameters) + { + RsaPrivateCrtKeyParameters _key = (RsaPrivateCrtKeyParameters)key; + + keyStruct = new RsaPrivateKeyStructure( + _key.Modulus, + _key.PublicExponent, + _key.Exponent, + _key.P, + _key.Q, + _key.DP, + _key.DQ, + _key.QInv); + } + else + { + RsaKeyParameters _key = (RsaKeyParameters) key; + + keyStruct = new RsaPrivateKeyStructure( + _key.Modulus, + BigInteger.Zero, + _key.Exponent, + BigInteger.Zero, + BigInteger.Zero, + BigInteger.Zero, + BigInteger.Zero, + BigInteger.Zero); + } + + return new PrivateKeyInfo(algID, keyStruct.ToAsn1Object()); + } + + if (key is ECPrivateKeyParameters) + { + ECPrivateKeyParameters _key = (ECPrivateKeyParameters)key; + AlgorithmIdentifier algID; + ECPrivateKeyStructure ec; + + if (_key.AlgorithmName == "ECGOST3410") + { + if (_key.PublicKeyParamSet == null) + throw Platform.CreateNotImplementedException("Not a CryptoPro parameter set"); + + Gost3410PublicKeyAlgParameters gostParams = new Gost3410PublicKeyAlgParameters( + _key.PublicKeyParamSet, CryptoProObjectIdentifiers.GostR3411x94CryptoProParamSet); + + algID = new AlgorithmIdentifier( + CryptoProObjectIdentifiers.GostR3410x2001, + gostParams.ToAsn1Object()); + + // TODO Do we need to pass any parameters here? + ec = new ECPrivateKeyStructure(_key.D); + } + else + { + X962Parameters x962; + if (_key.PublicKeyParamSet == null) + { + ECDomainParameters kp = _key.Parameters; + X9ECParameters ecP = new X9ECParameters(kp.Curve, kp.G, kp.N, kp.H, kp.GetSeed()); + + x962 = new X962Parameters(ecP); + } + else + { + x962 = new X962Parameters(_key.PublicKeyParamSet); + } + + Asn1Object x962Object = x962.ToAsn1Object(); + + // TODO Possible to pass the publicKey bitstring here? + ec = new ECPrivateKeyStructure(_key.D, x962Object); + + algID = new AlgorithmIdentifier(X9ObjectIdentifiers.IdECPublicKey, x962Object); + } + + return new PrivateKeyInfo(algID, ec.ToAsn1Object()); + } + + if (key is Gost3410PrivateKeyParameters) + { + Gost3410PrivateKeyParameters _key = (Gost3410PrivateKeyParameters)key; + + if (_key.PublicKeyParamSet == null) + throw Platform.CreateNotImplementedException("Not a CryptoPro parameter set"); + + byte[] keyEnc = _key.X.ToByteArrayUnsigned(); + byte[] keyBytes = new byte[keyEnc.Length]; + + for (int i = 0; i != keyBytes.Length; i++) + { + keyBytes[i] = keyEnc[keyEnc.Length - 1 - i]; // must be little endian + } + + Gost3410PublicKeyAlgParameters algParams = new Gost3410PublicKeyAlgParameters( + _key.PublicKeyParamSet, CryptoProObjectIdentifiers.GostR3411x94CryptoProParamSet, null); + + AlgorithmIdentifier algID = new AlgorithmIdentifier( + CryptoProObjectIdentifiers.GostR3410x94, + algParams.ToAsn1Object()); + + return new PrivateKeyInfo(algID, new DerOctetString(keyBytes)); + } + + throw new ArgumentException("Class provided is not convertible: " + key.GetType().FullName); + } + + public static PrivateKeyInfo CreatePrivateKeyInfo( + char[] passPhrase, + EncryptedPrivateKeyInfo encInfo) + { + return CreatePrivateKeyInfo(passPhrase, false, encInfo); + } + + public static PrivateKeyInfo CreatePrivateKeyInfo( + char[] passPhrase, + bool wrongPkcs12Zero, + EncryptedPrivateKeyInfo encInfo) + { + AlgorithmIdentifier algID = encInfo.EncryptionAlgorithm; + IBufferedCipher cipher = PbeUtilities.CreateEngine(algID) as IBufferedCipher; + + if (cipher == null) + { + // TODO Throw exception? + } + + ICipherParameters keyParameters = PbeUtilities.GenerateCipherParameters( + algID, passPhrase, wrongPkcs12Zero); + + cipher.Init(false, keyParameters); + + byte[] keyBytes = encInfo.GetEncryptedData(); + byte[] encoding = cipher.DoFinal(keyBytes); + Asn1Object asn1Data = Asn1Object.FromByteArray(encoding); + + return PrivateKeyInfo.GetInstance(asn1Data); + } + } +} diff --git a/Crypto/src/pkcs/X509CertificateEntry.cs b/Crypto/src/pkcs/X509CertificateEntry.cs new file mode 100644
index 000000000..2f81dd87b --- /dev/null +++ b/Crypto/src/pkcs/X509CertificateEntry.cs
@@ -0,0 +1,60 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Pkcs +{ + public class X509CertificateEntry + : Pkcs12Entry + { + private readonly X509Certificate cert; + + public X509CertificateEntry( + X509Certificate cert) + : base(Platform.CreateHashtable()) + { + this.cert = cert; + } + +#if !(SILVERLIGHT || PORTABLE) + [Obsolete] + public X509CertificateEntry( + X509Certificate cert, + Hashtable attributes) + : base(attributes) + { + this.cert = cert; + } +#endif + + public X509CertificateEntry( + X509Certificate cert, + IDictionary attributes) + : base(attributes) + { + this.cert = cert; + } + + public X509Certificate Certificate + { + get { return this.cert; } + } + + public override bool Equals(object obj) + { + X509CertificateEntry other = obj as X509CertificateEntry; + + if (other == null) + return false; + + return cert.Equals(other.cert); + } + + public override int GetHashCode() + { + return ~cert.GetHashCode(); + } + } +} diff --git a/Crypto/src/pkix/CertStatus.cs b/Crypto/src/pkix/CertStatus.cs new file mode 100644
index 000000000..4f40b7bc6 --- /dev/null +++ b/Crypto/src/pkix/CertStatus.cs
@@ -0,0 +1,35 @@ +using System; + +using Org.BouncyCastle.Utilities.Date; + +namespace Org.BouncyCastle.Pkix +{ + public class CertStatus + { + public const int Unrevoked = 11; + + public const int Undetermined = 12; + + private int status = Unrevoked; + + DateTimeObject revocationDate = null; + + /// <summary> + /// Returns the revocationDate. + /// </summary> + public DateTimeObject RevocationDate + { + get { return revocationDate; } + set { this.revocationDate = value; } + } + + /// <summary> + /// Returns the certStatus. + /// </summary> + public int Status + { + get { return status; } + set { this.status = value; } + } + } +} diff --git a/Crypto/src/pkix/PkixAttrCertChecker.cs b/Crypto/src/pkix/PkixAttrCertChecker.cs new file mode 100644
index 000000000..a6eab8480 --- /dev/null +++ b/Crypto/src/pkix/PkixAttrCertChecker.cs
@@ -0,0 +1,57 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Pkix +{ + public abstract class PkixAttrCertChecker + { + /** + * Returns an immutable <code>Set</code> of X.509 attribute certificate + * extensions that this <code>PkixAttrCertChecker</code> supports or + * <code>null</code> if no extensions are supported. + * <p> + * Each element of the set is a <code>String</code> representing the + * Object Identifier (OID) of the X.509 extension that is supported. + * </p> + * <p> + * All X.509 attribute certificate extensions that a + * <code>PkixAttrCertChecker</code> might possibly be able to process + * should be included in the set. + * </p> + * + * @return an immutable <code>Set</code> of X.509 extension OIDs (in + * <code>String</code> format) supported by this + * <code>PkixAttrCertChecker</code>, or <code>null</code> if no + * extensions are supported + */ + public abstract ISet GetSupportedExtensions(); + + /** + * Performs checks on the specified attribute certificate. Every handled + * extension is rmeoved from the <code>unresolvedCritExts</code> + * collection. + * + * @param attrCert The attribute certificate to be checked. + * @param certPath The certificate path which belongs to the attribute + * certificate issuer public key certificate. + * @param holderCertPath The certificate path which belongs to the holder + * certificate. + * @param unresolvedCritExts a <code>Collection</code> of OID strings + * representing the current set of unresolved critical extensions + * @throws CertPathValidatorException if the specified attribute certificate + * does not pass the check. + */ + public abstract void Check(IX509AttributeCertificate attrCert, PkixCertPath certPath, + PkixCertPath holderCertPath, ICollection unresolvedCritExts); + + /** + * Returns a clone of this object. + * + * @return a copy of this <code>PkixAttrCertChecker</code> + */ + public abstract PkixAttrCertChecker Clone(); + } +} diff --git a/Crypto/src/pkix/PkixAttrCertPathBuilder.cs b/Crypto/src/pkix/PkixAttrCertPathBuilder.cs new file mode 100644
index 000000000..646cc5db5 --- /dev/null +++ b/Crypto/src/pkix/PkixAttrCertPathBuilder.cs
@@ -0,0 +1,215 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.Pkix +{ + public class PkixAttrCertPathBuilder + { + /** + * Build and validate a CertPath using the given parameter. + * + * @param params PKIXBuilderParameters object containing all information to + * build the CertPath + */ + public virtual PkixCertPathBuilderResult Build( + PkixBuilderParameters pkixParams) + { + // search target certificates + + IX509Selector certSelect = pkixParams.GetTargetConstraints(); + if (!(certSelect is X509AttrCertStoreSelector)) + { + throw new PkixCertPathBuilderException( + "TargetConstraints must be an instance of " + + typeof(X509AttrCertStoreSelector).FullName + + " for " + + typeof(PkixAttrCertPathBuilder).FullName + " class."); + } + + ICollection targets; + try + { + targets = PkixCertPathValidatorUtilities.FindCertificates( + (X509AttrCertStoreSelector)certSelect, pkixParams.GetStores()); + } + catch (Exception e) + { + throw new PkixCertPathBuilderException("Error finding target attribute certificate.", e); + } + + if (targets.Count == 0) + { + throw new PkixCertPathBuilderException( + "No attribute certificate found matching targetContraints."); + } + + PkixCertPathBuilderResult result = null; + + // check all potential target certificates + foreach (IX509AttributeCertificate cert in targets) + { + X509CertStoreSelector selector = new X509CertStoreSelector(); + X509Name[] principals = cert.Issuer.GetPrincipals(); + ISet issuers = new HashSet(); + for (int i = 0; i < principals.Length; i++) + { + try + { + selector.Subject = principals[i]; + + issuers.AddAll(PkixCertPathValidatorUtilities.FindCertificates(selector, pkixParams.GetStores())); + } + catch (Exception e) + { + throw new PkixCertPathBuilderException( + "Public key certificate for attribute certificate cannot be searched.", + e); + } + } + + if (issuers.IsEmpty) + throw new PkixCertPathBuilderException("Public key certificate for attribute certificate cannot be found."); + + IList certPathList = Platform.CreateArrayList(); + + foreach (X509Certificate issuer in issuers) + { + result = Build(cert, issuer, pkixParams, certPathList); + + if (result != null) + break; + } + + if (result != null) + break; + } + + if (result == null && certPathException != null) + { + throw new PkixCertPathBuilderException( + "Possible certificate chain could not be validated.", + certPathException); + } + + if (result == null && certPathException == null) + { + throw new PkixCertPathBuilderException( + "Unable to find certificate chain."); + } + + return result; + } + + private Exception certPathException; + + private PkixCertPathBuilderResult Build( + IX509AttributeCertificate attrCert, + X509Certificate tbvCert, + PkixBuilderParameters pkixParams, + IList tbvPath) + { + // If tbvCert is readily present in tbvPath, it indicates having run + // into a cycle in the + // PKI graph. + if (tbvPath.Contains(tbvCert)) + return null; + + // step out, the certificate is not allowed to appear in a certification + // chain + if (pkixParams.GetExcludedCerts().Contains(tbvCert)) + return null; + + // test if certificate path exceeds maximum length + if (pkixParams.MaxPathLength != -1) + { + if (tbvPath.Count - 1 > pkixParams.MaxPathLength) + return null; + } + + tbvPath.Add(tbvCert); + + PkixCertPathBuilderResult builderResult = null; + +// X509CertificateParser certParser = new X509CertificateParser(); + PkixAttrCertPathValidator validator = new PkixAttrCertPathValidator(); + + try + { + // check whether the issuer of <tbvCert> is a TrustAnchor + if (PkixCertPathValidatorUtilities.FindTrustAnchor(tbvCert, pkixParams.GetTrustAnchors()) != null) + { + PkixCertPath certPath = new PkixCertPath(tbvPath); + PkixCertPathValidatorResult result; + + try + { + result = validator.Validate(certPath, pkixParams); + } + catch (Exception e) + { + throw new Exception("Certification path could not be validated.", e); + } + + return new PkixCertPathBuilderResult(certPath, result.TrustAnchor, + result.PolicyTree, result.SubjectPublicKey); + } + else + { + // add additional X.509 stores from locations in certificate + try + { + PkixCertPathValidatorUtilities.AddAdditionalStoresFromAltNames(tbvCert, pkixParams); + } + catch (CertificateParsingException e) + { + throw new Exception("No additional X.509 stores can be added from certificate locations.", e); + } + + // try to get the issuer certificate from one of the stores + ISet issuers = new HashSet(); + try + { + issuers.AddAll(PkixCertPathValidatorUtilities.FindIssuerCerts(tbvCert, pkixParams)); + } + catch (Exception e) + { + throw new Exception("Cannot find issuer certificate for certificate in certification path.", e); + } + + if (issuers.IsEmpty) + throw new Exception("No issuer certificate for certificate in certification path found."); + + foreach (X509Certificate issuer in issuers) + { + // if untrusted self signed certificate continue + if (PkixCertPathValidatorUtilities.IsSelfIssued(issuer)) + continue; + + builderResult = Build(attrCert, issuer, pkixParams, tbvPath); + + if (builderResult != null) + break; + } + } + } + catch (Exception e) + { + certPathException = new Exception("No valid certification path could be build.", e); + } + + if (builderResult == null) + { + tbvPath.Remove(tbvCert); + } + + return builderResult; + } + } +} diff --git a/Crypto/src/pkix/PkixAttrCertPathValidator.cs b/Crypto/src/pkix/PkixAttrCertPathValidator.cs new file mode 100644
index 000000000..5f53bcde6 --- /dev/null +++ b/Crypto/src/pkix/PkixAttrCertPathValidator.cs
@@ -0,0 +1,76 @@ +using System; + +using Org.BouncyCastle.X509; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.Pkix +{ + /** + * CertPathValidatorSpi implementation for X.509 Attribute Certificates la RFC 3281. + * + * @see org.bouncycastle.x509.ExtendedPkixParameters + */ + public class PkixAttrCertPathValidator + // extends CertPathValidatorSpi + { + /** + * Validates an attribute certificate with the given certificate path. + * + * <p> + * <code>params</code> must be an instance of + * <code>ExtendedPkixParameters</code>. + * </p><p> + * The target constraints in the <code>params</code> must be an + * <code>X509AttrCertStoreSelector</code> with at least the attribute + * certificate criterion set. Obey that also target informations may be + * necessary to correctly validate this attribute certificate. + * </p><p> + * The attribute certificate issuer must be added to the trusted attribute + * issuers with {@link ExtendedPkixParameters#setTrustedACIssuers(Set)}. + * </p> + * @param certPath The certificate path which belongs to the attribute + * certificate issuer public key certificate. + * @param params The PKIX parameters. + * @return A <code>PKIXCertPathValidatorResult</code> of the result of + * validating the <code>certPath</code>. + * @throws InvalidAlgorithmParameterException if <code>params</code> is + * inappropriate for this validator. + * @throws CertPathValidatorException if the verification fails. + */ + public virtual PkixCertPathValidatorResult Validate( + PkixCertPath certPath, + PkixParameters pkixParams) + { + IX509Selector certSelect = pkixParams.GetTargetConstraints(); + if (!(certSelect is X509AttrCertStoreSelector)) + { + throw new ArgumentException( + "TargetConstraints must be an instance of " + typeof(X509AttrCertStoreSelector).FullName, + "pkixParams"); + } + IX509AttributeCertificate attrCert = ((X509AttrCertStoreSelector) certSelect).AttributeCert; + + PkixCertPath holderCertPath = Rfc3281CertPathUtilities.ProcessAttrCert1(attrCert, pkixParams); + PkixCertPathValidatorResult result = Rfc3281CertPathUtilities.ProcessAttrCert2(certPath, pkixParams); + X509Certificate issuerCert = (X509Certificate)certPath.Certificates[0]; + Rfc3281CertPathUtilities.ProcessAttrCert3(issuerCert, pkixParams); + Rfc3281CertPathUtilities.ProcessAttrCert4(issuerCert, pkixParams); + Rfc3281CertPathUtilities.ProcessAttrCert5(attrCert, pkixParams); + // 6 already done in X509AttrCertStoreSelector + Rfc3281CertPathUtilities.ProcessAttrCert7(attrCert, certPath, holderCertPath, pkixParams); + Rfc3281CertPathUtilities.AdditionalChecks(attrCert, pkixParams); + DateTime date; + try + { + date = PkixCertPathValidatorUtilities.GetValidCertDateFromValidityModel(pkixParams, null, -1); + } + catch (Exception e) + { + throw new PkixCertPathValidatorException( + "Could not get validity date from attribute certificate.", e); + } + Rfc3281CertPathUtilities.CheckCrls(attrCert, pkixParams, issuerCert, date, certPath.Certificates); + return result; + } + } +} diff --git a/Crypto/src/pkix/PkixBuilderParameters.cs b/Crypto/src/pkix/PkixBuilderParameters.cs new file mode 100644
index 000000000..32fc04360 --- /dev/null +++ b/Crypto/src/pkix/PkixBuilderParameters.cs
@@ -0,0 +1,140 @@ +using System; +using System.Text; + +using Org.BouncyCastle.Security; +using Org.BouncyCastle.X509.Store; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Pkix +{ + /// <summary> + /// Summary description for PkixBuilderParameters. + /// </summary> + public class PkixBuilderParameters + : PkixParameters + { + private int maxPathLength = 5; + + private ISet excludedCerts = new HashSet(); + + /** + * Returns an instance of <code>PkixBuilderParameters</code>. + * <p> + * This method can be used to get a copy from other + * <code>PKIXBuilderParameters</code>, <code>PKIXParameters</code>, + * and <code>ExtendedPKIXParameters</code> instances. + * </p> + * + * @param pkixParams The PKIX parameters to create a copy of. + * @return An <code>PkixBuilderParameters</code> instance. + */ + public static PkixBuilderParameters GetInstance( + PkixParameters pkixParams) + { + PkixBuilderParameters parameters = new PkixBuilderParameters( + pkixParams.GetTrustAnchors(), + new X509CertStoreSelector(pkixParams.GetTargetCertConstraints())); + parameters.SetParams(pkixParams); + return parameters; + } + + public PkixBuilderParameters( + ISet trustAnchors, + IX509Selector targetConstraints) + : base(trustAnchors) + { + SetTargetCertConstraints(targetConstraints); + } + + public virtual int MaxPathLength + { + get { return maxPathLength; } + set + { + if (value < -1) + { + throw new InvalidParameterException( + "The maximum path length parameter can not be less than -1."); + } + this.maxPathLength = value; + } + } + + /// <summary> + /// Excluded certificates are not used for building a certification path. + /// </summary> + /// <returns>the excluded certificates.</returns> + public virtual ISet GetExcludedCerts() + { + return new HashSet(excludedCerts); + } + + /// <summary> + /// Sets the excluded certificates which are not used for building a + /// certification path. If the <code>ISet</code> is <code>null</code> an + /// empty set is assumed. + /// </summary> + /// <remarks> + /// The given set is cloned to protect it against subsequent modifications. + /// </remarks> + /// <param name="excludedCerts">The excluded certificates to set.</param> + public virtual void SetExcludedCerts( + ISet excludedCerts) + { + if (excludedCerts == null) + { + excludedCerts = new HashSet(); + } + else + { + this.excludedCerts = new HashSet(excludedCerts); + } + } + + /** + * Can alse handle <code>ExtendedPKIXBuilderParameters</code> and + * <code>PKIXBuilderParameters</code>. + * + * @param params Parameters to set. + * @see org.bouncycastle.x509.ExtendedPKIXParameters#setParams(java.security.cert.PKIXParameters) + */ + protected override void SetParams( + PkixParameters parameters) + { + base.SetParams(parameters); + if (parameters is PkixBuilderParameters) + { + PkixBuilderParameters _params = (PkixBuilderParameters) parameters; + maxPathLength = _params.maxPathLength; + excludedCerts = new HashSet(_params.excludedCerts); + } + } + + /** + * Makes a copy of this <code>PKIXParameters</code> object. Changes to the + * copy will not affect the original and vice versa. + * + * @return a copy of this <code>PKIXParameters</code> object + */ + public override object Clone() + { + PkixBuilderParameters parameters = new PkixBuilderParameters( + GetTrustAnchors(), GetTargetCertConstraints()); + parameters.SetParams(this); + return parameters; + } + + public override string ToString() + { + string nl = Platform.NewLine; + StringBuilder s = new StringBuilder(); + s.Append("PkixBuilderParameters [" + nl); + s.Append(base.ToString()); + s.Append(" Maximum Path Length: "); + s.Append(MaxPathLength); + s.Append(nl + "]" + nl); + return s.ToString(); + } + } +} diff --git a/Crypto/src/pkix/PkixCertPath.cs b/Crypto/src/pkix/PkixCertPath.cs new file mode 100644
index 000000000..e13e50995 --- /dev/null +++ b/Crypto/src/pkix/PkixCertPath.cs
@@ -0,0 +1,460 @@ +using System; +using System.Collections; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Cms; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.OpenSsl; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Pkix +{ + /** + * An immutable sequence of certificates (a certification path).<br /> + * <br /> + * This is an abstract class that defines the methods common to all CertPaths. + * Subclasses can handle different kinds of certificates (X.509, PGP, etc.).<br /> + * <br /> + * All CertPath objects have a type, a list of Certificates, and one or more + * supported encodings. Because the CertPath class is immutable, a CertPath + * cannot change in any externally visible way after being constructed. This + * stipulation applies to all public fields and methods of this class and any + * added or overridden by subclasses.<br /> + * <br /> + * The type is a string that identifies the type of Certificates in the + * certification path. For each certificate cert in a certification path + * certPath, cert.getType().equals(certPath.getType()) must be true.<br /> + * <br /> + * The list of Certificates is an ordered List of zero or more Certificates. + * This List and all of the Certificates contained in it must be immutable.<br /> + * <br /> + * Each CertPath object must support one or more encodings so that the object + * can be translated into a byte array for storage or transmission to other + * parties. Preferably, these encodings should be well-documented standards + * (such as PKCS#7). One of the encodings supported by a CertPath is considered + * the default encoding. This encoding is used if no encoding is explicitly + * requested (for the {@link #getEncoded()} method, for instance).<br /> + * <br /> + * All CertPath objects are also Serializable. CertPath objects are resolved + * into an alternate {@link CertPathRep} object during serialization. This + * allows a CertPath object to be serialized into an equivalent representation + * regardless of its underlying implementation.<br /> + * <br /> + * CertPath objects can be created with a CertificateFactory or they can be + * returned by other classes, such as a CertPathBuilder.<br /> + * <br /> + * By convention, X.509 CertPaths (consisting of X509Certificates), are ordered + * starting with the target certificate and ending with a certificate issued by + * the trust anchor. That is, the issuer of one certificate is the subject of + * the following one. The certificate representing the + * {@link TrustAnchor TrustAnchor} should not be included in the certification + * path. Unvalidated X.509 CertPaths may not follow these conventions. PKIX + * CertPathValidators will detect any departure from these conventions that + * cause the certification path to be invalid and throw a + * CertPathValidatorException.<br /> + * <br /> + * <strong>Concurrent Access</strong><br /> + * <br /> + * All CertPath objects must be thread-safe. That is, multiple threads may + * concurrently invoke the methods defined in this class on a single CertPath + * object (or more than one) with no ill effects. This is also true for the List + * returned by CertPath.getCertificates.<br /> + * <br /> + * Requiring CertPath objects to be immutable and thread-safe allows them to be + * passed around to various pieces of code without worrying about coordinating + * access. Providing this thread-safety is generally not difficult, since the + * CertPath and List objects in question are immutable. + * + * @see CertificateFactory + * @see CertPathBuilder + */ + /// <summary> + /// CertPath implementation for X.509 certificates. + /// </summary> + public class PkixCertPath +// : CertPath + { + internal static readonly IList certPathEncodings; + + static PkixCertPath() + { + IList encodings = Platform.CreateArrayList(); + encodings.Add("PkiPath"); + encodings.Add("PEM"); + encodings.Add("PKCS7"); + certPathEncodings = CollectionUtilities.ReadOnly(encodings); + } + + private readonly IList certificates; + + /** + * @param certs + */ + private static IList SortCerts( + IList certs) + { + if (certs.Count < 2) + return certs; + + X509Name issuer = ((X509Certificate)certs[0]).IssuerDN; + bool okay = true; + + for (int i = 1; i != certs.Count; i++) + { + X509Certificate cert = (X509Certificate)certs[i]; + + if (issuer.Equivalent(cert.SubjectDN, true)) + { + issuer = ((X509Certificate)certs[i]).IssuerDN; + } + else + { + okay = false; + break; + } + } + + if (okay) + return certs; + + // find end-entity cert + IList retList = Platform.CreateArrayList(certs.Count); + IList orig = Platform.CreateArrayList(certs); + + for (int i = 0; i < certs.Count; i++) + { + X509Certificate cert = (X509Certificate)certs[i]; + bool found = false; + + X509Name subject = cert.SubjectDN; + foreach (X509Certificate c in certs) + { + if (c.IssuerDN.Equivalent(subject, true)) + { + found = true; + break; + } + } + + if (!found) + { + retList.Add(cert); + certs.RemoveAt(i); + } + } + + // can only have one end entity cert - something's wrong, give up. + if (retList.Count > 1) + return orig; + + for (int i = 0; i != retList.Count; i++) + { + issuer = ((X509Certificate)retList[i]).IssuerDN; + + for (int j = 0; j < certs.Count; j++) + { + X509Certificate c = (X509Certificate)certs[j]; + if (issuer.Equivalent(c.SubjectDN, true)) + { + retList.Add(c); + certs.RemoveAt(j); + break; + } + } + } + + // make sure all certificates are accounted for. + if (certs.Count > 0) + return orig; + + return retList; + } + + /** + * Creates a CertPath of the specified type. + * This constructor is protected because most users should use + * a CertificateFactory to create CertPaths. + * @param type the standard name of the type of Certificatesin this path + **/ + public PkixCertPath( + ICollection certificates) +// : base("X.509") + { + this.certificates = SortCerts(Platform.CreateArrayList(certificates)); + } + + public PkixCertPath( + Stream inStream) + : this(inStream, "PkiPath") + { + } + + /** + * Creates a CertPath of the specified type. + * This constructor is protected because most users should use + * a CertificateFactory to create CertPaths. + * + * @param type the standard name of the type of Certificatesin this path + **/ + public PkixCertPath( + Stream inStream, + string encoding) +// : base("X.509") + { + string upper = encoding.ToUpper(); + + IList certs; + try + { + if (upper.Equals("PkiPath".ToUpper())) + { + Asn1InputStream derInStream = new Asn1InputStream(inStream); + Asn1Object derObject = derInStream.ReadObject(); + if (!(derObject is Asn1Sequence)) + { + throw new CertificateException( + "input stream does not contain a ASN1 SEQUENCE while reading PkiPath encoded data to load CertPath"); + } + + certs = Platform.CreateArrayList(); + + foreach (Asn1Encodable ae in (Asn1Sequence)derObject) + { + byte[] derBytes = ae.GetEncoded(Asn1Encodable.Der); + Stream certInStream = new MemoryStream(derBytes, false); + + // TODO Is inserting at the front important (list will be sorted later anyway)? + certs.Insert(0, new X509CertificateParser().ReadCertificate(certInStream)); + } + } + else if (upper.Equals("PKCS7") || upper.Equals("PEM")) + { + certs = Platform.CreateArrayList(new X509CertificateParser().ReadCertificates(inStream)); + } + else + { + throw new CertificateException("unsupported encoding: " + encoding); + } + } + catch (IOException ex) + { + throw new CertificateException( + "IOException throw while decoding CertPath:\n" + + ex.ToString()); + } + + this.certificates = SortCerts(certs); + } + + /** + * Returns an iteration of the encodings supported by this + * certification path, with the default encoding + * first. Attempts to modify the returned Iterator via its + * remove method result in an UnsupportedOperationException. + * + * @return an Iterator over the names of the supported encodings (as Strings) + **/ + public virtual IEnumerable Encodings + { + get { return new EnumerableProxy(certPathEncodings); } + } + + /** + * Compares this certification path for equality with the specified object. + * Two CertPaths are equal if and only if their types are equal and their + * certificate Lists (and by implication the Certificates in those Lists) + * are equal. A CertPath is never equal to an object that is not a CertPath.<br /> + * <br /> + * This algorithm is implemented by this method. If it is overridden, the + * behavior specified here must be maintained. + * + * @param other + * the object to test for equality with this certification path + * + * @return true if the specified object is equal to this certification path, + * false otherwise + * + * @see Object#hashCode() Object.hashCode() + */ + public override bool Equals( + object obj) + { + if (this == obj) + return true; + + PkixCertPath other = obj as PkixCertPath; + if (other == null) + return false; + +// if (!this.Type.Equals(other.Type)) +// return false; + + //return this.Certificates.Equals(other.Certificates); + + // TODO Extract this to a utility class + IList thisCerts = this.Certificates; + IList otherCerts = other.Certificates; + + if (thisCerts.Count != otherCerts.Count) + return false; + + IEnumerator e1 = thisCerts.GetEnumerator(); + IEnumerator e2 = thisCerts.GetEnumerator(); + + while (e1.MoveNext()) + { + e2.MoveNext(); + + if (!Platform.Equals(e1.Current, e2.Current)) + return false; + } + + return true; + } + + public override int GetHashCode() + { + // FIXME? + return this.Certificates.GetHashCode(); + } + + /** + * Returns the encoded form of this certification path, using + * the default encoding. + * + * @return the encoded bytes + * @exception CertificateEncodingException if an encoding error occurs + **/ + public virtual byte[] GetEncoded() + { + foreach (object enc in Encodings) + { + if (enc is string) + { + return GetEncoded((string)enc); + } + } + return null; + } + + /** + * Returns the encoded form of this certification path, using + * the specified encoding. + * + * @param encoding the name of the encoding to use + * @return the encoded bytes + * @exception CertificateEncodingException if an encoding error + * occurs or the encoding requested is not supported + * + */ + public virtual byte[] GetEncoded( + string encoding) + { + if (Platform.CompareIgnoreCase(encoding, "PkiPath") == 0) + { + Asn1EncodableVector v = new Asn1EncodableVector(); + + for (int i = certificates.Count - 1; i >= 0; i--) + { + v.Add(ToAsn1Object((X509Certificate) certificates[i])); + } + + return ToDerEncoded(new DerSequence(v)); + } + else if (Platform.CompareIgnoreCase(encoding, "PKCS7") == 0) + { + Asn1.Pkcs.ContentInfo encInfo = new Asn1.Pkcs.ContentInfo( + PkcsObjectIdentifiers.Data, null); + + Asn1EncodableVector v = new Asn1EncodableVector(); + for (int i = 0; i != certificates.Count; i++) + { + v.Add(ToAsn1Object((X509Certificate)certificates[i])); + } + + Asn1.Pkcs.SignedData sd = new Asn1.Pkcs.SignedData( + new DerInteger(1), + new DerSet(), + encInfo, + new DerSet(v), + null, + new DerSet()); + + return ToDerEncoded(new Asn1.Pkcs.ContentInfo(PkcsObjectIdentifiers.SignedData, sd)); + } + else if (Platform.CompareIgnoreCase(encoding, "PEM") == 0) + { + MemoryStream bOut = new MemoryStream(); + PemWriter pWrt = new PemWriter(new StreamWriter(bOut)); + + try + { + for (int i = 0; i != certificates.Count; i++) + { + pWrt.WriteObject(certificates[i]); + } + + pWrt.Writer.Dispose(); + } + catch (Exception) + { + throw new CertificateEncodingException("can't encode certificate for PEM encoded path"); + } + + return bOut.ToArray(); + } + else + { + throw new CertificateEncodingException("unsupported encoding: " + encoding); + } + } + + /// <summary> + /// Returns the list of certificates in this certification + /// path. + /// </summary> + public virtual IList Certificates + { + get { return CollectionUtilities.ReadOnly(certificates); } + } + + /** + * Return a DERObject containing the encoded certificate. + * + * @param cert the X509Certificate object to be encoded + * + * @return the DERObject + **/ + private Asn1Object ToAsn1Object( + X509Certificate cert) + { + try + { + return Asn1Object.FromByteArray(cert.GetEncoded()); + } + catch (Exception e) + { + throw new CertificateEncodingException("Exception while encoding certificate", e); + } + } + + private byte[] ToDerEncoded(Asn1Encodable obj) + { + try + { + return obj.GetEncoded(Asn1Encodable.Der); + } + catch (IOException e) + { + throw new CertificateEncodingException("Exception thrown", e); + } + } + } +} diff --git a/Crypto/src/pkix/PkixCertPathBuilder.cs b/Crypto/src/pkix/PkixCertPathBuilder.cs new file mode 100644
index 000000000..7082fe409 --- /dev/null +++ b/Crypto/src/pkix/PkixCertPathBuilder.cs
@@ -0,0 +1,205 @@ +using System; +using System.Collections; +using System.Text; + +using Org.BouncyCastle.Asn1.IsisMtt; +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X500; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.Pkix +{ + /** + * Implements the PKIX CertPathBuilding algorithm for BouncyCastle. + * + * @see CertPathBuilderSpi + */ + public class PkixCertPathBuilder + // : CertPathBuilderSpi + { + /** + * Build and validate a CertPath using the given parameter. + * + * @param params PKIXBuilderParameters object containing all information to + * build the CertPath + */ + public virtual PkixCertPathBuilderResult Build( + PkixBuilderParameters pkixParams) + { + // search target certificates + + IX509Selector certSelect = pkixParams.GetTargetCertConstraints(); + if (!(certSelect is X509CertStoreSelector)) + { + throw new PkixCertPathBuilderException( + "TargetConstraints must be an instance of " + + typeof(X509CertStoreSelector).FullName + " for " + + this.GetType() + " class."); + } + + ISet targets = new HashSet(); + try + { + targets.AddAll(PkixCertPathValidatorUtilities.FindCertificates((X509CertStoreSelector)certSelect, pkixParams.GetStores())); + // TODO Should this include an entry for pkixParams.GetAdditionalStores() too? + } + catch (Exception e) + { + throw new PkixCertPathBuilderException( + "Error finding target certificate.", e); + } + + if (targets.IsEmpty) + throw new PkixCertPathBuilderException("No certificate found matching targetContraints."); + + PkixCertPathBuilderResult result = null; + IList certPathList = Platform.CreateArrayList(); + + // check all potential target certificates + foreach (X509Certificate cert in targets) + { + result = Build(cert, pkixParams, certPathList); + + if (result != null) + break; + } + + if (result == null && certPathException != null) + { + throw new PkixCertPathBuilderException(certPathException.Message, certPathException.InnerException); + } + + if (result == null && certPathException == null) + { + throw new PkixCertPathBuilderException("Unable to find certificate chain."); + } + + return result; + } + + private Exception certPathException; + + protected virtual PkixCertPathBuilderResult Build( + X509Certificate tbvCert, + PkixBuilderParameters pkixParams, + IList tbvPath) + { + // If tbvCert is readily present in tbvPath, it indicates having run + // into a cycle in the PKI graph. + if (tbvPath.Contains(tbvCert)) + return null; + + // step out, the certificate is not allowed to appear in a certification + // chain. + if (pkixParams.GetExcludedCerts().Contains(tbvCert)) + return null; + + // test if certificate path exceeds maximum length + if (pkixParams.MaxPathLength != -1) + { + if (tbvPath.Count - 1 > pkixParams.MaxPathLength) + return null; + } + + tbvPath.Add(tbvCert); + +// X509CertificateParser certParser = new X509CertificateParser(); + PkixCertPathBuilderResult builderResult = null; + PkixCertPathValidator validator = new PkixCertPathValidator(); + + try + { + // check whether the issuer of <tbvCert> is a TrustAnchor + if (PkixCertPathValidatorUtilities.FindTrustAnchor(tbvCert, pkixParams.GetTrustAnchors()) != null) + { + // exception message from possibly later tried certification + // chains + PkixCertPath certPath = null; + try + { + certPath = new PkixCertPath(tbvPath); + } + catch (Exception e) + { + throw new Exception( + "Certification path could not be constructed from certificate list.", + e); + } + + PkixCertPathValidatorResult result = null; + try + { + result = (PkixCertPathValidatorResult)validator.Validate( + certPath, pkixParams); + } + catch (Exception e) + { + throw new Exception( + "Certification path could not be validated.", e); + } + + return new PkixCertPathBuilderResult(certPath, result.TrustAnchor, + result.PolicyTree, result.SubjectPublicKey); + } + else + { + // add additional X.509 stores from locations in certificate + try + { + PkixCertPathValidatorUtilities.AddAdditionalStoresFromAltNames( + tbvCert, pkixParams); + } + catch (CertificateParsingException e) + { + throw new Exception( + "No additiontal X.509 stores can be added from certificate locations.", + e); + } + + // try to get the issuer certificate from one of the stores + HashSet issuers = new HashSet(); + try + { + issuers.AddAll(PkixCertPathValidatorUtilities.FindIssuerCerts(tbvCert, pkixParams)); + } + catch (Exception e) + { + throw new Exception( + "Cannot find issuer certificate for certificate in certification path.", + e); + } + + if (issuers.IsEmpty) + throw new Exception("No issuer certificate for certificate in certification path found."); + + foreach (X509Certificate issuer in issuers) + { + builderResult = Build(issuer, pkixParams, tbvPath); + + if (builderResult != null) + break; + } + } + } + catch (Exception e) + { + certPathException = e; + } + + if (builderResult == null) + { + tbvPath.Remove(tbvCert); + } + + return builderResult; + } + } +} diff --git a/Crypto/src/pkix/PkixCertPathBuilderException.cs b/Crypto/src/pkix/PkixCertPathBuilderException.cs new file mode 100644
index 000000000..106075a63 --- /dev/null +++ b/Crypto/src/pkix/PkixCertPathBuilderException.cs
@@ -0,0 +1,19 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Pkix +{ + /// <summary> + /// Summary description for PkixCertPathBuilderException. + /// </summary> + public class PkixCertPathBuilderException : GeneralSecurityException + { + public PkixCertPathBuilderException() : base() { } + + public PkixCertPathBuilderException(string message) : base(message) { } + + public PkixCertPathBuilderException(string message, Exception exception) : base(message, exception) { } + + } +} diff --git a/Crypto/src/pkix/PkixCertPathBuilderResult.cs b/Crypto/src/pkix/PkixCertPathBuilderResult.cs new file mode 100644
index 000000000..f8003032f --- /dev/null +++ b/Crypto/src/pkix/PkixCertPathBuilderResult.cs
@@ -0,0 +1,45 @@ +using System; +using System.Text; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Pkix; + +namespace Org.BouncyCastle.Pkix +{ + /// <summary> + /// Summary description for PkixCertPathBuilderResult. + /// </summary> + public class PkixCertPathBuilderResult + : PkixCertPathValidatorResult//, ICertPathBuilderResult + { + private PkixCertPath certPath; + + public PkixCertPathBuilderResult( + PkixCertPath certPath, + TrustAnchor trustAnchor, + PkixPolicyNode policyTree, + AsymmetricKeyParameter subjectPublicKey) + : base(trustAnchor, policyTree, subjectPublicKey) + { + if (certPath == null) + throw new ArgumentNullException("certPath"); + + this.certPath = certPath; + } + + public PkixCertPath CertPath + { + get { return certPath; } + } + + public override string ToString() + { + StringBuilder s = new StringBuilder(); + s.Append("SimplePKIXCertPathBuilderResult: [\n"); + s.Append(" Certification Path: ").Append(CertPath).Append('\n'); + s.Append(" Trust Anchor: ").Append(this.TrustAnchor.TrustedCert.IssuerDN.ToString()).Append('\n'); + s.Append(" Subject Public Key: ").Append(this.SubjectPublicKey).Append("\n]"); + return s.ToString(); + } + } +} diff --git a/Crypto/src/pkix/PkixCertPathChecker.cs b/Crypto/src/pkix/PkixCertPathChecker.cs new file mode 100644
index 000000000..f22738d89 --- /dev/null +++ b/Crypto/src/pkix/PkixCertPathChecker.cs
@@ -0,0 +1,101 @@ +using System; +using System.Collections; +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Pkix +{ + public abstract class PkixCertPathChecker + { + protected PkixCertPathChecker() + { + } + + /** + * Initializes the internal state of this <code>PKIXCertPathChecker</code>. + * <p> + * The <code>forward</code> flag specifies the order that certificates + * will be passed to the {@link #check check} method (forward or reverse). A + * <code>PKIXCertPathChecker</code> <b>must</b> support reverse checking + * and <b>may</b> support forward checking. + * </p> + * + * @param forward + * the order that certificates are presented to the + * <code>check</code> method. If <code>true</code>, + * certificates are presented from target to most-trusted CA + * (forward); if <code>false</code>, from most-trusted CA to + * target (reverse). + * @exception CertPathValidatorException + * if this <code>PKIXCertPathChecker</code> is unable to + * check certificates in the specified order; it should never + * be thrown if the forward flag is false since reverse + * checking must be supported + */ + public abstract void Init(bool forward); + //throws CertPathValidatorException; + + /** + * Indicates if forward checking is supported. Forward checking refers to + * the ability of the <code>PKIXCertPathChecker</code> to perform its + * checks when certificates are presented to the <code>check</code> method + * in the forward direction (from target to most-trusted CA). + * + * @return <code>true</code> if forward checking is supported, + * <code>false</code> otherwise + */ + public abstract bool IsForwardCheckingSupported(); + + /** + * Returns an immutable <code>Set</code> of X.509 certificate extensions + * that this <code>PKIXCertPathChecker</code> supports (i.e. recognizes, + * is able to process), or <code>null</code> if no extensions are + * supported. + * <p> + * Each element of the set is a <code>String</code> representing the + * Object Identifier (OID) of the X.509 extension that is supported. The OID + * is represented by a set of nonnegative integers separated by periods. + * </p><p> + * All X.509 certificate extensions that a <code>PKIXCertPathChecker</code> + * might possibly be able to process should be included in the set. + * </p> + * + * @return an immutable <code>Set</code> of X.509 extension OIDs (in + * <code>String</code> format) supported by this + * <code>PKIXCertPathChecker</code>, or <code>null</code> if no + * extensions are supported + */ + public abstract ISet GetSupportedExtensions(); + + /** + * Performs the check(s) on the specified certificate using its internal + * state and removes any critical extensions that it processes from the + * specified collection of OID strings that represent the unresolved + * critical extensions. The certificates are presented in the order + * specified by the <code>init</code> method. + * + * @param cert + * the <code>Certificate</code> to be checked + * @param unresolvedCritExts + * a <code>Collection</code> of OID strings representing the + * current set of unresolved critical extensions + * @exception CertPathValidatorException + * if the specified certificate does not pass the check + */ + public abstract void Check(X509Certificate cert, ICollection unresolvedCritExts); + //throws CertPathValidatorException; + + /** + * Returns a clone of this object. Calls the <code>Object.clone()</code> + * method. All subclasses which maintain state must support and override + * this method, if necessary. + * + * @return a copy of this <code>PKIXCertPathChecker</code> + */ + public virtual object Clone() + { + // TODO Check this + return base.MemberwiseClone(); + } + } +} diff --git a/Crypto/src/pkix/PkixCertPathValidator.cs b/Crypto/src/pkix/PkixCertPathValidator.cs new file mode 100644
index 000000000..7eb838886 --- /dev/null +++ b/Crypto/src/pkix/PkixCertPathValidator.cs
@@ -0,0 +1,420 @@ +using System; +using System.Collections; +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.Pkix +{ + /** + * The <i>Service Provider Interface</i> (<b>SPI</b>) + * for the {@link CertPathValidator CertPathValidator} class. All + * <code>CertPathValidator</code> implementations must include a class (the + * SPI class) that extends this class (<code>CertPathValidatorSpi</code>) + * and implements all of its methods. In general, instances of this class + * should only be accessed through the <code>CertPathValidator</code> class. + * For details, see the Java Cryptography Architecture.<br /> + * <br /> + * <b>Concurrent Access</b><br /> + * <br /> + * Instances of this class need not be protected against concurrent + * access from multiple threads. Threads that need to access a single + * <code>CertPathValidatorSpi</code> instance concurrently should synchronize + * amongst themselves and provide the necessary locking before calling the + * wrapping <code>CertPathValidator</code> object.<br /> + * <br /> + * However, implementations of <code>CertPathValidatorSpi</code> may still + * encounter concurrency issues, since multiple threads each + * manipulating a different <code>CertPathValidatorSpi</code> instance need not + * synchronize. + */ + /// <summary> + /// CertPathValidatorSpi implementation for X.509 Certificate validation a la RFC + /// 3280. + /// </summary> + public class PkixCertPathValidator + { + public virtual PkixCertPathValidatorResult Validate( + PkixCertPath certPath, + PkixParameters paramsPkix) + { + if (paramsPkix.GetTrustAnchors() == null) + { + throw new ArgumentException( + "trustAnchors is null, this is not allowed for certification path validation.", + "parameters"); + } + + // + // 6.1.1 - inputs + // + + // + // (a) + // + IList certs = certPath.Certificates; + int n = certs.Count; + + if (certs.Count == 0) + throw new PkixCertPathValidatorException("Certification path is empty.", null, certPath, 0); + + // + // (b) + // + // DateTime validDate = PkixCertPathValidatorUtilities.GetValidDate(paramsPkix); + + // + // (c) + // + ISet userInitialPolicySet = paramsPkix.GetInitialPolicies(); + + // + // (d) + // + TrustAnchor trust; + try + { + trust = PkixCertPathValidatorUtilities.FindTrustAnchor( + (X509Certificate)certs[certs.Count - 1], + paramsPkix.GetTrustAnchors()); + } + catch (Exception e) + { + throw new PkixCertPathValidatorException(e.Message, e, certPath, certs.Count - 1); + } + + if (trust == null) + throw new PkixCertPathValidatorException("Trust anchor for certification path not found.", null, certPath, -1); + + // + // (e), (f), (g) are part of the paramsPkix object. + // + IEnumerator certIter; + int index = 0; + int i; + // Certificate for each interation of the validation loop + // Signature information for each iteration of the validation loop + // + // 6.1.2 - setup + // + + // + // (a) + // + IList[] policyNodes = new IList[n + 1]; + for (int j = 0; j < policyNodes.Length; j++) + { + policyNodes[j] = Platform.CreateArrayList(); + } + + ISet policySet = new HashSet(); + + policySet.Add(Rfc3280CertPathUtilities.ANY_POLICY); + + PkixPolicyNode validPolicyTree = new PkixPolicyNode(Platform.CreateArrayList(), 0, policySet, null, new HashSet(), + Rfc3280CertPathUtilities.ANY_POLICY, false); + + policyNodes[0].Add(validPolicyTree); + + // + // (b) and (c) + // + PkixNameConstraintValidator nameConstraintValidator = new PkixNameConstraintValidator(); + + // (d) + // + int explicitPolicy; + ISet acceptablePolicies = new HashSet(); + + if (paramsPkix.IsExplicitPolicyRequired) + { + explicitPolicy = 0; + } + else + { + explicitPolicy = n + 1; + } + + // + // (e) + // + int inhibitAnyPolicy; + + if (paramsPkix.IsAnyPolicyInhibited) + { + inhibitAnyPolicy = 0; + } + else + { + inhibitAnyPolicy = n + 1; + } + + // + // (f) + // + int policyMapping; + + if (paramsPkix.IsPolicyMappingInhibited) + { + policyMapping = 0; + } + else + { + policyMapping = n + 1; + } + + // + // (g), (h), (i), (j) + // + AsymmetricKeyParameter workingPublicKey; + X509Name workingIssuerName; + + X509Certificate sign = trust.TrustedCert; + try + { + if (sign != null) + { + workingIssuerName = sign.SubjectDN; + workingPublicKey = sign.GetPublicKey(); + } + else + { + workingIssuerName = new X509Name(trust.CAName); + workingPublicKey = trust.CAPublicKey; + } + } + catch (ArgumentException ex) + { + throw new PkixCertPathValidatorException("Subject of trust anchor could not be (re)encoded.", ex, certPath, + -1); + } + + AlgorithmIdentifier workingAlgId = null; + try + { + workingAlgId = PkixCertPathValidatorUtilities.GetAlgorithmIdentifier(workingPublicKey); + } + catch (PkixCertPathValidatorException e) + { + throw new PkixCertPathValidatorException( + "Algorithm identifier of public key of trust anchor could not be read.", e, certPath, -1); + } + +// DerObjectIdentifier workingPublicKeyAlgorithm = workingAlgId.ObjectID; +// Asn1Encodable workingPublicKeyParameters = workingAlgId.Parameters; + + // + // (k) + // + int maxPathLength = n; + + // + // 6.1.3 + // + + X509CertStoreSelector certConstraints = paramsPkix.GetTargetCertConstraints(); + if (certConstraints != null && !certConstraints.Match((X509Certificate)certs[0])) + { + throw new PkixCertPathValidatorException( + "Target certificate in certification path does not match targetConstraints.", null, certPath, 0); + } + + // + // initialize CertPathChecker's + // + IList pathCheckers = paramsPkix.GetCertPathCheckers(); + certIter = pathCheckers.GetEnumerator(); + + while (certIter.MoveNext()) + { + ((PkixCertPathChecker)certIter.Current).Init(false); + } + + X509Certificate cert = null; + + for (index = certs.Count - 1; index >= 0; index--) + { + // try + // { + // + // i as defined in the algorithm description + // + i = n - index; + + // + // set certificate to be checked in this round + // sign and workingPublicKey and workingIssuerName are set + // at the end of the for loop and initialized the + // first time from the TrustAnchor + // + cert = (X509Certificate)certs[index]; + + // + // 6.1.3 + // + + Rfc3280CertPathUtilities.ProcessCertA(certPath, paramsPkix, index, workingPublicKey, + workingIssuerName, sign); + + Rfc3280CertPathUtilities.ProcessCertBC(certPath, index, nameConstraintValidator); + + validPolicyTree = Rfc3280CertPathUtilities.ProcessCertD(certPath, index, + acceptablePolicies, validPolicyTree, policyNodes, inhibitAnyPolicy); + + validPolicyTree = Rfc3280CertPathUtilities.ProcessCertE(certPath, index, validPolicyTree); + + Rfc3280CertPathUtilities.ProcessCertF(certPath, index, validPolicyTree, explicitPolicy); + + // + // 6.1.4 + // + + if (i != n) + { + if (cert != null && cert.Version == 1) + { + throw new PkixCertPathValidatorException( + "Version 1 certificates can't be used as CA ones.", null, certPath, index); + } + + Rfc3280CertPathUtilities.PrepareNextCertA(certPath, index); + + validPolicyTree = Rfc3280CertPathUtilities.PrepareCertB(certPath, index, policyNodes, + validPolicyTree, policyMapping); + + Rfc3280CertPathUtilities.PrepareNextCertG(certPath, index, nameConstraintValidator); + + // (h) + explicitPolicy = Rfc3280CertPathUtilities.PrepareNextCertH1(certPath, index, explicitPolicy); + policyMapping = Rfc3280CertPathUtilities.PrepareNextCertH2(certPath, index, policyMapping); + inhibitAnyPolicy = Rfc3280CertPathUtilities.PrepareNextCertH3(certPath, index, inhibitAnyPolicy); + + // + // (i) + // + explicitPolicy = Rfc3280CertPathUtilities.PrepareNextCertI1(certPath, index, explicitPolicy); + policyMapping = Rfc3280CertPathUtilities.PrepareNextCertI2(certPath, index, policyMapping); + + // (j) + inhibitAnyPolicy = Rfc3280CertPathUtilities.PrepareNextCertJ(certPath, index, inhibitAnyPolicy); + + // (k) + Rfc3280CertPathUtilities.PrepareNextCertK(certPath, index); + + // (l) + maxPathLength = Rfc3280CertPathUtilities.PrepareNextCertL(certPath, index, maxPathLength); + + // (m) + maxPathLength = Rfc3280CertPathUtilities.PrepareNextCertM(certPath, index, maxPathLength); + + // (n) + Rfc3280CertPathUtilities.PrepareNextCertN(certPath, index); + + ISet criticalExtensions1 = cert.GetCriticalExtensionOids(); + + if (criticalExtensions1 != null) + { + criticalExtensions1 = new HashSet(criticalExtensions1); + + // these extensions are handled by the algorithm + criticalExtensions1.Remove(X509Extensions.KeyUsage.Id); + criticalExtensions1.Remove(X509Extensions.CertificatePolicies.Id); + criticalExtensions1.Remove(X509Extensions.PolicyMappings.Id); + criticalExtensions1.Remove(X509Extensions.InhibitAnyPolicy.Id); + criticalExtensions1.Remove(X509Extensions.IssuingDistributionPoint.Id); + criticalExtensions1.Remove(X509Extensions.DeltaCrlIndicator.Id); + criticalExtensions1.Remove(X509Extensions.PolicyConstraints.Id); + criticalExtensions1.Remove(X509Extensions.BasicConstraints.Id); + criticalExtensions1.Remove(X509Extensions.SubjectAlternativeName.Id); + criticalExtensions1.Remove(X509Extensions.NameConstraints.Id); + } + else + { + criticalExtensions1 = new HashSet(); + } + + // (o) + Rfc3280CertPathUtilities.PrepareNextCertO(certPath, index, criticalExtensions1, pathCheckers); + + // set signing certificate for next round + sign = cert; + + // (c) + workingIssuerName = sign.SubjectDN; + + // (d) + try + { + workingPublicKey = PkixCertPathValidatorUtilities.GetNextWorkingKey(certPath.Certificates, index); + } + catch (PkixCertPathValidatorException e) + { + throw new PkixCertPathValidatorException("Next working key could not be retrieved.", e, certPath, index); + } + + workingAlgId = PkixCertPathValidatorUtilities.GetAlgorithmIdentifier(workingPublicKey); + // (f) +// workingPublicKeyAlgorithm = workingAlgId.ObjectID; + // (e) +// workingPublicKeyParameters = workingAlgId.Parameters; + } + } + + // + // 6.1.5 Wrap-up procedure + // + + explicitPolicy = Rfc3280CertPathUtilities.WrapupCertA(explicitPolicy, cert); + + explicitPolicy = Rfc3280CertPathUtilities.WrapupCertB(certPath, index + 1, explicitPolicy); + + // + // (c) (d) and (e) are already done + // + + // + // (f) + // + ISet criticalExtensions = cert.GetCriticalExtensionOids(); + + if (criticalExtensions != null) + { + criticalExtensions = new HashSet(criticalExtensions); + + // Requires .Id + // these extensions are handled by the algorithm + criticalExtensions.Remove(X509Extensions.KeyUsage.Id); + criticalExtensions.Remove(X509Extensions.CertificatePolicies.Id); + criticalExtensions.Remove(X509Extensions.PolicyMappings.Id); + criticalExtensions.Remove(X509Extensions.InhibitAnyPolicy.Id); + criticalExtensions.Remove(X509Extensions.IssuingDistributionPoint.Id); + criticalExtensions.Remove(X509Extensions.DeltaCrlIndicator.Id); + criticalExtensions.Remove(X509Extensions.PolicyConstraints.Id); + criticalExtensions.Remove(X509Extensions.BasicConstraints.Id); + criticalExtensions.Remove(X509Extensions.SubjectAlternativeName.Id); + criticalExtensions.Remove(X509Extensions.NameConstraints.Id); + criticalExtensions.Remove(X509Extensions.CrlDistributionPoints.Id); + } + else + { + criticalExtensions = new HashSet(); + } + + Rfc3280CertPathUtilities.WrapupCertF(certPath, index + 1, pathCheckers, criticalExtensions); + + PkixPolicyNode intersection = Rfc3280CertPathUtilities.WrapupCertG(certPath, paramsPkix, userInitialPolicySet, + index + 1, policyNodes, validPolicyTree, acceptablePolicies); + + if ((explicitPolicy > 0) || (intersection != null)) + { + return new PkixCertPathValidatorResult(trust, intersection, cert.GetPublicKey()); + } + + throw new PkixCertPathValidatorException("Path processing failed on policy.", null, certPath, index); + } + } +} diff --git a/Crypto/src/pkix/PkixCertPathValidatorException.cs b/Crypto/src/pkix/PkixCertPathValidatorException.cs new file mode 100644
index 000000000..504a3c646 --- /dev/null +++ b/Crypto/src/pkix/PkixCertPathValidatorException.cs
@@ -0,0 +1,218 @@ +using System; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Pkix +{ + /** + * An exception indicating one of a variety of problems encountered when + * validating a certification path. <br /> + * <br /> + * A <code>CertPathValidatorException</code> provides support for wrapping + * exceptions. The {@link #getCause getCause} method returns the throwable, + * if any, that caused this exception to be thrown. <br /> + * <br /> + * A <code>CertPathValidatorException</code> may also include the + * certification path that was being validated when the exception was thrown + * and the index of the certificate in the certification path that caused the + * exception to be thrown. Use the {@link #getCertPath getCertPath} and + * {@link #getIndex getIndex} methods to retrieve this information.<br /> + * <br /> + * <b>Concurrent Access</b><br /> + * <br /> + * Unless otherwise specified, the methods defined in this class are not + * thread-safe. Multiple threads that need to access a single + * object concurrently should synchronize amongst themselves and + * provide the necessary locking. Multiple threads each manipulating + * separate objects need not synchronize. + * + * @see CertPathValidator + **/ + + public class PkixCertPathValidatorException : GeneralSecurityException + { + private Exception cause; + private PkixCertPath certPath; + private int index = -1; + + public PkixCertPathValidatorException() : base() { } + + /// <summary> + /// Creates a <code>PkixCertPathValidatorException</code> with the given detail + /// message. A detail message is a <code>String</code> that describes this + /// particular exception. + /// </summary> + /// <param name="message">the detail message</param> + public PkixCertPathValidatorException(string message) : base(message) { } + + /// <summary> + /// Creates a <code>PkixCertPathValidatorException</code> with the specified + /// detail message and cause. + /// </summary> + /// <param name="message">the detail message</param> + /// <param name="cause">the cause (which is saved for later retrieval by the + /// {@link #getCause getCause()} method). (A <code>null</code> + /// value is permitted, and indicates that the cause is + /// nonexistent or unknown.)</param> + public PkixCertPathValidatorException(string message, Exception cause) : base(message) + { + this.cause = cause; + } + + /// <summary> + /// Creates a <code>PkixCertPathValidatorException</code> with the specified + /// detail message, cause, certification path, and index. + /// </summary> + /// <param name="message">the detail message (or <code>null</code> if none)</param> + /// <param name="cause">the cause (or <code>null</code> if none)</param> + /// <param name="certPath">the certification path that was in the process of being + /// validated when the error was encountered</param> + /// <param name="index">the index of the certificate in the certification path that</param> * + public PkixCertPathValidatorException( + string message, + Exception cause, + PkixCertPath certPath, + int index) + : base(message) + { + if (certPath == null && index != -1) + { + throw new ArgumentNullException( + "certPath = null and index != -1"); + } + if (index < -1 + || (certPath != null && index >= certPath.Certificates.Count)) + { + throw new IndexOutOfRangeException( + " index < -1 or out of bound of certPath.getCertificates()"); + } + + this.cause = cause; + this.certPath = certPath; + this.index = index; + } + + // + // Prints a stack trace to a <code>PrintWriter</code>, including the + // backtrace of the cause, if any. + // + // @param pw + // the <code>PrintWriter</code> to use for output + // + // public void printStackTrace(PrintWriter pw) + // { + // super.printStackTrace(pw); + // if (getCause() != null) + // { + // getCause().printStackTrace(pw); + // } + // } + //} + + + // /** + // * Creates a <code>CertPathValidatorException</code> that wraps the + // * specified throwable. This allows any exception to be converted into a + // * <code>CertPathValidatorException</code>, while retaining information + // * about the wrapped exception, which may be useful for debugging. The + // * detail message is set to (<code>cause==null ? null : cause.toString() + // * </code>) + // * (which typically contains the class and detail message of cause). + // * + // * @param cause + // * the cause (which is saved for later retrieval by the + // * {@link #getCause getCause()} method). (A <code>null</code> + // * value is permitted, and indicates that the cause is + // * nonexistent or unknown.) + // */ + // public PkixCertPathValidatorException(Throwable cause) + // { + // this.cause = cause; + // } + // + + /// <summary> + /// Returns the detail message for this <code>CertPathValidatorException</code>. + /// </summary> + /// <returns>the detail message, or <code>null</code> if neither the message nor cause were specified</returns> + public override string Message + { + get + { + string message = base.Message; + + if (message != null) + { + return message; + } + + if (cause != null) + { + return cause.Message; + } + + return null; + } + } + + /** + * Returns the certification path that was being validated when the + * exception was thrown. + * + * @return the <code>CertPath</code> that was being validated when the + * exception was thrown (or <code>null</code> if not specified) + */ + public PkixCertPath CertPath + { + get { return certPath; } + } + + /** + * Returns the index of the certificate in the certification path that + * caused the exception to be thrown. Note that the list of certificates in + * a <code>CertPath</code> is zero based. If no index has been set, -1 is + * returned. + * + * @return the index that has been set, or -1 if none has been set + */ + public int Index + { + get { return index; } + } + +// /** +// * Returns the cause of this <code>CertPathValidatorException</code> or +// * <code>null</code> if the cause is nonexistent or unknown. +// * +// * @return the cause of this throwable or <code>null</code> if the cause +// * is nonexistent or unknown. +// */ +// public Throwable getCause() +// { +// return cause; +// } +// +// /** +// * Returns a string describing this exception, including a description of +// * the internal (wrapped) cause if there is one. +// * +// * @return a string representation of this +// * <code>CertPathValidatorException</code> +// */ +// public String toString() +// { +// StringBuffer sb = new StringBuffer(); +// String s = getMessage(); +// if (s != null) +// { +// sb.append(s); +// } +// if (getIndex() >= 0) +// { +// sb.append("index in certpath: ").append(getIndex()).append('\n'); +// sb.append(getCertPath()); +// } +// return sb.toString(); +// } + + } +} diff --git a/Crypto/src/pkix/PkixCertPathValidatorResult.cs b/Crypto/src/pkix/PkixCertPathValidatorResult.cs new file mode 100644
index 000000000..c7d81c7f5 --- /dev/null +++ b/Crypto/src/pkix/PkixCertPathValidatorResult.cs
@@ -0,0 +1,69 @@ +using System; +using System.Text; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Pkix +{ + /// <summary> + /// Summary description for PkixCertPathValidatorResult. + /// </summary> + public class PkixCertPathValidatorResult + //: ICertPathValidatorResult + { + private TrustAnchor trustAnchor; + private PkixPolicyNode policyTree; + private AsymmetricKeyParameter subjectPublicKey; + + public PkixPolicyNode PolicyTree + { + get { return this.policyTree; } + } + + public TrustAnchor TrustAnchor + { + get { return this.trustAnchor; } + } + + public AsymmetricKeyParameter SubjectPublicKey + { + get { return this.subjectPublicKey; } + } + + public PkixCertPathValidatorResult( + TrustAnchor trustAnchor, + PkixPolicyNode policyTree, + AsymmetricKeyParameter subjectPublicKey) + { + if (subjectPublicKey == null) + { + throw new NullReferenceException("subjectPublicKey must be non-null"); + } + if (trustAnchor == null) + { + throw new NullReferenceException("trustAnchor must be non-null"); + } + + this.trustAnchor = trustAnchor; + this.policyTree = policyTree; + this.subjectPublicKey = subjectPublicKey; + } + + public object Clone() + { + return new PkixCertPathValidatorResult(this.TrustAnchor, this.PolicyTree, this.SubjectPublicKey); + } + + public override String ToString() + { + StringBuilder sB = new StringBuilder(); + sB.Append("PKIXCertPathValidatorResult: [ \n"); + sB.Append(" Trust Anchor: ").Append(this.TrustAnchor).Append('\n'); + sB.Append(" Policy Tree: ").Append(this.PolicyTree).Append('\n'); + sB.Append(" Subject Public Key: ").Append(this.SubjectPublicKey).Append("\n]"); + return sB.ToString(); + } + + } +} diff --git a/Crypto/src/pkix/PkixCertPathValidatorUtilities.cs b/Crypto/src/pkix/PkixCertPathValidatorUtilities.cs new file mode 100644
index 000000000..305b2de35 --- /dev/null +++ b/Crypto/src/pkix/PkixCertPathValidatorUtilities.cs
@@ -0,0 +1,1194 @@ +using System; +using System.Collections; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.IsisMtt; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.Utilities.Date; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.X509.Extension; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.Pkix +{ + /// <summary> + /// Summary description for PkixCertPathValidatorUtilities. + /// </summary> + public class PkixCertPathValidatorUtilities + { + private static readonly PkixCrlUtilities CrlUtilities = new PkixCrlUtilities(); + + internal static readonly string ANY_POLICY = "2.5.29.32.0"; + + internal static readonly string CRL_NUMBER = X509Extensions.CrlNumber.Id; + + /// <summary> + /// key usage bits + /// </summary> + internal static readonly int KEY_CERT_SIGN = 5; + internal static readonly int CRL_SIGN = 6; + + internal static readonly string[] crlReasons = new string[] + { + "unspecified", + "keyCompromise", + "cACompromise", + "affiliationChanged", + "superseded", + "cessationOfOperation", + "certificateHold", + "unknown", + "removeFromCRL", + "privilegeWithdrawn", + "aACompromise" + }; + + /// <summary> + /// Search the given Set of TrustAnchor's for one that is the + /// issuer of the given X509 certificate. + /// </summary> + /// <param name="cert">the X509 certificate</param> + /// <param name="trustAnchors">a Set of TrustAnchor's</param> + /// <returns>the <code>TrustAnchor</code> object if found or + /// <code>null</code> if not. + /// </returns> + /// @exception + internal static TrustAnchor FindTrustAnchor( + X509Certificate cert, + ISet trustAnchors) + { + IEnumerator iter = trustAnchors.GetEnumerator(); + TrustAnchor trust = null; + AsymmetricKeyParameter trustPublicKey = null; + Exception invalidKeyEx = null; + + X509CertStoreSelector certSelectX509 = new X509CertStoreSelector(); + + try + { + certSelectX509.Subject = GetIssuerPrincipal(cert); + } + catch (IOException ex) + { + throw new Exception("Cannot set subject search criteria for trust anchor.", ex); + } + + while (iter.MoveNext() && trust == null) + { + trust = (TrustAnchor) iter.Current; + if (trust.TrustedCert != null) + { + if (certSelectX509.Match(trust.TrustedCert)) + { + trustPublicKey = trust.TrustedCert.GetPublicKey(); + } + else + { + trust = null; + } + } + else if (trust.CAName != null && trust.CAPublicKey != null) + { + try + { + X509Name certIssuer = GetIssuerPrincipal(cert); + X509Name caName = new X509Name(trust.CAName); + + if (certIssuer.Equivalent(caName, true)) + { + trustPublicKey = trust.CAPublicKey; + } + else + { + trust = null; + } + } + catch (InvalidParameterException) + { + trust = null; + } + } + else + { + trust = null; + } + + if (trustPublicKey != null) + { + try + { + cert.Verify(trustPublicKey); + } + catch (Exception ex) + { + invalidKeyEx = ex; + trust = null; + } + } + } + + if (trust == null && invalidKeyEx != null) + { + throw new Exception("TrustAnchor found but certificate validation failed.", invalidKeyEx); + } + + return trust; + } + + internal static void AddAdditionalStoresFromAltNames( + X509Certificate cert, + PkixParameters pkixParams) + { + // if in the IssuerAltName extension an URI + // is given, add an additinal X.509 store + if (cert.GetIssuerAlternativeNames() != null) + { + IEnumerator it = cert.GetIssuerAlternativeNames().GetEnumerator(); + while (it.MoveNext()) + { + // look for URI + IList list = (IList)it.Current; + //if (list[0].Equals(new Integer(GeneralName.UniformResourceIdentifier))) + if (list[0].Equals(GeneralName.UniformResourceIdentifier)) + { + // found + string temp = (string)list[1]; + PkixCertPathValidatorUtilities.AddAdditionalStoreFromLocation(temp, pkixParams); + } + } + } + } + + internal static DateTime GetValidDate(PkixParameters paramsPKIX) + { + DateTimeObject validDate = paramsPKIX.Date; + + if (validDate == null) + return DateTime.UtcNow; + + return validDate.Value; + } + + /// <summary> + /// Returns the issuer of an attribute certificate or certificate. + /// </summary> + /// <param name="cert">The attribute certificate or certificate.</param> + /// <returns>The issuer as <code>X500Principal</code>.</returns> + internal static X509Name GetIssuerPrincipal( + object cert) + { + if (cert is X509Certificate) + { + return ((X509Certificate)cert).IssuerDN; + } + else + { + return ((IX509AttributeCertificate)cert).Issuer.GetPrincipals()[0]; + } + } + + internal static bool IsSelfIssued( + X509Certificate cert) + { + return cert.SubjectDN.Equivalent(cert.IssuerDN, true); + } + + internal static AlgorithmIdentifier GetAlgorithmIdentifier( + AsymmetricKeyParameter key) + { + try + { + SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(key); + + return info.AlgorithmID; + } + catch (Exception e) + { + throw new PkixCertPathValidatorException("Subject public key cannot be decoded.", e); + } + } + + internal static bool IsAnyPolicy( + ISet policySet) + { + return policySet == null || policySet.Contains(ANY_POLICY) || policySet.Count == 0; + } + + internal static void AddAdditionalStoreFromLocation( + string location, + PkixParameters pkixParams) + { + if (pkixParams.IsAdditionalLocationsEnabled) + { + try + { + if (location.StartsWith("ldap://")) + { + // ldap://directory.d-trust.net/CN=D-TRUST + // Qualified CA 2003 1:PN,O=D-Trust GmbH,C=DE + // skip "ldap://" + location = location.Substring(7); + // after first / baseDN starts + string url;//, baseDN; + int slashPos = location.IndexOf('/'); + if (slashPos != -1) + { + url = "ldap://" + location.Substring(0, slashPos); +// baseDN = location.Substring(slashPos); + } + else + { + url = "ldap://" + location; +// baseDN = nsull; + } + + throw Platform.CreateNotImplementedException("LDAP cert/CRL stores"); + + // use all purpose parameters + //X509LDAPCertStoreParameters ldapParams = new X509LDAPCertStoreParameters.Builder( + // url, baseDN).build(); + //pkixParams.AddAdditionalStore(X509Store.getInstance( + // "CERTIFICATE/LDAP", ldapParams)); + //pkixParams.AddAdditionalStore(X509Store.getInstance( + // "CRL/LDAP", ldapParams)); + //pkixParams.AddAdditionalStore(X509Store.getInstance( + // "ATTRIBUTECERTIFICATE/LDAP", ldapParams)); + //pkixParams.AddAdditionalStore(X509Store.getInstance( + // "CERTIFICATEPAIR/LDAP", ldapParams)); + } + } + catch (Exception) + { + // cannot happen + throw new Exception("Exception adding X.509 stores."); + } + } + } + + private static BigInteger GetSerialNumber( + object cert) + { + if (cert is X509Certificate) + { + return ((X509Certificate)cert).SerialNumber; + } + else + { + return ((X509V2AttributeCertificate)cert).SerialNumber; + } + } + + // + // policy checking + // + + internal static ISet GetQualifierSet(Asn1Sequence qualifiers) + { + ISet pq = new HashSet(); + + if (qualifiers == null) + { + return pq; + } + + foreach (Asn1Encodable ae in qualifiers) + { + try + { +// pq.Add(PolicyQualifierInfo.GetInstance(Asn1Object.FromByteArray(ae.GetEncoded()))); + pq.Add(PolicyQualifierInfo.GetInstance(ae.ToAsn1Object())); + } + catch (IOException ex) + { + throw new PkixCertPathValidatorException("Policy qualifier info cannot be decoded.", ex); + } + } + + return pq; + } + + internal static PkixPolicyNode RemovePolicyNode( + PkixPolicyNode validPolicyTree, + IList[] policyNodes, + PkixPolicyNode _node) + { + PkixPolicyNode _parent = (PkixPolicyNode)_node.Parent; + + if (validPolicyTree == null) + { + return null; + } + + if (_parent == null) + { + for (int j = 0; j < policyNodes.Length; j++) + { + policyNodes[j] = Platform.CreateArrayList(); + } + + return null; + } + else + { + _parent.RemoveChild(_node); + RemovePolicyNodeRecurse(policyNodes, _node); + + return validPolicyTree; + } + } + + private static void RemovePolicyNodeRecurse(IList[] policyNodes, PkixPolicyNode _node) + { + policyNodes[_node.Depth].Remove(_node); + + if (_node.HasChildren) + { + foreach (PkixPolicyNode _child in _node.Children) + { + RemovePolicyNodeRecurse(policyNodes, _child); + } + } + } + + internal static void PrepareNextCertB1( + int i, + IList[] policyNodes, + string id_p, + IDictionary m_idp, + X509Certificate cert) + { + bool idp_found = false; + IEnumerator nodes_i = policyNodes[i].GetEnumerator(); + while (nodes_i.MoveNext()) + { + PkixPolicyNode node = (PkixPolicyNode)nodes_i.Current; + if (node.ValidPolicy.Equals(id_p)) + { + idp_found = true; + node.ExpectedPolicies = (ISet)m_idp[id_p]; + break; + } + } + + if (!idp_found) + { + nodes_i = policyNodes[i].GetEnumerator(); + while (nodes_i.MoveNext()) + { + PkixPolicyNode node = (PkixPolicyNode)nodes_i.Current; + if (ANY_POLICY.Equals(node.ValidPolicy)) + { + ISet pq = null; + Asn1Sequence policies = null; + try + { + policies = DerSequence.GetInstance(GetExtensionValue(cert, X509Extensions.CertificatePolicies)); + } + catch (Exception e) + { + throw new Exception("Certificate policies cannot be decoded.", e); + } + + IEnumerator enm = policies.GetEnumerator(); + while (enm.MoveNext()) + { + PolicyInformation pinfo = null; + + try + { + pinfo = PolicyInformation.GetInstance(enm.Current); + } + catch (Exception ex) + { + throw new Exception("Policy information cannot be decoded.", ex); + } + + if (ANY_POLICY.Equals(pinfo.PolicyIdentifier.Id)) + { + try + { + pq = GetQualifierSet(pinfo.PolicyQualifiers); + } + catch (PkixCertPathValidatorException ex) + { + throw new PkixCertPathValidatorException( + "Policy qualifier info set could not be built.", ex); + } + break; + } + } + bool ci = false; + ISet critExtOids = cert.GetCriticalExtensionOids(); + if (critExtOids != null) + { + ci = critExtOids.Contains(X509Extensions.CertificatePolicies.Id); + } + + PkixPolicyNode p_node = (PkixPolicyNode)node.Parent; + if (ANY_POLICY.Equals(p_node.ValidPolicy)) + { + PkixPolicyNode c_node = new PkixPolicyNode( + Platform.CreateArrayList(), i, + (ISet)m_idp[id_p], + p_node, pq, id_p, ci); + p_node.AddChild(c_node); + policyNodes[i].Add(c_node); + } + break; + } + } + } + } + + internal static PkixPolicyNode PrepareNextCertB2( + int i, + IList[] policyNodes, + string id_p, + PkixPolicyNode validPolicyTree) + { + int pos = 0; + + // Copy to avoid RemoveAt calls interfering with enumeration + foreach (PkixPolicyNode node in Platform.CreateArrayList(policyNodes[i])) + { + if (node.ValidPolicy.Equals(id_p)) + { + PkixPolicyNode p_node = (PkixPolicyNode)node.Parent; + p_node.RemoveChild(node); + + // Removal of element at current iterator position not supported in C# + //nodes_i.remove(); + policyNodes[i].RemoveAt(pos); + + for (int k = (i - 1); k >= 0; k--) + { + IList nodes = policyNodes[k]; + for (int l = 0; l < nodes.Count; l++) + { + PkixPolicyNode node2 = (PkixPolicyNode)nodes[l]; + if (!node2.HasChildren) + { + validPolicyTree = RemovePolicyNode(validPolicyTree, policyNodes, node2); + if (validPolicyTree == null) + break; + } + } + } + } + else + { + ++pos; + } + } + return validPolicyTree; + } + + internal static void GetCertStatus( + DateTime validDate, + X509Crl crl, + Object cert, + CertStatus certStatus) + { + X509Crl bcCRL = null; + + try + { + bcCRL = new X509Crl(CertificateList.GetInstance((Asn1Sequence)Asn1Sequence.FromByteArray(crl.GetEncoded()))); + } + catch (Exception exception) + { + throw new Exception("Bouncy Castle X509Crl could not be created.", exception); + } + + X509CrlEntry crl_entry = (X509CrlEntry)bcCRL.GetRevokedCertificate(GetSerialNumber(cert)); + + if (crl_entry == null) + return; + + X509Name issuer = GetIssuerPrincipal(cert); + + if (issuer.Equivalent(crl_entry.GetCertificateIssuer(), true) + || issuer.Equivalent(crl.IssuerDN, true)) + { + DerEnumerated reasonCode = null; + if (crl_entry.HasExtensions) + { + try + { + reasonCode = DerEnumerated.GetInstance( + GetExtensionValue(crl_entry, X509Extensions.ReasonCode)); + } + catch (Exception e) + { + new Exception( + "Reason code CRL entry extension could not be decoded.", + e); + } + } + + // for reason keyCompromise, caCompromise, aACompromise or + // unspecified + if (!(validDate.Ticks < crl_entry.RevocationDate.Ticks) + || reasonCode == null + || reasonCode.Value.TestBit(0) + || reasonCode.Value.TestBit(1) + || reasonCode.Value.TestBit(2) + || reasonCode.Value.TestBit(8)) + { + if (reasonCode != null) // (i) or (j) (1) + { + certStatus.Status = reasonCode.Value.SignValue; + } + else // (i) or (j) (2) + { + certStatus.Status = CrlReason.Unspecified; + } + certStatus.RevocationDate = new DateTimeObject(crl_entry.RevocationDate); + } + } + } + + /** + * Return the next working key inheriting DSA parameters if necessary. + * <p> + * This methods inherits DSA parameters from the indexed certificate or + * previous certificates in the certificate chain to the returned + * <code>PublicKey</code>. The list is searched upwards, meaning the end + * certificate is at position 0 and previous certificates are following. + * </p> + * <p> + * If the indexed certificate does not contain a DSA key this method simply + * returns the public key. If the DSA key already contains DSA parameters + * the key is also only returned. + * </p> + * + * @param certs The certification path. + * @param index The index of the certificate which contains the public key + * which should be extended with DSA parameters. + * @return The public key of the certificate in list position + * <code>index</code> extended with DSA parameters if applicable. + * @throws Exception if DSA parameters cannot be inherited. + */ + internal static AsymmetricKeyParameter GetNextWorkingKey( + IList certs, + int index) + { + //Only X509Certificate + X509Certificate cert = (X509Certificate)certs[index]; + + AsymmetricKeyParameter pubKey = cert.GetPublicKey(); + + if (!(pubKey is DsaPublicKeyParameters)) + return pubKey; + + DsaPublicKeyParameters dsaPubKey = (DsaPublicKeyParameters)pubKey; + + if (dsaPubKey.Parameters != null) + return dsaPubKey; + + for (int i = index + 1; i < certs.Count; i++) + { + X509Certificate parentCert = (X509Certificate)certs[i]; + pubKey = parentCert.GetPublicKey(); + + if (!(pubKey is DsaPublicKeyParameters)) + { + throw new PkixCertPathValidatorException( + "DSA parameters cannot be inherited from previous certificate."); + } + + DsaPublicKeyParameters prevDSAPubKey = (DsaPublicKeyParameters)pubKey; + + if (prevDSAPubKey.Parameters == null) + continue; + + DsaParameters dsaParams = prevDSAPubKey.Parameters; + + try + { + return new DsaPublicKeyParameters(dsaPubKey.Y, dsaParams); + } + catch (Exception exception) + { + throw new Exception(exception.Message); + } + } + + throw new PkixCertPathValidatorException("DSA parameters cannot be inherited from previous certificate."); + } + + internal static DateTime GetValidCertDateFromValidityModel( + PkixParameters paramsPkix, + PkixCertPath certPath, + int index) + { + if (paramsPkix.ValidityModel != PkixParameters.ChainValidityModel) + { + return GetValidDate(paramsPkix); + } + + // if end cert use given signing/encryption/... time + if (index <= 0) + { + return PkixCertPathValidatorUtilities.GetValidDate(paramsPkix); + // else use time when previous cert was created + } + + if (index - 1 == 0) + { + DerGeneralizedTime dateOfCertgen = null; + try + { + X509Certificate cert = (X509Certificate)certPath.Certificates[index - 1]; + Asn1OctetString extVal = cert.GetExtensionValue( + IsisMttObjectIdentifiers.IdIsisMttATDateOfCertGen); + dateOfCertgen = DerGeneralizedTime.GetInstance(extVal); + } + catch (ArgumentException) + { + throw new Exception( + "Date of cert gen extension could not be read."); + } + if (dateOfCertgen != null) + { + try + { + return dateOfCertgen.ToDateTime(); + } + catch (ArgumentException e) + { + throw new Exception( + "Date from date of cert gen extension could not be parsed.", + e); + } + } + } + + return ((X509Certificate)certPath.Certificates[index - 1]).NotBefore; + } + + /// <summary> + /// Return a Collection of all certificates or attribute certificates found + /// in the X509Store's that are matching the certSelect criteriums. + /// </summary> + /// <param name="certSelect">a {@link Selector} object that will be used to select + /// the certificates</param> + /// <param name="certStores">a List containing only X509Store objects. These + /// are used to search for certificates.</param> + /// <returns>a Collection of all found <see cref="X509Certificate "/> or + /// org.bouncycastle.x509.X509AttributeCertificate objects. + /// May be empty but never <code>null</code>.</returns> + /// <exception cref="Exception"></exception> + internal static ICollection FindCertificates( + X509CertStoreSelector certSelect, + IList certStores) + { + ISet certs = new HashSet(); + + foreach (IX509Store certStore in certStores) + { + try + { +// certs.AddAll(certStore.GetMatches(certSelect)); + foreach (X509Certificate c in certStore.GetMatches(certSelect)) + { + certs.Add(c); + } + } + catch (Exception e) + { + throw new Exception("Problem while picking certificates from X.509 store.", e); + } + } + + return certs; + } + + /** + * Add the CRL issuers from the cRLIssuer field of the distribution point or + * from the certificate if not given to the issuer criterion of the + * <code>selector</code>. + * <p> + * The <code>issuerPrincipals</code> are a collection with a single + * <code>X500Principal</code> for <code>X509Certificate</code>s. For + * {@link X509AttributeCertificate}s the issuer may contain more than one + * <code>X500Principal</code>. + * </p> + * + * @param dp The distribution point. + * @param issuerPrincipals The issuers of the certificate or attribute + * certificate which contains the distribution point. + * @param selector The CRL selector. + * @param pkixParams The PKIX parameters containing the cert stores. + * @throws Exception if an exception occurs while processing. + * @throws ClassCastException if <code>issuerPrincipals</code> does not + * contain only <code>X500Principal</code>s. + */ + internal static void GetCrlIssuersFromDistributionPoint( + DistributionPoint dp, + ICollection issuerPrincipals, + X509CrlStoreSelector selector, + PkixParameters pkixParams) + { + IList issuers = Platform.CreateArrayList(); + // indirect CRL + if (dp.CrlIssuer != null) + { + GeneralName[] genNames = dp.CrlIssuer.GetNames(); + // look for a DN + for (int j = 0; j < genNames.Length; j++) + { + if (genNames[j].TagNo == GeneralName.DirectoryName) + { + try + { + issuers.Add(X509Name.GetInstance(genNames[j].Name.ToAsn1Object())); + } + catch (IOException e) + { + throw new Exception( + "CRL issuer information from distribution point cannot be decoded.", + e); + } + } + } + } + else + { + /* + * certificate issuer is CRL issuer, distributionPoint field MUST be + * present. + */ + if (dp.DistributionPointName == null) + { + throw new Exception( + "CRL issuer is omitted from distribution point but no distributionPoint field present."); + } + + // add and check issuer principals + for (IEnumerator it = issuerPrincipals.GetEnumerator(); it.MoveNext(); ) + { + issuers.Add((X509Name)it.Current); + } + } + // TODO: is not found although this should correctly add the rel name. selector of Sun is buggy here or PKI test case is invalid + // distributionPoint + // if (dp.getDistributionPoint() != null) + // { + // // look for nameRelativeToCRLIssuer + // if (dp.getDistributionPoint().getType() == DistributionPointName.NAME_RELATIVE_TO_CRL_ISSUER) + // { + // // append fragment to issuer, only one + // // issuer can be there, if this is given + // if (issuers.size() != 1) + // { + // throw new AnnotatedException( + // "nameRelativeToCRLIssuer field is given but more than one CRL issuer is given."); + // } + // DEREncodable relName = dp.getDistributionPoint().getName(); + // Iterator it = issuers.iterator(); + // List issuersTemp = new ArrayList(issuers.size()); + // while (it.hasNext()) + // { + // Enumeration e = null; + // try + // { + // e = ASN1Sequence.getInstance( + // new ASN1InputStream(((X500Principal) it.next()) + // .getEncoded()).readObject()).getObjects(); + // } + // catch (IOException ex) + // { + // throw new AnnotatedException( + // "Cannot decode CRL issuer information.", ex); + // } + // ASN1EncodableVector v = new ASN1EncodableVector(); + // while (e.hasMoreElements()) + // { + // v.add((DEREncodable) e.nextElement()); + // } + // v.add(relName); + // issuersTemp.add(new X500Principal(new DERSequence(v) + // .getDEREncoded())); + // } + // issuers.clear(); + // issuers.addAll(issuersTemp); + // } + // } + + selector.Issuers = issuers; + } + + /** + * Fetches complete CRLs according to RFC 3280. + * + * @param dp The distribution point for which the complete CRL + * @param cert The <code>X509Certificate</code> or + * {@link org.bouncycastle.x509.X509AttributeCertificate} for + * which the CRL should be searched. + * @param currentDate The date for which the delta CRLs must be valid. + * @param paramsPKIX The extended PKIX parameters. + * @return A <code>Set</code> of <code>X509CRL</code>s with complete + * CRLs. + * @throws Exception if an exception occurs while picking the CRLs + * or no CRLs are found. + */ + internal static ISet GetCompleteCrls( + DistributionPoint dp, + object cert, + DateTime currentDate, + PkixParameters paramsPKIX) + { + X509CrlStoreSelector crlselect = new X509CrlStoreSelector(); + try + { + ISet issuers = new HashSet(); + if (cert is X509V2AttributeCertificate) + { + issuers.Add(((X509V2AttributeCertificate)cert) + .Issuer.GetPrincipals()[0]); + } + else + { + issuers.Add(GetIssuerPrincipal(cert)); + } + PkixCertPathValidatorUtilities.GetCrlIssuersFromDistributionPoint(dp, issuers, crlselect, paramsPKIX); + } + catch (Exception e) + { + new Exception("Could not get issuer information from distribution point.", e); + } + + if (cert is X509Certificate) + { + crlselect.CertificateChecking = (X509Certificate)cert; + } + else if (cert is X509V2AttributeCertificate) + { + crlselect.AttrCertChecking = (IX509AttributeCertificate)cert; + } + + crlselect.CompleteCrlEnabled = true; + ISet crls = CrlUtilities.FindCrls(crlselect, paramsPKIX, currentDate); + + if (crls.IsEmpty) + { + if (cert is IX509AttributeCertificate) + { + IX509AttributeCertificate aCert = (IX509AttributeCertificate)cert; + + throw new Exception("No CRLs found for issuer \"" + aCert.Issuer.GetPrincipals()[0] + "\""); + } + else + { + X509Certificate xCert = (X509Certificate)cert; + + throw new Exception("No CRLs found for issuer \"" + xCert.IssuerDN + "\""); + } + } + + return crls; + } + + /** + * Fetches delta CRLs according to RFC 3280 section 5.2.4. + * + * @param currentDate The date for which the delta CRLs must be valid. + * @param paramsPKIX The extended PKIX parameters. + * @param completeCRL The complete CRL the delta CRL is for. + * @return A <code>Set</code> of <code>X509CRL</code>s with delta CRLs. + * @throws Exception if an exception occurs while picking the delta + * CRLs. + */ + internal static ISet GetDeltaCrls( + DateTime currentDate, + PkixParameters paramsPKIX, + X509Crl completeCRL) + { + X509CrlStoreSelector deltaSelect = new X509CrlStoreSelector(); + + // 5.2.4 (a) + try + { + IList deltaSelectIssuer = Platform.CreateArrayList(); + deltaSelectIssuer.Add(completeCRL.IssuerDN); + deltaSelect.Issuers = deltaSelectIssuer; + } + catch (IOException e) + { + new Exception("Cannot extract issuer from CRL.", e); + } + + BigInteger completeCRLNumber = null; + try + { + Asn1Object asn1Object = GetExtensionValue(completeCRL, X509Extensions.CrlNumber); + if (asn1Object != null) + { + completeCRLNumber = CrlNumber.GetInstance(asn1Object).PositiveValue; + } + } + catch (Exception e) + { + throw new Exception( + "CRL number extension could not be extracted from CRL.", e); + } + + // 5.2.4 (b) + byte[] idp = null; + + try + { + Asn1Object obj = GetExtensionValue(completeCRL, X509Extensions.IssuingDistributionPoint); + if (obj != null) + { + idp = obj.GetDerEncoded(); + } + } + catch (Exception e) + { + throw new Exception( + "Issuing distribution point extension value could not be read.", + e); + } + + // 5.2.4 (d) + + deltaSelect.MinCrlNumber = (completeCRLNumber == null) + ? null + : completeCRLNumber.Add(BigInteger.One); + + deltaSelect.IssuingDistributionPoint = idp; + deltaSelect.IssuingDistributionPointEnabled = true; + + // 5.2.4 (c) + deltaSelect.MaxBaseCrlNumber = completeCRLNumber; + + // find delta CRLs + ISet temp = CrlUtilities.FindCrls(deltaSelect, paramsPKIX, currentDate); + + ISet result = new HashSet(); + + foreach (X509Crl crl in temp) + { + if (isDeltaCrl(crl)) + { + result.Add(crl); + } + } + + return result; + } + + private static bool isDeltaCrl( + X509Crl crl) + { + ISet critical = crl.GetCriticalExtensionOids(); + + return critical.Contains(X509Extensions.DeltaCrlIndicator.Id); + } + + internal static ICollection FindCertificates( + X509AttrCertStoreSelector certSelect, + IList certStores) + { + ISet certs = new HashSet(); + + foreach (IX509Store certStore in certStores) + { + try + { +// certs.AddAll(certStore.GetMatches(certSelect)); + foreach (X509V2AttributeCertificate ac in certStore.GetMatches(certSelect)) + { + certs.Add(ac); + } + } + catch (Exception e) + { + throw new Exception( + "Problem while picking certificates from X.509 store.", e); + } + } + + return certs; + } + + internal static void AddAdditionalStoresFromCrlDistributionPoint( + CrlDistPoint crldp, + PkixParameters pkixParams) + { + if (crldp != null) + { + DistributionPoint[] dps = null; + try + { + dps = crldp.GetDistributionPoints(); + } + catch (Exception e) + { + throw new Exception( + "Distribution points could not be read.", e); + } + for (int i = 0; i < dps.Length; i++) + { + DistributionPointName dpn = dps[i].DistributionPointName; + // look for URIs in fullName + if (dpn != null) + { + if (dpn.PointType == DistributionPointName.FullName) + { + GeneralName[] genNames = GeneralNames.GetInstance( + dpn.Name).GetNames(); + // look for an URI + for (int j = 0; j < genNames.Length; j++) + { + if (genNames[j].TagNo == GeneralName.UniformResourceIdentifier) + { + string location = DerIA5String.GetInstance( + genNames[j].Name).GetString(); + PkixCertPathValidatorUtilities.AddAdditionalStoreFromLocation( + location, pkixParams); + } + } + } + } + } + } + } + + internal static bool ProcessCertD1i( + int index, + IList[] policyNodes, + DerObjectIdentifier pOid, + ISet pq) + { + IList policyNodeVec = policyNodes[index - 1]; + + for (int j = 0; j < policyNodeVec.Count; j++) + { + PkixPolicyNode node = (PkixPolicyNode)policyNodeVec[j]; + ISet expectedPolicies = node.ExpectedPolicies; + + if (expectedPolicies.Contains(pOid.Id)) + { + ISet childExpectedPolicies = new HashSet(); + childExpectedPolicies.Add(pOid.Id); + + PkixPolicyNode child = new PkixPolicyNode(Platform.CreateArrayList(), + index, + childExpectedPolicies, + node, + pq, + pOid.Id, + false); + node.AddChild(child); + policyNodes[index].Add(child); + + return true; + } + } + + return false; + } + + internal static void ProcessCertD1ii( + int index, + IList[] policyNodes, + DerObjectIdentifier _poid, + ISet _pq) + { + IList policyNodeVec = policyNodes[index - 1]; + + for (int j = 0; j < policyNodeVec.Count; j++) + { + PkixPolicyNode _node = (PkixPolicyNode)policyNodeVec[j]; + + if (ANY_POLICY.Equals(_node.ValidPolicy)) + { + ISet _childExpectedPolicies = new HashSet(); + _childExpectedPolicies.Add(_poid.Id); + + PkixPolicyNode _child = new PkixPolicyNode(Platform.CreateArrayList(), + index, + _childExpectedPolicies, + _node, + _pq, + _poid.Id, + false); + _node.AddChild(_child); + policyNodes[index].Add(_child); + return; + } + } + } + + /** + * Find the issuer certificates of a given certificate. + * + * @param cert + * The certificate for which an issuer should be found. + * @param pkixParams + * @return A <code>Collection</code> object containing the issuer + * <code>X509Certificate</code>s. Never <code>null</code>. + * + * @exception Exception + * if an error occurs. + */ + internal static ICollection FindIssuerCerts( + X509Certificate cert, + PkixBuilderParameters pkixParams) + { + X509CertStoreSelector certSelect = new X509CertStoreSelector(); + ISet certs = new HashSet(); + try + { + certSelect.Subject = cert.IssuerDN; + } + catch (IOException ex) + { + throw new Exception( + "Subject criteria for certificate selector to find issuer certificate could not be set.", ex); + } + + try + { + certs.AddAll(PkixCertPathValidatorUtilities.FindCertificates(certSelect, pkixParams.GetStores())); + certs.AddAll(PkixCertPathValidatorUtilities.FindCertificates(certSelect, pkixParams.GetAdditionalStores())); + } + catch (Exception e) + { + throw new Exception("Issuer certificate cannot be searched.", e); + } + + return certs; + } + + /// <summary> + /// Extract the value of the given extension, if it exists. + /// </summary> + /// <param name="ext">The extension object.</param> + /// <param name="oid">The object identifier to obtain.</param> + /// <returns>Asn1Object</returns> + /// <exception cref="Exception">if the extension cannot be read.</exception> + internal static Asn1Object GetExtensionValue( + IX509Extension ext, + DerObjectIdentifier oid) + { + Asn1OctetString bytes = ext.GetExtensionValue(oid); + + if (bytes == null) + return null; + + return X509ExtensionUtilities.FromExtensionValue(bytes); + } + } +} diff --git a/Crypto/src/pkix/PkixCrlUtilities.cs b/Crypto/src/pkix/PkixCrlUtilities.cs new file mode 100644
index 000000000..c386b8a05 --- /dev/null +++ b/Crypto/src/pkix/PkixCrlUtilities.cs
@@ -0,0 +1,114 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.Pkix +{ + public class PkixCrlUtilities + { + public virtual ISet FindCrls(X509CrlStoreSelector crlselect, PkixParameters paramsPkix, DateTime currentDate) + { + ISet initialSet = new HashSet(); + + // get complete CRL(s) + try + { + initialSet.AddAll(FindCrls(crlselect, paramsPkix.GetAdditionalStores())); + initialSet.AddAll(FindCrls(crlselect, paramsPkix.GetStores())); + } + catch (Exception e) + { + throw new Exception("Exception obtaining complete CRLs.", e); + } + + ISet finalSet = new HashSet(); + DateTime validityDate = currentDate; + + if (paramsPkix.Date != null) + { + validityDate = paramsPkix.Date.Value; + } + + // based on RFC 5280 6.3.3 + foreach (X509Crl crl in initialSet) + { + if (crl.NextUpdate.Value.CompareTo(validityDate) > 0) + { + X509Certificate cert = crlselect.CertificateChecking; + + if (cert != null) + { + if (crl.ThisUpdate.CompareTo(cert.NotAfter) < 0) + { + finalSet.Add(crl); + } + } + else + { + finalSet.Add(crl); + } + } + } + + return finalSet; + } + + public virtual ISet FindCrls(X509CrlStoreSelector crlselect, PkixParameters paramsPkix) + { + ISet completeSet = new HashSet(); + + // get complete CRL(s) + try + { + completeSet.AddAll(FindCrls(crlselect, paramsPkix.GetStores())); + } + catch (Exception e) + { + throw new Exception("Exception obtaining complete CRLs.", e); + } + + return completeSet; + } + + /// <summary> + /// crl checking + /// Return a Collection of all CRLs found in the X509Store's that are + /// matching the crlSelect criteriums. + /// </summary> + /// <param name="crlSelect">a {@link X509CRLStoreSelector} object that will be used + /// to select the CRLs</param> + /// <param name="crlStores">a List containing only {@link org.bouncycastle.x509.X509Store + /// X509Store} objects. These are used to search for CRLs</param> + /// <returns>a Collection of all found {@link X509CRL X509CRL} objects. May be + /// empty but never <code>null</code>. + /// </returns> + private ICollection FindCrls(X509CrlStoreSelector crlSelect, IList crlStores) + { + ISet crls = new HashSet(); + + Exception lastException = null; + bool foundValidStore = false; + + foreach (IX509Store store in crlStores) + { + try + { + crls.AddAll(store.GetMatches(crlSelect)); + foundValidStore = true; + } + catch (X509StoreException e) + { + lastException = new Exception("Exception searching in X.509 CRL store.", e); + } + } + + if (!foundValidStore && lastException != null) + throw lastException; + + return crls; + } + } +} diff --git a/Crypto/src/pkix/PkixNameConstraintValidator.cs b/Crypto/src/pkix/PkixNameConstraintValidator.cs new file mode 100644
index 000000000..535f95174 --- /dev/null +++ b/Crypto/src/pkix/PkixNameConstraintValidator.cs
@@ -0,0 +1,1937 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Pkix +{ + public class PkixNameConstraintValidator + { + private ISet excludedSubtreesDN = new HashSet(); + + private ISet excludedSubtreesDNS = new HashSet(); + + private ISet excludedSubtreesEmail = new HashSet(); + + private ISet excludedSubtreesURI = new HashSet(); + + private ISet excludedSubtreesIP = new HashSet(); + + private ISet permittedSubtreesDN; + + private ISet permittedSubtreesDNS; + + private ISet permittedSubtreesEmail; + + private ISet permittedSubtreesURI; + + private ISet permittedSubtreesIP; + + public PkixNameConstraintValidator() + { + } + + private static bool WithinDNSubtree( + Asn1Sequence dns, + Asn1Sequence subtree) + { + if (subtree.Count < 1) + { + return false; + } + + if (subtree.Count > dns.Count) + { + return false; + } + + for (int j = subtree.Count - 1; j >= 0; j--) + { + if (!(subtree[j].Equals(dns[j]))) + { + return false; + } + } + + return true; + } + + public void CheckPermittedDN(Asn1Sequence dns) + //throws PkixNameConstraintValidatorException + { + CheckPermittedDN(permittedSubtreesDN, dns); + } + + public void CheckExcludedDN(Asn1Sequence dns) + //throws PkixNameConstraintValidatorException + { + CheckExcludedDN(excludedSubtreesDN, dns); + } + + private void CheckPermittedDN(ISet permitted, Asn1Sequence dns) + //throws PkixNameConstraintValidatorException + { + if (permitted == null) + { + return; + } + + if ((permitted.Count == 0) && dns.Count == 0) + { + return; + } + + IEnumerator it = permitted.GetEnumerator(); + + while (it.MoveNext()) + { + Asn1Sequence subtree = (Asn1Sequence)it.Current; + + if (WithinDNSubtree(dns, subtree)) + { + return; + } + } + + throw new PkixNameConstraintValidatorException( + "Subject distinguished name is not from a permitted subtree"); + } + + private void CheckExcludedDN(ISet excluded, Asn1Sequence dns) + //throws PkixNameConstraintValidatorException + { + if (excluded.IsEmpty) + { + return; + } + + IEnumerator it = excluded.GetEnumerator(); + + while (it.MoveNext()) + { + Asn1Sequence subtree = (Asn1Sequence)it.Current; + + if (WithinDNSubtree(dns, subtree)) + { + throw new PkixNameConstraintValidatorException( + "Subject distinguished name is from an excluded subtree"); + } + } + } + + private ISet IntersectDN(ISet permitted, ISet dns) + { + ISet intersect = new HashSet(); + for (IEnumerator it = dns.GetEnumerator(); it.MoveNext(); ) + { + Asn1Sequence dn = Asn1Sequence.GetInstance(((GeneralSubtree)it + .Current).Base.Name.ToAsn1Object()); + if (permitted == null) + { + if (dn != null) + { + intersect.Add(dn); + } + } + else + { + IEnumerator _iter = permitted.GetEnumerator(); + while (_iter.MoveNext()) + { + Asn1Sequence subtree = (Asn1Sequence)_iter.Current; + + if (WithinDNSubtree(dn, subtree)) + { + intersect.Add(dn); + } + else if (WithinDNSubtree(subtree, dn)) + { + intersect.Add(subtree); + } + } + } + } + return intersect; + } + + private ISet UnionDN(ISet excluded, Asn1Sequence dn) + { + if (excluded.IsEmpty) + { + if (dn == null) + { + return excluded; + } + excluded.Add(dn); + + return excluded; + } + else + { + ISet intersect = new HashSet(); + + IEnumerator it = excluded.GetEnumerator(); + while (it.MoveNext()) + { + Asn1Sequence subtree = (Asn1Sequence)it.Current; + + if (WithinDNSubtree(dn, subtree)) + { + intersect.Add(subtree); + } + else if (WithinDNSubtree(subtree, dn)) + { + intersect.Add(dn); + } + else + { + intersect.Add(subtree); + intersect.Add(dn); + } + } + + return intersect; + } + } + + private ISet IntersectEmail(ISet permitted, ISet emails) + { + ISet intersect = new HashSet(); + for (IEnumerator it = emails.GetEnumerator(); it.MoveNext(); ) + { + String email = ExtractNameAsString(((GeneralSubtree)it.Current) + .Base); + + if (permitted == null) + { + if (email != null) + { + intersect.Add(email); + } + } + else + { + IEnumerator it2 = permitted.GetEnumerator(); + while (it2.MoveNext()) + { + String _permitted = (String)it2.Current; + + intersectEmail(email, _permitted, intersect); + } + } + } + return intersect; + } + + private ISet UnionEmail(ISet excluded, String email) + { + if (excluded.IsEmpty) + { + if (email == null) + { + return excluded; + } + excluded.Add(email); + return excluded; + } + else + { + ISet union = new HashSet(); + + IEnumerator it = excluded.GetEnumerator(); + while (it.MoveNext()) + { + String _excluded = (String)it.Current; + + unionEmail(_excluded, email, union); + } + + return union; + } + } + + /** + * Returns the intersection of the permitted IP ranges in + * <code>permitted</code> with <code>ip</code>. + * + * @param permitted A <code>Set</code> of permitted IP addresses with + * their subnet mask as byte arrays. + * @param ips The IP address with its subnet mask. + * @return The <code>Set</code> of permitted IP ranges intersected with + * <code>ip</code>. + */ + private ISet IntersectIP(ISet permitted, ISet ips) + { + ISet intersect = new HashSet(); + for (IEnumerator it = ips.GetEnumerator(); it.MoveNext(); ) + { + byte[] ip = Asn1OctetString.GetInstance( + ((GeneralSubtree)it.Current).Base.Name).GetOctets(); + if (permitted == null) + { + if (ip != null) + { + intersect.Add(ip); + } + } + else + { + IEnumerator it2 = permitted.GetEnumerator(); + while (it2.MoveNext()) + { + byte[] _permitted = (byte[])it2.Current; + intersect.AddAll(IntersectIPRange(_permitted, ip)); + } + } + } + return intersect; + } + + /** + * Returns the union of the excluded IP ranges in <code>excluded</code> + * with <code>ip</code>. + * + * @param excluded A <code>Set</code> of excluded IP addresses with their + * subnet mask as byte arrays. + * @param ip The IP address with its subnet mask. + * @return The <code>Set</code> of excluded IP ranges unified with + * <code>ip</code> as byte arrays. + */ + private ISet UnionIP(ISet excluded, byte[] ip) + { + if (excluded.IsEmpty) + { + if (ip == null) + { + return excluded; + } + excluded.Add(ip); + + return excluded; + } + else + { + ISet union = new HashSet(); + + IEnumerator it = excluded.GetEnumerator(); + while (it.MoveNext()) + { + byte[] _excluded = (byte[])it.Current; + union.AddAll(UnionIPRange(_excluded, ip)); + } + + return union; + } + } + + /** + * Calculates the union if two IP ranges. + * + * @param ipWithSubmask1 The first IP address with its subnet mask. + * @param ipWithSubmask2 The second IP address with its subnet mask. + * @return A <code>Set</code> with the union of both addresses. + */ + private ISet UnionIPRange(byte[] ipWithSubmask1, byte[] ipWithSubmask2) + { + ISet set = new HashSet(); + + // difficult, adding always all IPs is not wrong + if (Org.BouncyCastle.Utilities.Arrays.AreEqual(ipWithSubmask1, ipWithSubmask2)) + { + set.Add(ipWithSubmask1); + } + else + { + set.Add(ipWithSubmask1); + set.Add(ipWithSubmask2); + } + return set; + } + + /** + * Calculates the interesction if two IP ranges. + * + * @param ipWithSubmask1 The first IP address with its subnet mask. + * @param ipWithSubmask2 The second IP address with its subnet mask. + * @return A <code>Set</code> with the single IP address with its subnet + * mask as a byte array or an empty <code>Set</code>. + */ + private ISet IntersectIPRange(byte[] ipWithSubmask1, byte[] ipWithSubmask2) + { + if (ipWithSubmask1.Length != ipWithSubmask2.Length) + { + //Collections.EMPTY_SET; + return new HashSet(); + } + + byte[][] temp = ExtractIPsAndSubnetMasks(ipWithSubmask1, ipWithSubmask2); + byte[] ip1 = temp[0]; + byte[] subnetmask1 = temp[1]; + byte[] ip2 = temp[2]; + byte[] subnetmask2 = temp[3]; + + byte[][] minMax = MinMaxIPs(ip1, subnetmask1, ip2, subnetmask2); + byte[] min; + byte[] max; + max = Min(minMax[1], minMax[3]); + min = Max(minMax[0], minMax[2]); + + // minimum IP address must be bigger than max + if (CompareTo(min, max) == 1) + { + //return Collections.EMPTY_SET; + return new HashSet(); + } + // OR keeps all significant bits + byte[] ip = Or(minMax[0], minMax[2]); + byte[] subnetmask = Or(subnetmask1, subnetmask2); + + //return new HashSet( ICollectionsingleton(IpWithSubnetMask(ip, subnetmask)); + ISet hs = new HashSet(); + hs.Add(IpWithSubnetMask(ip, subnetmask)); + + return hs; + } + + /** + * Concatenates the IP address with its subnet mask. + * + * @param ip The IP address. + * @param subnetMask Its subnet mask. + * @return The concatenated IP address with its subnet mask. + */ + private byte[] IpWithSubnetMask(byte[] ip, byte[] subnetMask) + { + int ipLength = ip.Length; + byte[] temp = new byte[ipLength * 2]; + Array.Copy(ip, 0, temp, 0, ipLength); + Array.Copy(subnetMask, 0, temp, ipLength, ipLength); + return temp; + } + + /** + * Splits the IP addresses and their subnet mask. + * + * @param ipWithSubmask1 The first IP address with the subnet mask. + * @param ipWithSubmask2 The second IP address with the subnet mask. + * @return An array with two elements. Each element contains the IP address + * and the subnet mask in this order. + */ + private byte[][] ExtractIPsAndSubnetMasks( + byte[] ipWithSubmask1, + byte[] ipWithSubmask2) + { + int ipLength = ipWithSubmask1.Length / 2; + byte[] ip1 = new byte[ipLength]; + byte[] subnetmask1 = new byte[ipLength]; + Array.Copy(ipWithSubmask1, 0, ip1, 0, ipLength); + Array.Copy(ipWithSubmask1, ipLength, subnetmask1, 0, ipLength); + + byte[] ip2 = new byte[ipLength]; + byte[] subnetmask2 = new byte[ipLength]; + Array.Copy(ipWithSubmask2, 0, ip2, 0, ipLength); + Array.Copy(ipWithSubmask2, ipLength, subnetmask2, 0, ipLength); + return new byte[][] + {ip1, subnetmask1, ip2, subnetmask2}; + } + + /** + * Based on the two IP addresses and their subnet masks the IP range is + * computed for each IP address - subnet mask pair and returned as the + * minimum IP address and the maximum address of the range. + * + * @param ip1 The first IP address. + * @param subnetmask1 The subnet mask of the first IP address. + * @param ip2 The second IP address. + * @param subnetmask2 The subnet mask of the second IP address. + * @return A array with two elements. The first/second element contains the + * min and max IP address of the first/second IP address and its + * subnet mask. + */ + private byte[][] MinMaxIPs( + byte[] ip1, + byte[] subnetmask1, + byte[] ip2, + byte[] subnetmask2) + { + int ipLength = ip1.Length; + byte[] min1 = new byte[ipLength]; + byte[] max1 = new byte[ipLength]; + + byte[] min2 = new byte[ipLength]; + byte[] max2 = new byte[ipLength]; + + for (int i = 0; i < ipLength; i++) + { + min1[i] = (byte)(ip1[i] & subnetmask1[i]); + max1[i] = (byte)(ip1[i] & subnetmask1[i] | ~subnetmask1[i]); + + min2[i] = (byte)(ip2[i] & subnetmask2[i]); + max2[i] = (byte)(ip2[i] & subnetmask2[i] | ~subnetmask2[i]); + } + + return new byte[][] { min1, max1, min2, max2 }; + } + + private void CheckPermittedEmail(ISet permitted, String email) + //throws PkixNameConstraintValidatorException + { + if (permitted == null) + { + return; + } + + IEnumerator it = permitted.GetEnumerator(); + + while (it.MoveNext()) + { + String str = ((String)it.Current); + + if (EmailIsConstrained(email, str)) + { + return; + } + } + + if (email.Length == 0 && permitted.Count == 0) + { + return; + } + + throw new PkixNameConstraintValidatorException( + "Subject email address is not from a permitted subtree."); + } + + private void CheckExcludedEmail(ISet excluded, String email) + //throws PkixNameConstraintValidatorException + { + if (excluded.IsEmpty) + { + return; + } + + IEnumerator it = excluded.GetEnumerator(); + + while (it.MoveNext()) + { + String str = (String)it.Current; + + if (EmailIsConstrained(email, str)) + { + throw new PkixNameConstraintValidatorException( + "Email address is from an excluded subtree."); + } + } + } + + /** + * Checks if the IP <code>ip</code> is included in the permitted ISet + * <code>permitted</code>. + * + * @param permitted A <code>Set</code> of permitted IP addresses with + * their subnet mask as byte arrays. + * @param ip The IP address. + * @throws PkixNameConstraintValidatorException + * if the IP is not permitted. + */ + private void CheckPermittedIP(ISet permitted, byte[] ip) + //throws PkixNameConstraintValidatorException + { + if (permitted == null) + { + return; + } + + IEnumerator it = permitted.GetEnumerator(); + + while (it.MoveNext()) + { + byte[] ipWithSubnet = (byte[])it.Current; + + if (IsIPConstrained(ip, ipWithSubnet)) + { + return; + } + } + if (ip.Length == 0 && permitted.Count == 0) + { + return; + } + throw new PkixNameConstraintValidatorException( + "IP is not from a permitted subtree."); + } + + /** + * Checks if the IP <code>ip</code> is included in the excluded ISet + * <code>excluded</code>. + * + * @param excluded A <code>Set</code> of excluded IP addresses with their + * subnet mask as byte arrays. + * @param ip The IP address. + * @throws PkixNameConstraintValidatorException + * if the IP is excluded. + */ + private void checkExcludedIP(ISet excluded, byte[] ip) + //throws PkixNameConstraintValidatorException + { + if (excluded.IsEmpty) + { + return; + } + + IEnumerator it = excluded.GetEnumerator(); + + while (it.MoveNext()) + { + byte[] ipWithSubnet = (byte[])it.Current; + + if (IsIPConstrained(ip, ipWithSubnet)) + { + throw new PkixNameConstraintValidatorException( + "IP is from an excluded subtree."); + } + } + } + + /** + * Checks if the IP address <code>ip</code> is constrained by + * <code>constraint</code>. + * + * @param ip The IP address. + * @param constraint The constraint. This is an IP address concatenated with + * its subnetmask. + * @return <code>true</code> if constrained, <code>false</code> + * otherwise. + */ + private bool IsIPConstrained(byte[] ip, byte[] constraint) + { + int ipLength = ip.Length; + + if (ipLength != (constraint.Length / 2)) + { + return false; + } + + byte[] subnetMask = new byte[ipLength]; + Array.Copy(constraint, ipLength, subnetMask, 0, ipLength); + + byte[] permittedSubnetAddress = new byte[ipLength]; + + byte[] ipSubnetAddress = new byte[ipLength]; + + // the resulting IP address by applying the subnet mask + for (int i = 0; i < ipLength; i++) + { + permittedSubnetAddress[i] = (byte)(constraint[i] & subnetMask[i]); + ipSubnetAddress[i] = (byte)(ip[i] & subnetMask[i]); + } + + return Org.BouncyCastle.Utilities.Arrays.AreEqual(permittedSubnetAddress, ipSubnetAddress); + } + + private bool EmailIsConstrained(String email, String constraint) + { + String sub = email.Substring(email.IndexOf('@') + 1); + // a particular mailbox + if (constraint.IndexOf('@') != -1) + { + if (email.ToUpper().Equals(constraint.ToUpper())) + { + return true; + } + } + // on particular host + else if (!(constraint[0].Equals('.'))) + { + if (sub.ToUpper().Equals(constraint.ToUpper())) + { + return true; + } + } + // address in sub domain + else if (WithinDomain(sub, constraint)) + { + return true; + } + return false; + } + + private bool WithinDomain(String testDomain, String domain) + { + String tempDomain = domain; + if (tempDomain.StartsWith(".")) + { + tempDomain = tempDomain.Substring(1); + } + String[] domainParts = tempDomain.Split('.'); // Strings.split(tempDomain, '.'); + String[] testDomainParts = testDomain.Split('.'); // Strings.split(testDomain, '.'); + + // must have at least one subdomain + if (testDomainParts.Length <= domainParts.Length) + { + return false; + } + + int d = testDomainParts.Length - domainParts.Length; + for (int i = -1; i < domainParts.Length; i++) + { + if (i == -1) + { + if (testDomainParts[i + d].Equals("")) + { + return false; + } + } + else if (!(Platform.CompareIgnoreCase(testDomainParts[i + d], domainParts[i]) == 0)) + { + return false; + } + } + return true; + } + + private void CheckPermittedDNS(ISet permitted, String dns) + //throws PkixNameConstraintValidatorException + { + if (permitted == null) + { + return; + } + + IEnumerator it = permitted.GetEnumerator(); + + while (it.MoveNext()) + { + String str = ((String)it.Current); + + // is sub domain + if (WithinDomain(dns, str) || dns.ToUpper().Equals(str.ToUpper())) + { + return; + } + } + if (dns.Length == 0 && permitted.Count == 0) + { + return; + } + throw new PkixNameConstraintValidatorException( + "DNS is not from a permitted subtree."); + } + + private void checkExcludedDNS(ISet excluded, String dns) + // throws PkixNameConstraintValidatorException + { + if (excluded.IsEmpty) + { + return; + } + + IEnumerator it = excluded.GetEnumerator(); + + while (it.MoveNext()) + { + String str = ((String)it.Current); + + // is sub domain or the same + if (WithinDomain(dns, str) || (Platform.CompareIgnoreCase(dns, str) == 0)) + { + throw new PkixNameConstraintValidatorException( + "DNS is from an excluded subtree."); + } + } + } + + /** + * The common part of <code>email1</code> and <code>email2</code> is + * added to the union <code>union</code>. If <code>email1</code> and + * <code>email2</code> have nothing in common they are added both. + * + * @param email1 Email address constraint 1. + * @param email2 Email address constraint 2. + * @param union The union. + */ + private void unionEmail(String email1, String email2, ISet union) + { + // email1 is a particular address + if (email1.IndexOf('@') != -1) + { + String _sub = email1.Substring(email1.IndexOf('@') + 1); + // both are a particular mailbox + if (email2.IndexOf('@') != -1) + { + if (Platform.CompareIgnoreCase(email1, email2) == 0) + { + union.Add(email1); + } + else + { + union.Add(email1); + union.Add(email2); + } + } + // email2 specifies a domain + else if (email2.StartsWith(".")) + { + if (WithinDomain(_sub, email2)) + { + union.Add(email2); + } + else + { + union.Add(email1); + union.Add(email2); + } + } + // email2 specifies a particular host + else + { + if (Platform.CompareIgnoreCase(_sub, email2) == 0) + { + union.Add(email2); + } + else + { + union.Add(email1); + union.Add(email2); + } + } + } + // email1 specifies a domain + else if (email1.StartsWith(".")) + { + if (email2.IndexOf('@') != -1) + { + String _sub = email2.Substring(email1.IndexOf('@') + 1); + if (WithinDomain(_sub, email1)) + { + union.Add(email1); + } + else + { + union.Add(email1); + union.Add(email2); + } + } + // email2 specifies a domain + else if (email2.StartsWith(".")) + { + if (WithinDomain(email1, email2) || Platform.CompareIgnoreCase(email1, email2) == 0) + { + union.Add(email2); + } + else if (WithinDomain(email2, email1)) + { + union.Add(email1); + } + else + { + union.Add(email1); + union.Add(email2); + } + } + else + { + if (WithinDomain(email2, email1)) + { + union.Add(email1); + } + else + { + union.Add(email1); + union.Add(email2); + } + } + } + // email specifies a host + else + { + if (email2.IndexOf('@') != -1) + { + String _sub = email2.Substring(email1.IndexOf('@') + 1); + if (Platform.CompareIgnoreCase(_sub, email1) == 0) + { + union.Add(email1); + } + else + { + union.Add(email1); + union.Add(email2); + } + } + // email2 specifies a domain + else if (email2.StartsWith(".")) + { + if (WithinDomain(email1, email2)) + { + union.Add(email2); + } + else + { + union.Add(email1); + union.Add(email2); + } + } + // email2 specifies a particular host + else + { + if (Platform.CompareIgnoreCase(email1, email2) == 0) + { + union.Add(email1); + } + else + { + union.Add(email1); + union.Add(email2); + } + } + } + } + + private void unionURI(String email1, String email2, ISet union) + { + // email1 is a particular address + if (email1.IndexOf('@') != -1) + { + String _sub = email1.Substring(email1.IndexOf('@') + 1); + // both are a particular mailbox + if (email2.IndexOf('@') != -1) + { + if (Platform.CompareIgnoreCase(email1, email2) == 0) + { + union.Add(email1); + } + else + { + union.Add(email1); + union.Add(email2); + } + } + // email2 specifies a domain + else if (email2.StartsWith(".")) + { + if (WithinDomain(_sub, email2)) + { + union.Add(email2); + } + else + { + union.Add(email1); + union.Add(email2); + } + } + // email2 specifies a particular host + else + { + if (Platform.CompareIgnoreCase(_sub, email2) == 0) + { + union.Add(email2); + } + else + { + union.Add(email1); + union.Add(email2); + + } + } + } + // email1 specifies a domain + else if (email1.StartsWith(".")) + { + if (email2.IndexOf('@') != -1) + { + String _sub = email2.Substring(email1.IndexOf('@') + 1); + if (WithinDomain(_sub, email1)) + { + union.Add(email1); + } + else + { + union.Add(email1); + union.Add(email2); + } + } + // email2 specifies a domain + else if (email2.StartsWith(".")) + { + if (WithinDomain(email1, email2) || Platform.CompareIgnoreCase(email1, email2) == 0) + { + union.Add(email2); + } + else if (WithinDomain(email2, email1)) + { + union.Add(email1); + } + else + { + union.Add(email1); + union.Add(email2); + } + } + else + { + if (WithinDomain(email2, email1)) + { + union.Add(email1); + } + else + { + union.Add(email1); + union.Add(email2); + } + } + } + // email specifies a host + else + { + if (email2.IndexOf('@') != -1) + { + String _sub = email2.Substring(email1.IndexOf('@') + 1); + if (Platform.CompareIgnoreCase(_sub, email1) == 0) + { + union.Add(email1); + } + else + { + union.Add(email1); + union.Add(email2); + } + } + // email2 specifies a domain + else if (email2.StartsWith(".")) + { + if (WithinDomain(email1, email2)) + { + union.Add(email2); + } + else + { + union.Add(email1); + union.Add(email2); + } + } + // email2 specifies a particular host + else + { + if (Platform.CompareIgnoreCase(email1, email2) == 0) + { + union.Add(email1); + } + else + { + union.Add(email1); + union.Add(email2); + } + } + } + } + + private ISet intersectDNS(ISet permitted, ISet dnss) + { + ISet intersect = new HashSet(); + for (IEnumerator it = dnss.GetEnumerator(); it.MoveNext(); ) + { + String dns = ExtractNameAsString(((GeneralSubtree)it.Current) + .Base); + if (permitted == null) + { + if (dns != null) + { + intersect.Add(dns); + } + } + else + { + IEnumerator _iter = permitted.GetEnumerator(); + while (_iter.MoveNext()) + { + String _permitted = (String)_iter.Current; + + if (WithinDomain(_permitted, dns)) + { + intersect.Add(_permitted); + } + else if (WithinDomain(dns, _permitted)) + { + intersect.Add(dns); + } + } + } + } + + return intersect; + } + + protected ISet unionDNS(ISet excluded, String dns) + { + if (excluded.IsEmpty) + { + if (dns == null) + { + return excluded; + } + excluded.Add(dns); + + return excluded; + } + else + { + ISet union = new HashSet(); + + IEnumerator _iter = excluded.GetEnumerator(); + while (_iter.MoveNext()) + { + String _permitted = (String)_iter.Current; + + if (WithinDomain(_permitted, dns)) + { + union.Add(dns); + } + else if (WithinDomain(dns, _permitted)) + { + union.Add(_permitted); + } + else + { + union.Add(_permitted); + union.Add(dns); + } + } + + return union; + } + } + + /** + * The most restricting part from <code>email1</code> and + * <code>email2</code> is added to the intersection <code>intersect</code>. + * + * @param email1 Email address constraint 1. + * @param email2 Email address constraint 2. + * @param intersect The intersection. + */ + private void intersectEmail(String email1, String email2, ISet intersect) + { + // email1 is a particular address + if (email1.IndexOf('@') != -1) + { + String _sub = email1.Substring(email1.IndexOf('@') + 1); + // both are a particular mailbox + if (email2.IndexOf('@') != -1) + { + if (Platform.CompareIgnoreCase(email1, email2) == 0) + { + intersect.Add(email1); + } + } + // email2 specifies a domain + else if (email2.StartsWith(".")) + { + if (WithinDomain(_sub, email2)) + { + intersect.Add(email1); + } + } + // email2 specifies a particular host + else + { + if (Platform.CompareIgnoreCase(_sub, email2) == 0) + { + intersect.Add(email1); + } + } + } + // email specifies a domain + else if (email1.StartsWith(".")) + { + if (email2.IndexOf('@') != -1) + { + String _sub = email2.Substring(email1.IndexOf('@') + 1); + if (WithinDomain(_sub, email1)) + { + intersect.Add(email2); + } + } + // email2 specifies a domain + else if (email2.StartsWith(".")) + { + if (WithinDomain(email1, email2) || (Platform.CompareIgnoreCase(email1, email2) == 0)) + { + intersect.Add(email1); + } + else if (WithinDomain(email2, email1)) + { + intersect.Add(email2); + } + } + else + { + if (WithinDomain(email2, email1)) + { + intersect.Add(email2); + } + } + } + // email1 specifies a host + else + { + if (email2.IndexOf('@') != -1) + { + String _sub = email2.Substring(email2.IndexOf('@') + 1); + if (Platform.CompareIgnoreCase(_sub, email1) == 0) + { + intersect.Add(email2); + } + } + // email2 specifies a domain + else if (email2.StartsWith(".")) + { + if (WithinDomain(email1, email2)) + { + intersect.Add(email1); + } + } + // email2 specifies a particular host + else + { + if (Platform.CompareIgnoreCase(email1, email2) == 0) + { + intersect.Add(email1); + } + } + } + } + + private void checkExcludedURI(ISet excluded, String uri) + // throws PkixNameConstraintValidatorException + { + if (excluded.IsEmpty) + { + return; + } + + IEnumerator it = excluded.GetEnumerator(); + + while (it.MoveNext()) + { + String str = ((String)it.Current); + + if (IsUriConstrained(uri, str)) + { + throw new PkixNameConstraintValidatorException( + "URI is from an excluded subtree."); + } + } + } + + private ISet intersectURI(ISet permitted, ISet uris) + { + ISet intersect = new HashSet(); + for (IEnumerator it = uris.GetEnumerator(); it.MoveNext(); ) + { + String uri = ExtractNameAsString(((GeneralSubtree)it.Current) + .Base); + if (permitted == null) + { + if (uri != null) + { + intersect.Add(uri); + } + } + else + { + IEnumerator _iter = permitted.GetEnumerator(); + while (_iter.MoveNext()) + { + String _permitted = (String)_iter.Current; + intersectURI(_permitted, uri, intersect); + } + } + } + return intersect; + } + + private ISet unionURI(ISet excluded, String uri) + { + if (excluded.IsEmpty) + { + if (uri == null) + { + return excluded; + } + excluded.Add(uri); + + return excluded; + } + else + { + ISet union = new HashSet(); + + IEnumerator _iter = excluded.GetEnumerator(); + while (_iter.MoveNext()) + { + String _excluded = (String)_iter.Current; + + unionURI(_excluded, uri, union); + } + + return union; + } + } + + private void intersectURI(String email1, String email2, ISet intersect) + { + // email1 is a particular address + if (email1.IndexOf('@') != -1) + { + String _sub = email1.Substring(email1.IndexOf('@') + 1); + // both are a particular mailbox + if (email2.IndexOf('@') != -1) + { + if (Platform.CompareIgnoreCase(email1, email2) == 0) + { + intersect.Add(email1); + } + } + // email2 specifies a domain + else if (email2.StartsWith(".")) + { + if (WithinDomain(_sub, email2)) + { + intersect.Add(email1); + } + } + // email2 specifies a particular host + else + { + if (Platform.CompareIgnoreCase(_sub, email2) == 0) + { + intersect.Add(email1); + } + } + } + // email specifies a domain + else if (email1.StartsWith(".")) + { + if (email2.IndexOf('@') != -1) + { + String _sub = email2.Substring(email1.IndexOf('@') + 1); + if (WithinDomain(_sub, email1)) + { + intersect.Add(email2); + } + } + // email2 specifies a domain + else if (email2.StartsWith(".")) + { + if (WithinDomain(email1, email2) || (Platform.CompareIgnoreCase(email1, email2) == 0)) + { + intersect.Add(email1); + } + else if (WithinDomain(email2, email1)) + { + intersect.Add(email2); + } + } + else + { + if (WithinDomain(email2, email1)) + { + intersect.Add(email2); + } + } + } + // email1 specifies a host + else + { + if (email2.IndexOf('@') != -1) + { + String _sub = email2.Substring(email2.IndexOf('@') + 1); + if (Platform.CompareIgnoreCase(_sub, email1) == 0) + { + intersect.Add(email2); + } + } + // email2 specifies a domain + else if (email2.StartsWith(".")) + { + if (WithinDomain(email1, email2)) + { + intersect.Add(email1); + } + } + // email2 specifies a particular host + else + { + if (Platform.CompareIgnoreCase(email1, email2) == 0) + { + intersect.Add(email1); + } + } + } + } + + private void CheckPermittedURI(ISet permitted, String uri) + // throws PkixNameConstraintValidatorException + { + if (permitted == null) + { + return; + } + + IEnumerator it = permitted.GetEnumerator(); + + while (it.MoveNext()) + { + String str = ((String)it.Current); + + if (IsUriConstrained(uri, str)) + { + return; + } + } + if (uri.Length == 0 && permitted.Count == 0) + { + return; + } + throw new PkixNameConstraintValidatorException( + "URI is not from a permitted subtree."); + } + + private bool IsUriConstrained(String uri, String constraint) + { + String host = ExtractHostFromURL(uri); + // a host + if (!constraint.StartsWith(".")) + { + if (Platform.CompareIgnoreCase(host, constraint) == 0) + { + return true; + } + } + + // in sub domain or domain + else if (WithinDomain(host, constraint)) + { + return true; + } + + return false; + } + + private static String ExtractHostFromURL(String url) + { + // see RFC 1738 + // remove ':' after protocol, e.g. http: + String sub = url.Substring(url.IndexOf(':') + 1); + // extract host from Common Internet Scheme Syntax, e.g. http:// + if (sub.IndexOf("//") != -1) + { + sub = sub.Substring(sub.IndexOf("//") + 2); + } + // first remove port, e.g. http://test.com:21 + if (sub.LastIndexOf(':') != -1) + { + sub = sub.Substring(0, sub.LastIndexOf(':')); + } + // remove user and password, e.g. http://john:password@test.com + sub = sub.Substring(sub.IndexOf(':') + 1); + sub = sub.Substring(sub.IndexOf('@') + 1); + // remove local parts, e.g. http://test.com/bla + if (sub.IndexOf('/') != -1) + { + sub = sub.Substring(0, sub.IndexOf('/')); + } + return sub; + } + + /** + * Checks if the given GeneralName is in the permitted ISet. + * + * @param name The GeneralName + * @throws PkixNameConstraintValidatorException + * If the <code>name</code> + */ + public void checkPermitted(GeneralName name) + // throws PkixNameConstraintValidatorException + { + switch (name.TagNo) + { + case 1: + CheckPermittedEmail(permittedSubtreesEmail, + ExtractNameAsString(name)); + break; + case 2: + CheckPermittedDNS(permittedSubtreesDNS, DerIA5String.GetInstance( + name.Name).GetString()); + break; + case 4: + CheckPermittedDN(Asn1Sequence.GetInstance(name.Name.ToAsn1Object())); + break; + case 6: + CheckPermittedURI(permittedSubtreesURI, DerIA5String.GetInstance( + name.Name).GetString()); + break; + case 7: + byte[] ip = Asn1OctetString.GetInstance(name.Name).GetOctets(); + + CheckPermittedIP(permittedSubtreesIP, ip); + break; + } + } + + /** + * Check if the given GeneralName is contained in the excluded ISet. + * + * @param name The GeneralName. + * @throws PkixNameConstraintValidatorException + * If the <code>name</code> is + * excluded. + */ + public void checkExcluded(GeneralName name) + // throws PkixNameConstraintValidatorException + { + switch (name.TagNo) + { + case 1: + CheckExcludedEmail(excludedSubtreesEmail, ExtractNameAsString(name)); + break; + case 2: + checkExcludedDNS(excludedSubtreesDNS, DerIA5String.GetInstance( + name.Name).GetString()); + break; + case 4: + CheckExcludedDN(Asn1Sequence.GetInstance(name.Name.ToAsn1Object())); + break; + case 6: + checkExcludedURI(excludedSubtreesURI, DerIA5String.GetInstance( + name.Name).GetString()); + break; + case 7: + byte[] ip = Asn1OctetString.GetInstance(name.Name).GetOctets(); + + checkExcludedIP(excludedSubtreesIP, ip); + break; + } + } + + /** + * Updates the permitted ISet of these name constraints with the intersection + * with the given subtree. + * + * @param permitted The permitted subtrees + */ + + public void IntersectPermittedSubtree(Asn1Sequence permitted) + { + IDictionary subtreesMap = Platform.CreateHashtable(); + + // group in ISets in a map ordered by tag no. + for (IEnumerator e = permitted.GetEnumerator(); e.MoveNext(); ) + { + GeneralSubtree subtree = GeneralSubtree.GetInstance(e.Current); + + int tagNo = subtree.Base.TagNo; + if (subtreesMap[tagNo] == null) + { + subtreesMap[tagNo] = new HashSet(); + } + + ((ISet)subtreesMap[tagNo]).Add(subtree); + } + + for (IEnumerator it = subtreesMap.GetEnumerator(); it.MoveNext(); ) + { + DictionaryEntry entry = (DictionaryEntry)it.Current; + + // go through all subtree groups + switch ((int)entry.Key ) + { + case 1: + permittedSubtreesEmail = IntersectEmail(permittedSubtreesEmail, + (ISet)entry.Value); + break; + case 2: + permittedSubtreesDNS = intersectDNS(permittedSubtreesDNS, + (ISet)entry.Value); + break; + case 4: + permittedSubtreesDN = IntersectDN(permittedSubtreesDN, + (ISet)entry.Value); + break; + case 6: + permittedSubtreesURI = intersectURI(permittedSubtreesURI, + (ISet)entry.Value); + break; + case 7: + permittedSubtreesIP = IntersectIP(permittedSubtreesIP, + (ISet)entry.Value); + break; + } + } + } + + private String ExtractNameAsString(GeneralName name) + { + return DerIA5String.GetInstance(name.Name).GetString(); + } + + public void IntersectEmptyPermittedSubtree(int nameType) + { + switch (nameType) + { + case 1: + permittedSubtreesEmail = new HashSet(); + break; + case 2: + permittedSubtreesDNS = new HashSet(); + break; + case 4: + permittedSubtreesDN = new HashSet(); + break; + case 6: + permittedSubtreesURI = new HashSet(); + break; + case 7: + permittedSubtreesIP = new HashSet(); + break; + } + } + + /** + * Adds a subtree to the excluded ISet of these name constraints. + * + * @param subtree A subtree with an excluded GeneralName. + */ + public void AddExcludedSubtree(GeneralSubtree subtree) + { + GeneralName subTreeBase = subtree.Base; + + switch (subTreeBase.TagNo) + { + case 1: + excludedSubtreesEmail = UnionEmail(excludedSubtreesEmail, + ExtractNameAsString(subTreeBase)); + break; + case 2: + excludedSubtreesDNS = unionDNS(excludedSubtreesDNS, + ExtractNameAsString(subTreeBase)); + break; + case 4: + excludedSubtreesDN = UnionDN(excludedSubtreesDN, + (Asn1Sequence)subTreeBase.Name.ToAsn1Object()); + break; + case 6: + excludedSubtreesURI = unionURI(excludedSubtreesURI, + ExtractNameAsString(subTreeBase)); + break; + case 7: + excludedSubtreesIP = UnionIP(excludedSubtreesIP, Asn1OctetString + .GetInstance(subTreeBase.Name).GetOctets()); + break; + } + } + + /** + * Returns the maximum IP address. + * + * @param ip1 The first IP address. + * @param ip2 The second IP address. + * @return The maximum IP address. + */ + private static byte[] Max(byte[] ip1, byte[] ip2) + { + for (int i = 0; i < ip1.Length; i++) + { + if ((ip1[i] & 0xFFFF) > (ip2[i] & 0xFFFF)) + { + return ip1; + } + } + return ip2; + } + + /** + * Returns the minimum IP address. + * + * @param ip1 The first IP address. + * @param ip2 The second IP address. + * @return The minimum IP address. + */ + private static byte[] Min(byte[] ip1, byte[] ip2) + { + for (int i = 0; i < ip1.Length; i++) + { + if ((ip1[i] & 0xFFFF) < (ip2[i] & 0xFFFF)) + { + return ip1; + } + } + return ip2; + } + + /** + * Compares IP address <code>ip1</code> with <code>ip2</code>. If ip1 + * is equal to ip2 0 is returned. If ip1 is bigger 1 is returned, -1 + * otherwise. + * + * @param ip1 The first IP address. + * @param ip2 The second IP address. + * @return 0 if ip1 is equal to ip2, 1 if ip1 is bigger, -1 otherwise. + */ + private static int CompareTo(byte[] ip1, byte[] ip2) + { + if (Org.BouncyCastle.Utilities.Arrays.AreEqual(ip1, ip2)) + { + return 0; + } + if (Org.BouncyCastle.Utilities.Arrays.AreEqual(Max(ip1, ip2), ip1)) + { + return 1; + } + return -1; + } + + /** + * Returns the logical OR of the IP addresses <code>ip1</code> and + * <code>ip2</code>. + * + * @param ip1 The first IP address. + * @param ip2 The second IP address. + * @return The OR of <code>ip1</code> and <code>ip2</code>. + */ + private static byte[] Or(byte[] ip1, byte[] ip2) + { + byte[] temp = new byte[ip1.Length]; + for (int i = 0; i < ip1.Length; i++) + { + temp[i] = (byte)(ip1[i] | ip2[i]); + } + return temp; + } + + [Obsolete("Use GetHashCode instead")] + public int HashCode() + { + return GetHashCode(); + } + + public override int GetHashCode() + { + return HashCollection(excludedSubtreesDN) + + HashCollection(excludedSubtreesDNS) + + HashCollection(excludedSubtreesEmail) + + HashCollection(excludedSubtreesIP) + + HashCollection(excludedSubtreesURI) + + HashCollection(permittedSubtreesDN) + + HashCollection(permittedSubtreesDNS) + + HashCollection(permittedSubtreesEmail) + + HashCollection(permittedSubtreesIP) + + HashCollection(permittedSubtreesURI); + } + + private int HashCollection(ICollection coll) + { + if (coll == null) + { + return 0; + } + int hash = 0; + IEnumerator it1 = coll.GetEnumerator(); + while (it1.MoveNext()) + { + Object o = it1.Current; + if (o is byte[]) + { + hash += Org.BouncyCastle.Utilities.Arrays.GetHashCode((byte[])o); + } + else + { + hash += o.GetHashCode(); + } + } + return hash; + } + + public override bool Equals(Object o) + { + if (!(o is PkixNameConstraintValidator)) + return false; + + PkixNameConstraintValidator constraintValidator = (PkixNameConstraintValidator)o; + + return CollectionsAreEqual(constraintValidator.excludedSubtreesDN, excludedSubtreesDN) + && CollectionsAreEqual(constraintValidator.excludedSubtreesDNS, excludedSubtreesDNS) + && CollectionsAreEqual(constraintValidator.excludedSubtreesEmail, excludedSubtreesEmail) + && CollectionsAreEqual(constraintValidator.excludedSubtreesIP, excludedSubtreesIP) + && CollectionsAreEqual(constraintValidator.excludedSubtreesURI, excludedSubtreesURI) + && CollectionsAreEqual(constraintValidator.permittedSubtreesDN, permittedSubtreesDN) + && CollectionsAreEqual(constraintValidator.permittedSubtreesDNS, permittedSubtreesDNS) + && CollectionsAreEqual(constraintValidator.permittedSubtreesEmail, permittedSubtreesEmail) + && CollectionsAreEqual(constraintValidator.permittedSubtreesIP, permittedSubtreesIP) + && CollectionsAreEqual(constraintValidator.permittedSubtreesURI, permittedSubtreesURI); + } + + private bool CollectionsAreEqual(ICollection coll1, ICollection coll2) + { + if (coll1 == coll2) + { + return true; + } + if (coll1 == null || coll2 == null) + { + return false; + } + if (coll1.Count != coll2.Count) + { + return false; + } + IEnumerator it1 = coll1.GetEnumerator(); + + while (it1.MoveNext()) + { + Object a = it1.Current; + IEnumerator it2 = coll2.GetEnumerator(); + bool found = false; + while (it2.MoveNext()) + { + Object b = it2.Current; + if (SpecialEquals(a, b)) + { + found = true; + break; + } + } + if (!found) + { + return false; + } + } + return true; + } + + private bool SpecialEquals(Object o1, Object o2) + { + if (o1 == o2) + { + return true; + } + if (o1 == null || o2 == null) + { + return false; + } + if ((o1 is byte[]) && (o2 is byte[])) + { + return Org.BouncyCastle.Utilities.Arrays.AreEqual((byte[])o1, (byte[])o2); + } + else + { + return o1.Equals(o2); + } + } + + /** + * Stringifies an IPv4 or v6 address with subnet mask. + * + * @param ip The IP with subnet mask. + * @return The stringified IP address. + */ + private String StringifyIP(byte[] ip) + { + String temp = ""; + for (int i = 0; i < ip.Length / 2; i++) + { + //temp += Integer.toString(ip[i] & 0x00FF) + "."; + temp += (ip[i] & 0x00FF) + "."; + } + temp = temp.Substring(0, temp.Length - 1); + temp += "/"; + for (int i = ip.Length / 2; i < ip.Length; i++) + { + //temp += Integer.toString(ip[i] & 0x00FF) + "."; + temp += (ip[i] & 0x00FF) + "."; + } + temp = temp.Substring(0, temp.Length - 1); + return temp; + } + + private String StringifyIPCollection(ISet ips) + { + String temp = ""; + temp += "["; + for (IEnumerator it = ips.GetEnumerator(); it.MoveNext(); ) + { + temp += StringifyIP((byte[])it.Current) + ","; + } + if (temp.Length > 1) + { + temp = temp.Substring(0, temp.Length - 1); + } + temp += "]"; + + return temp; + } + + public override String ToString() + { + String temp = ""; + + temp += "permitted:\n"; + if (permittedSubtreesDN != null) + { + temp += "DN:\n"; + temp += permittedSubtreesDN.ToString() + "\n"; + } + if (permittedSubtreesDNS != null) + { + temp += "DNS:\n"; + temp += permittedSubtreesDNS.ToString() + "\n"; + } + if (permittedSubtreesEmail != null) + { + temp += "Email:\n"; + temp += permittedSubtreesEmail.ToString() + "\n"; + } + if (permittedSubtreesURI != null) + { + temp += "URI:\n"; + temp += permittedSubtreesURI.ToString() + "\n"; + } + if (permittedSubtreesIP != null) + { + temp += "IP:\n"; + temp += StringifyIPCollection(permittedSubtreesIP) + "\n"; + } + temp += "excluded:\n"; + if (!(excludedSubtreesDN.IsEmpty)) + { + temp += "DN:\n"; + temp += excludedSubtreesDN.ToString() + "\n"; + } + if (!excludedSubtreesDNS.IsEmpty) + { + temp += "DNS:\n"; + temp += excludedSubtreesDNS.ToString() + "\n"; + } + if (!excludedSubtreesEmail.IsEmpty) + { + temp += "Email:\n"; + temp += excludedSubtreesEmail.ToString() + "\n"; + } + if (!excludedSubtreesURI.IsEmpty) + { + temp += "URI:\n"; + temp += excludedSubtreesURI.ToString() + "\n"; + } + if (!excludedSubtreesIP.IsEmpty) + { + temp += "IP:\n"; + temp += StringifyIPCollection(excludedSubtreesIP) + "\n"; + } + return temp; + } + + } +} diff --git a/Crypto/src/pkix/PkixNameConstraintValidatorException.cs b/Crypto/src/pkix/PkixNameConstraintValidatorException.cs new file mode 100644
index 000000000..9f01e8765 --- /dev/null +++ b/Crypto/src/pkix/PkixNameConstraintValidatorException.cs
@@ -0,0 +1,12 @@ +using System; + +namespace Org.BouncyCastle.Pkix +{ + public class PkixNameConstraintValidatorException : Exception + { + public PkixNameConstraintValidatorException(String msg) + : base(msg) + { + } + } +} diff --git a/Crypto/src/pkix/PkixParameters.cs b/Crypto/src/pkix/PkixParameters.cs new file mode 100644
index 000000000..6df1b646f --- /dev/null +++ b/Crypto/src/pkix/PkixParameters.cs
@@ -0,0 +1,893 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.Utilities.Date; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.Pkix +{ + /// <summary> + /// Summary description for PkixParameters. + /// </summary> + public class PkixParameters +// : ICertPathParameters + { + /** + * This is the default PKIX validity model. Actually there are two variants + * of this: The PKIX model and the modified PKIX model. The PKIX model + * verifies that all involved certificates must have been valid at the + * current time. The modified PKIX model verifies that all involved + * certificates were valid at the signing time. Both are indirectly choosen + * with the {@link PKIXParameters#setDate(java.util.Date)} method, so this + * methods sets the Date when <em>all</em> certificates must have been + * valid. + */ + public const int PkixValidityModel = 0; + + /** + * This model uses the following validity model. Each certificate must have + * been valid at the moment where is was used. That means the end + * certificate must have been valid at the time the signature was done. The + * CA certificate which signed the end certificate must have been valid, + * when the end certificate was signed. The CA (or Root CA) certificate must + * have been valid, when the CA certificate was signed and so on. So the + * {@link PKIXParameters#setDate(java.util.Date)} method sets the time, when + * the <em>end certificate</em> must have been valid. <p/> It is used e.g. + * in the German signature law. + */ + public const int ChainValidityModel = 1; + + private ISet trustAnchors; + private DateTimeObject date; + private IList certPathCheckers; + private bool revocationEnabled = true; + private ISet initialPolicies; + //private bool checkOnlyEECertificateCrl = false; + private bool explicitPolicyRequired = false; + private bool anyPolicyInhibited = false; + private bool policyMappingInhibited = false; + private bool policyQualifiersRejected = true; + private IX509Selector certSelector; + private IList stores; + private IX509Selector selector; + private bool additionalLocationsEnabled; + private IList additionalStores; + private ISet trustedACIssuers; + private ISet necessaryACAttributes; + private ISet prohibitedACAttributes; + private ISet attrCertCheckers; + private int validityModel = PkixValidityModel; + private bool useDeltas = false; + + /** + * Creates an instance of PKIXParameters with the specified Set of + * most-trusted CAs. Each element of the set is a TrustAnchor.<br /> + * <br /> + * Note that the Set is copied to protect against subsequent modifications. + * + * @param trustAnchors + * a Set of TrustAnchors + * + * @exception InvalidAlgorithmParameterException + * if the specified Set is empty + * <code>(trustAnchors.isEmpty() == true)</code> + * @exception NullPointerException + * if the specified Set is <code>null</code> + * @exception ClassCastException + * if any of the elements in the Set are not of type + * <code>java.security.cert.TrustAnchor</code> + */ + public PkixParameters( + ISet trustAnchors) + { + SetTrustAnchors(trustAnchors); + + this.initialPolicies = new HashSet(); + this.certPathCheckers = Platform.CreateArrayList(); + this.stores = Platform.CreateArrayList(); + this.additionalStores = Platform.CreateArrayList(); + this.trustedACIssuers = new HashSet(); + this.necessaryACAttributes = new HashSet(); + this.prohibitedACAttributes = new HashSet(); + this.attrCertCheckers = new HashSet(); + } + +// // TODO implement for other keystores (see Java build)? +// /** +// * Creates an instance of <code>PKIXParameters</code> that +// * populates the set of most-trusted CAs from the trusted +// * certificate entries contained in the specified <code>KeyStore</code>. +// * Only keystore entries that contain trusted <code>X509Certificates</code> +// * are considered; all other certificate types are ignored. +// * +// * @param keystore a <code>KeyStore</code> from which the set of +// * most-trusted CAs will be populated +// * @throws KeyStoreException if the keystore has not been initialized +// * @throws InvalidAlgorithmParameterException if the keystore does +// * not contain at least one trusted certificate entry +// * @throws NullPointerException if the keystore is <code>null</code> +// */ +// public PkixParameters( +// Pkcs12Store keystore) +//// throws KeyStoreException, InvalidAlgorithmParameterException +// { +// if (keystore == null) +// throw new ArgumentNullException("keystore"); +// ISet trustAnchors = new HashSet(); +// foreach (string alias in keystore.Aliases) +// { +// if (keystore.IsCertificateEntry(alias)) +// { +// X509CertificateEntry x509Entry = keystore.GetCertificate(alias); +// trustAnchors.Add(new TrustAnchor(x509Entry.Certificate, null)); +// } +// } +// SetTrustAnchors(trustAnchors); +// +// this.initialPolicies = new HashSet(); +// this.certPathCheckers = new ArrayList(); +// this.stores = new ArrayList(); +// this.additionalStores = new ArrayList(); +// this.trustedACIssuers = new HashSet(); +// this.necessaryACAttributes = new HashSet(); +// this.prohibitedACAttributes = new HashSet(); +// this.attrCertCheckers = new HashSet(); +// } + + public virtual bool IsRevocationEnabled + { + get { return revocationEnabled; } + set { revocationEnabled = value; } + } + + public virtual bool IsExplicitPolicyRequired + { + get { return explicitPolicyRequired; } + set { this.explicitPolicyRequired = value; } + } + + public virtual bool IsAnyPolicyInhibited + { + get { return anyPolicyInhibited; } + set { this.anyPolicyInhibited = value; } + } + + public virtual bool IsPolicyMappingInhibited + { + get { return policyMappingInhibited; } + set { this.policyMappingInhibited = value; } + } + + public virtual bool IsPolicyQualifiersRejected + { + get { return policyQualifiersRejected; } + set { this.policyQualifiersRejected = value; } + } + + //public bool IsCheckOnlyEECertificateCrl + //{ + // get { return this.checkOnlyEECertificateCrl; } + // set { this.checkOnlyEECertificateCrl = value; } + //} + + public virtual DateTimeObject Date + { + get { return this.date; } + set { this.date = value; } + } + + // Returns a Set of the most-trusted CAs. + public virtual ISet GetTrustAnchors() + { + return new HashSet(this.trustAnchors); + } + + // Sets the set of most-trusted CAs. + // Set is copied to protect against subsequent modifications. + public virtual void SetTrustAnchors( + ISet tas) + { + if (tas == null) + throw new ArgumentNullException("value"); + if (tas.IsEmpty) + throw new ArgumentException("non-empty set required", "value"); + + // Explicit copy to enforce type-safety + this.trustAnchors = new HashSet(); + foreach (TrustAnchor ta in tas) + { + if (ta != null) + { + trustAnchors.Add(ta); + } + } + } + + /** + * Returns the required constraints on the target certificate. The + * constraints are returned as an instance of CertSelector. If + * <code>null</code>, no constraints are defined.<br /> + * <br /> + * Note that the CertSelector returned is cloned to protect against + * subsequent modifications. + * + * @return a CertSelector specifying the constraints on the target + * certificate (or <code>null</code>) + * + * @see #setTargetCertConstraints(CertSelector) + */ + public virtual X509CertStoreSelector GetTargetCertConstraints() + { + if (certSelector == null) + { + return null; + } + + return (X509CertStoreSelector)certSelector.Clone(); + } + + /** + * Sets the required constraints on the target certificate. The constraints + * are specified as an instance of CertSelector. If null, no constraints are + * defined.<br /> + * <br /> + * Note that the CertSelector specified is cloned to protect against + * subsequent modifications. + * + * @param selector + * a CertSelector specifying the constraints on the target + * certificate (or <code>null</code>) + * + * @see #getTargetCertConstraints() + */ + public virtual void SetTargetCertConstraints( + IX509Selector selector) + { + if (selector == null) + { + certSelector = null; + } + else + { + certSelector = (IX509Selector)selector.Clone(); + } + } + + /** + * Returns an immutable Set of initial policy identifiers (OID strings), + * indicating that any one of these policies would be acceptable to the + * certificate user for the purposes of certification path processing. The + * default return value is an empty <code>Set</code>, which is + * interpreted as meaning that any policy would be acceptable. + * + * @return an immutable <code>Set</code> of initial policy OIDs in String + * format, or an empty <code>Set</code> (implying any policy is + * acceptable). Never returns <code>null</code>. + * + * @see #setInitialPolicies(java.util.Set) + */ + public virtual ISet GetInitialPolicies() + { + ISet returnSet = initialPolicies; + + // TODO Can it really be null? + if (initialPolicies == null) + { + returnSet = new HashSet(); + } + + return new HashSet(returnSet); + } + + /** + * Sets the <code>Set</code> of initial policy identifiers (OID strings), + * indicating that any one of these policies would be acceptable to the + * certificate user for the purposes of certification path processing. By + * default, any policy is acceptable (i.e. all policies), so a user that + * wants to allow any policy as acceptable does not need to call this + * method, or can call it with an empty <code>Set</code> (or + * <code>null</code>).<br /> + * <br /> + * Note that the Set is copied to protect against subsequent modifications.<br /> + * <br /> + * + * @param initialPolicies + * a Set of initial policy OIDs in String format (or + * <code>null</code>) + * + * @exception ClassCastException + * if any of the elements in the set are not of type String + * + * @see #getInitialPolicies() + */ + public virtual void SetInitialPolicies( + ISet initialPolicies) + { + this.initialPolicies = new HashSet(); + if (initialPolicies != null) + { + foreach (string obj in initialPolicies) + { + if (obj != null) + { + this.initialPolicies.Add(obj); + } + } + } + } + + /** + * Sets a <code>List</code> of additional certification path checkers. If + * the specified List contains an object that is not a PKIXCertPathChecker, + * it is ignored.<br /> + * <br /> + * Each <code>PKIXCertPathChecker</code> specified implements additional + * checks on a certificate. Typically, these are checks to process and + * verify private extensions contained in certificates. Each + * <code>PKIXCertPathChecker</code> should be instantiated with any + * initialization parameters needed to execute the check.<br /> + * <br /> + * This method allows sophisticated applications to extend a PKIX + * <code>CertPathValidator</code> or <code>CertPathBuilder</code>. Each + * of the specified PKIXCertPathCheckers will be called, in turn, by a PKIX + * <code>CertPathValidator</code> or <code>CertPathBuilder</code> for + * each certificate processed or validated.<br /> + * <br /> + * Regardless of whether these additional PKIXCertPathCheckers are set, a + * PKIX <code>CertPathValidator</code> or <code>CertPathBuilder</code> + * must perform all of the required PKIX checks on each certificate. The one + * exception to this rule is if the RevocationEnabled flag is set to false + * (see the {@link #setRevocationEnabled(boolean) setRevocationEnabled} + * method).<br /> + * <br /> + * Note that the List supplied here is copied and each PKIXCertPathChecker + * in the list is cloned to protect against subsequent modifications. + * + * @param checkers + * a List of PKIXCertPathCheckers. May be null, in which case no + * additional checkers will be used. + * @exception ClassCastException + * if any of the elements in the list are not of type + * <code>java.security.cert.PKIXCertPathChecker</code> + * @see #getCertPathCheckers() + */ + public virtual void SetCertPathCheckers(IList checkers) + { + certPathCheckers = Platform.CreateArrayList(); + if (checkers != null) + { + foreach (PkixCertPathChecker obj in checkers) + { + certPathCheckers.Add(obj.Clone()); + } + } + } + + /** + * Returns the List of certification path checkers. Each PKIXCertPathChecker + * in the returned IList is cloned to protect against subsequent modifications. + * + * @return an immutable List of PKIXCertPathCheckers (may be empty, but not + * <code>null</code>) + * + * @see #setCertPathCheckers(java.util.List) + */ + public virtual IList GetCertPathCheckers() + { + IList checkers = Platform.CreateArrayList(); + foreach (PkixCertPathChecker obj in certPathCheckers) + { + checkers.Add(obj.Clone()); + } + return checkers; + } + + /** + * Adds a <code>PKIXCertPathChecker</code> to the list of certification + * path checkers. See the {@link #setCertPathCheckers setCertPathCheckers} + * method for more details. + * <p> + * Note that the <code>PKIXCertPathChecker</code> is cloned to protect + * against subsequent modifications.</p> + * + * @param checker a <code>PKIXCertPathChecker</code> to add to the list of + * checks. If <code>null</code>, the checker is ignored (not added to list). + */ + public virtual void AddCertPathChecker( + PkixCertPathChecker checker) + { + if (checker != null) + { + certPathCheckers.Add(checker.Clone()); + } + } + + public virtual object Clone() + { + // FIXME Check this whole method against the Java implementation! + + PkixParameters parameters = new PkixParameters(GetTrustAnchors()); + parameters.SetParams(this); + return parameters; + + +// PkixParameters obj = new PkixParameters(new HashSet()); +//// (PkixParameters) this.MemberwiseClone(); +// obj.x509Stores = new ArrayList(x509Stores); +// obj.certPathCheckers = new ArrayList(certPathCheckers); +// +// //Iterator iter = certPathCheckers.iterator(); +// //obj.certPathCheckers = new ArrayList(); +// //while (iter.hasNext()) +// //{ +// // obj.certPathCheckers.add(((PKIXCertPathChecker)iter.next()) +// // .clone()); +// //} +// //if (initialPolicies != null) +// //{ +// // obj.initialPolicies = new HashSet(initialPolicies); +// //} +//// if (trustAnchors != null) +//// { +//// obj.trustAnchors = new HashSet(trustAnchors); +//// } +//// if (certSelector != null) +//// { +//// obj.certSelector = (X509CertStoreSelector) certSelector.Clone(); +//// } +// return obj; + } + + /** + * Method to support <code>Clone()</code> under J2ME. + * <code>super.Clone()</code> does not exist and fields are not copied. + * + * @param params Parameters to set. If this are + * <code>ExtendedPkixParameters</code> they are copied to. + */ + protected virtual void SetParams( + PkixParameters parameters) + { + Date = parameters.Date; + SetCertPathCheckers(parameters.GetCertPathCheckers()); + IsAnyPolicyInhibited = parameters.IsAnyPolicyInhibited; + IsExplicitPolicyRequired = parameters.IsExplicitPolicyRequired; + IsPolicyMappingInhibited = parameters.IsPolicyMappingInhibited; + IsRevocationEnabled = parameters.IsRevocationEnabled; + SetInitialPolicies(parameters.GetInitialPolicies()); + IsPolicyQualifiersRejected = parameters.IsPolicyQualifiersRejected; + SetTargetCertConstraints(parameters.GetTargetCertConstraints()); + SetTrustAnchors(parameters.GetTrustAnchors()); + + validityModel = parameters.validityModel; + useDeltas = parameters.useDeltas; + additionalLocationsEnabled = parameters.additionalLocationsEnabled; + selector = parameters.selector == null ? null + : (IX509Selector) parameters.selector.Clone(); + stores = Platform.CreateArrayList(parameters.stores); + additionalStores = Platform.CreateArrayList(parameters.additionalStores); + trustedACIssuers = new HashSet(parameters.trustedACIssuers); + prohibitedACAttributes = new HashSet(parameters.prohibitedACAttributes); + necessaryACAttributes = new HashSet(parameters.necessaryACAttributes); + attrCertCheckers = new HashSet(parameters.attrCertCheckers); + } + + /** + * Whether delta CRLs should be used for checking the revocation status. + * Defaults to <code>false</code>. + */ + public virtual bool IsUseDeltasEnabled + { + get { return useDeltas; } + set { useDeltas = value; } + } + + /** + * The validity model. + * @see #CHAIN_VALIDITY_MODEL + * @see #PKIX_VALIDITY_MODEL + */ + public virtual int ValidityModel + { + get { return validityModel; } + set { validityModel = value; } + } + + /** + * Sets the Bouncy Castle Stores for finding CRLs, certificates, attribute + * certificates or cross certificates. + * <p> + * The <code>IList</code> is cloned. + * </p> + * + * @param stores A list of stores to use. + * @see #getStores + * @throws ClassCastException if an element of <code>stores</code> is not + * a {@link Store}. + */ + public virtual void SetStores( + IList stores) + { + if (stores == null) + { + this.stores = Platform.CreateArrayList(); + } + else + { + foreach (object obj in stores) + { + if (!(obj is IX509Store)) + { + throw new InvalidCastException( + "All elements of list must be of type " + typeof(IX509Store).FullName); + } + } + this.stores = Platform.CreateArrayList(stores); + } + } + + /** + * Adds a Bouncy Castle {@link Store} to find CRLs, certificates, attribute + * certificates or cross certificates. + * <p> + * This method should be used to add local stores, like collection based + * X.509 stores, if available. Local stores should be considered first, + * before trying to use additional (remote) locations, because they do not + * need possible additional network traffic. + * </p><p> + * If <code>store</code> is <code>null</code> it is ignored. + * </p> + * + * @param store The store to add. + * @see #getStores + */ + public virtual void AddStore( + IX509Store store) + { + if (store != null) + { + stores.Add(store); + } + } + + /** + * Adds an additional Bouncy Castle {@link Store} to find CRLs, certificates, + * attribute certificates or cross certificates. + * <p> + * You should not use this method. This method is used for adding additional + * X.509 stores, which are used to add (remote) locations, e.g. LDAP, found + * during X.509 object processing, e.g. in certificates or CRLs. This method + * is used in PKIX certification path processing. + * </p><p> + * If <code>store</code> is <code>null</code> it is ignored. + * </p> + * + * @param store The store to add. + * @see #getStores() + */ + public virtual void AddAdditionalStore( + IX509Store store) + { + if (store != null) + { + additionalStores.Add(store); + } + } + + /** + * Returns an <code>IList</code> of additional Bouncy Castle + * <code>Store</code>s used for finding CRLs, certificates, attribute + * certificates or cross certificates. + * + * @return an immutable <code>IList</code> of additional Bouncy Castle + * <code>Store</code>s. Never <code>null</code>. + * + * @see #addAddionalStore(Store) + */ + public virtual IList GetAdditionalStores() + { + return Platform.CreateArrayList(additionalStores); + } + + /** + * Returns an <code>IList</code> of Bouncy Castle + * <code>Store</code>s used for finding CRLs, certificates, attribute + * certificates or cross certificates. + * + * @return an immutable <code>IList</code> of Bouncy Castle + * <code>Store</code>s. Never <code>null</code>. + * + * @see #setStores(IList) + */ + public virtual IList GetStores() + { + return Platform.CreateArrayList(stores); + } + + /** + * Returns if additional {@link X509Store}s for locations like LDAP found + * in certificates or CRLs should be used. + * + * @return Returns <code>true</code> if additional stores are used. + */ + public virtual bool IsAdditionalLocationsEnabled + { + get { return additionalLocationsEnabled; } + } + + /** + * Sets if additional {@link X509Store}s for locations like LDAP found in + * certificates or CRLs should be used. + * + * @param enabled <code>true</code> if additional stores are used. + */ + public virtual void SetAdditionalLocationsEnabled( + bool enabled) + { + additionalLocationsEnabled = enabled; + } + + /** + * Returns the required constraints on the target certificate or attribute + * certificate. The constraints are returned as an instance of + * <code>IX509Selector</code>. If <code>null</code>, no constraints are + * defined. + * + * <p> + * The target certificate in a PKIX path may be a certificate or an + * attribute certificate. + * </p><p> + * Note that the <code>IX509Selector</code> returned is cloned to protect + * against subsequent modifications. + * </p> + * @return a <code>IX509Selector</code> specifying the constraints on the + * target certificate or attribute certificate (or <code>null</code>) + * @see #setTargetConstraints + * @see X509CertStoreSelector + * @see X509AttributeCertStoreSelector + */ + public virtual IX509Selector GetTargetConstraints() + { + if (selector != null) + { + return (IX509Selector) selector.Clone(); + } + else + { + return null; + } + } + + /** + * Sets the required constraints on the target certificate or attribute + * certificate. The constraints are specified as an instance of + * <code>IX509Selector</code>. If <code>null</code>, no constraints are + * defined. + * <p> + * The target certificate in a PKIX path may be a certificate or an + * attribute certificate. + * </p><p> + * Note that the <code>IX509Selector</code> specified is cloned to protect + * against subsequent modifications. + * </p> + * + * @param selector a <code>IX509Selector</code> specifying the constraints on + * the target certificate or attribute certificate (or + * <code>null</code>) + * @see #getTargetConstraints + * @see X509CertStoreSelector + * @see X509AttributeCertStoreSelector + */ + public virtual void SetTargetConstraints(IX509Selector selector) + { + if (selector != null) + { + this.selector = (IX509Selector) selector.Clone(); + } + else + { + this.selector = null; + } + } + + /** + * Returns the trusted attribute certificate issuers. If attribute + * certificates is verified the trusted AC issuers must be set. + * <p> + * The returned <code>ISet</code> consists of <code>TrustAnchor</code>s. + * </p><p> + * The returned <code>ISet</code> is immutable. Never <code>null</code> + * </p> + * + * @return Returns an immutable set of the trusted AC issuers. + */ + public virtual ISet GetTrustedACIssuers() + { + return new HashSet(trustedACIssuers); + } + + /** + * Sets the trusted attribute certificate issuers. If attribute certificates + * is verified the trusted AC issuers must be set. + * <p> + * The <code>trustedACIssuers</code> must be a <code>ISet</code> of + * <code>TrustAnchor</code> + * </p><p> + * The given set is cloned. + * </p> + * + * @param trustedACIssuers The trusted AC issuers to set. Is never + * <code>null</code>. + * @throws ClassCastException if an element of <code>stores</code> is not + * a <code>TrustAnchor</code>. + */ + public virtual void SetTrustedACIssuers( + ISet trustedACIssuers) + { + if (trustedACIssuers == null) + { + this.trustedACIssuers = new HashSet(); + } + else + { + foreach (object obj in trustedACIssuers) + { + if (!(obj is TrustAnchor)) + { + throw new InvalidCastException("All elements of set must be " + + "of type " + typeof(TrustAnchor).Name + "."); + } + } + this.trustedACIssuers = new HashSet(trustedACIssuers); + } + } + + /** + * Returns the neccessary attributes which must be contained in an attribute + * certificate. + * <p> + * The returned <code>ISet</code> is immutable and contains + * <code>String</code>s with the OIDs. + * </p> + * + * @return Returns the necessary AC attributes. + */ + public virtual ISet GetNecessaryACAttributes() + { + return new HashSet(necessaryACAttributes); + } + + /** + * Sets the neccessary which must be contained in an attribute certificate. + * <p> + * The <code>ISet</code> must contain <code>String</code>s with the + * OIDs. + * </p><p> + * The set is cloned. + * </p> + * + * @param necessaryACAttributes The necessary AC attributes to set. + * @throws ClassCastException if an element of + * <code>necessaryACAttributes</code> is not a + * <code>String</code>. + */ + public virtual void SetNecessaryACAttributes( + ISet necessaryACAttributes) + { + if (necessaryACAttributes == null) + { + this.necessaryACAttributes = new HashSet(); + } + else + { + foreach (object obj in necessaryACAttributes) + { + if (!(obj is string)) + { + throw new InvalidCastException("All elements of set must be " + + "of type string."); + } + } + this.necessaryACAttributes = new HashSet(necessaryACAttributes); + } + } + + /** + * Returns the attribute certificates which are not allowed. + * <p> + * The returned <code>ISet</code> is immutable and contains + * <code>String</code>s with the OIDs. + * </p> + * + * @return Returns the prohibited AC attributes. Is never <code>null</code>. + */ + public virtual ISet GetProhibitedACAttributes() + { + return new HashSet(prohibitedACAttributes); + } + + /** + * Sets the attribute certificates which are not allowed. + * <p> + * The <code>ISet</code> must contain <code>String</code>s with the + * OIDs. + * </p><p> + * The set is cloned. + * </p> + * + * @param prohibitedACAttributes The prohibited AC attributes to set. + * @throws ClassCastException if an element of + * <code>prohibitedACAttributes</code> is not a + * <code>String</code>. + */ + public virtual void SetProhibitedACAttributes( + ISet prohibitedACAttributes) + { + if (prohibitedACAttributes == null) + { + this.prohibitedACAttributes = new HashSet(); + } + else + { + foreach (object obj in prohibitedACAttributes) + { + if (!(obj is String)) + { + throw new InvalidCastException("All elements of set must be " + + "of type string."); + } + } + this.prohibitedACAttributes = new HashSet(prohibitedACAttributes); + } + } + + /** + * Returns the attribute certificate checker. The returned set contains + * {@link PKIXAttrCertChecker}s and is immutable. + * + * @return Returns the attribute certificate checker. Is never + * <code>null</code>. + */ + public virtual ISet GetAttrCertCheckers() + { + return new HashSet(attrCertCheckers); + } + + /** + * Sets the attribute certificate checkers. + * <p> + * All elements in the <code>ISet</code> must a {@link PKIXAttrCertChecker}. + * </p> + * <p> + * The given set is cloned. + * </p> + * + * @param attrCertCheckers The attribute certificate checkers to set. Is + * never <code>null</code>. + * @throws ClassCastException if an element of <code>attrCertCheckers</code> + * is not a <code>PKIXAttrCertChecker</code>. + */ + public virtual void SetAttrCertCheckers( + ISet attrCertCheckers) + { + if (attrCertCheckers == null) + { + this.attrCertCheckers = new HashSet(); + } + else + { + foreach (object obj in attrCertCheckers) + { + if (!(obj is PkixAttrCertChecker)) + { + throw new InvalidCastException("All elements of set must be " + + "of type " + typeof(PkixAttrCertChecker).FullName + "."); + } + } + this.attrCertCheckers = new HashSet(attrCertCheckers); + } + } + } +} diff --git a/Crypto/src/pkix/PkixPolicyNode.cs b/Crypto/src/pkix/PkixPolicyNode.cs new file mode 100644
index 000000000..fc5b82f6f --- /dev/null +++ b/Crypto/src/pkix/PkixPolicyNode.cs
@@ -0,0 +1,158 @@ +using System; +using System.Collections; +using System.Text; + +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Pkix +{ + /// <summary> + /// Summary description for PkixPolicyNode. + /// </summary> + public class PkixPolicyNode +// : IPolicyNode + { + protected IList mChildren; + protected int mDepth; + protected ISet mExpectedPolicies; + protected PkixPolicyNode mParent; + protected ISet mPolicyQualifiers; + protected string mValidPolicy; + protected bool mCritical; + + public virtual int Depth + { + get { return this.mDepth; } + } + + public virtual IEnumerable Children + { + get { return new EnumerableProxy(mChildren); } + } + + public virtual bool IsCritical + { + get { return this.mCritical; } + set { this.mCritical = value; } + } + + public virtual ISet PolicyQualifiers + { + get { return new HashSet(this.mPolicyQualifiers); } + } + + public virtual string ValidPolicy + { + get { return this.mValidPolicy; } + } + + public virtual bool HasChildren + { + get { return mChildren.Count != 0; } + } + + public virtual ISet ExpectedPolicies + { + get { return new HashSet(this.mExpectedPolicies); } + set { this.mExpectedPolicies = new HashSet(value); } + } + + public virtual PkixPolicyNode Parent + { + get { return this.mParent; } + set { this.mParent = value; } + } + + /// Constructors + public PkixPolicyNode( + IList children, + int depth, + ISet expectedPolicies, + PkixPolicyNode parent, + ISet policyQualifiers, + string validPolicy, + bool critical) + { + if (children == null) + { + this.mChildren = Platform.CreateArrayList(); + } + else + { + this.mChildren = Platform.CreateArrayList(children); + } + + this.mDepth = depth; + this.mExpectedPolicies = expectedPolicies; + this.mParent = parent; + this.mPolicyQualifiers = policyQualifiers; + this.mValidPolicy = validPolicy; + this.mCritical = critical; + } + + public virtual void AddChild( + PkixPolicyNode child) + { + child.Parent = this; + mChildren.Add(child); + } + + public virtual void RemoveChild( + PkixPolicyNode child) + { + mChildren.Remove(child); + } + + public override string ToString() + { + return ToString(""); + } + + public virtual string ToString( + string indent) + { + StringBuilder buf = new StringBuilder(); + buf.Append(indent); + buf.Append(mValidPolicy); + buf.Append(" {"); + buf.Append(Platform.NewLine); + + foreach (PkixPolicyNode child in mChildren) + { + buf.Append(child.ToString(indent + " ")); + } + + buf.Append(indent); + buf.Append("}"); + buf.Append(Platform.NewLine); + return buf.ToString(); + } + + public virtual object Clone() + { + return Copy(); + } + + public virtual PkixPolicyNode Copy() + { + PkixPolicyNode node = new PkixPolicyNode( + Platform.CreateArrayList(), + mDepth, + new HashSet(mExpectedPolicies), + null, + new HashSet(mPolicyQualifiers), + mValidPolicy, + mCritical); + + foreach (PkixPolicyNode child in mChildren) + { + PkixPolicyNode copy = child.Copy(); + copy.Parent = node; + node.AddChild(copy); + } + + return node; + } + } +} diff --git a/Crypto/src/pkix/ReasonsMask.cs b/Crypto/src/pkix/ReasonsMask.cs new file mode 100644
index 000000000..e389bfe11 --- /dev/null +++ b/Crypto/src/pkix/ReasonsMask.cs
@@ -0,0 +1,96 @@ +using System; + +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Pkix +{ + /// <summary> + /// This class helps to handle CRL revocation reasons mask. Each CRL handles a + /// certain set of revocation reasons. + /// </summary> + internal class ReasonsMask + { + private int _reasons; + + /// <summary> + /// Constructs are reason mask with the reasons. + /// </summary> + /// <param name="reasons">The reasons.</param> + internal ReasonsMask( + int reasons) + { + _reasons = reasons; + } + + /// <summary> + /// A reason mask with no reason. + /// </summary> + internal ReasonsMask() + : this(0) + { + } + + /// <summary> + /// A mask with all revocation reasons. + /// </summary> + internal static readonly ReasonsMask AllReasons = new ReasonsMask( + ReasonFlags.AACompromise | ReasonFlags.AffiliationChanged | ReasonFlags.CACompromise + | ReasonFlags.CertificateHold | ReasonFlags.CessationOfOperation + | ReasonFlags.KeyCompromise | ReasonFlags.PrivilegeWithdrawn | ReasonFlags.Unused + | ReasonFlags.Superseded); + + /** + * Adds all reasons from the reasons mask to this mask. + * + * @param mask The reasons mask to add. + */ + internal void AddReasons( + ReasonsMask mask) + { + _reasons = _reasons | mask.Reasons.IntValue; + } + + /// <summary> + /// Returns <code>true</code> if this reasons mask contains all possible + /// reasons. + /// </summary> + /// <returns>true if this reasons mask contains all possible reasons. + /// </returns> + internal bool IsAllReasons + { + get { return _reasons == AllReasons._reasons; } + } + + /// <summary> + /// Intersects this mask with the given reasons mask. + /// </summary> + /// <param name="mask">mask The mask to intersect with.</param> + /// <returns>The intersection of this and teh given mask.</returns> + internal ReasonsMask Intersect( + ReasonsMask mask) + { + ReasonsMask _mask = new ReasonsMask(); + _mask.AddReasons(new ReasonsMask(_reasons & mask.Reasons.IntValue)); + return _mask; + } + + /// <summary> + /// Returns <c>true</c> if the passed reasons mask has new reasons. + /// </summary> + /// <param name="mask">The reasons mask which should be tested for new reasons.</param> + /// <returns><c>true</c> if the passed reasons mask has new reasons.</returns> + internal bool HasNewReasons( + ReasonsMask mask) + { + return ((_reasons | mask.Reasons.IntValue ^ _reasons) != 0); + } + + /// <summary> + /// Returns the reasons in this mask. + /// </summary> + public ReasonFlags Reasons + { + get { return new ReasonFlags(_reasons); } + } + } +} diff --git a/Crypto/src/pkix/Rfc3280CertPathUtilities.cs b/Crypto/src/pkix/Rfc3280CertPathUtilities.cs new file mode 100644
index 000000000..bae657d90 --- /dev/null +++ b/Crypto/src/pkix/Rfc3280CertPathUtilities.cs
@@ -0,0 +1,2448 @@ +using System; +using System.Collections; +using System.Globalization; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.Utilities.Date; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.Pkix +{ + public class Rfc3280CertPathUtilities + { + private static readonly PkixCrlUtilities CrlUtilities = new PkixCrlUtilities(); + + internal static readonly string ANY_POLICY = "2.5.29.32.0"; + + // key usage bits + internal static readonly int KEY_CERT_SIGN = 5; + internal static readonly int CRL_SIGN = 6; + + /** + * If the complete CRL includes an issuing distribution point (IDP) CRL + * extension check the following: + * <p> + * (i) If the distribution point name is present in the IDP CRL extension + * and the distribution field is present in the DP, then verify that one of + * the names in the IDP matches one of the names in the DP. If the + * distribution point name is present in the IDP CRL extension and the + * distribution field is omitted from the DP, then verify that one of the + * names in the IDP matches one of the names in the cRLIssuer field of the + * DP. + * </p> + * <p> + * (ii) If the onlyContainsUserCerts boolean is asserted in the IDP CRL + * extension, verify that the certificate does not include the basic + * constraints extension with the cA boolean asserted. + * </p> + * <p> + * (iii) If the onlyContainsCACerts boolean is asserted in the IDP CRL + * extension, verify that the certificate includes the basic constraints + * extension with the cA boolean asserted. + * </p> + * <p> + * (iv) Verify that the onlyContainsAttributeCerts boolean is not asserted. + * </p> + * + * @param dp The distribution point. + * @param cert The certificate. + * @param crl The CRL. + * @throws AnnotatedException if one of the conditions is not met or an error occurs. + */ + internal static void ProcessCrlB2( + DistributionPoint dp, + object cert, + X509Crl crl) + { + IssuingDistributionPoint idp = null; + try + { + idp = IssuingDistributionPoint.GetInstance(PkixCertPathValidatorUtilities.GetExtensionValue(crl, X509Extensions.IssuingDistributionPoint)); + } + catch (Exception e) + { + throw new Exception("0 Issuing distribution point extension could not be decoded.", e); + } + // (b) (2) (i) + // distribution point name is present + if (idp != null) + { + if (idp.DistributionPoint != null) + { + // make list of names + DistributionPointName dpName = IssuingDistributionPoint.GetInstance(idp).DistributionPoint; + IList names = Platform.CreateArrayList(); + + if (dpName.PointType == DistributionPointName.FullName) + { + GeneralName[] genNames = GeneralNames.GetInstance(dpName.Name).GetNames(); + for (int j = 0; j < genNames.Length; j++) + { + names.Add(genNames[j]); + } + } + if (dpName.PointType == DistributionPointName.NameRelativeToCrlIssuer) + { + Asn1EncodableVector vec = new Asn1EncodableVector(); + try + { + IEnumerator e = Asn1Sequence.GetInstance( + Asn1Sequence.FromByteArray(crl.IssuerDN.GetEncoded())).GetEnumerator(); + while (e.MoveNext()) + { + vec.Add((Asn1Encodable)e.Current); + } + } + catch (IOException e) + { + throw new Exception("Could not read CRL issuer.", e); + } + vec.Add(dpName.Name); + names.Add(new GeneralName(X509Name.GetInstance(new DerSequence(vec)))); + } + bool matches = false; + // verify that one of the names in the IDP matches one + // of the names in the DP. + if (dp.DistributionPointName != null) + { + dpName = dp.DistributionPointName; + GeneralName[] genNames = null; + if (dpName.PointType == DistributionPointName.FullName) + { + genNames = GeneralNames.GetInstance(dpName.Name).GetNames(); + } + if (dpName.PointType == DistributionPointName.NameRelativeToCrlIssuer) + { + if (dp.CrlIssuer != null) + { + genNames = dp.CrlIssuer.GetNames(); + } + else + { + genNames = new GeneralName[1]; + try + { + genNames[0] = new GeneralName( + PkixCertPathValidatorUtilities.GetIssuerPrincipal(cert)); + } + catch (IOException e) + { + throw new Exception("Could not read certificate issuer.", e); + } + } + for (int j = 0; j < genNames.Length; j++) + { + IEnumerator e = Asn1Sequence.GetInstance(genNames[j].Name.ToAsn1Object()).GetEnumerator(); + Asn1EncodableVector vec = new Asn1EncodableVector(); + while (e.MoveNext()) + { + vec.Add((Asn1Encodable)e.Current); + } + vec.Add(dpName.Name); + genNames[j] = new GeneralName(X509Name.GetInstance(new DerSequence(vec))); + } + } + if (genNames != null) + { + for (int j = 0; j < genNames.Length; j++) + { + if (names.Contains(genNames[j])) + { + matches = true; + break; + } + } + } + if (!matches) + { + throw new Exception( + "No match for certificate CRL issuing distribution point name to cRLIssuer CRL distribution point."); + } + } + // verify that one of the names in + // the IDP matches one of the names in the cRLIssuer field of + // the DP + else + { + if (dp.CrlIssuer == null) + { + throw new Exception("Either the cRLIssuer or the distributionPoint field must " + + "be contained in DistributionPoint."); + } + GeneralName[] genNames = dp.CrlIssuer.GetNames(); + for (int j = 0; j < genNames.Length; j++) + { + if (names.Contains(genNames[j])) + { + matches = true; + break; + } + } + if (!matches) + { + throw new Exception( + "No match for certificate CRL issuing distribution point name to cRLIssuer CRL distribution point."); + } + } + } + BasicConstraints bc = null; + try + { + bc = BasicConstraints.GetInstance(PkixCertPathValidatorUtilities.GetExtensionValue( + (IX509Extension)cert, X509Extensions.BasicConstraints)); + } + catch (Exception e) + { + throw new Exception("Basic constraints extension could not be decoded.", e); + } + + //if (cert is X509Certificate) + { + // (b) (2) (ii) + if (idp.OnlyContainsUserCerts && ((bc != null) && bc.IsCA())) + { + throw new Exception("CA Cert CRL only contains user certificates."); + } + + // (b) (2) (iii) + if (idp.OnlyContainsCACerts && (bc == null || !bc.IsCA())) + { + throw new Exception("End CRL only contains CA certificates."); + } + } + + // (b) (2) (iv) + if (idp.OnlyContainsAttributeCerts) + { + throw new Exception("onlyContainsAttributeCerts boolean is asserted."); + } + } + } + + internal static void ProcessCertBC( + PkixCertPath certPath, + int index, + PkixNameConstraintValidator nameConstraintValidator) + //throws CertPathValidatorException + { + IList certs = certPath.Certificates; + X509Certificate cert = (X509Certificate)certs[index]; + int n = certs.Count; + // i as defined in the algorithm description + int i = n - index; + // + // (b), (c) permitted and excluded subtree checking. + // + if (!(PkixCertPathValidatorUtilities.IsSelfIssued(cert) && (i < n))) + { + X509Name principal = cert.SubjectDN; + Asn1InputStream aIn = new Asn1InputStream(principal.GetEncoded()); + Asn1Sequence dns; + + try + { + dns = DerSequence.GetInstance(aIn.ReadObject()); + } + catch (Exception e) + { + throw new PkixCertPathValidatorException( + "Exception extracting subject name when checking subtrees.", e, certPath, index); + } + + try + { + nameConstraintValidator.CheckPermittedDN(dns); + nameConstraintValidator.CheckExcludedDN(dns); + } + catch (PkixNameConstraintValidatorException e) + { + throw new PkixCertPathValidatorException( + "Subtree check for certificate subject failed.", e, certPath, index); + } + + GeneralNames altName = null; + try + { + altName = GeneralNames.GetInstance( + PkixCertPathValidatorUtilities.GetExtensionValue(cert, X509Extensions.SubjectAlternativeName)); + } + catch (Exception e) + { + throw new PkixCertPathValidatorException( + "Subject alternative name extension could not be decoded.", e, certPath, index); + } + + IList emails = X509Name.GetInstance(dns).GetValueList(X509Name.EmailAddress); + foreach (string email in emails) + { + GeneralName emailAsGeneralName = new GeneralName(GeneralName.Rfc822Name, email); + try + { + nameConstraintValidator.checkPermitted(emailAsGeneralName); + nameConstraintValidator.checkExcluded(emailAsGeneralName); + } + catch (PkixNameConstraintValidatorException ex) + { + throw new PkixCertPathValidatorException( + "Subtree check for certificate subject alternative email failed.", ex, certPath, index); + } + } + if (altName != null) + { + GeneralName[] genNames = null; + try + { + genNames = altName.GetNames(); + } + catch (Exception e) + { + throw new PkixCertPathValidatorException( + "Subject alternative name contents could not be decoded.", e, certPath, index); + } + foreach (GeneralName genName in genNames) + { + try + { + nameConstraintValidator.checkPermitted(genName); + nameConstraintValidator.checkExcluded(genName); + } + catch (PkixNameConstraintValidatorException e) + { + throw new PkixCertPathValidatorException( + "Subtree check for certificate subject alternative name failed.", e, certPath, index); + } + } + } + } + } + + internal static void PrepareNextCertA( + PkixCertPath certPath, + int index) + //throws CertPathValidatorException + { + IList certs = certPath.Certificates; + X509Certificate cert = (X509Certificate)certs[index]; + // + // + // (a) check the policy mappings + // + Asn1Sequence pm = null; + try + { + pm = Asn1Sequence.GetInstance( + PkixCertPathValidatorUtilities.GetExtensionValue(cert, X509Extensions.PolicyMappings)); + } + catch (Exception ex) + { + throw new PkixCertPathValidatorException( + "Policy mappings extension could not be decoded.", ex, certPath, index); + } + if (pm != null) + { + Asn1Sequence mappings = pm; + + for (int j = 0; j < mappings.Count; j++) + { + DerObjectIdentifier issuerDomainPolicy = null; + DerObjectIdentifier subjectDomainPolicy = null; + try + { + Asn1Sequence mapping = DerSequence.GetInstance(mappings[j]); + + issuerDomainPolicy = DerObjectIdentifier.GetInstance(mapping[0]); + subjectDomainPolicy = DerObjectIdentifier.GetInstance(mapping[1]); + } + catch (Exception e) + { + throw new PkixCertPathValidatorException( + "Policy mappings extension contents could not be decoded.", e, certPath, index); + } + + if (Rfc3280CertPathUtilities.ANY_POLICY.Equals(issuerDomainPolicy.Id)) + throw new PkixCertPathValidatorException( + "IssuerDomainPolicy is anyPolicy", null, certPath, index); + + if (Rfc3280CertPathUtilities.ANY_POLICY.Equals(subjectDomainPolicy.Id)) + throw new PkixCertPathValidatorException( + "SubjectDomainPolicy is anyPolicy,", null, certPath, index); + } + } + } + + internal static PkixPolicyNode ProcessCertD( + PkixCertPath certPath, + int index, + ISet acceptablePolicies, + PkixPolicyNode validPolicyTree, + IList[] policyNodes, + int inhibitAnyPolicy) + //throws CertPathValidatorException + { + IList certs = certPath.Certificates; + X509Certificate cert = (X509Certificate)certs[index]; + int n = certs.Count; + // i as defined in the algorithm description + int i = n - index; + // + // (d) policy Information checking against initial policy and + // policy mapping + // + Asn1Sequence certPolicies = null; + try + { + certPolicies = DerSequence.GetInstance( + PkixCertPathValidatorUtilities.GetExtensionValue(cert, X509Extensions.CertificatePolicies)); + } + catch (Exception e) + { + throw new PkixCertPathValidatorException( + "Could not read certificate policies extension from certificate.", e, certPath, index); + } + if (certPolicies != null && validPolicyTree != null) + { + // + // (d) (1) + // + ISet pols = new HashSet(); + + foreach (Asn1Encodable ae in certPolicies) + { + PolicyInformation pInfo = PolicyInformation.GetInstance(ae.ToAsn1Object()); + DerObjectIdentifier pOid = pInfo.PolicyIdentifier; + + pols.Add(pOid.Id); + + if (!Rfc3280CertPathUtilities.ANY_POLICY.Equals(pOid.Id)) + { + ISet pq = null; + try + { + pq = PkixCertPathValidatorUtilities.GetQualifierSet(pInfo.PolicyQualifiers); + } + catch (PkixCertPathValidatorException ex) + { + throw new PkixCertPathValidatorException( + "Policy qualifier info set could not be build.", ex, certPath, index); + } + + bool match = PkixCertPathValidatorUtilities.ProcessCertD1i(i, policyNodes, pOid, pq); + + if (!match) + { + PkixCertPathValidatorUtilities.ProcessCertD1ii(i, policyNodes, pOid, pq); + } + } + } + + if (acceptablePolicies.IsEmpty || acceptablePolicies.Contains(Rfc3280CertPathUtilities.ANY_POLICY)) + { + acceptablePolicies.Clear(); + acceptablePolicies.AddAll(pols); + } + else + { + ISet t1 = new HashSet(); + + foreach (object o in acceptablePolicies) + { + if (pols.Contains(o)) + { + t1.Add(o); + } + } + acceptablePolicies.Clear(); + acceptablePolicies.AddAll(t1); + } + + // + // (d) (2) + // + if ((inhibitAnyPolicy > 0) || ((i < n) && PkixCertPathValidatorUtilities.IsSelfIssued(cert))) + { + foreach (Asn1Encodable ae in certPolicies) + { + PolicyInformation pInfo = PolicyInformation.GetInstance(ae.ToAsn1Object()); + if (Rfc3280CertPathUtilities.ANY_POLICY.Equals(pInfo.PolicyIdentifier.Id)) + { + ISet _apq = PkixCertPathValidatorUtilities.GetQualifierSet(pInfo.PolicyQualifiers); + IList _nodes = policyNodes[i - 1]; + + for (int k = 0; k < _nodes.Count; k++) + { + PkixPolicyNode _node = (PkixPolicyNode)_nodes[k]; + + IEnumerator _policySetIter = _node.ExpectedPolicies.GetEnumerator(); + while (_policySetIter.MoveNext()) + { + object _tmp = _policySetIter.Current; + + string _policy; + if (_tmp is string) + { + _policy = (string)_tmp; + } + else if (_tmp is DerObjectIdentifier) + { + _policy = ((DerObjectIdentifier)_tmp).Id; + } + else + { + continue; + } + + bool _found = false; + + foreach (PkixPolicyNode _child in _node.Children) + { + if (_policy.Equals(_child.ValidPolicy)) + { + _found = true; + } + } + + if (!_found) + { + ISet _newChildExpectedPolicies = new HashSet(); + _newChildExpectedPolicies.Add(_policy); + + PkixPolicyNode _newChild = new PkixPolicyNode(Platform.CreateArrayList(), i, + _newChildExpectedPolicies, _node, _apq, _policy, false); + _node.AddChild(_newChild); + policyNodes[i].Add(_newChild); + } + } + } + break; + } + } + } + + PkixPolicyNode _validPolicyTree = validPolicyTree; + // + // (d) (3) + // + for (int j = (i - 1); j >= 0; j--) + { + IList nodes = policyNodes[j]; + + for (int k = 0; k < nodes.Count; k++) + { + PkixPolicyNode node = (PkixPolicyNode)nodes[k]; + if (!node.HasChildren) + { + _validPolicyTree = PkixCertPathValidatorUtilities.RemovePolicyNode(_validPolicyTree, policyNodes, + node); + if (_validPolicyTree == null) + { + break; + } + } + } + } + + // + // d (4) + // + ISet criticalExtensionOids = cert.GetCriticalExtensionOids(); + + if (criticalExtensionOids != null) + { + bool critical = criticalExtensionOids.Contains(X509Extensions.CertificatePolicies.Id); + + IList nodes = policyNodes[i]; + for (int j = 0; j < nodes.Count; j++) + { + PkixPolicyNode node = (PkixPolicyNode)nodes[j]; + node.IsCritical = critical; + } + } + return _validPolicyTree; + } + return null; + } + + /** + * If the DP includes cRLIssuer, then verify that the issuer field in the + * complete CRL matches cRLIssuer in the DP and that the complete CRL + * contains an + * g distribution point extension with the indirectCRL + * boolean asserted. Otherwise, verify that the CRL issuer matches the + * certificate issuer. + * + * @param dp The distribution point. + * @param cert The certificate ot attribute certificate. + * @param crl The CRL for <code>cert</code>. + * @throws AnnotatedException if one of the above conditions does not apply or an error + * occurs. + */ + internal static void ProcessCrlB1( + DistributionPoint dp, + object cert, + X509Crl crl) + { + Asn1Object idp = PkixCertPathValidatorUtilities.GetExtensionValue( + crl, X509Extensions.IssuingDistributionPoint); + + bool isIndirect = false; + if (idp != null) + { + if (IssuingDistributionPoint.GetInstance(idp).IsIndirectCrl) + { + isIndirect = true; + } + } + byte[] issuerBytes = crl.IssuerDN.GetEncoded(); + + bool matchIssuer = false; + if (dp.CrlIssuer != null) + { + GeneralName[] genNames = dp.CrlIssuer.GetNames(); + for (int j = 0; j < genNames.Length; j++) + { + if (genNames[j].TagNo == GeneralName.DirectoryName) + { + try + { + if (Org.BouncyCastle.Utilities.Arrays.AreEqual(genNames[j].Name.ToAsn1Object().GetEncoded(), issuerBytes)) + { + matchIssuer = true; + } + } + catch (IOException e) + { + throw new Exception( + "CRL issuer information from distribution point cannot be decoded.", e); + } + } + } + if (matchIssuer && !isIndirect) + { + throw new Exception("Distribution point contains cRLIssuer field but CRL is not indirect."); + } + if (!matchIssuer) + { + throw new Exception("CRL issuer of CRL does not match CRL issuer of distribution point."); + } + } + else + { + if (crl.IssuerDN.Equivalent(PkixCertPathValidatorUtilities.GetIssuerPrincipal(cert), true)) + { + matchIssuer = true; + } + } + if (!matchIssuer) + { + throw new Exception("Cannot find matching CRL issuer for certificate."); + } + } + + internal static ReasonsMask ProcessCrlD( + X509Crl crl, + DistributionPoint dp) + //throws AnnotatedException + { + IssuingDistributionPoint idp = null; + try + { + idp = IssuingDistributionPoint.GetInstance(PkixCertPathValidatorUtilities.GetExtensionValue(crl, X509Extensions.IssuingDistributionPoint)); + } + catch (Exception e) + { + throw new Exception("issuing distribution point extension could not be decoded.", e); + } + + // (d) (1) + if (idp != null && idp.OnlySomeReasons != null && dp.Reasons != null) + { + return new ReasonsMask(dp.Reasons.IntValue).Intersect(new ReasonsMask(idp.OnlySomeReasons + .IntValue)); + } + // (d) (4) + if ((idp == null || idp.OnlySomeReasons == null) && dp.Reasons == null) + { + return ReasonsMask.AllReasons; + } + + // (d) (2) and (d)(3) + + ReasonsMask dpReasons = null; + + if (dp.Reasons == null) + { + dpReasons = ReasonsMask.AllReasons; + } + else + { + dpReasons = new ReasonsMask(dp.Reasons.IntValue); + } + + ReasonsMask idpReasons = null; + + if (idp == null) + { + idpReasons = ReasonsMask.AllReasons; + } + else + { + idpReasons = new ReasonsMask(idp.OnlySomeReasons.IntValue); + } + + return dpReasons.Intersect(idpReasons); + } + + /** + * Obtain and validate the certification path for the complete CRL issuer. + * If a key usage extension is present in the CRL issuer's certificate, + * verify that the cRLSign bit is set. + * + * @param crl CRL which contains revocation information for the certificate + * <code>cert</code>. + * @param cert The attribute certificate or certificate to check if it is + * revoked. + * @param defaultCRLSignCert The issuer certificate of the certificate <code>cert</code>. + * @param defaultCRLSignKey The public key of the issuer certificate + * <code>defaultCRLSignCert</code>. + * @param paramsPKIX paramsPKIX PKIX parameters. + * @param certPathCerts The certificates on the certification path. + * @return A <code>Set</code> with all keys of possible CRL issuer + * certificates. + * @throws AnnotatedException if the CRL is not valid or the status cannot be checked or + * some error occurs. + */ + internal static ISet ProcessCrlF( + X509Crl crl, + object cert, + X509Certificate defaultCRLSignCert, + AsymmetricKeyParameter defaultCRLSignKey, + PkixParameters paramsPKIX, + IList certPathCerts) + { + // (f) + + // get issuer from CRL + X509CertStoreSelector selector = new X509CertStoreSelector(); + try + { + selector.Subject = crl.IssuerDN; + } + catch (IOException e) + { + throw new Exception( + "Subject criteria for certificate selector to find issuer certificate for CRL could not be set.", e); + } + + // get CRL signing certs + IList coll = Platform.CreateArrayList(); + + try + { + CollectionUtilities.AddRange(coll, PkixCertPathValidatorUtilities.FindCertificates(selector, paramsPKIX.GetStores())); + CollectionUtilities.AddRange(coll, PkixCertPathValidatorUtilities.FindCertificates(selector, paramsPKIX.GetAdditionalStores())); + } + catch (Exception e) + { + throw new Exception("Issuer certificate for CRL cannot be searched.", e); + } + + coll.Add(defaultCRLSignCert); + + IEnumerator cert_it = coll.GetEnumerator(); + + IList validCerts = Platform.CreateArrayList(); + IList validKeys = Platform.CreateArrayList(); + + while (cert_it.MoveNext()) + { + X509Certificate signingCert = (X509Certificate)cert_it.Current; + + /* + * CA of the certificate, for which this CRL is checked, has also + * signed CRL, so skip the path validation, because is already done + */ + if (signingCert.Equals(defaultCRLSignCert)) + { + validCerts.Add(signingCert); + validKeys.Add(defaultCRLSignKey); + continue; + } + try + { +// CertPathBuilder builder = CertPathBuilder.GetInstance("PKIX"); + PkixCertPathBuilder builder = new PkixCertPathBuilder(); + selector = new X509CertStoreSelector(); + selector.Certificate = signingCert; + + PkixParameters temp = (PkixParameters)paramsPKIX.Clone(); + temp.SetTargetCertConstraints(selector); + + PkixBuilderParameters parameters = (PkixBuilderParameters) + PkixBuilderParameters.GetInstance(temp); + + /* + * if signingCert is placed not higher on the cert path a + * dependency loop results. CRL for cert is checked, but + * signingCert is needed for checking the CRL which is dependent + * on checking cert because it is higher in the cert path and so + * signing signingCert transitively. so, revocation is disabled, + * forgery attacks of the CRL are detected in this outer loop + * for all other it must be enabled to prevent forgery attacks + */ + if (certPathCerts.Contains(signingCert)) + { + parameters.IsRevocationEnabled = false; + } + else + { + parameters.IsRevocationEnabled = true; + } + IList certs = builder.Build(parameters).CertPath.Certificates; + validCerts.Add(signingCert); + validKeys.Add(PkixCertPathValidatorUtilities.GetNextWorkingKey(certs, 0)); + } + catch (PkixCertPathBuilderException e) + { + throw new Exception("Internal error.", e); + } + catch (PkixCertPathValidatorException e) + { + throw new Exception("Public key of issuer certificate of CRL could not be retrieved.", e); + } + //catch (Exception e) + //{ + // throw new Exception(e.Message); + //} + } + + ISet checkKeys = new HashSet(); + + Exception lastException = null; + for (int i = 0; i < validCerts.Count; i++) + { + X509Certificate signCert = (X509Certificate)validCerts[i]; + bool[] keyusage = signCert.GetKeyUsage(); + + if (keyusage != null && (keyusage.Length < 7 || !keyusage[CRL_SIGN])) + { + lastException = new Exception( + "Issuer certificate key usage extension does not permit CRL signing."); + } + else + { + checkKeys.Add(validKeys[i]); + } + } + + if ((checkKeys.Count == 0) && lastException == null) + { + throw new Exception("Cannot find a valid issuer certificate."); + } + if ((checkKeys.Count == 0) && lastException != null) + { + throw lastException; + } + + return checkKeys; + } + + internal static AsymmetricKeyParameter ProcessCrlG( + X509Crl crl, + ISet keys) + { + Exception lastException = null; + foreach (AsymmetricKeyParameter key in keys) + { + try + { + crl.Verify(key); + return key; + } + catch (Exception e) + { + lastException = e; + } + } + throw new Exception("Cannot verify CRL.", lastException); + } + + internal static X509Crl ProcessCrlH( + ISet deltaCrls, + AsymmetricKeyParameter key) + { + Exception lastException = null; + foreach (X509Crl crl in deltaCrls) + { + try + { + crl.Verify(key); + return crl; + } + catch (Exception e) + { + lastException = e; + } + } + if (lastException != null) + { + throw new Exception("Cannot verify delta CRL.", lastException); + } + return null; + } + + /** + * Checks a distribution point for revocation information for the + * certificate <code>cert</code>. + * + * @param dp The distribution point to consider. + * @param paramsPKIX PKIX parameters. + * @param cert Certificate to check if it is revoked. + * @param validDate The date when the certificate revocation status should be + * checked. + * @param defaultCRLSignCert The issuer certificate of the certificate <code>cert</code>. + * @param defaultCRLSignKey The public key of the issuer certificate + * <code>defaultCRLSignCert</code>. + * @param certStatus The current certificate revocation status. + * @param reasonMask The reasons mask which is already checked. + * @param certPathCerts The certificates of the certification path. + * @throws AnnotatedException if the certificate is revoked or the status cannot be checked + * or some error occurs. + */ + private static void CheckCrl( + DistributionPoint dp, + PkixParameters paramsPKIX, + X509Certificate cert, + DateTime validDate, + X509Certificate defaultCRLSignCert, + AsymmetricKeyParameter defaultCRLSignKey, + CertStatus certStatus, + ReasonsMask reasonMask, + IList certPathCerts) + //throws AnnotatedException + { + DateTime currentDate = DateTime.UtcNow; + + if (validDate.Ticks > currentDate.Ticks) + { + throw new Exception("Validation time is in future."); + } + + // (a) + /* + * We always get timely valid CRLs, so there is no step (a) (1). + * "locally cached" CRLs are assumed to be in getStore(), additional + * CRLs must be enabled in the ExtendedPKIXParameters and are in + * getAdditionalStore() + */ + + ISet crls = PkixCertPathValidatorUtilities.GetCompleteCrls(dp, cert, currentDate, paramsPKIX); + bool validCrlFound = false; + Exception lastException = null; + + IEnumerator crl_iter = crls.GetEnumerator(); + + while (crl_iter.MoveNext() && certStatus.Status == CertStatus.Unrevoked && !reasonMask.IsAllReasons) + { + try + { + X509Crl crl = (X509Crl)crl_iter.Current; + + // (d) + ReasonsMask interimReasonsMask = Rfc3280CertPathUtilities.ProcessCrlD(crl, dp); + + // (e) + /* + * The reasons mask is updated at the end, so only valid CRLs + * can update it. If this CRL does not contain new reasons it + * must be ignored. + */ + if (!interimReasonsMask.HasNewReasons(reasonMask)) + { + continue; + } + + // (f) + ISet keys = Rfc3280CertPathUtilities.ProcessCrlF(crl, cert, defaultCRLSignCert, defaultCRLSignKey, + paramsPKIX, certPathCerts); + // (g) + AsymmetricKeyParameter key = Rfc3280CertPathUtilities.ProcessCrlG(crl, keys); + + X509Crl deltaCRL = null; + + if (paramsPKIX.IsUseDeltasEnabled) + { + // get delta CRLs + ISet deltaCRLs = PkixCertPathValidatorUtilities.GetDeltaCrls(currentDate, paramsPKIX, crl); + // we only want one valid delta CRL + // (h) + deltaCRL = Rfc3280CertPathUtilities.ProcessCrlH(deltaCRLs, key); + } + + /* + * CRL must be be valid at the current time, not the validation + * time. If a certificate is revoked with reason keyCompromise, + * cACompromise, it can be used for forgery, also for the past. + * This reason may not be contained in older CRLs. + */ + + /* + * in the chain model signatures stay valid also after the + * certificate has been expired, so they do not have to be in + * the CRL validity time + */ + + if (paramsPKIX.ValidityModel != PkixParameters.ChainValidityModel) + { + /* + * if a certificate has expired, but was revoked, it is not + * more in the CRL, so it would be regarded as valid if the + * first check is not done + */ + if (cert.NotAfter.Ticks < crl.ThisUpdate.Ticks) + { + throw new Exception("No valid CRL for current time found."); + } + } + + Rfc3280CertPathUtilities.ProcessCrlB1(dp, cert, crl); + + // (b) (2) + Rfc3280CertPathUtilities.ProcessCrlB2(dp, cert, crl); + + // (c) + Rfc3280CertPathUtilities.ProcessCrlC(deltaCRL, crl, paramsPKIX); + + // (i) + Rfc3280CertPathUtilities.ProcessCrlI(validDate, deltaCRL, cert, certStatus, paramsPKIX); + + // (j) + Rfc3280CertPathUtilities.ProcessCrlJ(validDate, crl, cert, certStatus); + + // (k) + if (certStatus.Status == CrlReason.RemoveFromCrl) + { + certStatus.Status = CertStatus.Unrevoked; + } + + // update reasons mask + reasonMask.AddReasons(interimReasonsMask); + + ISet criticalExtensions = crl.GetCriticalExtensionOids(); + + if (criticalExtensions != null) + { + criticalExtensions = new HashSet(criticalExtensions); + criticalExtensions.Remove(X509Extensions.IssuingDistributionPoint.Id); + criticalExtensions.Remove(X509Extensions.DeltaCrlIndicator.Id); + + if (!criticalExtensions.IsEmpty) + throw new Exception("CRL contains unsupported critical extensions."); + } + + if (deltaCRL != null) + { + criticalExtensions = deltaCRL.GetCriticalExtensionOids(); + if (criticalExtensions != null) + { + criticalExtensions = new HashSet(criticalExtensions); + criticalExtensions.Remove(X509Extensions.IssuingDistributionPoint.Id); + criticalExtensions.Remove(X509Extensions.DeltaCrlIndicator.Id); + + if (!criticalExtensions.IsEmpty) + throw new Exception("Delta CRL contains unsupported critical extension."); + } + } + + validCrlFound = true; + } + catch (Exception e) + { + lastException = e; + } + } + if (!validCrlFound) + { + throw lastException; + } + } + + /** + * Checks a certificate if it is revoked. + * + * @param paramsPKIX PKIX parameters. + * @param cert Certificate to check if it is revoked. + * @param validDate The date when the certificate revocation status should be + * checked. + * @param sign The issuer certificate of the certificate <code>cert</code>. + * @param workingPublicKey The public key of the issuer certificate <code>sign</code>. + * @param certPathCerts The certificates of the certification path. + * @throws AnnotatedException if the certificate is revoked or the status cannot be checked + * or some error occurs. + */ + protected static void CheckCrls( + PkixParameters paramsPKIX, + X509Certificate cert, + DateTime validDate, + X509Certificate sign, + AsymmetricKeyParameter workingPublicKey, + IList certPathCerts) + { + Exception lastException = null; + CrlDistPoint crldp = null; + + try + { + crldp = CrlDistPoint.GetInstance(PkixCertPathValidatorUtilities.GetExtensionValue(cert, X509Extensions.CrlDistributionPoints)); + } + catch (Exception e) + { + throw new Exception("CRL distribution point extension could not be read.", e); + } + + try + { + PkixCertPathValidatorUtilities.AddAdditionalStoresFromCrlDistributionPoint(crldp, paramsPKIX); + } + catch (Exception e) + { + throw new Exception( + "No additional CRL locations could be decoded from CRL distribution point extension.", e); + } + CertStatus certStatus = new CertStatus(); + ReasonsMask reasonsMask = new ReasonsMask(); + + bool validCrlFound = false; + + // for each distribution point + if (crldp != null) + { + DistributionPoint[] dps = null; + try + { + dps = crldp.GetDistributionPoints(); + } + catch (Exception e) + { + throw new Exception("Distribution points could not be read.", e); + } + if (dps != null) + { + for (int i = 0; i < dps.Length && certStatus.Status == CertStatus.Unrevoked && !reasonsMask.IsAllReasons; i++) + { + PkixParameters paramsPKIXClone = (PkixParameters)paramsPKIX.Clone(); + try + { + CheckCrl(dps[i], paramsPKIXClone, cert, validDate, sign, workingPublicKey, certStatus, reasonsMask, certPathCerts); + validCrlFound = true; + } + catch (Exception e) + { + lastException = e; + } + } + } + } + + /* + * If the revocation status has not been determined, repeat the process + * above with any available CRLs not specified in a distribution point + * but issued by the certificate issuer. + */ + + if (certStatus.Status == CertStatus.Unrevoked && !reasonsMask.IsAllReasons) + { + try + { + /* + * assume a DP with both the reasons and the cRLIssuer fields + * omitted and a distribution point name of the certificate + * issuer. + */ + Asn1Object issuer = null; + try + { + issuer = new Asn1InputStream(cert.IssuerDN.GetEncoded()).ReadObject(); + } + catch (Exception e) + { + throw new Exception("Issuer from certificate for CRL could not be reencoded.", e); + } + DistributionPoint dp = new DistributionPoint(new DistributionPointName(0, new GeneralNames( + new GeneralName(GeneralName.DirectoryName, issuer))), null, null); + PkixParameters paramsPKIXClone = (PkixParameters)paramsPKIX.Clone(); + + CheckCrl(dp, paramsPKIXClone, cert, validDate, sign, workingPublicKey, certStatus, reasonsMask, + certPathCerts); + + validCrlFound = true; + } + catch (Exception e) + { + lastException = e; + } + } + + if (!validCrlFound) + { + throw lastException; + } + if (certStatus.Status != CertStatus.Unrevoked) + { + // TODO This format is forced by the NistCertPath tests + string formattedDate = certStatus.RevocationDate.Value.ToString( + "G", new CultureInfo("en-us")); + string message = "Certificate revocation after " + formattedDate; + message += ", reason: " + CrlReasons[certStatus.Status]; + throw new Exception(message); + } + + if (!reasonsMask.IsAllReasons && certStatus.Status == CertStatus.Unrevoked) + { + certStatus.Status = CertStatus.Undetermined; + } + + if (certStatus.Status == CertStatus.Undetermined) + { + throw new Exception("Certificate status could not be determined."); + } + } + + internal static PkixPolicyNode PrepareCertB( + PkixCertPath certPath, + int index, + IList[] policyNodes, + PkixPolicyNode validPolicyTree, + int policyMapping) + //throws CertPathValidatorException + { + IList certs = certPath.Certificates; + X509Certificate cert = (X509Certificate)certs[index]; + int n = certs.Count; + // i as defined in the algorithm description + int i = n - index; + // (b) + // + Asn1Sequence pm = null; + try + { + pm = (Asn1Sequence)Asn1Sequence.GetInstance(PkixCertPathValidatorUtilities.GetExtensionValue(cert, X509Extensions.PolicyMappings)); + } + catch (Exception ex) + { + throw new PkixCertPathValidatorException( + "Policy mappings extension could not be decoded.", ex, certPath, index); + } + PkixPolicyNode _validPolicyTree = validPolicyTree; + if (pm != null) + { + Asn1Sequence mappings = (Asn1Sequence)pm; + IDictionary m_idp = Platform.CreateHashtable(); + ISet s_idp = new HashSet(); + + for (int j = 0; j < mappings.Count; j++) + { + Asn1Sequence mapping = (Asn1Sequence) mappings[j]; + string id_p = ((DerObjectIdentifier) mapping[0]).Id; + string sd_p = ((DerObjectIdentifier) mapping[1]).Id; + ISet tmp; + + if (!m_idp.Contains(id_p)) + { + tmp = new HashSet(); + tmp.Add(sd_p); + m_idp[id_p] = tmp; + s_idp.Add(id_p); + } + else + { + tmp = (ISet)m_idp[id_p]; + tmp.Add(sd_p); + } + } + + IEnumerator it_idp = s_idp.GetEnumerator(); + while (it_idp.MoveNext()) + { + string id_p = (string)it_idp.Current; + + // + // (1) + // + if (policyMapping > 0) + { + bool idp_found = false; + IEnumerator nodes_i = policyNodes[i].GetEnumerator(); + + while (nodes_i.MoveNext()) + { + PkixPolicyNode node = (PkixPolicyNode)nodes_i.Current; + if (node.ValidPolicy.Equals(id_p)) + { + idp_found = true; + node.ExpectedPolicies = (ISet)m_idp[id_p]; + break; + } + } + + if (!idp_found) + { + nodes_i = policyNodes[i].GetEnumerator(); + while (nodes_i.MoveNext()) + { + PkixPolicyNode node = (PkixPolicyNode)nodes_i.Current; + if (Rfc3280CertPathUtilities.ANY_POLICY.Equals(node.ValidPolicy)) + { + ISet pq = null; + Asn1Sequence policies = null; + try + { + policies = (Asn1Sequence)PkixCertPathValidatorUtilities.GetExtensionValue(cert, + X509Extensions.CertificatePolicies); + } + catch (Exception e) + { + throw new PkixCertPathValidatorException( + "Certificate policies extension could not be decoded.", e, certPath, index); + } + + foreach (Asn1Encodable ae in policies) + { + PolicyInformation pinfo = null; + try + { + pinfo = PolicyInformation.GetInstance(ae.ToAsn1Object()); + } + catch (Exception ex) + { + throw new PkixCertPathValidatorException( + "Policy information could not be decoded.", ex, certPath, index); + } + if (Rfc3280CertPathUtilities.ANY_POLICY.Equals(pinfo.PolicyIdentifier.Id)) + { + try + { + pq = PkixCertPathValidatorUtilities + .GetQualifierSet(pinfo.PolicyQualifiers); + } + catch (PkixCertPathValidatorException ex) + { + throw new PkixCertPathValidatorException( + "Policy qualifier info set could not be decoded.", ex, certPath, + index); + } + break; + } + } + bool ci = false; + ISet critExtOids = cert.GetCriticalExtensionOids(); + if (critExtOids != null) + { + ci = critExtOids.Contains(X509Extensions.CertificatePolicies.Id); + } + + PkixPolicyNode p_node = (PkixPolicyNode)node.Parent; + if (Rfc3280CertPathUtilities.ANY_POLICY.Equals(p_node.ValidPolicy)) + { + PkixPolicyNode c_node = new PkixPolicyNode(Platform.CreateArrayList(), i, + (ISet)m_idp[id_p], p_node, pq, id_p, ci); + p_node.AddChild(c_node); + policyNodes[i].Add(c_node); + } + break; + } + } + } + + // + // (2) + // + } + else if (policyMapping <= 0) + { + foreach (PkixPolicyNode node in Platform.CreateArrayList(policyNodes[i])) + { + if (node.ValidPolicy.Equals(id_p)) + { + node.Parent.RemoveChild(node); + + for (int k = i - 1; k >= 0; k--) + { + foreach (PkixPolicyNode node2 in Platform.CreateArrayList(policyNodes[k])) + { + if (!node2.HasChildren) + { + _validPolicyTree = PkixCertPathValidatorUtilities.RemovePolicyNode( + _validPolicyTree, policyNodes, node2); + + if (_validPolicyTree == null) + break; + } + } + } + } + } + } + } + } + return _validPolicyTree; + } + + internal static ISet[] ProcessCrlA1ii( + DateTime currentDate, + PkixParameters paramsPKIX, + X509Certificate cert, + X509Crl crl) + { + ISet deltaSet = new HashSet(); + X509CrlStoreSelector crlselect = new X509CrlStoreSelector(); + crlselect.CertificateChecking = cert; + + try + { + IList issuer = Platform.CreateArrayList(); + issuer.Add(crl.IssuerDN); + crlselect.Issuers = issuer; + } + catch (IOException e) + { + throw new Exception("Cannot extract issuer from CRL." + e, e); + } + + crlselect.CompleteCrlEnabled = true; + ISet completeSet = CrlUtilities.FindCrls(crlselect, paramsPKIX, currentDate); + + if (paramsPKIX.IsUseDeltasEnabled) + { + // get delta CRL(s) + try + { + deltaSet.AddAll(PkixCertPathValidatorUtilities.GetDeltaCrls(currentDate, paramsPKIX, crl)); + } + catch (Exception e) + { + throw new Exception("Exception obtaining delta CRLs.", e); + } + } + + return new ISet[]{ completeSet, deltaSet }; + } + + internal static ISet ProcessCrlA1i( + DateTime currentDate, + PkixParameters paramsPKIX, + X509Certificate cert, + X509Crl crl) + { + ISet deltaSet = new HashSet(); + if (paramsPKIX.IsUseDeltasEnabled) + { + CrlDistPoint freshestCRL = null; + try + { + freshestCRL = CrlDistPoint.GetInstance( + PkixCertPathValidatorUtilities.GetExtensionValue(cert, X509Extensions.FreshestCrl)); + } + catch (Exception e) + { + throw new Exception("Freshest CRL extension could not be decoded from certificate.", e); + } + + if (freshestCRL == null) + { + try + { + freshestCRL = CrlDistPoint.GetInstance(PkixCertPathValidatorUtilities.GetExtensionValue(crl, X509Extensions.FreshestCrl)); + } + catch (Exception e) + { + throw new Exception("Freshest CRL extension could not be decoded from CRL.", e); + } + } + if (freshestCRL != null) + { + try + { + PkixCertPathValidatorUtilities.AddAdditionalStoresFromCrlDistributionPoint(freshestCRL, paramsPKIX); + } + catch (Exception e) + { + throw new Exception( + "No new delta CRL locations could be added from Freshest CRL extension.", e); + } + // get delta CRL(s) + try + { + deltaSet.AddAll(PkixCertPathValidatorUtilities.GetDeltaCrls(currentDate, paramsPKIX, crl)); + } + catch (Exception e) + { + throw new Exception("Exception obtaining delta CRLs.", e); + } + } + } + return deltaSet; + } + + internal static void ProcessCertF( + PkixCertPath certPath, + int index, + PkixPolicyNode validPolicyTree, + int explicitPolicy) + { + // + // (f) + // + if (explicitPolicy <= 0 && validPolicyTree == null) + { + throw new PkixCertPathValidatorException( + "No valid policy tree found when one expected.", null, certPath, index); + } + } + + internal static void ProcessCertA( + PkixCertPath certPath, + PkixParameters paramsPKIX, + int index, + AsymmetricKeyParameter workingPublicKey, + X509Name workingIssuerName, + X509Certificate sign) + { + IList certs = certPath.Certificates; + X509Certificate cert = (X509Certificate)certs[index]; + // + // (a) verify + // + try + { + // (a) (1) + // + cert.Verify(workingPublicKey); + } + catch (GeneralSecurityException e) + { + throw new PkixCertPathValidatorException("Could not validate certificate signature.", e, certPath, index); + } + + try + { + // (a) (2) + // + cert.CheckValidity(PkixCertPathValidatorUtilities + .GetValidCertDateFromValidityModel(paramsPKIX, certPath, index)); + } + catch (CertificateExpiredException e) + { + throw new PkixCertPathValidatorException("Could not validate certificate: " + e.Message, e, certPath, index); + } + catch (CertificateNotYetValidException e) + { + throw new PkixCertPathValidatorException("Could not validate certificate: " + e.Message, e, certPath, index); + } + catch (Exception e) + { + throw new PkixCertPathValidatorException("Could not validate time of certificate.", e, certPath, index); + } + + // + // (a) (3) + // + if (paramsPKIX.IsRevocationEnabled) + { + try + { + CheckCrls(paramsPKIX, cert, PkixCertPathValidatorUtilities.GetValidCertDateFromValidityModel(paramsPKIX, + certPath, index), sign, workingPublicKey, certs); + } + catch (Exception e) + { + Exception cause = e.InnerException; + if (cause == null) + { + cause = e; + } + throw new PkixCertPathValidatorException(e.Message, cause, certPath, index); + } + } + + // + // (a) (4) name chaining + // + X509Name issuer = PkixCertPathValidatorUtilities.GetIssuerPrincipal(cert); + if (!issuer.Equivalent(workingIssuerName, true)) + { + throw new PkixCertPathValidatorException("IssuerName(" + issuer + + ") does not match SubjectName(" + workingIssuerName + ") of signing certificate.", null, + certPath, index); + } + } + + internal static int PrepareNextCertI1( + PkixCertPath certPath, + int index, + int explicitPolicy) + { + IList certs = certPath.Certificates; + X509Certificate cert = (X509Certificate)certs[index]; + // + // (i) + // + Asn1Sequence pc = null; + try + { + pc = DerSequence.GetInstance( + PkixCertPathValidatorUtilities.GetExtensionValue(cert, X509Extensions.PolicyConstraints)); + } + catch (Exception e) + { + throw new PkixCertPathValidatorException( + "Policy constraints extension cannot be decoded.", e, certPath, index); + } + + int tmpInt; + + if (pc != null) + { + IEnumerator policyConstraints = pc.GetEnumerator(); + + while (policyConstraints.MoveNext()) + { + try + { + Asn1TaggedObject constraint = Asn1TaggedObject.GetInstance(policyConstraints.Current); + if (constraint.TagNo == 0) + { + tmpInt = DerInteger.GetInstance(constraint, false).Value.IntValue; + if (tmpInt < explicitPolicy) + { + return tmpInt; + } + break; + } + } + catch (ArgumentException e) + { + throw new PkixCertPathValidatorException( + "Policy constraints extension contents cannot be decoded.", e, certPath, index); + } + } + } + return explicitPolicy; + } + + internal static int PrepareNextCertI2( + PkixCertPath certPath, + int index, + int policyMapping) + //throws CertPathValidatorException + { + IList certs = certPath.Certificates; + X509Certificate cert = (X509Certificate)certs[index]; + + // + // (i) + // + Asn1Sequence pc = null; + try + { + pc = DerSequence.GetInstance( + PkixCertPathValidatorUtilities.GetExtensionValue(cert, X509Extensions.PolicyConstraints)); + } + catch (Exception e) + { + throw new PkixCertPathValidatorException( + "Policy constraints extension cannot be decoded.", e, certPath, index); + } + + int tmpInt; + + if (pc != null) + { + IEnumerator policyConstraints = pc.GetEnumerator(); + + while (policyConstraints.MoveNext()) + { + try + { + Asn1TaggedObject constraint = Asn1TaggedObject.GetInstance(policyConstraints.Current); + if (constraint.TagNo == 1) + { + tmpInt = DerInteger.GetInstance(constraint, false).Value.IntValue; + if (tmpInt < policyMapping) + { + return tmpInt; + } + break; + } + } + catch (ArgumentException e) + { + throw new PkixCertPathValidatorException( + "Policy constraints extension contents cannot be decoded.", e, certPath, index); + } + } + } + return policyMapping; + } + + internal static void PrepareNextCertG( + PkixCertPath certPath, + int index, + PkixNameConstraintValidator nameConstraintValidator) + //throws CertPathValidatorException + { + IList certs = certPath.Certificates; + X509Certificate cert = (X509Certificate)certs[index]; + + // + // (g) handle the name constraints extension + // + NameConstraints nc = null; + try + { + Asn1Sequence ncSeq = DerSequence.GetInstance( + PkixCertPathValidatorUtilities.GetExtensionValue(cert, X509Extensions.NameConstraints)); + if (ncSeq != null) + { + nc = new NameConstraints(ncSeq); + } + } + catch (Exception e) + { + throw new PkixCertPathValidatorException( + "Name constraints extension could not be decoded.", e, certPath, index); + } + if (nc != null) + { + // + // (g) (1) permitted subtrees + // + Asn1Sequence permitted = nc.PermittedSubtrees; + if (permitted != null) + { + try + { + nameConstraintValidator.IntersectPermittedSubtree(permitted); + } + catch (Exception ex) + { + throw new PkixCertPathValidatorException( + "Permitted subtrees cannot be build from name constraints extension.", ex, certPath, index); + } + } + + // + // (g) (2) excluded subtrees + // + Asn1Sequence excluded = nc.ExcludedSubtrees; + if (excluded != null) + { + IEnumerator e = excluded.GetEnumerator(); + try + { + while (e.MoveNext()) + { + GeneralSubtree subtree = GeneralSubtree.GetInstance(e.Current); + nameConstraintValidator.AddExcludedSubtree(subtree); + } + } + catch (Exception ex) + { + throw new PkixCertPathValidatorException( + "Excluded subtrees cannot be build from name constraints extension.", ex, certPath, index); + } + } + } + } + + internal static int PrepareNextCertJ( + PkixCertPath certPath, + int index, + int inhibitAnyPolicy) + //throws CertPathValidatorException + { + IList certs = certPath.Certificates; + X509Certificate cert = (X509Certificate)certs[index]; + + // + // (j) + // + DerInteger iap = null; + try + { + iap = DerInteger.GetInstance( + PkixCertPathValidatorUtilities.GetExtensionValue(cert, X509Extensions.InhibitAnyPolicy)); + } + catch (Exception e) + { + throw new PkixCertPathValidatorException( + "Inhibit any-policy extension cannot be decoded.", e, certPath, index); + } + + if (iap != null) + { + int _inhibitAnyPolicy = iap.Value.IntValue; + + if (_inhibitAnyPolicy < inhibitAnyPolicy) + return _inhibitAnyPolicy; + } + return inhibitAnyPolicy; + } + + internal static void PrepareNextCertK( + PkixCertPath certPath, + int index) + //throws CertPathValidatorException + { + IList certs = certPath.Certificates; + X509Certificate cert = (X509Certificate)certs[index]; + // + // (k) + // + BasicConstraints bc = null; + try + { + bc = BasicConstraints.GetInstance( + PkixCertPathValidatorUtilities.GetExtensionValue(cert, X509Extensions.BasicConstraints)); + } + catch (Exception e) + { + throw new PkixCertPathValidatorException("Basic constraints extension cannot be decoded.", e, certPath, + index); + } + if (bc != null) + { + if (!(bc.IsCA())) + throw new PkixCertPathValidatorException("Not a CA certificate"); + } + else + { + throw new PkixCertPathValidatorException("Intermediate certificate lacks BasicConstraints"); + } + } + + internal static int PrepareNextCertL( + PkixCertPath certPath, + int index, + int maxPathLength) + //throws CertPathValidatorException + { + IList certs = certPath.Certificates; + X509Certificate cert = (X509Certificate)certs[index]; + // + // (l) + // + if (!PkixCertPathValidatorUtilities.IsSelfIssued(cert)) + { + if (maxPathLength <= 0) + { + throw new PkixCertPathValidatorException("Max path length not greater than zero", null, certPath, index); + } + + return maxPathLength - 1; + } + return maxPathLength; + } + + internal static int PrepareNextCertM( + PkixCertPath certPath, + int index, + int maxPathLength) + //throws CertPathValidatorException + { + IList certs = certPath.Certificates; + X509Certificate cert = (X509Certificate)certs[index]; + + // + // (m) + // + BasicConstraints bc = null; + try + { + bc = BasicConstraints.GetInstance( + PkixCertPathValidatorUtilities.GetExtensionValue(cert, X509Extensions.BasicConstraints)); + } + catch (Exception e) + { + throw new PkixCertPathValidatorException("Basic constraints extension cannot be decoded.", e, certPath, + index); + } + if (bc != null) + { + BigInteger _pathLengthConstraint = bc.PathLenConstraint; + + if (_pathLengthConstraint != null) + { + int _plc = _pathLengthConstraint.IntValue; + + if (_plc < maxPathLength) + { + return _plc; + } + } + } + return maxPathLength; + } + + internal static void PrepareNextCertN( + PkixCertPath certPath, + int index) + //throws CertPathValidatorException + { + IList certs = certPath.Certificates; + X509Certificate cert = (X509Certificate)certs[index]; + + // + // (n) + // + bool[] _usage = cert.GetKeyUsage(); + + if ((_usage != null) && !_usage[Rfc3280CertPathUtilities.KEY_CERT_SIGN]) + { + throw new PkixCertPathValidatorException( + "Issuer certificate keyusage extension is critical and does not permit key signing.", null, + certPath, index); + } + } + + internal static void PrepareNextCertO( + PkixCertPath certPath, + int index, + ISet criticalExtensions, + IList pathCheckers) + //throws CertPathValidatorException + { + IList certs = certPath.Certificates; + X509Certificate cert = (X509Certificate)certs[index]; + + // + // (o) + // + IEnumerator tmpIter = pathCheckers.GetEnumerator(); + while (tmpIter.MoveNext()) + { + try + { + ((PkixCertPathChecker)tmpIter.Current).Check(cert, criticalExtensions); + } + catch (PkixCertPathValidatorException e) + { + throw new PkixCertPathValidatorException(e.Message, e.InnerException, certPath, index); + } + } + if (!criticalExtensions.IsEmpty) + { + throw new PkixCertPathValidatorException("Certificate has unsupported critical extension.", null, certPath, + index); + } + } + + internal static int PrepareNextCertH1( + PkixCertPath certPath, + int index, + int explicitPolicy) + { + IList certs = certPath.Certificates; + X509Certificate cert = (X509Certificate)certs[index]; + + // + // (h) + // + if (!PkixCertPathValidatorUtilities.IsSelfIssued(cert)) + { + // + // (1) + // + if (explicitPolicy != 0) + return explicitPolicy - 1; + } + return explicitPolicy; + } + + internal static int PrepareNextCertH2( + PkixCertPath certPath, + int index, + int policyMapping) + { + IList certs = certPath.Certificates; + X509Certificate cert = (X509Certificate)certs[index]; + + // + // (h) + // + if (!PkixCertPathValidatorUtilities.IsSelfIssued(cert)) + { + // + // (2) + // + if (policyMapping != 0) + return policyMapping - 1; + } + return policyMapping; + } + + + internal static int PrepareNextCertH3( + PkixCertPath certPath, + int index, + int inhibitAnyPolicy) + { + IList certs = certPath.Certificates; + X509Certificate cert = (X509Certificate)certs[index]; + + // + // (h) + // + if (!PkixCertPathValidatorUtilities.IsSelfIssued(cert)) + { + // + // (3) + // + if (inhibitAnyPolicy != 0) + return inhibitAnyPolicy - 1; + } + return inhibitAnyPolicy; + } + + internal static int WrapupCertA( + int explicitPolicy, + X509Certificate cert) + { + // + // (a) + // + if (!PkixCertPathValidatorUtilities.IsSelfIssued(cert) && (explicitPolicy != 0)) + { + explicitPolicy--; + } + return explicitPolicy; + } + + internal static int WrapupCertB( + PkixCertPath certPath, + int index, + int explicitPolicy) + //throws CertPathValidatorException + { + IList certs = certPath.Certificates; + X509Certificate cert = (X509Certificate)certs[index]; + + // + // (b) + // + int tmpInt; + Asn1Sequence pc = null; + try + { + pc = DerSequence.GetInstance( + PkixCertPathValidatorUtilities.GetExtensionValue(cert, X509Extensions.PolicyConstraints)); + } + catch (Exception e) + { + throw new PkixCertPathValidatorException("Policy constraints could not be decoded.", e, certPath, index); + } + + if (pc != null) + { + IEnumerator policyConstraints = pc.GetEnumerator(); + + while (policyConstraints.MoveNext()) + { + Asn1TaggedObject constraint = (Asn1TaggedObject)policyConstraints.Current; + switch (constraint.TagNo) + { + case 0: + try + { + tmpInt = DerInteger.GetInstance(constraint, false).Value.IntValue; + } + catch (Exception e) + { + throw new PkixCertPathValidatorException( + "Policy constraints requireExplicitPolicy field could not be decoded.", e, certPath, + index); + } + if (tmpInt == 0) + { + return 0; + } + break; + } + } + } + return explicitPolicy; + } + + internal static void WrapupCertF( + PkixCertPath certPath, + int index, + IList pathCheckers, + ISet criticalExtensions) + //throws CertPathValidatorException + { + IList certs = certPath.Certificates; + X509Certificate cert = (X509Certificate)certs[index]; + IEnumerator tmpIter = pathCheckers.GetEnumerator(); + + while (tmpIter.MoveNext()) + { + try + { + ((PkixCertPathChecker)tmpIter.Current).Check(cert, criticalExtensions); + } + catch (PkixCertPathValidatorException e) + { + throw new PkixCertPathValidatorException("Additional certificate path checker failed.", e, certPath, + index); + } + } + + if (!criticalExtensions.IsEmpty) + { + throw new PkixCertPathValidatorException("Certificate has unsupported critical extension", + null, certPath, index); + } + } + + internal static PkixPolicyNode WrapupCertG( + PkixCertPath certPath, + PkixParameters paramsPKIX, + ISet userInitialPolicySet, + int index, + IList[] policyNodes, + PkixPolicyNode validPolicyTree, + ISet acceptablePolicies) + { + int n = certPath.Certificates.Count; + + // + // (g) + // + PkixPolicyNode intersection; + + // + // (g) (i) + // + if (validPolicyTree == null) + { + if (paramsPKIX.IsExplicitPolicyRequired) + { + throw new PkixCertPathValidatorException( + "Explicit policy requested but none available.", null, certPath, index); + } + intersection = null; + } + else if (PkixCertPathValidatorUtilities.IsAnyPolicy(userInitialPolicySet)) // (g) + // (ii) + { + if (paramsPKIX.IsExplicitPolicyRequired) + { + if (acceptablePolicies.IsEmpty) + { + throw new PkixCertPathValidatorException( + "Explicit policy requested but none available.", null, certPath, index); + } + else + { + ISet _validPolicyNodeSet = new HashSet(); + + for (int j = 0; j < policyNodes.Length; j++) + { + IList _nodeDepth = policyNodes[j]; + + for (int k = 0; k < _nodeDepth.Count; k++) + { + PkixPolicyNode _node = (PkixPolicyNode)_nodeDepth[k]; + + if (Rfc3280CertPathUtilities.ANY_POLICY.Equals(_node.ValidPolicy)) + { + foreach (object o in _node.Children) + { + _validPolicyNodeSet.Add(o); + } + } + } + } + + foreach (PkixPolicyNode _node in _validPolicyNodeSet) + { + string _validPolicy = _node.ValidPolicy; + + if (!acceptablePolicies.Contains(_validPolicy)) + { + // TODO? + // validPolicyTree = + // removePolicyNode(validPolicyTree, policyNodes, + // _node); + } + } + if (validPolicyTree != null) + { + for (int j = (n - 1); j >= 0; j--) + { + IList nodes = policyNodes[j]; + + for (int k = 0; k < nodes.Count; k++) + { + PkixPolicyNode node = (PkixPolicyNode)nodes[k]; + if (!node.HasChildren) + { + validPolicyTree = PkixCertPathValidatorUtilities.RemovePolicyNode(validPolicyTree, + policyNodes, node); + } + } + } + } + } + } + + intersection = validPolicyTree; + } + else + { + // + // (g) (iii) + // + // This implementation is not exactly same as the one described in + // RFC3280. + // However, as far as the validation result is concerned, both + // produce + // adequate result. The only difference is whether AnyPolicy is + // remain + // in the policy tree or not. + // + // (g) (iii) 1 + // + ISet _validPolicyNodeSet = new HashSet(); + + for (int j = 0; j < policyNodes.Length; j++) + { + IList _nodeDepth = policyNodes[j]; + + for (int k = 0; k < _nodeDepth.Count; k++) + { + PkixPolicyNode _node = (PkixPolicyNode)_nodeDepth[k]; + + if (Rfc3280CertPathUtilities.ANY_POLICY.Equals(_node.ValidPolicy)) + { + foreach (PkixPolicyNode _c_node in _node.Children) + { + if (!Rfc3280CertPathUtilities.ANY_POLICY.Equals(_c_node.ValidPolicy)) + { + _validPolicyNodeSet.Add(_c_node); + } + } + } + } + } + + // + // (g) (iii) 2 + // + IEnumerator _vpnsIter = _validPolicyNodeSet.GetEnumerator(); + while (_vpnsIter.MoveNext()) + { + PkixPolicyNode _node = (PkixPolicyNode)_vpnsIter.Current; + string _validPolicy = _node.ValidPolicy; + + if (!userInitialPolicySet.Contains(_validPolicy)) + { + validPolicyTree = PkixCertPathValidatorUtilities.RemovePolicyNode(validPolicyTree, policyNodes, _node); + } + } + + // + // (g) (iii) 4 + // + if (validPolicyTree != null) + { + for (int j = (n - 1); j >= 0; j--) + { + IList nodes = policyNodes[j]; + + for (int k = 0; k < nodes.Count; k++) + { + PkixPolicyNode node = (PkixPolicyNode)nodes[k]; + if (!node.HasChildren) + { + validPolicyTree = PkixCertPathValidatorUtilities.RemovePolicyNode(validPolicyTree, policyNodes, + node); + } + } + } + } + + intersection = validPolicyTree; + } + return intersection; + } + + /** + * If use-deltas is set, verify the issuer and scope of the delta CRL. + * + * @param deltaCRL The delta CRL. + * @param completeCRL The complete CRL. + * @param pkixParams The PKIX paramaters. + * @throws AnnotatedException if an exception occurs. + */ + internal static void ProcessCrlC( + X509Crl deltaCRL, + X509Crl completeCRL, + PkixParameters pkixParams) + { + if (deltaCRL == null) + return; + + IssuingDistributionPoint completeidp = null; + try + { + completeidp = IssuingDistributionPoint.GetInstance( + PkixCertPathValidatorUtilities.GetExtensionValue(completeCRL, X509Extensions.IssuingDistributionPoint)); + } + catch (Exception e) + { + throw new Exception("000 Issuing distribution point extension could not be decoded.", e); + } + + if (pkixParams.IsUseDeltasEnabled) + { + // (c) (1) + if (!deltaCRL.IssuerDN.Equivalent(completeCRL.IssuerDN, true)) + throw new Exception("Complete CRL issuer does not match delta CRL issuer."); + + // (c) (2) + IssuingDistributionPoint deltaidp = null; + try + { + deltaidp = IssuingDistributionPoint.GetInstance( + PkixCertPathValidatorUtilities.GetExtensionValue(deltaCRL, X509Extensions.IssuingDistributionPoint)); + } + catch (Exception e) + { + throw new Exception( + "Issuing distribution point extension from delta CRL could not be decoded.", e); + } + + if (!Platform.Equals(completeidp, deltaidp)) + { + throw new Exception( + "Issuing distribution point extension from delta CRL and complete CRL does not match."); + } + + // (c) (3) + Asn1Object completeKeyIdentifier = null; + try + { + completeKeyIdentifier = PkixCertPathValidatorUtilities.GetExtensionValue( + completeCRL, X509Extensions.AuthorityKeyIdentifier); + } + catch (Exception e) + { + throw new Exception( + "Authority key identifier extension could not be extracted from complete CRL.", e); + } + + Asn1Object deltaKeyIdentifier = null; + try + { + deltaKeyIdentifier = PkixCertPathValidatorUtilities.GetExtensionValue( + deltaCRL, X509Extensions.AuthorityKeyIdentifier); + } + catch (Exception e) + { + throw new Exception( + "Authority key identifier extension could not be extracted from delta CRL.", e); + } + + if (completeKeyIdentifier == null) + throw new Exception("CRL authority key identifier is null."); + + if (deltaKeyIdentifier == null) + throw new Exception("Delta CRL authority key identifier is null."); + + if (!completeKeyIdentifier.Equals(deltaKeyIdentifier)) + { + throw new Exception( + "Delta CRL authority key identifier does not match complete CRL authority key identifier."); + } + } + } + + internal static void ProcessCrlI( + DateTime validDate, + X509Crl deltacrl, + object cert, + CertStatus certStatus, + PkixParameters pkixParams) + { + if (pkixParams.IsUseDeltasEnabled && deltacrl != null) + { + PkixCertPathValidatorUtilities.GetCertStatus(validDate, deltacrl, cert, certStatus); + } + } + + internal static void ProcessCrlJ( + DateTime validDate, + X509Crl completecrl, + object cert, + CertStatus certStatus) + { + if (certStatus.Status == CertStatus.Unrevoked) + { + PkixCertPathValidatorUtilities.GetCertStatus(validDate, completecrl, cert, certStatus); + } + } + + internal static PkixPolicyNode ProcessCertE( + PkixCertPath certPath, + int index, + PkixPolicyNode validPolicyTree) + { + IList certs = certPath.Certificates; + X509Certificate cert = (X509Certificate)certs[index]; + + // + // (e) + // + Asn1Sequence certPolicies = null; + try + { + certPolicies = DerSequence.GetInstance( + PkixCertPathValidatorUtilities.GetExtensionValue(cert, X509Extensions.CertificatePolicies)); + } + catch (Exception e) + { + throw new PkixCertPathValidatorException("Could not read certificate policies extension from certificate.", + e, certPath, index); + } + if (certPolicies == null) + { + validPolicyTree = null; + } + return validPolicyTree; + } + + internal static readonly string[] CrlReasons = new string[] + { + "unspecified", + "keyCompromise", + "cACompromise", + "affiliationChanged", + "superseded", + "cessationOfOperation", + "certificateHold", + "unknown", + "removeFromCRL", + "privilegeWithdrawn", + "aACompromise" + }; + } +} diff --git a/Crypto/src/pkix/Rfc3281CertPathUtilities.cs b/Crypto/src/pkix/Rfc3281CertPathUtilities.cs new file mode 100644
index 000000000..bda2aa737 --- /dev/null +++ b/Crypto/src/pkix/Rfc3281CertPathUtilities.cs
@@ -0,0 +1,608 @@ +using System; +using System.Collections; +using System.Globalization; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.Pkix +{ + internal class Rfc3281CertPathUtilities + { + internal static void ProcessAttrCert7( + IX509AttributeCertificate attrCert, + PkixCertPath certPath, + PkixCertPath holderCertPath, + PkixParameters pkixParams) + { + // TODO: + // AA Controls + // Attribute encryption + // Proxy + ISet critExtOids = attrCert.GetCriticalExtensionOids(); + + // 7.1 + // process extensions + + // target information checked in step 6 / X509AttributeCertStoreSelector + if (critExtOids.Contains(X509Extensions.TargetInformation.Id)) + { + try + { + TargetInformation.GetInstance(PkixCertPathValidatorUtilities + .GetExtensionValue(attrCert, X509Extensions.TargetInformation)); + } + catch (Exception e) + { + throw new PkixCertPathValidatorException( + "Target information extension could not be read.", e); + } + } + critExtOids.Remove(X509Extensions.TargetInformation.Id); + foreach (PkixAttrCertChecker checker in pkixParams.GetAttrCertCheckers()) + { + checker.Check(attrCert, certPath, holderCertPath, critExtOids); + } + if (!critExtOids.IsEmpty) + { + throw new PkixCertPathValidatorException( + "Attribute certificate contains unsupported critical extensions: " + + critExtOids); + } + } + + /** + * Checks if an attribute certificate is revoked. + * + * @param attrCert Attribute certificate to check if it is revoked. + * @param paramsPKIX PKIX parameters. + * @param issuerCert The issuer certificate of the attribute certificate + * <code>attrCert</code>. + * @param validDate The date when the certificate revocation status should + * be checked. + * @param certPathCerts The certificates of the certification path to be + * checked. + * + * @throws CertPathValidatorException if the certificate is revoked or the + * status cannot be checked or some error occurs. + */ + internal static void CheckCrls( + IX509AttributeCertificate attrCert, + PkixParameters paramsPKIX, + X509Certificate issuerCert, + DateTime validDate, + IList certPathCerts) + { + if (paramsPKIX.IsRevocationEnabled) + { + // check if revocation is available + if (attrCert.GetExtensionValue(X509Extensions.NoRevAvail) == null) + { + CrlDistPoint crldp = null; + try + { + crldp = CrlDistPoint.GetInstance( + PkixCertPathValidatorUtilities.GetExtensionValue( + attrCert, X509Extensions.CrlDistributionPoints)); + } + catch (Exception e) + { + throw new PkixCertPathValidatorException( + "CRL distribution point extension could not be read.", e); + } + try + { + PkixCertPathValidatorUtilities + .AddAdditionalStoresFromCrlDistributionPoint(crldp, paramsPKIX); + } + catch (Exception e) + { + throw new PkixCertPathValidatorException( + "No additional CRL locations could be decoded from CRL distribution point extension.", e); + } + CertStatus certStatus = new CertStatus(); + ReasonsMask reasonsMask = new ReasonsMask(); + + Exception lastException = null; + bool validCrlFound = false; + // for each distribution point + if (crldp != null) + { + DistributionPoint[] dps = null; + try + { + dps = crldp.GetDistributionPoints(); + } + catch (Exception e) + { + throw new PkixCertPathValidatorException( + "Distribution points could not be read.", e); + } + try + { + for (int i = 0; i < dps.Length + && certStatus.Status == CertStatus.Unrevoked + && !reasonsMask.IsAllReasons; i++) + { + PkixParameters paramsPKIXClone = (PkixParameters) paramsPKIX + .Clone(); + CheckCrl(dps[i], attrCert, paramsPKIXClone, + validDate, issuerCert, certStatus, reasonsMask, + certPathCerts); + validCrlFound = true; + } + } + catch (Exception e) + { + lastException = new Exception( + "No valid CRL for distribution point found.", e); + } + } + + /* + * If the revocation status has not been determined, repeat the + * process above with any available CRLs not specified in a + * distribution point but issued by the certificate issuer. + */ + + if (certStatus.Status == CertStatus.Unrevoked + && !reasonsMask.IsAllReasons) + { + try + { + /* + * assume a DP with both the reasons and the cRLIssuer + * fields omitted and a distribution point name of the + * certificate issuer. + */ + Asn1Object issuer = null; + try + { + issuer = new Asn1InputStream( + attrCert.Issuer.GetPrincipals()[0].GetEncoded()).ReadObject(); + } + catch (Exception e) + { + throw new Exception( + "Issuer from certificate for CRL could not be reencoded.", + e); + } + DistributionPoint dp = new DistributionPoint( + new DistributionPointName(0, new GeneralNames( + new GeneralName(GeneralName.DirectoryName, issuer))), null, null); + PkixParameters paramsPKIXClone = (PkixParameters) paramsPKIX.Clone(); + CheckCrl(dp, attrCert, paramsPKIXClone, validDate, + issuerCert, certStatus, reasonsMask, certPathCerts); + validCrlFound = true; + } + catch (Exception e) + { + lastException = new Exception( + "No valid CRL for distribution point found.", e); + } + } + + if (!validCrlFound) + { + throw new PkixCertPathValidatorException( + "No valid CRL found.", lastException); + } + if (certStatus.Status != CertStatus.Unrevoked) + { + // TODO This format is forced by the NistCertPath tests + string formattedDate = certStatus.RevocationDate.Value.ToString( + "G", new CultureInfo("en-us")); + string message = "Attribute certificate revocation after " + + formattedDate; + message += ", reason: " + + Rfc3280CertPathUtilities.CrlReasons[certStatus.Status]; + throw new PkixCertPathValidatorException(message); + } + if (!reasonsMask.IsAllReasons + && certStatus.Status == CertStatus.Unrevoked) + { + certStatus.Status = CertStatus.Undetermined; + } + if (certStatus.Status == CertStatus.Undetermined) + { + throw new PkixCertPathValidatorException( + "Attribute certificate status could not be determined."); + } + + } + else + { + if (attrCert.GetExtensionValue(X509Extensions.CrlDistributionPoints) != null + || attrCert.GetExtensionValue(X509Extensions.AuthorityInfoAccess) != null) + { + throw new PkixCertPathValidatorException( + "No rev avail extension is set, but also an AC revocation pointer."); + } + } + } + } + + internal static void AdditionalChecks( + IX509AttributeCertificate attrCert, + PkixParameters pkixParams) + { + // 1 + foreach (string oid in pkixParams.GetProhibitedACAttributes()) + { + if (attrCert.GetAttributes(oid) != null) + { + throw new PkixCertPathValidatorException( + "Attribute certificate contains prohibited attribute: " + + oid + "."); + } + } + foreach (string oid in pkixParams.GetNecessaryACAttributes()) + { + if (attrCert.GetAttributes(oid) == null) + { + throw new PkixCertPathValidatorException( + "Attribute certificate does not contain necessary attribute: " + + oid + "."); + } + } + } + + internal static void ProcessAttrCert5( + IX509AttributeCertificate attrCert, + PkixParameters pkixParams) + { + try + { + attrCert.CheckValidity(PkixCertPathValidatorUtilities.GetValidDate(pkixParams)); + } + catch (CertificateExpiredException e) + { + throw new PkixCertPathValidatorException( + "Attribute certificate is not valid.", e); + } + catch (CertificateNotYetValidException e) + { + throw new PkixCertPathValidatorException( + "Attribute certificate is not valid.", e); + } + } + + internal static void ProcessAttrCert4( + X509Certificate acIssuerCert, + PkixParameters pkixParams) + { + ISet set = pkixParams.GetTrustedACIssuers(); + bool trusted = false; + foreach (TrustAnchor anchor in set) + { + IDictionary symbols = X509Name.RFC2253Symbols; + if (acIssuerCert.SubjectDN.ToString(false, symbols).Equals(anchor.CAName) + || acIssuerCert.Equals(anchor.TrustedCert)) + { + trusted = true; + } + } + if (!trusted) + { + throw new PkixCertPathValidatorException( + "Attribute certificate issuer is not directly trusted."); + } + } + + internal static void ProcessAttrCert3( + X509Certificate acIssuerCert, + PkixParameters pkixParams) + { + if (acIssuerCert.GetKeyUsage() != null + && (!acIssuerCert.GetKeyUsage()[0] && !acIssuerCert.GetKeyUsage()[1])) + { + throw new PkixCertPathValidatorException( + "Attribute certificate issuer public key cannot be used to validate digital signatures."); + } + if (acIssuerCert.GetBasicConstraints() != -1) + { + throw new PkixCertPathValidatorException( + "Attribute certificate issuer is also a public key certificate issuer."); + } + } + + internal static PkixCertPathValidatorResult ProcessAttrCert2( + PkixCertPath certPath, + PkixParameters pkixParams) + { + PkixCertPathValidator validator = new PkixCertPathValidator(); + + try + { + return validator.Validate(certPath, pkixParams); + } + catch (PkixCertPathValidatorException e) + { + throw new PkixCertPathValidatorException( + "Certification path for issuer certificate of attribute certificate could not be validated.", + e); + } + } + + /** + * Searches for a holder public key certificate and verifies its + * certification path. + * + * @param attrCert the attribute certificate. + * @param pkixParams The PKIX parameters. + * @return The certificate path of the holder certificate. + * @throws Exception if + * <ul> + * <li>no public key certificate can be found although holder + * information is given by an entity name or a base certificate + * ID</li> + * <li>support classes cannot be created</li> + * <li>no certification path for the public key certificate can + * be built</li> + * </ul> + */ + internal static PkixCertPath ProcessAttrCert1( + IX509AttributeCertificate attrCert, + PkixParameters pkixParams) + { + PkixCertPathBuilderResult result = null; + // find holder PKCs + ISet holderPKCs = new HashSet(); + if (attrCert.Holder.GetIssuer() != null) + { + X509CertStoreSelector selector = new X509CertStoreSelector(); + selector.SerialNumber = attrCert.Holder.SerialNumber; + X509Name[] principals = attrCert.Holder.GetIssuer(); + for (int i = 0; i < principals.Length; i++) + { + try + { +// if (principals[i] is X500Principal) + { + selector.Issuer = principals[i]; + } + holderPKCs.AddAll(PkixCertPathValidatorUtilities + .FindCertificates(selector, pkixParams.GetStores())); + } + catch (Exception e) + { + throw new PkixCertPathValidatorException( + "Public key certificate for attribute certificate cannot be searched.", + e); + } + } + if (holderPKCs.IsEmpty) + { + throw new PkixCertPathValidatorException( + "Public key certificate specified in base certificate ID for attribute certificate cannot be found."); + } + } + if (attrCert.Holder.GetEntityNames() != null) + { + X509CertStoreSelector selector = new X509CertStoreSelector(); + X509Name[] principals = attrCert.Holder.GetEntityNames(); + for (int i = 0; i < principals.Length; i++) + { + try + { +// if (principals[i] is X500Principal) + { + selector.Issuer = principals[i]; + } + holderPKCs.AddAll(PkixCertPathValidatorUtilities + .FindCertificates(selector, pkixParams.GetStores())); + } + catch (Exception e) + { + throw new PkixCertPathValidatorException( + "Public key certificate for attribute certificate cannot be searched.", + e); + } + } + if (holderPKCs.IsEmpty) + { + throw new PkixCertPathValidatorException( + "Public key certificate specified in entity name for attribute certificate cannot be found."); + } + } + + // verify cert paths for PKCs + PkixBuilderParameters parameters = (PkixBuilderParameters) + PkixBuilderParameters.GetInstance(pkixParams); + + PkixCertPathValidatorException lastException = null; + foreach (X509Certificate cert in holderPKCs) + { + X509CertStoreSelector selector = new X509CertStoreSelector(); + selector.Certificate = cert; + parameters.SetTargetConstraints(selector); + + PkixCertPathBuilder builder = new PkixCertPathBuilder(); + + try + { + result = builder.Build(PkixBuilderParameters.GetInstance(parameters)); + } + catch (PkixCertPathBuilderException e) + { + lastException = new PkixCertPathValidatorException( + "Certification path for public key certificate of attribute certificate could not be build.", + e); + } + } + if (lastException != null) + { + throw lastException; + } + return result.CertPath; + } + + /** + * + * Checks a distribution point for revocation information for the + * certificate <code>attrCert</code>. + * + * @param dp The distribution point to consider. + * @param attrCert The attribute certificate which should be checked. + * @param paramsPKIX PKIX parameters. + * @param validDate The date when the certificate revocation status should + * be checked. + * @param issuerCert Certificate to check if it is revoked. + * @param reasonMask The reasons mask which is already checked. + * @param certPathCerts The certificates of the certification path to be + * checked. + * @throws Exception if the certificate is revoked or the status + * cannot be checked or some error occurs. + */ + private static void CheckCrl( + DistributionPoint dp, + IX509AttributeCertificate attrCert, + PkixParameters paramsPKIX, + DateTime validDate, + X509Certificate issuerCert, + CertStatus certStatus, + ReasonsMask reasonMask, + IList certPathCerts) + { + /* + * 4.3.6 No Revocation Available + * + * The noRevAvail extension, defined in [X.509-2000], allows an AC + * issuer to indicate that no revocation information will be made + * available for this AC. + */ + if (attrCert.GetExtensionValue(X509Extensions.NoRevAvail) != null) + { + return; + } + + DateTime currentDate = DateTime.UtcNow; + if (validDate.CompareTo(currentDate) > 0) + { + throw new Exception("Validation time is in future."); + } + + // (a) + /* + * We always get timely valid CRLs, so there is no step (a) (1). + * "locally cached" CRLs are assumed to be in getStore(), additional + * CRLs must be enabled in the ExtendedPkixParameters and are in + * getAdditionalStore() + */ + ISet crls = PkixCertPathValidatorUtilities.GetCompleteCrls(dp, attrCert, + currentDate, paramsPKIX); + bool validCrlFound = false; + Exception lastException = null; + + IEnumerator crl_iter = crls.GetEnumerator(); + + while (crl_iter.MoveNext() + && certStatus.Status == CertStatus.Unrevoked + && !reasonMask.IsAllReasons) + { + try + { + X509Crl crl = (X509Crl) crl_iter.Current; + + // (d) + ReasonsMask interimReasonsMask = Rfc3280CertPathUtilities.ProcessCrlD(crl, dp); + + // (e) + /* + * The reasons mask is updated at the end, so only valid CRLs + * can update it. If this CRL does not contain new reasons it + * must be ignored. + */ + if (!interimReasonsMask.HasNewReasons(reasonMask)) + { + continue; + } + + // (f) + ISet keys = Rfc3280CertPathUtilities.ProcessCrlF(crl, attrCert, + null, null, paramsPKIX, certPathCerts); + // (g) + AsymmetricKeyParameter pubKey = Rfc3280CertPathUtilities.ProcessCrlG(crl, keys); + + X509Crl deltaCRL = null; + + if (paramsPKIX.IsUseDeltasEnabled) + { + // get delta CRLs + ISet deltaCRLs = PkixCertPathValidatorUtilities.GetDeltaCrls( + currentDate, paramsPKIX, crl); + // we only want one valid delta CRL + // (h) + deltaCRL = Rfc3280CertPathUtilities.ProcessCrlH(deltaCRLs, pubKey); + } + + /* + * CRL must be be valid at the current time, not the validation + * time. If a certificate is revoked with reason keyCompromise, + * cACompromise, it can be used for forgery, also for the past. + * This reason may not be contained in older CRLs. + */ + + /* + * in the chain model signatures stay valid also after the + * certificate has been expired, so they do not have to be in + * the CRL vality time + */ + if (paramsPKIX.ValidityModel != PkixParameters.ChainValidityModel) + { + /* + * if a certificate has expired, but was revoked, it is not + * more in the CRL, so it would be regarded as valid if the + * first check is not done + */ + if (attrCert.NotAfter.CompareTo(crl.ThisUpdate) < 0) + { + throw new Exception( + "No valid CRL for current time found."); + } + } + + Rfc3280CertPathUtilities.ProcessCrlB1(dp, attrCert, crl); + + // (b) (2) + Rfc3280CertPathUtilities.ProcessCrlB2(dp, attrCert, crl); + + // (c) + Rfc3280CertPathUtilities.ProcessCrlC(deltaCRL, crl, paramsPKIX); + + // (i) + Rfc3280CertPathUtilities.ProcessCrlI(validDate, deltaCRL, + attrCert, certStatus, paramsPKIX); + + // (j) + Rfc3280CertPathUtilities.ProcessCrlJ(validDate, crl, attrCert, + certStatus); + + // (k) + if (certStatus.Status == CrlReason.RemoveFromCrl) + { + certStatus.Status = CertStatus.Unrevoked; + } + + // update reasons mask + reasonMask.AddReasons(interimReasonsMask); + validCrlFound = true; + } + catch (Exception e) + { + lastException = e; + } + } + if (!validCrlFound) + { + throw lastException; + } + } + } +} diff --git a/Crypto/src/pkix/TrustAnchor.cs b/Crypto/src/pkix/TrustAnchor.cs new file mode 100644
index 000000000..22078baf2 --- /dev/null +++ b/Crypto/src/pkix/TrustAnchor.cs
@@ -0,0 +1,259 @@ +using System; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Pkix +{ + /// <summary> + /// A trust anchor or most-trusted Certification Authority (CA). + /// + /// This class represents a "most-trusted CA", which is used as a trust anchor + /// for validating X.509 certification paths. A most-trusted CA includes the + /// public key of the CA, the CA's name, and any constraints upon the set of + /// paths which may be validated using this key. These parameters can be + /// specified in the form of a trusted X509Certificate or as individual + /// parameters. + /// </summary> + public class TrustAnchor + { + private readonly AsymmetricKeyParameter pubKey; + private readonly string caName; + private readonly X509Name caPrincipal; + private readonly X509Certificate trustedCert; + private byte[] ncBytes; + private NameConstraints nc; + + /// <summary> + /// Creates an instance of TrustAnchor with the specified X509Certificate and + /// optional name constraints, which are intended to be used as additional + /// constraints when validating an X.509 certification path. + /// The name constraints are specified as a byte array. This byte array + /// should contain the DER encoded form of the name constraints, as they + /// would appear in the NameConstraints structure defined in RFC 2459 and + /// X.509. The ASN.1 definition of this structure appears below. + /// + /// <pre> + /// NameConstraints ::= SEQUENCE { + /// permittedSubtrees [0] GeneralSubtrees OPTIONAL, + /// excludedSubtrees [1] GeneralSubtrees OPTIONAL } + /// + /// GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree + /// + /// GeneralSubtree ::= SEQUENCE { + /// base GeneralName, + /// minimum [0] BaseDistance DEFAULT 0, + /// maximum [1] BaseDistance OPTIONAL } + /// + /// BaseDistance ::= INTEGER (0..MAX) + /// + /// 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} + /// </pre> + /// + /// Note that the name constraints byte array supplied is cloned to protect + /// against subsequent modifications. + /// </summary> + /// <param name="trustedCert">a trusted X509Certificate</param> + /// <param name="nameConstraints">a byte array containing the ASN.1 DER encoding of a + /// NameConstraints extension to be used for checking name + /// constraints. Only the value of the extension is included, not + /// the OID or criticality flag. Specify null to omit the + /// parameter.</param> + /// <exception cref="ArgumentNullException">if the specified X509Certificate is null</exception> + public TrustAnchor( + X509Certificate trustedCert, + byte[] nameConstraints) + { + if (trustedCert == null) + throw new ArgumentNullException("trustedCert"); + + this.trustedCert = trustedCert; + this.pubKey = null; + this.caName = null; + this.caPrincipal = null; + setNameConstraints(nameConstraints); + } + + /// <summary> + /// Creates an instance of <c>TrustAnchor</c> where the + /// most-trusted CA is specified as an X500Principal and public key. + /// </summary> + /// <remarks> + /// <p> + /// Name constraints are an optional parameter, and are intended to be used + /// as additional constraints when validating an X.509 certification path. + /// </p><p> + /// The name constraints are specified as a byte array. This byte array + /// contains the DER encoded form of the name constraints, as they + /// would appear in the NameConstraints structure defined in RFC 2459 + /// and X.509. The ASN.1 notation for this structure is supplied in the + /// documentation for the other constructors. + /// </p><p> + /// Note that the name constraints byte array supplied here is cloned to + /// protect against subsequent modifications. + /// </p> + /// </remarks> + /// <param name="caPrincipal">the name of the most-trusted CA as X509Name</param> + /// <param name="pubKey">the public key of the most-trusted CA</param> + /// <param name="nameConstraints"> + /// a byte array containing the ASN.1 DER encoding of a NameConstraints extension to + /// be used for checking name constraints. Only the value of the extension is included, + /// not the OID or criticality flag. Specify <c>null</c> to omit the parameter. + /// </param> + /// <exception cref="ArgumentNullException"> + /// if <c>caPrincipal</c> or <c>pubKey</c> is null + /// </exception> + public TrustAnchor( + X509Name caPrincipal, + AsymmetricKeyParameter pubKey, + byte[] nameConstraints) + { + if (caPrincipal == null) + throw new ArgumentNullException("caPrincipal"); + if (pubKey == null) + throw new ArgumentNullException("pubKey"); + + this.trustedCert = null; + this.caPrincipal = caPrincipal; + this.caName = caPrincipal.ToString(); + this.pubKey = pubKey; + setNameConstraints(nameConstraints); + } + + /// <summary> + /// Creates an instance of <code>TrustAnchor</code> where the most-trusted + /// CA is specified as a distinguished name and public key. Name constraints + /// are an optional parameter, and are intended to be used as additional + /// constraints when validating an X.509 certification path. + /// <br/> + /// The name constraints are specified as a byte array. This byte array + /// contains the DER encoded form of the name constraints, as they would + /// appear in the NameConstraints structure defined in RFC 2459 and X.509. + /// </summary> + /// <param name="caName">the X.500 distinguished name of the most-trusted CA in RFC + /// 2253 string format</param> + /// <param name="pubKey">the public key of the most-trusted CA</param> + /// <param name="nameConstraints">a byte array containing the ASN.1 DER encoding of a + /// NameConstraints extension to be used for checking name + /// constraints. Only the value of the extension is included, not + /// the OID or criticality flag. Specify null to omit the + /// parameter.</param> + /// throws NullPointerException, IllegalArgumentException + public TrustAnchor( + string caName, + AsymmetricKeyParameter pubKey, + byte[] nameConstraints) + { + if (caName == null) + throw new ArgumentNullException("caName"); + if (pubKey == null) + throw new ArgumentNullException("pubKey"); + if (caName.Length == 0) + throw new ArgumentException("caName can not be an empty string"); + + this.caPrincipal = new X509Name(caName); + this.pubKey = pubKey; + this.caName = caName; + this.trustedCert = null; + setNameConstraints(nameConstraints); + } + + /// <summary> + /// Returns the most-trusted CA certificate. + /// </summary> + public X509Certificate TrustedCert + { + get { return this.trustedCert; } + } + + /// <summary> + /// Returns the name of the most-trusted CA as an X509Name. + /// </summary> + public X509Name CA + { + get { return this.caPrincipal; } + } + + /// <summary> + /// Returns the name of the most-trusted CA in RFC 2253 string format. + /// </summary> + public string CAName + { + get { return this.caName; } + } + + /// <summary> + /// Returns the public key of the most-trusted CA. + /// </summary> + public AsymmetricKeyParameter CAPublicKey + { + get { return this.pubKey; } + } + + /// <summary> + /// Decode the name constraints and clone them if not null. + /// </summary> + private void setNameConstraints( + byte[] bytes) + { + if (bytes == null) + { + ncBytes = null; + nc = null; + } + else + { + ncBytes = (byte[]) bytes.Clone(); + // validate DER encoding + //nc = new NameConstraintsExtension(Boolean.FALSE, bytes); + nc = NameConstraints.GetInstance(Asn1Object.FromByteArray(bytes)); + } + } + + public byte[] GetNameConstraints + { + get { return Arrays.Clone(ncBytes); } + } + + /// <summary> + /// Returns a formatted string describing the <code>TrustAnchor</code>. + /// </summary> + /// <returns>a formatted string describing the <code>TrustAnchor</code></returns> + public override string ToString() + { + // TODO Some of the sub-objects might not implement ToString() properly + string nl = Platform.NewLine; + StringBuilder sb = new StringBuilder(); + sb.Append("["); + sb.Append(nl); + if (this.pubKey != null) + { + sb.Append(" Trusted CA Public Key: ").Append(this.pubKey).Append(nl); + sb.Append(" Trusted CA Issuer Name: ").Append(this.caName).Append(nl); + } + else + { + sb.Append(" Trusted CA cert: ").Append(this.TrustedCert).Append(nl); + } + if (nc != null) + { + sb.Append(" Name Constraints: ").Append(nc).Append(nl); + } + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/Crypto/src/security/AgreementUtilities.cs b/Crypto/src/security/AgreementUtilities.cs new file mode 100644
index 000000000..d74ec7368 --- /dev/null +++ b/Crypto/src/security/AgreementUtilities.cs
@@ -0,0 +1,106 @@ +using System.Collections; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Agreement; +using Org.BouncyCastle.Crypto.Agreement.Kdf; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Security +{ + /// <remarks> + /// Utility class for creating IBasicAgreement objects from their names/Oids + /// </remarks> + public sealed class AgreementUtilities + { + private AgreementUtilities() + { + } + + private static readonly IDictionary algorithms = Platform.CreateHashtable(); + //private static readonly IDictionary oids = Platform.CreateHashtable(); + + static AgreementUtilities() + { + //algorithms[X9ObjectIdentifiers.DHSinglePassCofactorDHSha1KdfScheme.Id] = ?; + algorithms[X9ObjectIdentifiers.DHSinglePassStdDHSha1KdfScheme.Id] = "ECDHWITHSHA1KDF"; + algorithms[X9ObjectIdentifiers.MqvSinglePassSha1KdfScheme.Id] = "ECMQVWITHSHA1KDF"; + } + + public static IBasicAgreement GetBasicAgreement( + DerObjectIdentifier oid) + { + return GetBasicAgreement(oid.Id); + } + + public static IBasicAgreement GetBasicAgreement( + string algorithm) + { + string upper = algorithm.ToUpperInvariant(); + string mechanism = (string) algorithms[upper]; + + if (mechanism == null) + { + mechanism = upper; + } + + if (mechanism == "DH" || mechanism == "DIFFIEHELLMAN") + return new DHBasicAgreement(); + + if (mechanism == "ECDH") + return new ECDHBasicAgreement(); + + if (mechanism == "ECDHC") + return new ECDHCBasicAgreement(); + + if (mechanism == "ECMQV") + return new ECMqvBasicAgreement(); + + throw new SecurityUtilityException("Basic Agreement " + algorithm + " not recognised."); + } + + public static IBasicAgreement GetBasicAgreementWithKdf( + DerObjectIdentifier oid, + string wrapAlgorithm) + { + return GetBasicAgreementWithKdf(oid.Id, wrapAlgorithm); + } + + public static IBasicAgreement GetBasicAgreementWithKdf( + string agreeAlgorithm, + string wrapAlgorithm) + { + string upper = agreeAlgorithm.ToUpperInvariant(); + string mechanism = (string) algorithms[upper]; + + if (mechanism == null) + { + mechanism = upper; + } + + // 'DHWITHSHA1KDF' retained for backward compatibility + if (mechanism == "DHWITHSHA1KDF" || mechanism == "ECDHWITHSHA1KDF") + return new ECDHWithKdfBasicAgreement( + wrapAlgorithm, + new ECDHKekGenerator( + new Sha1Digest())); + + if (mechanism == "ECMQVWITHSHA1KDF") + return new ECMqvWithKdfBasicAgreement( + wrapAlgorithm, + new ECDHKekGenerator( + new Sha1Digest())); + + throw new SecurityUtilityException("Basic Agreement (with KDF) " + agreeAlgorithm + " not recognised."); + } + + public static string GetAlgorithmName( + DerObjectIdentifier oid) + { + return (string) algorithms[oid.Id]; + } + } +} diff --git a/Crypto/src/security/CipherUtilities.cs b/Crypto/src/security/CipherUtilities.cs new file mode 100644
index 000000000..ffd7d1373 --- /dev/null +++ b/Crypto/src/security/CipherUtilities.cs
@@ -0,0 +1,696 @@ +using System; +using System.Collections; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Kisa; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Ntt; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Agreement; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Encodings; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Macs; +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Paddings; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Security +{ + /// <remarks> + /// Cipher Utility class contains methods that can not be specifically grouped into other classes. + /// </remarks> + public sealed class CipherUtilities + { + internal enum CipherAlgorithm { + AES, + ARC4, + BLOWFISH, + CAMELLIA, + CAST5, + CAST6, + DES, + DESEDE, + ELGAMAL, + GOST28147, + HC128, + HC256, + IDEA, + NOEKEON, + PBEWITHSHAAND128BITRC4, + PBEWITHSHAAND40BITRC4, + RC2, + RC5, + RC5_64, + RC6, + RIJNDAEL, + RSA, + SALSA20, + SEED, + SERPENT, + SKIPJACK, + TEA, + TWOFISH, + VMPC, + VMPC_KSA3, + XTEA, + }; + + internal enum CipherMode { ECB, NONE, CBC, CCM, CFB, CTR, CTS, EAX, GCM, GOFB, OFB, OPENPGPCFB, SIC }; + internal enum CipherPadding + { + NOPADDING, + RAW, + ISO10126PADDING, + ISO10126D2PADDING, + ISO10126_2PADDING, + ISO7816_4PADDING, + ISO9797_1PADDING, + ISO9796_1, + ISO9796_1PADDING, + OAEP, + OAEPPADDING, + OAEPWITHMD5ANDMGF1PADDING, + OAEPWITHSHA1ANDMGF1PADDING, + OAEPWITHSHA_1ANDMGF1PADDING, + OAEPWITHSHA224ANDMGF1PADDING, + OAEPWITHSHA_224ANDMGF1PADDING, + OAEPWITHSHA256ANDMGF1PADDING, + OAEPWITHSHA_256ANDMGF1PADDING, + OAEPWITHSHA384ANDMGF1PADDING, + OAEPWITHSHA_384ANDMGF1PADDING, + OAEPWITHSHA512ANDMGF1PADDING, + OAEPWITHSHA_512ANDMGF1PADDING, + PKCS1, + PKCS1PADDING, + PKCS5, + PKCS5PADDING, + PKCS7, + PKCS7PADDING, + TBCPADDING, + WITHCTS, + X923PADDING, + ZEROBYTEPADDING, + }; + + private static readonly IDictionary algorithms = Platform.CreateHashtable(); + private static readonly IDictionary oids = Platform.CreateHashtable(); + + static CipherUtilities() + { + // Signal to obfuscation tools not to change enum constants + ((CipherAlgorithm)Enums.GetArbitraryValue(typeof(CipherAlgorithm))).ToString(); + ((CipherMode)Enums.GetArbitraryValue(typeof(CipherMode))).ToString(); + ((CipherPadding)Enums.GetArbitraryValue(typeof(CipherPadding))).ToString(); + + // TODO Flesh out the list of aliases + + algorithms[NistObjectIdentifiers.IdAes128Ecb.Id] = "AES/ECB/PKCS7PADDING"; + algorithms[NistObjectIdentifiers.IdAes192Ecb.Id] = "AES/ECB/PKCS7PADDING"; + algorithms[NistObjectIdentifiers.IdAes256Ecb.Id] = "AES/ECB/PKCS7PADDING"; + algorithms["AES//PKCS7"] = "AES/ECB/PKCS7PADDING"; + algorithms["AES//PKCS7PADDING"] = "AES/ECB/PKCS7PADDING"; + algorithms["AES//PKCS5"] = "AES/ECB/PKCS7PADDING"; + algorithms["AES//PKCS5PADDING"] = "AES/ECB/PKCS7PADDING"; + + algorithms[NistObjectIdentifiers.IdAes128Cbc.Id] = "AES/CBC/PKCS7PADDING"; + algorithms[NistObjectIdentifiers.IdAes192Cbc.Id] = "AES/CBC/PKCS7PADDING"; + algorithms[NistObjectIdentifiers.IdAes256Cbc.Id] = "AES/CBC/PKCS7PADDING"; + + algorithms[NistObjectIdentifiers.IdAes128Ofb.Id] = "AES/OFB/NOPADDING"; + algorithms[NistObjectIdentifiers.IdAes192Ofb.Id] = "AES/OFB/NOPADDING"; + algorithms[NistObjectIdentifiers.IdAes256Ofb.Id] = "AES/OFB/NOPADDING"; + + algorithms[NistObjectIdentifiers.IdAes128Cfb.Id] = "AES/CFB/NOPADDING"; + algorithms[NistObjectIdentifiers.IdAes192Cfb.Id] = "AES/CFB/NOPADDING"; + algorithms[NistObjectIdentifiers.IdAes256Cfb.Id] = "AES/CFB/NOPADDING"; + + algorithms["RSA/ECB/PKCS1"] = "RSA//PKCS1PADDING"; + algorithms["RSA/ECB/PKCS1PADDING"] = "RSA//PKCS1PADDING"; + algorithms[PkcsObjectIdentifiers.RsaEncryption.Id] = "RSA//PKCS1PADDING"; + algorithms[PkcsObjectIdentifiers.IdRsaesOaep.Id] = "RSA//OAEPPADDING"; + + algorithms[OiwObjectIdentifiers.DesCbc.Id] = "DES/CBC"; + algorithms[OiwObjectIdentifiers.DesCfb.Id] = "DES/CFB"; + algorithms[OiwObjectIdentifiers.DesEcb.Id] = "DES/ECB"; + algorithms[OiwObjectIdentifiers.DesOfb.Id] = "DES/OFB"; + algorithms[OiwObjectIdentifiers.DesEde.Id] = "DESEDE"; + algorithms[PkcsObjectIdentifiers.DesEde3Cbc.Id] = "DESEDE/CBC"; + algorithms[PkcsObjectIdentifiers.RC2Cbc.Id] = "RC2/CBC"; + algorithms["1.3.6.1.4.1.188.7.1.1.2"] = "IDEA/CBC"; + algorithms["1.2.840.113533.7.66.10"] = "CAST5/CBC"; + + algorithms["RC4"] = "ARC4"; + algorithms["ARCFOUR"] = "ARC4"; + algorithms["1.2.840.113549.3.4"] = "ARC4"; + + + + algorithms["PBEWITHSHA1AND128BITRC4"] = "PBEWITHSHAAND128BITRC4"; + algorithms[PkcsObjectIdentifiers.PbeWithShaAnd128BitRC4.Id] = "PBEWITHSHAAND128BITRC4"; + algorithms["PBEWITHSHA1AND40BITRC4"] = "PBEWITHSHAAND40BITRC4"; + algorithms[PkcsObjectIdentifiers.PbeWithShaAnd40BitRC4.Id] = "PBEWITHSHAAND40BITRC4"; + + algorithms["PBEWITHSHA1ANDDES"] = "PBEWITHSHA1ANDDES-CBC"; + algorithms[PkcsObjectIdentifiers.PbeWithSha1AndDesCbc.Id] = "PBEWITHSHA1ANDDES-CBC"; + algorithms["PBEWITHSHA1ANDRC2"] = "PBEWITHSHA1ANDRC2-CBC"; + algorithms[PkcsObjectIdentifiers.PbeWithSha1AndRC2Cbc.Id] = "PBEWITHSHA1ANDRC2-CBC"; + + algorithms["PBEWITHSHA1AND3-KEYTRIPLEDES-CBC"] = "PBEWITHSHAAND3-KEYTRIPLEDES-CBC"; + algorithms["PBEWITHSHAAND3KEYTRIPLEDES"] = "PBEWITHSHAAND3-KEYTRIPLEDES-CBC"; + algorithms[PkcsObjectIdentifiers.PbeWithShaAnd3KeyTripleDesCbc.Id] = "PBEWITHSHAAND3-KEYTRIPLEDES-CBC"; + algorithms["PBEWITHSHA1ANDDESEDE"] = "PBEWITHSHAAND3-KEYTRIPLEDES-CBC"; + + algorithms["PBEWITHSHA1AND2-KEYTRIPLEDES-CBC"] = "PBEWITHSHAAND2-KEYTRIPLEDES-CBC"; + algorithms[PkcsObjectIdentifiers.PbeWithShaAnd2KeyTripleDesCbc.Id] = "PBEWITHSHAAND2-KEYTRIPLEDES-CBC"; + + algorithms["PBEWITHSHA1AND128BITRC2-CBC"] = "PBEWITHSHAAND128BITRC2-CBC"; + algorithms[PkcsObjectIdentifiers.PbeWithShaAnd128BitRC2Cbc.Id] = "PBEWITHSHAAND128BITRC2-CBC"; + + algorithms["PBEWITHSHA1AND40BITRC2-CBC"] = "PBEWITHSHAAND40BITRC2-CBC"; + algorithms[PkcsObjectIdentifiers.PbewithShaAnd40BitRC2Cbc.Id] = "PBEWITHSHAAND40BITRC2-CBC"; + + algorithms["PBEWITHSHA1AND128BITAES-CBC-BC"] = "PBEWITHSHAAND128BITAES-CBC-BC"; + algorithms["PBEWITHSHA-1AND128BITAES-CBC-BC"] = "PBEWITHSHAAND128BITAES-CBC-BC"; + + algorithms["PBEWITHSHA1AND192BITAES-CBC-BC"] = "PBEWITHSHAAND192BITAES-CBC-BC"; + algorithms["PBEWITHSHA-1AND192BITAES-CBC-BC"] = "PBEWITHSHAAND192BITAES-CBC-BC"; + + algorithms["PBEWITHSHA1AND256BITAES-CBC-BC"] = "PBEWITHSHAAND256BITAES-CBC-BC"; + algorithms["PBEWITHSHA-1AND256BITAES-CBC-BC"] = "PBEWITHSHAAND256BITAES-CBC-BC"; + + algorithms["PBEWITHSHA-256AND128BITAES-CBC-BC"] = "PBEWITHSHA256AND128BITAES-CBC-BC"; + algorithms["PBEWITHSHA-256AND192BITAES-CBC-BC"] = "PBEWITHSHA256AND192BITAES-CBC-BC"; + algorithms["PBEWITHSHA-256AND256BITAES-CBC-BC"] = "PBEWITHSHA256AND256BITAES-CBC-BC"; + + + algorithms["GOST"] = "GOST28147"; + algorithms["GOST-28147"] = "GOST28147"; + algorithms[CryptoProObjectIdentifiers.GostR28147Cbc.Id] = "GOST28147/CBC/PKCS7PADDING"; + + algorithms["RC5-32"] = "RC5"; + + algorithms[NttObjectIdentifiers.IdCamellia128Cbc.Id] = "CAMELLIA/CBC/PKCS7PADDING"; + algorithms[NttObjectIdentifiers.IdCamellia192Cbc.Id] = "CAMELLIA/CBC/PKCS7PADDING"; + algorithms[NttObjectIdentifiers.IdCamellia256Cbc.Id] = "CAMELLIA/CBC/PKCS7PADDING"; + + algorithms[KisaObjectIdentifiers.IdSeedCbc.Id] = "SEED/CBC/PKCS7PADDING"; + + algorithms["1.3.6.1.4.1.3029.1.2"] = "BLOWFISH/CBC"; + } + + private CipherUtilities() + { + } + + /// <summary> + /// Returns a ObjectIdentifier for a give encoding. + /// </summary> + /// <param name="mechanism">A string representation of the encoding.</param> + /// <returns>A DerObjectIdentifier, null if the Oid is not available.</returns> + // TODO Don't really want to support this + public static DerObjectIdentifier GetObjectIdentifier( + string mechanism) + { + if (mechanism == null) + throw new ArgumentNullException("mechanism"); + + mechanism = mechanism.ToUpperInvariant(); + string aliased = (string) algorithms[mechanism]; + + if (aliased != null) + mechanism = aliased; + + return (DerObjectIdentifier) oids[mechanism]; + } + + public static ICollection Algorithms + { + get { return oids.Keys; } + } + + public static IBufferedCipher GetCipher( + DerObjectIdentifier oid) + { + return GetCipher(oid.Id); + } + + public static IBufferedCipher GetCipher( + string algorithm) + { + if (algorithm == null) + throw new ArgumentNullException("algorithm"); + + algorithm = algorithm.ToUpperInvariant(); + + string aliased = (string) algorithms[algorithm]; + + if (aliased != null) + algorithm = aliased; + + + + IBasicAgreement iesAgreement = null; + if (algorithm == "IES") + { + iesAgreement = new DHBasicAgreement(); + } + else if (algorithm == "ECIES") + { + iesAgreement = new ECDHBasicAgreement(); + } + + if (iesAgreement != null) + { + return new BufferedIesCipher( + new IesEngine( + iesAgreement, + new Kdf2BytesGenerator( + new Sha1Digest()), + new HMac( + new Sha1Digest()))); + } + + + + if (algorithm.StartsWith("PBE")) + { + if (algorithm.EndsWith("-CBC")) + { + if (algorithm == "PBEWITHSHA1ANDDES-CBC") + { + return new PaddedBufferedBlockCipher( + new CbcBlockCipher(new DesEngine())); + } + else if (algorithm == "PBEWITHSHA1ANDRC2-CBC") + { + return new PaddedBufferedBlockCipher( + new CbcBlockCipher(new RC2Engine())); + } + else if (Strings.IsOneOf(algorithm, + "PBEWITHSHAAND2-KEYTRIPLEDES-CBC", "PBEWITHSHAAND3-KEYTRIPLEDES-CBC")) + { + return new PaddedBufferedBlockCipher( + new CbcBlockCipher(new DesEdeEngine())); + } + else if (Strings.IsOneOf(algorithm, + "PBEWITHSHAAND128BITRC2-CBC", "PBEWITHSHAAND40BITRC2-CBC")) + { + return new PaddedBufferedBlockCipher( + new CbcBlockCipher(new RC2Engine())); + } + } + else if (algorithm.EndsWith("-BC") || algorithm.EndsWith("-OPENSSL")) + { + if (Strings.IsOneOf(algorithm, + "PBEWITHSHAAND128BITAES-CBC-BC", + "PBEWITHSHAAND192BITAES-CBC-BC", + "PBEWITHSHAAND256BITAES-CBC-BC", + "PBEWITHSHA256AND128BITAES-CBC-BC", + "PBEWITHSHA256AND192BITAES-CBC-BC", + "PBEWITHSHA256AND256BITAES-CBC-BC", + "PBEWITHMD5AND128BITAES-CBC-OPENSSL", + "PBEWITHMD5AND192BITAES-CBC-OPENSSL", + "PBEWITHMD5AND256BITAES-CBC-OPENSSL")) + { + return new PaddedBufferedBlockCipher( + new CbcBlockCipher(new AesFastEngine())); + } + } + } + + + + string[] parts = algorithm.Split('/'); + + IBlockCipher blockCipher = null; + IAsymmetricBlockCipher asymBlockCipher = null; + IStreamCipher streamCipher = null; + + string algorithmName = parts[0]; + CipherAlgorithm cipherAlgorithm; + try + { + cipherAlgorithm = (CipherAlgorithm)Enums.GetEnumValue(typeof(CipherAlgorithm), algorithmName); + } + catch (ArgumentException) + { + throw new SecurityUtilityException("Cipher " + algorithm + " not recognised."); + } + + switch (cipherAlgorithm) + { + case CipherAlgorithm.AES: + blockCipher = new AesFastEngine(); + break; + case CipherAlgorithm.ARC4: + streamCipher = new RC4Engine(); + break; + case CipherAlgorithm.BLOWFISH: + blockCipher = new BlowfishEngine(); + break; + case CipherAlgorithm.CAMELLIA: + blockCipher = new CamelliaEngine(); + break; + case CipherAlgorithm.CAST5: + blockCipher = new Cast5Engine(); + break; + case CipherAlgorithm.CAST6: + blockCipher = new Cast6Engine(); + break; + case CipherAlgorithm.DES: + blockCipher = new DesEngine(); + break; + case CipherAlgorithm.DESEDE: + blockCipher = new DesEdeEngine(); + break; + case CipherAlgorithm.ELGAMAL: + asymBlockCipher = new ElGamalEngine(); + break; + case CipherAlgorithm.GOST28147: + blockCipher = new Gost28147Engine(); + break; + case CipherAlgorithm.HC128: + streamCipher = new HC128Engine(); + break; + case CipherAlgorithm.HC256: + streamCipher = new HC256Engine(); + break; +#if INCLUDE_IDEA + case CipherAlgorithm.IDEA: + blockCipher = new IdeaEngine(); + break; +#endif + case CipherAlgorithm.NOEKEON: + blockCipher = new NoekeonEngine(); + break; + case CipherAlgorithm.PBEWITHSHAAND128BITRC4: + case CipherAlgorithm.PBEWITHSHAAND40BITRC4: + streamCipher = new RC4Engine(); + break; + case CipherAlgorithm.RC2: + blockCipher = new RC2Engine(); + break; + case CipherAlgorithm.RC5: + blockCipher = new RC532Engine(); + break; + case CipherAlgorithm.RC5_64: + blockCipher = new RC564Engine(); + break; + case CipherAlgorithm.RC6: + blockCipher = new RC6Engine(); + break; + case CipherAlgorithm.RIJNDAEL: + blockCipher = new RijndaelEngine(); + break; + case CipherAlgorithm.RSA: + asymBlockCipher = new RsaBlindedEngine(); + break; + case CipherAlgorithm.SALSA20: + streamCipher = new Salsa20Engine(); + break; + case CipherAlgorithm.SEED: + blockCipher = new SeedEngine(); + break; + case CipherAlgorithm.SERPENT: + blockCipher = new SerpentEngine(); + break; + case CipherAlgorithm.SKIPJACK: + blockCipher = new SkipjackEngine(); + break; + case CipherAlgorithm.TEA: + blockCipher = new TeaEngine(); + break; + case CipherAlgorithm.TWOFISH: + blockCipher = new TwofishEngine(); + break; + case CipherAlgorithm.VMPC: + streamCipher = new VmpcEngine(); + break; + case CipherAlgorithm.VMPC_KSA3: + streamCipher = new VmpcKsa3Engine(); + break; + case CipherAlgorithm.XTEA: + blockCipher = new XteaEngine(); + break; + default: + throw new SecurityUtilityException("Cipher " + algorithm + " not recognised."); + } + + if (streamCipher != null) + { + if (parts.Length > 1) + throw new ArgumentException("Modes and paddings not used for stream ciphers"); + + return new BufferedStreamCipher(streamCipher); + } + + + bool cts = false; + bool padded = true; + IBlockCipherPadding padding = null; + IAeadBlockCipher aeadBlockCipher = null; + + if (parts.Length > 2) + { + if (streamCipher != null) + throw new ArgumentException("Paddings not used for stream ciphers"); + + string paddingName = parts[2]; + + CipherPadding cipherPadding; + if (paddingName == "") + { + cipherPadding = CipherPadding.RAW; + } + else if (paddingName == "X9.23PADDING") + { + cipherPadding = CipherPadding.X923PADDING; + } + else + { + try + { + cipherPadding = (CipherPadding)Enums.GetEnumValue(typeof(CipherPadding), paddingName); + } + catch (ArgumentException) + { + throw new SecurityUtilityException("Cipher " + algorithm + " not recognised."); + } + } + + switch (cipherPadding) + { + case CipherPadding.NOPADDING: + padded = false; + break; + case CipherPadding.RAW: + break; + case CipherPadding.ISO10126PADDING: + case CipherPadding.ISO10126D2PADDING: + case CipherPadding.ISO10126_2PADDING: + padding = new ISO10126d2Padding(); + break; + case CipherPadding.ISO7816_4PADDING: + case CipherPadding.ISO9797_1PADDING: + padding = new ISO7816d4Padding(); + break; + case CipherPadding.ISO9796_1: + case CipherPadding.ISO9796_1PADDING: + asymBlockCipher = new ISO9796d1Encoding(asymBlockCipher); + break; + case CipherPadding.OAEP: + case CipherPadding.OAEPPADDING: + asymBlockCipher = new OaepEncoding(asymBlockCipher); + break; + case CipherPadding.OAEPWITHMD5ANDMGF1PADDING: + asymBlockCipher = new OaepEncoding(asymBlockCipher, new MD5Digest()); + break; + case CipherPadding.OAEPWITHSHA1ANDMGF1PADDING: + case CipherPadding.OAEPWITHSHA_1ANDMGF1PADDING: + asymBlockCipher = new OaepEncoding(asymBlockCipher, new Sha1Digest()); + break; + case CipherPadding.OAEPWITHSHA224ANDMGF1PADDING: + case CipherPadding.OAEPWITHSHA_224ANDMGF1PADDING: + asymBlockCipher = new OaepEncoding(asymBlockCipher, new Sha224Digest()); + break; + case CipherPadding.OAEPWITHSHA256ANDMGF1PADDING: + case CipherPadding.OAEPWITHSHA_256ANDMGF1PADDING: + asymBlockCipher = new OaepEncoding(asymBlockCipher, new Sha256Digest()); + break; + case CipherPadding.OAEPWITHSHA384ANDMGF1PADDING: + case CipherPadding.OAEPWITHSHA_384ANDMGF1PADDING: + asymBlockCipher = new OaepEncoding(asymBlockCipher, new Sha384Digest()); + break; + case CipherPadding.OAEPWITHSHA512ANDMGF1PADDING: + case CipherPadding.OAEPWITHSHA_512ANDMGF1PADDING: + asymBlockCipher = new OaepEncoding(asymBlockCipher, new Sha512Digest()); + break; + case CipherPadding.PKCS1: + case CipherPadding.PKCS1PADDING: + asymBlockCipher = new Pkcs1Encoding(asymBlockCipher); + break; + case CipherPadding.PKCS5: + case CipherPadding.PKCS5PADDING: + case CipherPadding.PKCS7: + case CipherPadding.PKCS7PADDING: + padding = new Pkcs7Padding(); + break; + case CipherPadding.TBCPADDING: + padding = new TbcPadding(); + break; + case CipherPadding.WITHCTS: + cts = true; + break; + case CipherPadding.X923PADDING: + padding = new X923Padding(); + break; + case CipherPadding.ZEROBYTEPADDING: + padding = new ZeroBytePadding(); + break; + default: + throw new SecurityUtilityException("Cipher " + algorithm + " not recognised."); + } + } + + string mode = ""; + if (parts.Length > 1) + { + mode = parts[1]; + + int di = GetDigitIndex(mode); + string modeName = di >= 0 ? mode.Substring(0, di) : mode; + + try + { + CipherMode cipherMode = modeName == "" + ? CipherMode.NONE + : (CipherMode)Enums.GetEnumValue(typeof(CipherMode), modeName); + + switch (cipherMode) + { + case CipherMode.ECB: + case CipherMode.NONE: + break; + case CipherMode.CBC: + blockCipher = new CbcBlockCipher(blockCipher); + break; + case CipherMode.CCM: + aeadBlockCipher = new CcmBlockCipher(blockCipher); + break; + case CipherMode.CFB: + { + int bits = (di < 0) + ? 8 * blockCipher.GetBlockSize() + : int.Parse(mode.Substring(di)); + + blockCipher = new CfbBlockCipher(blockCipher, bits); + break; + } + case CipherMode.CTR: + blockCipher = new SicBlockCipher(blockCipher); + break; + case CipherMode.CTS: + cts = true; + blockCipher = new CbcBlockCipher(blockCipher); + break; + case CipherMode.EAX: + aeadBlockCipher = new EaxBlockCipher(blockCipher); + break; + case CipherMode.GCM: + aeadBlockCipher = new GcmBlockCipher(blockCipher); + break; + case CipherMode.GOFB: + blockCipher = new GOfbBlockCipher(blockCipher); + break; + case CipherMode.OFB: + { + int bits = (di < 0) + ? 8 * blockCipher.GetBlockSize() + : int.Parse(mode.Substring(di)); + + blockCipher = new OfbBlockCipher(blockCipher, bits); + break; + } + case CipherMode.OPENPGPCFB: + blockCipher = new OpenPgpCfbBlockCipher(blockCipher); + break; + case CipherMode.SIC: + if (blockCipher.GetBlockSize() < 16) + { + throw new ArgumentException("Warning: SIC-Mode can become a twotime-pad if the blocksize of the cipher is too small. Use a cipher with a block size of at least 128 bits (e.g. AES)"); + } + blockCipher = new SicBlockCipher(blockCipher); + break; + default: + throw new SecurityUtilityException("Cipher " + algorithm + " not recognised."); + } + } + catch (ArgumentException) + { + throw new SecurityUtilityException("Cipher " + algorithm + " not recognised."); + } + } + + if (aeadBlockCipher != null) + { + if (cts) + throw new SecurityUtilityException("CTS mode not valid for AEAD ciphers."); + if (padded && parts.Length > 2 && parts[2] != "") + throw new SecurityUtilityException("Bad padding specified for AEAD cipher."); + + return new BufferedAeadBlockCipher(aeadBlockCipher); + } + + if (blockCipher != null) + { + if (cts) + { + return new CtsBlockCipher(blockCipher); + } + + if (padding != null) + { + return new PaddedBufferedBlockCipher(blockCipher, padding); + } + + if (!padded || blockCipher.IsPartialBlockOkay) + { + return new BufferedBlockCipher(blockCipher); + } + + return new PaddedBufferedBlockCipher(blockCipher); + } + + if (asymBlockCipher != null) + { + return new BufferedAsymmetricBlockCipher(asymBlockCipher); + } + + throw new SecurityUtilityException("Cipher " + algorithm + " not recognised."); + } + + public static string GetAlgorithmName( + DerObjectIdentifier oid) + { + return (string) algorithms[oid.Id]; + } + + private static int GetDigitIndex( + string s) + { + for (int i = 0; i < s.Length; ++i) + { + if (char.IsDigit(s[i])) + return i; + } + + return -1; + } + } +} diff --git a/Crypto/src/security/DigestUtilities.cs b/Crypto/src/security/DigestUtilities.cs new file mode 100644
index 000000000..a507b4e9b --- /dev/null +++ b/Crypto/src/security/DigestUtilities.cs
@@ -0,0 +1,189 @@ +using System; +using System.Collections; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Security +{ + /// <remarks> + /// Utility class for creating IDigest objects from their names/Oids + /// </remarks> + public sealed class DigestUtilities + { + internal enum DigestAlgorithm { + GOST3411, + MD2, MD4, MD5, + RIPEMD128, RIPEMD160, RIPEMD256, RIPEMD320, + SHA_1, SHA_224, SHA_256, SHA_384, SHA_512, + TIGER, + WHIRLPOOL, + }; + + private DigestUtilities() + { + } + + private static readonly IDictionary algorithms = Platform.CreateHashtable(); + private static readonly IDictionary oids = Platform.CreateHashtable(); + + static DigestUtilities() + { + // Signal to obfuscation tools not to change enum constants + ((DigestAlgorithm)Enums.GetArbitraryValue(typeof(DigestAlgorithm))).ToString(); + + algorithms[PkcsObjectIdentifiers.MD2.Id] = "MD2"; + algorithms[PkcsObjectIdentifiers.MD4.Id] = "MD4"; + algorithms[PkcsObjectIdentifiers.MD5.Id] = "MD5"; + + algorithms["SHA1"] = "SHA-1"; + algorithms[OiwObjectIdentifiers.IdSha1.Id] = "SHA-1"; + algorithms["SHA224"] = "SHA-224"; + algorithms[NistObjectIdentifiers.IdSha224.Id] = "SHA-224"; + algorithms["SHA256"] = "SHA-256"; + algorithms[NistObjectIdentifiers.IdSha256.Id] = "SHA-256"; + algorithms["SHA384"] = "SHA-384"; + algorithms[NistObjectIdentifiers.IdSha384.Id] = "SHA-384"; + algorithms["SHA512"] = "SHA-512"; + algorithms[NistObjectIdentifiers.IdSha512.Id] = "SHA-512"; + + algorithms["RIPEMD-128"] = "RIPEMD128"; + algorithms[TeleTrusTObjectIdentifiers.RipeMD128.Id] = "RIPEMD128"; + algorithms["RIPEMD-160"] = "RIPEMD160"; + algorithms[TeleTrusTObjectIdentifiers.RipeMD160.Id] = "RIPEMD160"; + algorithms["RIPEMD-256"] = "RIPEMD256"; + algorithms[TeleTrusTObjectIdentifiers.RipeMD256.Id] = "RIPEMD256"; + algorithms["RIPEMD-320"] = "RIPEMD320"; +// algorithms[TeleTrusTObjectIdentifiers.RipeMD320.Id] = "RIPEMD320"; + + algorithms[CryptoProObjectIdentifiers.GostR3411.Id] = "GOST3411"; + + + + oids["MD2"] = PkcsObjectIdentifiers.MD2; + oids["MD4"] = PkcsObjectIdentifiers.MD4; + oids["MD5"] = PkcsObjectIdentifiers.MD5; + oids["SHA-1"] = OiwObjectIdentifiers.IdSha1; + oids["SHA-224"] = NistObjectIdentifiers.IdSha224; + oids["SHA-256"] = NistObjectIdentifiers.IdSha256; + oids["SHA-384"] = NistObjectIdentifiers.IdSha384; + oids["SHA-512"] = NistObjectIdentifiers.IdSha512; + oids["RIPEMD128"] = TeleTrusTObjectIdentifiers.RipeMD128; + oids["RIPEMD160"] = TeleTrusTObjectIdentifiers.RipeMD160; + oids["RIPEMD256"] = TeleTrusTObjectIdentifiers.RipeMD256; + oids["GOST3411"] = CryptoProObjectIdentifiers.GostR3411; + } + + /// <summary> + /// Returns a ObjectIdentifier for a given digest mechanism. + /// </summary> + /// <param name="mechanism">A string representation of the digest meanism.</param> + /// <returns>A DerObjectIdentifier, null if the Oid is not available.</returns> + + public static DerObjectIdentifier GetObjectIdentifier( + string mechanism) + { + if (mechanism == null) + throw new System.ArgumentNullException("mechanism"); + + mechanism = mechanism.ToUpperInvariant(); + string aliased = (string) algorithms[mechanism]; + + if (aliased != null) + mechanism = aliased; + + return (DerObjectIdentifier) oids[mechanism]; + } + + public static ICollection Algorithms + { + get { return oids.Keys; } + } + + public static IDigest GetDigest( + DerObjectIdentifier id) + { + return GetDigest(id.Id); + } + + public static IDigest GetDigest( + string algorithm) + { + string upper = algorithm.ToUpperInvariant(); + string mechanism = (string) algorithms[upper]; + + if (mechanism == null) + { + mechanism = upper; + } + + try + { + DigestAlgorithm digestAlgorithm = (DigestAlgorithm)Enums.GetEnumValue( + typeof(DigestAlgorithm), mechanism); + + switch (digestAlgorithm) + { + case DigestAlgorithm.GOST3411: return new Gost3411Digest(); + case DigestAlgorithm.MD2: return new MD2Digest(); + case DigestAlgorithm.MD4: return new MD4Digest(); + case DigestAlgorithm.MD5: return new MD5Digest(); + case DigestAlgorithm.RIPEMD128: return new RipeMD128Digest(); + case DigestAlgorithm.RIPEMD160: return new RipeMD160Digest(); + case DigestAlgorithm.RIPEMD256: return new RipeMD256Digest(); + case DigestAlgorithm.RIPEMD320: return new RipeMD320Digest(); + case DigestAlgorithm.SHA_1: return new Sha1Digest(); + case DigestAlgorithm.SHA_224: return new Sha224Digest(); + case DigestAlgorithm.SHA_256: return new Sha256Digest(); + case DigestAlgorithm.SHA_384: return new Sha384Digest(); + case DigestAlgorithm.SHA_512: return new Sha512Digest(); + case DigestAlgorithm.TIGER: return new TigerDigest(); + case DigestAlgorithm.WHIRLPOOL: return new WhirlpoolDigest(); + } + } + catch (ArgumentException) + { + } + + throw new SecurityUtilityException("Digest " + mechanism + " not recognised."); + } + + public static string GetAlgorithmName( + DerObjectIdentifier oid) + { + return (string) algorithms[oid.Id]; + } + + public static byte[] CalculateDigest(string algorithm, byte[] input) + { + IDigest digest = GetDigest(algorithm); + digest.BlockUpdate(input, 0, input.Length); + return DoFinal(digest); + } + + public static byte[] DoFinal( + IDigest digest) + { + byte[] b = new byte[digest.GetDigestSize()]; + digest.DoFinal(b, 0); + return b; + } + + public static byte[] DoFinal( + IDigest digest, + byte[] input) + { + digest.BlockUpdate(input, 0, input.Length); + return DoFinal(digest); + } + } +} diff --git a/Crypto/src/security/DotNetUtilities.cs b/Crypto/src/security/DotNetUtilities.cs new file mode 100644
index 000000000..4df1ae739 --- /dev/null +++ b/Crypto/src/security/DotNetUtilities.cs
@@ -0,0 +1,222 @@ +#if !(NETCF_1_0 || SILVERLIGHT || PORTABLE) + +using System; +using System.Security.Cryptography; +using SystemX509 = System.Security.Cryptography.X509Certificates; + +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Security +{ + /// <summary> + /// A class containing methods to interface the BouncyCastle world to the .NET Crypto world. + /// </summary> + public sealed class DotNetUtilities + { + private DotNetUtilities() + { + } + + /// <summary> + /// Create an System.Security.Cryptography.X509Certificate from an X509Certificate Structure. + /// </summary> + /// <param name="x509Struct"></param> + /// <returns>A System.Security.Cryptography.X509Certificate.</returns> + public static SystemX509.X509Certificate ToX509Certificate( + X509CertificateStructure x509Struct) + { + return new SystemX509.X509Certificate(x509Struct.GetDerEncoded()); + } + + public static SystemX509.X509Certificate ToX509Certificate( + X509Certificate x509Cert) + { + return new SystemX509.X509Certificate(x509Cert.GetEncoded()); + } + + public static X509Certificate FromX509Certificate( + SystemX509.X509Certificate x509Cert) + { + return new X509CertificateParser().ReadCertificate(x509Cert.GetRawCertData()); + } + + public static AsymmetricCipherKeyPair GetDsaKeyPair( + DSA dsa) + { + return GetDsaKeyPair(dsa.ExportParameters(true)); + } + + public static AsymmetricCipherKeyPair GetDsaKeyPair( + DSAParameters dp) + { + DsaValidationParameters validationParameters = (dp.Seed != null) + ? new DsaValidationParameters(dp.Seed, dp.Counter) + : null; + + DsaParameters parameters = new DsaParameters( + new BigInteger(1, dp.P), + new BigInteger(1, dp.Q), + new BigInteger(1, dp.G), + validationParameters); + + DsaPublicKeyParameters pubKey = new DsaPublicKeyParameters( + new BigInteger(1, dp.Y), + parameters); + + DsaPrivateKeyParameters privKey = new DsaPrivateKeyParameters( + new BigInteger(1, dp.X), + parameters); + + return new AsymmetricCipherKeyPair(pubKey, privKey); + } + + public static DsaPublicKeyParameters GetDsaPublicKey( + DSA dsa) + { + return GetDsaPublicKey(dsa.ExportParameters(false)); + } + + public static DsaPublicKeyParameters GetDsaPublicKey( + DSAParameters dp) + { + DsaValidationParameters validationParameters = (dp.Seed != null) + ? new DsaValidationParameters(dp.Seed, dp.Counter) + : null; + + DsaParameters parameters = new DsaParameters( + new BigInteger(1, dp.P), + new BigInteger(1, dp.Q), + new BigInteger(1, dp.G), + validationParameters); + + return new DsaPublicKeyParameters( + new BigInteger(1, dp.Y), + parameters); + } + + public static AsymmetricCipherKeyPair GetRsaKeyPair( + RSA rsa) + { + return GetRsaKeyPair(rsa.ExportParameters(true)); + } + + public static AsymmetricCipherKeyPair GetRsaKeyPair( + RSAParameters rp) + { + BigInteger modulus = new BigInteger(1, rp.Modulus); + BigInteger pubExp = new BigInteger(1, rp.Exponent); + + RsaKeyParameters pubKey = new RsaKeyParameters( + false, + modulus, + pubExp); + + RsaPrivateCrtKeyParameters privKey = new RsaPrivateCrtKeyParameters( + modulus, + pubExp, + new BigInteger(1, rp.D), + new BigInteger(1, rp.P), + new BigInteger(1, rp.Q), + new BigInteger(1, rp.DP), + new BigInteger(1, rp.DQ), + new BigInteger(1, rp.InverseQ)); + + return new AsymmetricCipherKeyPair(pubKey, privKey); + } + + public static RsaKeyParameters GetRsaPublicKey( + RSA rsa) + { + return GetRsaPublicKey(rsa.ExportParameters(false)); + } + + public static RsaKeyParameters GetRsaPublicKey( + RSAParameters rp) + { + return new RsaKeyParameters( + false, + new BigInteger(1, rp.Modulus), + new BigInteger(1, rp.Exponent)); + } + + public static AsymmetricCipherKeyPair GetKeyPair(AsymmetricAlgorithm privateKey) + { + if (privateKey is DSA) + { + return GetDsaKeyPair((DSA)privateKey); + } + + if (privateKey is RSA) + { + return GetRsaKeyPair((RSA)privateKey); + } + + throw new ArgumentException("Unsupported algorithm specified", "privateKey"); + } + + public static RSA ToRSA(RsaKeyParameters rsaKey) + { + RSAParameters rp = ToRSAParameters(rsaKey); + RSACryptoServiceProvider rsaCsp = new RSACryptoServiceProvider(); + // TODO This call appears to not work for private keys (when no CRT info) + rsaCsp.ImportParameters(rp); + return rsaCsp; + } + + public static RSA ToRSA(RsaPrivateCrtKeyParameters privKey) + { + RSAParameters rp = ToRSAParameters(privKey); + RSACryptoServiceProvider rsaCsp = new RSACryptoServiceProvider(); + rsaCsp.ImportParameters(rp); + return rsaCsp; + } + + public static RSAParameters ToRSAParameters(RsaKeyParameters rsaKey) + { + RSAParameters rp = new RSAParameters(); + rp.Modulus = rsaKey.Modulus.ToByteArrayUnsigned(); + if (rsaKey.IsPrivate) + rp.D = ConvertRSAParametersField(rsaKey.Exponent, rp.Modulus.Length); + else + rp.Exponent = rsaKey.Exponent.ToByteArrayUnsigned(); + return rp; + } + + public static RSAParameters ToRSAParameters(RsaPrivateCrtKeyParameters privKey) + { + RSAParameters rp = new RSAParameters(); + rp.Modulus = privKey.Modulus.ToByteArrayUnsigned(); + rp.Exponent = privKey.PublicExponent.ToByteArrayUnsigned(); + rp.P = privKey.P.ToByteArrayUnsigned(); + rp.Q = privKey.Q.ToByteArrayUnsigned(); + rp.D = ConvertRSAParametersField(privKey.Exponent, rp.Modulus.Length); + rp.DP = ConvertRSAParametersField(privKey.DP, rp.P.Length); + rp.DQ = ConvertRSAParametersField(privKey.DQ, rp.Q.Length); + rp.InverseQ = ConvertRSAParametersField(privKey.QInv, rp.Q.Length); + return rp; + } + + // TODO Move functionality to more general class + private static byte[] ConvertRSAParametersField(BigInteger n, int size) + { + byte[] bs = n.ToByteArrayUnsigned(); + + if (bs.Length == size) + return bs; + + if (bs.Length > size) + throw new ArgumentException("Specified size too small", "size"); + + byte[] padded = new byte[size]; + Array.Copy(bs, 0, padded, size - bs.Length, bs.Length); + return padded; + } + } +} + +#endif diff --git a/Crypto/src/security/GeneralSecurityException.cs b/Crypto/src/security/GeneralSecurityException.cs new file mode 100644
index 000000000..c0e412f98 --- /dev/null +++ b/Crypto/src/security/GeneralSecurityException.cs
@@ -0,0 +1,26 @@ +using System; + +namespace Org.BouncyCastle.Security +{ + public class GeneralSecurityException + : Exception + { + public GeneralSecurityException() + : base() + { + } + + public GeneralSecurityException( + string message) + : base(message) + { + } + + public GeneralSecurityException( + string message, + Exception exception) + : base(message, exception) + { + } + } +} diff --git a/Crypto/src/security/GeneratorUtilities.cs b/Crypto/src/security/GeneratorUtilities.cs new file mode 100644
index 000000000..8e9290222 --- /dev/null +++ b/Crypto/src/security/GeneratorUtilities.cs
@@ -0,0 +1,341 @@ +using System.Collections; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Iana; +using Org.BouncyCastle.Asn1.Kisa; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Ntt; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Security +{ + public sealed class GeneratorUtilities + { + private GeneratorUtilities() + { + } + + private static readonly IDictionary kgAlgorithms = Platform.CreateHashtable(); + private static readonly IDictionary kpgAlgorithms = Platform.CreateHashtable(); + private static readonly IDictionary defaultKeySizes = Platform.CreateHashtable(); + + static GeneratorUtilities() + { + // + // key generators. + // + AddKgAlgorithm("AES", + "AESWRAP"); + AddKgAlgorithm("AES128", + "2.16.840.1.101.3.4.2", + NistObjectIdentifiers.IdAes128Cbc, + NistObjectIdentifiers.IdAes128Cfb, + NistObjectIdentifiers.IdAes128Ecb, + NistObjectIdentifiers.IdAes128Ofb, + NistObjectIdentifiers.IdAes128Wrap); + AddKgAlgorithm("AES192", + "2.16.840.1.101.3.4.22", + NistObjectIdentifiers.IdAes192Cbc, + NistObjectIdentifiers.IdAes192Cfb, + NistObjectIdentifiers.IdAes192Ecb, + NistObjectIdentifiers.IdAes192Ofb, + NistObjectIdentifiers.IdAes192Wrap); + AddKgAlgorithm("AES256", + "2.16.840.1.101.3.4.42", + NistObjectIdentifiers.IdAes256Cbc, + NistObjectIdentifiers.IdAes256Cfb, + NistObjectIdentifiers.IdAes256Ecb, + NistObjectIdentifiers.IdAes256Ofb, + NistObjectIdentifiers.IdAes256Wrap); + AddKgAlgorithm("BLOWFISH", + "1.3.6.1.4.1.3029.1.2"); + AddKgAlgorithm("CAMELLIA", + "CAMELLIAWRAP"); + AddKgAlgorithm("CAMELLIA128", + NttObjectIdentifiers.IdCamellia128Cbc, + NttObjectIdentifiers.IdCamellia128Wrap); + AddKgAlgorithm("CAMELLIA192", + NttObjectIdentifiers.IdCamellia192Cbc, + NttObjectIdentifiers.IdCamellia192Wrap); + AddKgAlgorithm("CAMELLIA256", + NttObjectIdentifiers.IdCamellia256Cbc, + NttObjectIdentifiers.IdCamellia256Wrap); + AddKgAlgorithm("CAST5", + "1.2.840.113533.7.66.10"); + AddKgAlgorithm("CAST6"); + AddKgAlgorithm("DES", + OiwObjectIdentifiers.DesCbc, + OiwObjectIdentifiers.DesCfb, + OiwObjectIdentifiers.DesEcb, + OiwObjectIdentifiers.DesOfb); + AddKgAlgorithm("DESEDE", + "DESEDEWRAP", + OiwObjectIdentifiers.DesEde); + AddKgAlgorithm("DESEDE3", + PkcsObjectIdentifiers.DesEde3Cbc, + PkcsObjectIdentifiers.IdAlgCms3DesWrap); + AddKgAlgorithm("GOST28147", + "GOST", + "GOST-28147", + CryptoProObjectIdentifiers.GostR28147Cbc); + AddKgAlgorithm("HC128"); + AddKgAlgorithm("HC256"); + AddKgAlgorithm("IDEA", + "1.3.6.1.4.1.188.7.1.1.2"); + AddKgAlgorithm("NOEKEON"); + AddKgAlgorithm("RC2", + PkcsObjectIdentifiers.RC2Cbc, + PkcsObjectIdentifiers.IdAlgCmsRC2Wrap); + AddKgAlgorithm("RC4", + "ARC4", + "1.2.840.113549.3.4"); + AddKgAlgorithm("RC5", + "RC5-32"); + AddKgAlgorithm("RC5-64"); + AddKgAlgorithm("RC6"); + AddKgAlgorithm("RIJNDAEL"); + AddKgAlgorithm("SALSA20"); + AddKgAlgorithm("SEED", + KisaObjectIdentifiers.IdNpkiAppCmsSeedWrap, + KisaObjectIdentifiers.IdSeedCbc); + AddKgAlgorithm("SERPENT"); + AddKgAlgorithm("SKIPJACK"); + AddKgAlgorithm("TEA"); + AddKgAlgorithm("TWOFISH"); + AddKgAlgorithm("VMPC"); + AddKgAlgorithm("VMPC-KSA3"); + AddKgAlgorithm("XTEA"); + + // + // HMac key generators + // + AddHMacKeyGenerator("MD2"); + AddHMacKeyGenerator("MD4"); + AddHMacKeyGenerator("MD5", + IanaObjectIdentifiers.HmacMD5); + AddHMacKeyGenerator("SHA1", + PkcsObjectIdentifiers.IdHmacWithSha1, + IanaObjectIdentifiers.HmacSha1); + AddHMacKeyGenerator("SHA224", + PkcsObjectIdentifiers.IdHmacWithSha224); + AddHMacKeyGenerator("SHA256", + PkcsObjectIdentifiers.IdHmacWithSha256); + AddHMacKeyGenerator("SHA384", + PkcsObjectIdentifiers.IdHmacWithSha384); + AddHMacKeyGenerator("SHA512", + PkcsObjectIdentifiers.IdHmacWithSha512); + AddHMacKeyGenerator("RIPEMD128"); + AddHMacKeyGenerator("RIPEMD160", + IanaObjectIdentifiers.HmacRipeMD160); + AddHMacKeyGenerator("TIGER", + IanaObjectIdentifiers.HmacTiger); + + + + // + // key pair generators. + // + AddKpgAlgorithm("DH", + "DIFFIEHELLMAN"); + AddKpgAlgorithm("DSA"); + AddKpgAlgorithm("EC", + // TODO Should this be an alias for ECDH? + X9ObjectIdentifiers.DHSinglePassStdDHSha1KdfScheme); + AddKpgAlgorithm("ECDH", + "ECIES"); + AddKpgAlgorithm("ECDHC"); + AddKpgAlgorithm("ECMQV", + X9ObjectIdentifiers.MqvSinglePassSha1KdfScheme); + AddKpgAlgorithm("ECDSA"); + AddKpgAlgorithm("ECGOST3410", + "ECGOST-3410", + "GOST-3410-2001"); + AddKpgAlgorithm("ELGAMAL"); + AddKpgAlgorithm("GOST3410", + "GOST-3410", + "GOST-3410-94"); + AddKpgAlgorithm("RSA", + "1.2.840.113549.1.1.1"); + + AddDefaultKeySizeEntries(64, "DES"); + AddDefaultKeySizeEntries(80, "SKIPJACK"); + AddDefaultKeySizeEntries(128, "AES128", "BLOWFISH", "CAMELLIA128", "CAST5", "DESEDE", + "HC128", "HMACMD2", "HMACMD4", "HMACMD5", "HMACRIPEMD128", "IDEA", "NOEKEON", + "RC2", "RC4", "RC5", "SALSA20", "SEED", "TEA", "XTEA", "VMPC", "VMPC-KSA3"); + AddDefaultKeySizeEntries(160, "HMACRIPEMD160", "HMACSHA1"); + AddDefaultKeySizeEntries(192, "AES", "AES192", "CAMELLIA192", "DESEDE3", "HMACTIGER", + "RIJNDAEL", "SERPENT"); + AddDefaultKeySizeEntries(224, "HMACSHA224"); + AddDefaultKeySizeEntries(256, "AES256", "CAMELLIA", "CAMELLIA256", "CAST6", "GOST28147", + "HC256", "HMACSHA256", "RC5-64", "RC6", "TWOFISH"); + AddDefaultKeySizeEntries(384, "HMACSHA384"); + AddDefaultKeySizeEntries(512, "HMACSHA512"); + } + + private static void AddDefaultKeySizeEntries(int size, params string[] algorithms) + { + foreach (string algorithm in algorithms) + { + defaultKeySizes.Add(algorithm, size); + } + } + + private static void AddKgAlgorithm( + string canonicalName, + params object[] aliases) + { + kgAlgorithms[canonicalName] = canonicalName; + + foreach (object alias in aliases) + { + kgAlgorithms[alias.ToString()] = canonicalName; + } + } + + private static void AddKpgAlgorithm( + string canonicalName, + params object[] aliases) + { + kpgAlgorithms[canonicalName] = canonicalName; + + foreach (object alias in aliases) + { + kpgAlgorithms[alias.ToString()] = canonicalName; + } + } + + private static void AddHMacKeyGenerator( + string algorithm, + params object[] aliases) + { + string mainName = "HMAC" + algorithm; + + kgAlgorithms[mainName] = mainName; + kgAlgorithms["HMAC-" + algorithm] = mainName; + kgAlgorithms["HMAC/" + algorithm] = mainName; + + foreach (object alias in aliases) + { + kgAlgorithms[alias.ToString()] = mainName; + } + } + + // TODO Consider making this public + internal static string GetCanonicalKeyGeneratorAlgorithm( + string algorithm) + { + return (string) kgAlgorithms[algorithm.ToUpperInvariant()]; + } + + // TODO Consider making this public + internal static string GetCanonicalKeyPairGeneratorAlgorithm( + string algorithm) + { + return (string) kpgAlgorithms[algorithm.ToUpperInvariant()]; + } + + public static CipherKeyGenerator GetKeyGenerator( + DerObjectIdentifier oid) + { + return GetKeyGenerator(oid.Id); + } + + public static CipherKeyGenerator GetKeyGenerator( + string algorithm) + { + string canonicalName = GetCanonicalKeyGeneratorAlgorithm(algorithm); + + if (canonicalName == null) + throw new SecurityUtilityException("KeyGenerator " + algorithm + " not recognised."); + + int defaultKeySize = FindDefaultKeySize(canonicalName); + if (defaultKeySize == -1) + throw new SecurityUtilityException("KeyGenerator " + algorithm + + " (" + canonicalName + ") not supported."); + + if (canonicalName == "DES") + return new DesKeyGenerator(defaultKeySize); + + if (canonicalName == "DESEDE" || canonicalName == "DESEDE3") + return new DesEdeKeyGenerator(defaultKeySize); + + return new CipherKeyGenerator(defaultKeySize); + } + + public static IAsymmetricCipherKeyPairGenerator GetKeyPairGenerator( + DerObjectIdentifier oid) + { + return GetKeyPairGenerator(oid.Id); + } + + public static IAsymmetricCipherKeyPairGenerator GetKeyPairGenerator( + string algorithm) + { + string canonicalName = GetCanonicalKeyPairGeneratorAlgorithm(algorithm); + + if (canonicalName == null) + throw new SecurityUtilityException("KeyPairGenerator " + algorithm + " not recognised."); + + if (canonicalName == "DH") + return new DHKeyPairGenerator(); + + if (canonicalName == "DSA") + return new DsaKeyPairGenerator(); + + // "EC", "ECDH", "ECDHC", "ECDSA", "ECGOST3410", "ECMQV" + if (canonicalName.StartsWith("EC")) + return new ECKeyPairGenerator(canonicalName); + + if (canonicalName == "ELGAMAL") + return new ElGamalKeyPairGenerator(); + + if (canonicalName == "GOST3410") + return new Gost3410KeyPairGenerator(); + + if (canonicalName == "RSA") + return new RsaKeyPairGenerator(); + + throw new SecurityUtilityException("KeyPairGenerator " + algorithm + + " (" + canonicalName + ") not supported."); + } + + internal static int GetDefaultKeySize( + DerObjectIdentifier oid) + { + return GetDefaultKeySize(oid.Id); + } + + internal static int GetDefaultKeySize( + string algorithm) + { + string canonicalName = GetCanonicalKeyGeneratorAlgorithm(algorithm); + + if (canonicalName == null) + throw new SecurityUtilityException("KeyGenerator " + algorithm + " not recognised."); + + int defaultKeySize = FindDefaultKeySize(canonicalName); + if (defaultKeySize == -1) + throw new SecurityUtilityException("KeyGenerator " + algorithm + + " (" + canonicalName + ") not supported."); + + return defaultKeySize; + } + + private static int FindDefaultKeySize( + string canonicalName) + { + if (!defaultKeySizes.Contains(canonicalName)) + return -1; + + return (int)defaultKeySizes[canonicalName]; + } + } +} diff --git a/Crypto/src/security/InvalidKeyException.cs b/Crypto/src/security/InvalidKeyException.cs new file mode 100644
index 000000000..97d48e091 --- /dev/null +++ b/Crypto/src/security/InvalidKeyException.cs
@@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Security +{ + public class InvalidKeyException : KeyException + { + public InvalidKeyException() : base() { } + public InvalidKeyException(string message) : base(message) { } + public InvalidKeyException(string message, Exception exception) : base(message, exception) { } + } +} diff --git a/Crypto/src/security/InvalidParameterException.cs b/Crypto/src/security/InvalidParameterException.cs new file mode 100644
index 000000000..08ca4a076 --- /dev/null +++ b/Crypto/src/security/InvalidParameterException.cs
@@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Security +{ + public class InvalidParameterException : KeyException + { + public InvalidParameterException() : base() { } + public InvalidParameterException(string message) : base(message) { } + public InvalidParameterException(string message, Exception exception) : base(message, exception) { } + } +} diff --git a/Crypto/src/security/KeyException.cs b/Crypto/src/security/KeyException.cs new file mode 100644
index 000000000..f19264e5f --- /dev/null +++ b/Crypto/src/security/KeyException.cs
@@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Security +{ + public class KeyException : GeneralSecurityException + { + public KeyException() : base() { } + public KeyException(string message) : base(message) { } + public KeyException(string message, Exception exception) : base(message, exception) { } + } +} diff --git a/Crypto/src/security/MacUtilities.cs b/Crypto/src/security/MacUtilities.cs new file mode 100644
index 000000000..8550d7550 --- /dev/null +++ b/Crypto/src/security/MacUtilities.cs
@@ -0,0 +1,239 @@ +using System.Collections; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Iana; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Macs; +using Org.BouncyCastle.Crypto.Paddings; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Security +{ + /// <remarks> + /// Utility class for creating HMac object from their names/Oids + /// </remarks> + public sealed class MacUtilities + { + private MacUtilities() + { + } + + private static readonly IDictionary algorithms = Platform.CreateHashtable(); + //private static readonly IDictionary oids = Platform.CreateHashtable(); + + static MacUtilities() + { + algorithms[IanaObjectIdentifiers.HmacMD5.Id] = "HMAC-MD5"; + algorithms[IanaObjectIdentifiers.HmacRipeMD160.Id] = "HMAC-RIPEMD160"; + algorithms[IanaObjectIdentifiers.HmacSha1.Id] = "HMAC-SHA1"; + algorithms[IanaObjectIdentifiers.HmacTiger.Id] = "HMAC-TIGER"; + + algorithms[PkcsObjectIdentifiers.IdHmacWithSha1.Id] = "HMAC-SHA1"; + algorithms[PkcsObjectIdentifiers.IdHmacWithSha224.Id] = "HMAC-SHA224"; + algorithms[PkcsObjectIdentifiers.IdHmacWithSha256.Id] = "HMAC-SHA256"; + algorithms[PkcsObjectIdentifiers.IdHmacWithSha384.Id] = "HMAC-SHA384"; + algorithms[PkcsObjectIdentifiers.IdHmacWithSha512.Id] = "HMAC-SHA512"; + + // TODO AESMAC? + + algorithms["DES"] = "DESMAC"; + algorithms["DES/CFB8"] = "DESMAC/CFB8"; + algorithms["DES64"] = "DESMAC64"; + algorithms["DESEDE"] = "DESEDEMAC"; + algorithms[PkcsObjectIdentifiers.DesEde3Cbc.Id] = "DESEDEMAC"; + algorithms["DESEDE/CFB8"] = "DESEDEMAC/CFB8"; + algorithms["DESISO9797MAC"] = "DESWITHISO9797"; + algorithms["DESEDE64"] = "DESEDEMAC64"; + + algorithms["DESEDE64WITHISO7816-4PADDING"] = "DESEDEMAC64WITHISO7816-4PADDING"; + algorithms["DESEDEISO9797ALG1MACWITHISO7816-4PADDING"] = "DESEDEMAC64WITHISO7816-4PADDING"; + algorithms["DESEDEISO9797ALG1WITHISO7816-4PADDING"] = "DESEDEMAC64WITHISO7816-4PADDING"; + + algorithms["ISO9797ALG3"] = "ISO9797ALG3MAC"; + algorithms["ISO9797ALG3MACWITHISO7816-4PADDING"] = "ISO9797ALG3WITHISO7816-4PADDING"; + + algorithms["SKIPJACK"] = "SKIPJACKMAC"; + algorithms["SKIPJACK/CFB8"] = "SKIPJACKMAC/CFB8"; + algorithms["IDEA"] = "IDEAMAC"; + algorithms["IDEA/CFB8"] = "IDEAMAC/CFB8"; + algorithms["RC2"] = "RC2MAC"; + algorithms["RC2/CFB8"] = "RC2MAC/CFB8"; + algorithms["RC5"] = "RC5MAC"; + algorithms["RC5/CFB8"] = "RC5MAC/CFB8"; + algorithms["GOST28147"] = "GOST28147MAC"; + algorithms["VMPC"] = "VMPCMAC"; + algorithms["VMPC-MAC"] = "VMPCMAC"; + + algorithms["PBEWITHHMACSHA"] = "PBEWITHHMACSHA1"; + algorithms["1.3.14.3.2.26"] = "PBEWITHHMACSHA1"; + } + +// /// <summary> +// /// Returns a ObjectIdentifier for a given digest mechanism. +// /// </summary> +// /// <param name="mechanism">A string representation of the digest meanism.</param> +// /// <returns>A DerObjectIdentifier, null if the Oid is not available.</returns> +// public static DerObjectIdentifier GetObjectIdentifier( +// string mechanism) +// { +// mechanism = (string) algorithms[mechanism.ToUpperInvariant()]; +// +// if (mechanism != null) +// { +// return (DerObjectIdentifier)oids[mechanism]; +// } +// +// return null; +// } + +// public static ICollection Algorithms +// { +// get { return oids.Keys; } +// } + + public static IMac GetMac( + DerObjectIdentifier id) + { + return GetMac(id.Id); + } + + public static IMac GetMac( + string algorithm) + { + string upper = algorithm.ToUpperInvariant(); + + string mechanism = (string) algorithms[upper]; + + if (mechanism == null) + { + mechanism = upper; + } + + if (mechanism.StartsWith("PBEWITH")) + { + mechanism = mechanism.Substring("PBEWITH".Length); + } + + if (mechanism.StartsWith("HMAC")) + { + string digestName; + if (mechanism.StartsWith("HMAC-") || mechanism.StartsWith("HMAC/")) + { + digestName = mechanism.Substring(5); + } + else + { + digestName = mechanism.Substring(4); + } + + return new HMac(DigestUtilities.GetDigest(digestName)); + } + + if (mechanism == "AESCMAC") + { + return new CMac(new AesFastEngine()); + } + if (mechanism == "DESMAC") + { + return new CbcBlockCipherMac(new DesEngine()); + } + if (mechanism == "DESMAC/CFB8") + { + return new CfbBlockCipherMac(new DesEngine()); + } + if (mechanism == "DESMAC64") + { + return new CbcBlockCipherMac(new DesEngine(), 64); + } + if (mechanism == "DESEDECMAC") + { + return new CMac(new DesEdeEngine()); + } + if (mechanism == "DESEDEMAC") + { + return new CbcBlockCipherMac(new DesEdeEngine()); + } + if (mechanism == "DESEDEMAC/CFB8") + { + return new CfbBlockCipherMac(new DesEdeEngine()); + } + if (mechanism == "DESEDEMAC64") + { + return new CbcBlockCipherMac(new DesEdeEngine(), 64); + } + if (mechanism == "DESEDEMAC64WITHISO7816-4PADDING") + { + return new CbcBlockCipherMac(new DesEdeEngine(), 64, new ISO7816d4Padding()); + } + if (mechanism == "DESWITHISO9797" + || mechanism == "ISO9797ALG3MAC") + { + return new ISO9797Alg3Mac(new DesEngine()); + } + if (mechanism == "ISO9797ALG3WITHISO7816-4PADDING") + { + return new ISO9797Alg3Mac(new DesEngine(), new ISO7816d4Padding()); + } + if (mechanism == "SKIPJACKMAC") + { + return new CbcBlockCipherMac(new SkipjackEngine()); + } + if (mechanism == "SKIPJACKMAC/CFB8") + { + return new CfbBlockCipherMac(new SkipjackEngine()); + } +#if INCLUDE_IDEA + if (mechanism == "IDEAMAC") + { + return new CbcBlockCipherMac(new IdeaEngine()); + } + if (mechanism == "IDEAMAC/CFB8") + { + return new CfbBlockCipherMac(new IdeaEngine()); + } +#endif + if (mechanism == "RC2MAC") + { + return new CbcBlockCipherMac(new RC2Engine()); + } + if (mechanism == "RC2MAC/CFB8") + { + return new CfbBlockCipherMac(new RC2Engine()); + } + if (mechanism == "RC5MAC") + { + return new CbcBlockCipherMac(new RC532Engine()); + } + if (mechanism == "RC5MAC/CFB8") + { + return new CfbBlockCipherMac(new RC532Engine()); + } + if (mechanism == "GOST28147MAC") + { + return new Gost28147Mac(); + } + if (mechanism == "VMPCMAC") + { + return new VmpcMac(); + } + throw new SecurityUtilityException("Mac " + mechanism + " not recognised."); + } + + public static string GetAlgorithmName( + DerObjectIdentifier oid) + { + return (string) algorithms[oid.Id]; + } + + public static byte[] DoFinal( + IMac mac) + { + byte[] b = new byte[mac.GetMacSize()]; + mac.DoFinal(b, 0); + return b; + } + } +} diff --git a/Crypto/src/security/NoSuchAlgorithmException.cs b/Crypto/src/security/NoSuchAlgorithmException.cs new file mode 100644
index 000000000..25c0ab04f --- /dev/null +++ b/Crypto/src/security/NoSuchAlgorithmException.cs
@@ -0,0 +1,12 @@ +using System; + +namespace Org.BouncyCastle.Security +{ + [Obsolete("Never thrown")] + public class NoSuchAlgorithmException : GeneralSecurityException + { + public NoSuchAlgorithmException() : base() {} + public NoSuchAlgorithmException(string message) : base(message) {} + public NoSuchAlgorithmException(string message, Exception exception) : base(message, exception) {} + } +} diff --git a/Crypto/src/security/ParameterUtilities.cs b/Crypto/src/security/ParameterUtilities.cs new file mode 100644
index 000000000..b44fbda73 --- /dev/null +++ b/Crypto/src/security/ParameterUtilities.cs
@@ -0,0 +1,327 @@ +using System; +using System.Collections; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Kisa; +using Org.BouncyCastle.Asn1.Misc; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Ntt; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Security +{ + public sealed class ParameterUtilities + { + private ParameterUtilities() + { + } + + private static readonly IDictionary algorithms = Platform.CreateHashtable(); + private static readonly IDictionary basicIVSizes = Platform.CreateHashtable(); + + static ParameterUtilities() + { + AddAlgorithm("AES", + "AESWRAP"); + AddAlgorithm("AES128", + "2.16.840.1.101.3.4.2", + NistObjectIdentifiers.IdAes128Cbc, + NistObjectIdentifiers.IdAes128Cfb, + NistObjectIdentifiers.IdAes128Ecb, + NistObjectIdentifiers.IdAes128Ofb, + NistObjectIdentifiers.IdAes128Wrap); + AddAlgorithm("AES192", + "2.16.840.1.101.3.4.22", + NistObjectIdentifiers.IdAes192Cbc, + NistObjectIdentifiers.IdAes192Cfb, + NistObjectIdentifiers.IdAes192Ecb, + NistObjectIdentifiers.IdAes192Ofb, + NistObjectIdentifiers.IdAes192Wrap); + AddAlgorithm("AES256", + "2.16.840.1.101.3.4.42", + NistObjectIdentifiers.IdAes256Cbc, + NistObjectIdentifiers.IdAes256Cfb, + NistObjectIdentifiers.IdAes256Ecb, + NistObjectIdentifiers.IdAes256Ofb, + NistObjectIdentifiers.IdAes256Wrap); + AddAlgorithm("BLOWFISH", + "1.3.6.1.4.1.3029.1.2"); + AddAlgorithm("CAMELLIA", + "CAMELLIAWRAP"); + AddAlgorithm("CAMELLIA128", + NttObjectIdentifiers.IdCamellia128Cbc, + NttObjectIdentifiers.IdCamellia128Wrap); + AddAlgorithm("CAMELLIA192", + NttObjectIdentifiers.IdCamellia192Cbc, + NttObjectIdentifiers.IdCamellia192Wrap); + AddAlgorithm("CAMELLIA256", + NttObjectIdentifiers.IdCamellia256Cbc, + NttObjectIdentifiers.IdCamellia256Wrap); + AddAlgorithm("CAST5", + "1.2.840.113533.7.66.10"); + AddAlgorithm("CAST6"); + AddAlgorithm("DES", + OiwObjectIdentifiers.DesCbc, + OiwObjectIdentifiers.DesCfb, + OiwObjectIdentifiers.DesEcb, + OiwObjectIdentifiers.DesOfb); + AddAlgorithm("DESEDE", + "DESEDEWRAP", + OiwObjectIdentifiers.DesEde, + PkcsObjectIdentifiers.IdAlgCms3DesWrap); + AddAlgorithm("DESEDE3", + PkcsObjectIdentifiers.DesEde3Cbc); + AddAlgorithm("GOST28147", + "GOST", + "GOST-28147", + CryptoProObjectIdentifiers.GostR28147Cbc); + AddAlgorithm("HC128"); + AddAlgorithm("HC256"); +#if INCLUDE_IDEA + AddAlgorithm("IDEA", + "1.3.6.1.4.1.188.7.1.1.2"); +#endif + AddAlgorithm("NOEKEON"); + AddAlgorithm("RC2", + PkcsObjectIdentifiers.RC2Cbc, + PkcsObjectIdentifiers.IdAlgCmsRC2Wrap); + AddAlgorithm("RC4", + "ARC4", + "1.2.840.113549.3.4"); + AddAlgorithm("RC5", + "RC5-32"); + AddAlgorithm("RC5-64"); + AddAlgorithm("RC6"); + AddAlgorithm("RIJNDAEL"); + AddAlgorithm("SALSA20"); + AddAlgorithm("SEED", + KisaObjectIdentifiers.IdNpkiAppCmsSeedWrap, + KisaObjectIdentifiers.IdSeedCbc); + AddAlgorithm("SERPENT"); + AddAlgorithm("SKIPJACK"); + AddAlgorithm("TEA"); + AddAlgorithm("TWOFISH"); + AddAlgorithm("VMPC"); + AddAlgorithm("VMPC-KSA3"); + AddAlgorithm("XTEA"); + + AddBasicIVSizeEntries(8, "BLOWFISH", "DES", "DESEDE", "DESEDE3"); + AddBasicIVSizeEntries(16, "AES", "AES128", "AES192", "AES256", + "CAMELLIA", "CAMELLIA128", "CAMELLIA192", "CAMELLIA256", "NOEKEON", "SEED"); + + // TODO These algorithms support an IV + // but JCE doesn't seem to provide an AlgorithmParametersGenerator for them + // "RIJNDAEL", "SKIPJACK", "TWOFISH" + } + + private static void AddAlgorithm( + string canonicalName, + params object[] aliases) + { + algorithms[canonicalName] = canonicalName; + + foreach (object alias in aliases) + { + algorithms[alias.ToString()] = canonicalName; + } + } + + private static void AddBasicIVSizeEntries(int size, params string[] algorithms) + { + foreach (string algorithm in algorithms) + { + basicIVSizes.Add(algorithm, size); + } + } + + public static string GetCanonicalAlgorithmName( + string algorithm) + { + return (string) algorithms[algorithm.ToUpperInvariant()]; + } + + public static KeyParameter CreateKeyParameter( + DerObjectIdentifier algOid, + byte[] keyBytes) + { + return CreateKeyParameter(algOid.Id, keyBytes, 0, keyBytes.Length); + } + + public static KeyParameter CreateKeyParameter( + string algorithm, + byte[] keyBytes) + { + return CreateKeyParameter(algorithm, keyBytes, 0, keyBytes.Length); + } + + public static KeyParameter CreateKeyParameter( + DerObjectIdentifier algOid, + byte[] keyBytes, + int offset, + int length) + { + return CreateKeyParameter(algOid.Id, keyBytes, offset, length); + } + + public static KeyParameter CreateKeyParameter( + string algorithm, + byte[] keyBytes, + int offset, + int length) + { + if (algorithm == null) + throw new ArgumentNullException("algorithm"); + + string canonical = GetCanonicalAlgorithmName(algorithm); + + if (canonical == null) + throw new SecurityUtilityException("Algorithm " + algorithm + " not recognised."); + + if (canonical == "DES") + return new DesParameters(keyBytes, offset, length); + + if (canonical == "DESEDE" || canonical =="DESEDE3") + return new DesEdeParameters(keyBytes, offset, length); + + if (canonical == "RC2") + return new RC2Parameters(keyBytes, offset, length); + + return new KeyParameter(keyBytes, offset, length); + } + + public static ICipherParameters GetCipherParameters( + DerObjectIdentifier algOid, + ICipherParameters key, + Asn1Object asn1Params) + { + return GetCipherParameters(algOid.Id, key, asn1Params); + } + + public static ICipherParameters GetCipherParameters( + string algorithm, + ICipherParameters key, + Asn1Object asn1Params) + { + if (algorithm == null) + throw new ArgumentNullException("algorithm"); + + string canonical = GetCanonicalAlgorithmName(algorithm); + + if (canonical == null) + throw new SecurityUtilityException("Algorithm " + algorithm + " not recognised."); + + byte[] iv = null; + + try + { + // TODO These algorithms support an IV + // but JCE doesn't seem to provide an AlgorithmParametersGenerator for them + // "RIJNDAEL", "SKIPJACK", "TWOFISH" + + int basicIVKeySize = FindBasicIVSize(canonical); + if (basicIVKeySize != -1 + || canonical == "RIJNDAEL" || canonical == "SKIPJACK" || canonical == "TWOFISH") + { + iv = ((Asn1OctetString) asn1Params).GetOctets(); + } + else if (canonical == "CAST5") + { + iv = Cast5CbcParameters.GetInstance(asn1Params).GetIV(); + } +#if INCLUDE_IDEA + else if (canonical == "IDEA") + { + iv = IdeaCbcPar.GetInstance(asn1Params).GetIV(); + } +#endif + else if (canonical == "RC2") + { + iv = RC2CbcParameter.GetInstance(asn1Params).GetIV(); + } + } + catch (Exception e) + { + throw new ArgumentException("Could not process ASN.1 parameters", e); + } + + if (iv != null) + { + return new ParametersWithIV(key, iv); + } + + throw new SecurityUtilityException("Algorithm " + algorithm + " not recognised."); + } + + public static Asn1Encodable GenerateParameters( + DerObjectIdentifier algID, + SecureRandom random) + { + return GenerateParameters(algID.Id, random); + } + + public static Asn1Encodable GenerateParameters( + string algorithm, + SecureRandom random) + { + if (algorithm == null) + throw new ArgumentNullException("algorithm"); + + string canonical = GetCanonicalAlgorithmName(algorithm); + + if (canonical == null) + throw new SecurityUtilityException("Algorithm " + algorithm + " not recognised."); + + // TODO These algorithms support an IV + // but JCE doesn't seem to provide an AlgorithmParametersGenerator for them + // "RIJNDAEL", "SKIPJACK", "TWOFISH" + + int basicIVKeySize = FindBasicIVSize(canonical); + if (basicIVKeySize != -1) + return CreateIVOctetString(random, basicIVKeySize); + + if (canonical == "CAST5") + return new Cast5CbcParameters(CreateIV(random, 8), 128); + +#if INCLUDE_IDEA + if (canonical == "IDEA") + return new IdeaCbcPar(CreateIV(random, 8)); +#endif + + if (canonical == "RC2") + return new RC2CbcParameter(CreateIV(random, 8)); + + throw new SecurityUtilityException("Algorithm " + algorithm + " not recognised."); + } + + private static Asn1OctetString CreateIVOctetString( + SecureRandom random, + int ivLength) + { + return new DerOctetString(CreateIV(random, ivLength)); + } + + private static byte[] CreateIV( + SecureRandom random, + int ivLength) + { + byte[] iv = new byte[ivLength]; + random.NextBytes(iv); + return iv; + } + + private static int FindBasicIVSize( + string canonicalName) + { + if (!basicIVSizes.Contains(canonicalName)) + return -1; + + return (int)basicIVSizes[canonicalName]; + } + } +} diff --git a/Crypto/src/security/PbeUtilities.cs b/Crypto/src/security/PbeUtilities.cs new file mode 100644
index 000000000..f59ab104b --- /dev/null +++ b/Crypto/src/security/PbeUtilities.cs
@@ -0,0 +1,658 @@ +using System; +using System.Collections; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.BC; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Macs; +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Paddings; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Security +{ + /// <summary> + /// + /// </summary> + public sealed class PbeUtilities + { + private PbeUtilities() + { + } + + const string Pkcs5S1 = "Pkcs5S1"; + const string Pkcs5S2 = "Pkcs5S2"; + const string Pkcs12 = "Pkcs12"; + const string OpenSsl = "OpenSsl"; + + private static readonly IDictionary algorithms = Platform.CreateHashtable(); + private static readonly IDictionary algorithmType = Platform.CreateHashtable(); + private static readonly IDictionary oids = Platform.CreateHashtable(); + + static PbeUtilities() + { + algorithms["PKCS5SCHEME1"] = "Pkcs5scheme1"; + algorithms["PKCS5SCHEME2"] = "Pkcs5scheme2"; + algorithms[PkcsObjectIdentifiers.IdPbeS2.Id] = "Pkcs5scheme2"; +// algorithms[PkcsObjectIdentifiers.IdPbkdf2.Id] = "Pkcs5scheme2"; + + // FIXME Add support for these? (see Pkcs8Generator) +// algorithms[PkcsObjectIdentifiers.DesEde3Cbc.Id] = "Pkcs5scheme2"; +// algorithms[NistObjectIdentifiers.IdAes128Cbc.Id] = "Pkcs5scheme2"; +// algorithms[NistObjectIdentifiers.IdAes192Cbc.Id] = "Pkcs5scheme2"; +// algorithms[NistObjectIdentifiers.IdAes256Cbc.Id] = "Pkcs5scheme2"; + + algorithms["PBEWITHMD2ANDDES-CBC"] = "PBEwithMD2andDES-CBC"; + algorithms[PkcsObjectIdentifiers.PbeWithMD2AndDesCbc.Id] = "PBEwithMD2andDES-CBC"; + algorithms["PBEWITHMD2ANDRC2-CBC"] = "PBEwithMD2andRC2-CBC"; + algorithms[PkcsObjectIdentifiers.PbeWithMD2AndRC2Cbc.Id] = "PBEwithMD2andRC2-CBC"; + algorithms["PBEWITHMD5ANDDES-CBC"] = "PBEwithMD5andDES-CBC"; + algorithms[PkcsObjectIdentifiers.PbeWithMD5AndDesCbc.Id] = "PBEwithMD5andDES-CBC"; + algorithms["PBEWITHMD5ANDRC2-CBC"] = "PBEwithMD5andRC2-CBC"; + algorithms[PkcsObjectIdentifiers.PbeWithMD5AndRC2Cbc.Id] = "PBEwithMD5andRC2-CBC"; + algorithms["PBEWITHSHA1ANDDES"] = "PBEwithSHA-1andDES-CBC"; + algorithms["PBEWITHSHA-1ANDDES"] = "PBEwithSHA-1andDES-CBC"; + algorithms["PBEWITHSHA1ANDDES-CBC"] = "PBEwithSHA-1andDES-CBC"; + algorithms["PBEWITHSHA-1ANDDES-CBC"] = "PBEwithSHA-1andDES-CBC"; + algorithms[PkcsObjectIdentifiers.PbeWithSha1AndDesCbc.Id] = "PBEwithSHA-1andDES-CBC"; + algorithms["PBEWITHSHA1ANDRC2"] = "PBEwithSHA-1andRC2-CBC"; + algorithms["PBEWITHSHA-1ANDRC2"] = "PBEwithSHA-1andRC2-CBC"; + algorithms["PBEWITHSHA1ANDRC2-CBC"] = "PBEwithSHA-1andRC2-CBC"; + algorithms["PBEWITHSHA-1ANDRC2-CBC"] = "PBEwithSHA-1andRC2-CBC"; + algorithms[PkcsObjectIdentifiers.PbeWithSha1AndRC2Cbc.Id] = "PBEwithSHA-1andRC2-CBC"; + algorithms["PKCS12"] = "Pkcs12"; + algorithms[BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes128_cbc.Id] = "PBEWITHSHAAND128BITAES-CBC-BC"; + algorithms[BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes192_cbc.Id] = "PBEWITHSHAAND192BITAES-CBC-BC"; + algorithms[BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes256_cbc.Id] = "PBEWITHSHAAND256BITAES-CBC-BC"; + algorithms[BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes128_cbc.Id] = "PBEWITHSHA256AND128BITAES-CBC-BC"; + algorithms[BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes192_cbc.Id] = "PBEWITHSHA256AND192BITAES-CBC-BC"; + algorithms[BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes256_cbc.Id] = "PBEWITHSHA256AND256BITAES-CBC-BC"; + algorithms["PBEWITHSHAAND128BITRC4"] = "PBEwithSHA-1and128bitRC4"; + algorithms["PBEWITHSHA1AND128BITRC4"] = "PBEwithSHA-1and128bitRC4"; + algorithms["PBEWITHSHA-1AND128BITRC4"] = "PBEwithSHA-1and128bitRC4"; + algorithms[PkcsObjectIdentifiers.PbeWithShaAnd128BitRC4.Id] = "PBEwithSHA-1and128bitRC4"; + algorithms["PBEWITHSHAAND40BITRC4"] = "PBEwithSHA-1and40bitRC4"; + algorithms["PBEWITHSHA1AND40BITRC4"] = "PBEwithSHA-1and40bitRC4"; + algorithms["PBEWITHSHA-1AND40BITRC4"] = "PBEwithSHA-1and40bitRC4"; + algorithms[PkcsObjectIdentifiers.PbeWithShaAnd40BitRC4.Id] = "PBEwithSHA-1and40bitRC4"; + algorithms["PBEWITHSHAAND3-KEYDESEDE-CBC"] = "PBEwithSHA-1and3-keyDESEDE-CBC"; + algorithms["PBEWITHSHAAND3-KEYTRIPLEDES-CBC"] = "PBEwithSHA-1and3-keyDESEDE-CBC"; + algorithms["PBEWITHSHA1AND3-KEYDESEDE-CBC"] = "PBEwithSHA-1and3-keyDESEDE-CBC"; + algorithms["PBEWITHSHA1AND3-KEYTRIPLEDES-CBC"] = "PBEwithSHA-1and3-keyDESEDE-CBC"; + algorithms["PBEWITHSHA-1AND3-KEYDESEDE-CBC"] = "PBEwithSHA-1and3-keyDESEDE-CBC"; + algorithms["PBEWITHSHA-1AND3-KEYTRIPLEDES-CBC"] = "PBEwithSHA-1and3-keyDESEDE-CBC"; + algorithms[PkcsObjectIdentifiers.PbeWithShaAnd3KeyTripleDesCbc.Id] = "PBEwithSHA-1and3-keyDESEDE-CBC"; + algorithms["PBEWITHSHAAND2-KEYDESEDE-CBC"] = "PBEwithSHA-1and2-keyDESEDE-CBC"; + algorithms["PBEWITHSHAAND2-KEYTRIPLEDES-CBC"] = "PBEwithSHA-1and2-keyDESEDE-CBC"; + algorithms["PBEWITHSHA1AND2-KEYDESEDE-CBC"] = "PBEwithSHA-1and2-keyDESEDE-CBC"; + algorithms["PBEWITHSHA1AND2-KEYTRIPLEDES-CBC"] = "PBEwithSHA-1and2-keyDESEDE-CBC"; + algorithms["PBEWITHSHA-1AND2-KEYDESEDE-CBC"] = "PBEwithSHA-1and2-keyDESEDE-CBC"; + algorithms["PBEWITHSHA-1AND2-KEYTRIPLEDES-CBC"] = "PBEwithSHA-1and2-keyDESEDE-CBC"; + algorithms[PkcsObjectIdentifiers.PbeWithShaAnd2KeyTripleDesCbc.Id] = "PBEwithSHA-1and2-keyDESEDE-CBC"; + algorithms["PBEWITHSHAAND128BITRC2-CBC"] = "PBEwithSHA-1and128bitRC2-CBC"; + algorithms["PBEWITHSHA1AND128BITRC2-CBC"] = "PBEwithSHA-1and128bitRC2-CBC"; + algorithms["PBEWITHSHA-1AND128BITRC2-CBC"] = "PBEwithSHA-1and128bitRC2-CBC"; + algorithms[PkcsObjectIdentifiers.PbeWithShaAnd128BitRC2Cbc.Id] = "PBEwithSHA-1and128bitRC2-CBC"; + algorithms["PBEWITHSHAAND40BITRC2-CBC"] = "PBEwithSHA-1and40bitRC2-CBC"; + algorithms["PBEWITHSHA1AND40BITRC2-CBC"] = "PBEwithSHA-1and40bitRC2-CBC"; + algorithms["PBEWITHSHA-1AND40BITRC2-CBC"] = "PBEwithSHA-1and40bitRC2-CBC"; + algorithms[PkcsObjectIdentifiers.PbewithShaAnd40BitRC2Cbc.Id] = "PBEwithSHA-1and40bitRC2-CBC"; + algorithms["PBEWITHSHAAND128BITAES-CBC-BC"] = "PBEwithSHA-1and128bitAES-CBC-BC"; + algorithms["PBEWITHSHA1AND128BITAES-CBC-BC"] = "PBEwithSHA-1and128bitAES-CBC-BC"; + algorithms["PBEWITHSHA-1AND128BITAES-CBC-BC"] = "PBEwithSHA-1and128bitAES-CBC-BC"; + algorithms["PBEWITHSHAAND192BITAES-CBC-BC"] = "PBEwithSHA-1and192bitAES-CBC-BC"; + algorithms["PBEWITHSHA1AND192BITAES-CBC-BC"] = "PBEwithSHA-1and192bitAES-CBC-BC"; + algorithms["PBEWITHSHA-1AND192BITAES-CBC-BC"] = "PBEwithSHA-1and192bitAES-CBC-BC"; + algorithms["PBEWITHSHAAND256BITAES-CBC-BC"] = "PBEwithSHA-1and256bitAES-CBC-BC"; + algorithms["PBEWITHSHA1AND256BITAES-CBC-BC"] = "PBEwithSHA-1and256bitAES-CBC-BC"; + algorithms["PBEWITHSHA-1AND256BITAES-CBC-BC"] = "PBEwithSHA-1and256bitAES-CBC-BC"; + algorithms["PBEWITHSHA256AND128BITAES-CBC-BC"] = "PBEwithSHA-256and128bitAES-CBC-BC"; + algorithms["PBEWITHSHA-256AND128BITAES-CBC-BC"] = "PBEwithSHA-256and128bitAES-CBC-BC"; + algorithms["PBEWITHSHA256AND192BITAES-CBC-BC"] = "PBEwithSHA-256and192bitAES-CBC-BC"; + algorithms["PBEWITHSHA-256AND192BITAES-CBC-BC"] = "PBEwithSHA-256and192bitAES-CBC-BC"; + algorithms["PBEWITHSHA256AND256BITAES-CBC-BC"] = "PBEwithSHA-256and256bitAES-CBC-BC"; + algorithms["PBEWITHSHA-256AND256BITAES-CBC-BC"] = "PBEwithSHA-256and256bitAES-CBC-BC"; + algorithms["PBEWITHSHAANDIDEA"] = "PBEwithSHA-1andIDEA-CBC"; + algorithms["PBEWITHSHAANDIDEA-CBC"] = "PBEwithSHA-1andIDEA-CBC"; + algorithms["PBEWITHSHAANDTWOFISH"] = "PBEwithSHA-1andTWOFISH-CBC"; + algorithms["PBEWITHSHAANDTWOFISH-CBC"] = "PBEwithSHA-1andTWOFISH-CBC"; + algorithms["PBEWITHHMACSHA1"] = "PBEwithHmacSHA-1"; + algorithms["PBEWITHHMACSHA-1"] = "PBEwithHmacSHA-1"; + algorithms[OiwObjectIdentifiers.IdSha1.Id] = "PBEwithHmacSHA-1"; + algorithms["PBEWITHHMACSHA224"] = "PBEwithHmacSHA-224"; + algorithms["PBEWITHHMACSHA-224"] = "PBEwithHmacSHA-224"; + algorithms[NistObjectIdentifiers.IdSha224.Id] = "PBEwithHmacSHA-224"; + algorithms["PBEWITHHMACSHA256"] = "PBEwithHmacSHA-256"; + algorithms["PBEWITHHMACSHA-256"] = "PBEwithHmacSHA-256"; + algorithms[NistObjectIdentifiers.IdSha256.Id] = "PBEwithHmacSHA-256"; + algorithms["PBEWITHHMACRIPEMD128"] = "PBEwithHmacRipeMD128"; + algorithms[TeleTrusTObjectIdentifiers.RipeMD128.Id] = "PBEwithHmacRipeMD128"; + algorithms["PBEWITHHMACRIPEMD160"] = "PBEwithHmacRipeMD160"; + algorithms[TeleTrusTObjectIdentifiers.RipeMD160.Id] = "PBEwithHmacRipeMD160"; + algorithms["PBEWITHHMACRIPEMD256"] = "PBEwithHmacRipeMD256"; + algorithms[TeleTrusTObjectIdentifiers.RipeMD256.Id] = "PBEwithHmacRipeMD256"; + algorithms["PBEWITHHMACTIGER"] = "PBEwithHmacTiger"; + + algorithms["PBEWITHMD5AND128BITAES-CBC-OPENSSL"] = "PBEwithMD5and128bitAES-CBC-OpenSSL"; + algorithms["PBEWITHMD5AND192BITAES-CBC-OPENSSL"] = "PBEwithMD5and192bitAES-CBC-OpenSSL"; + algorithms["PBEWITHMD5AND256BITAES-CBC-OPENSSL"] = "PBEwithMD5and256bitAES-CBC-OpenSSL"; + + algorithmType["Pkcs5scheme1"] = Pkcs5S1; + algorithmType["Pkcs5scheme2"] = Pkcs5S2; + algorithmType["PBEwithMD2andDES-CBC"] = Pkcs5S1; + algorithmType["PBEwithMD2andRC2-CBC"] = Pkcs5S1; + algorithmType["PBEwithMD5andDES-CBC"] = Pkcs5S1; + algorithmType["PBEwithMD5andRC2-CBC"] = Pkcs5S1; + algorithmType["PBEwithSHA-1andDES-CBC"] = Pkcs5S1; + algorithmType["PBEwithSHA-1andRC2-CBC"] = Pkcs5S1; + algorithmType["Pkcs12"] = Pkcs12; + algorithmType["PBEwithSHA-1and128bitRC4"] = Pkcs12; + algorithmType["PBEwithSHA-1and40bitRC4"] = Pkcs12; + algorithmType["PBEwithSHA-1and3-keyDESEDE-CBC"] = Pkcs12; + algorithmType["PBEwithSHA-1and2-keyDESEDE-CBC"] = Pkcs12; + algorithmType["PBEwithSHA-1and128bitRC2-CBC"] = Pkcs12; + algorithmType["PBEwithSHA-1and40bitRC2-CBC"] = Pkcs12; + algorithmType["PBEwithSHA-1and128bitAES-CBC-BC"] = Pkcs12; + algorithmType["PBEwithSHA-1and192bitAES-CBC-BC"] = Pkcs12; + algorithmType["PBEwithSHA-1and256bitAES-CBC-BC"] = Pkcs12; + algorithmType["PBEwithSHA-256and128bitAES-CBC-BC"] = Pkcs12; + algorithmType["PBEwithSHA-256and192bitAES-CBC-BC"] = Pkcs12; + algorithmType["PBEwithSHA-256and256bitAES-CBC-BC"] = Pkcs12; + algorithmType["PBEwithSHA-1andIDEA-CBC"] = Pkcs12; + algorithmType["PBEwithSHA-1andTWOFISH-CBC"] = Pkcs12; + algorithmType["PBEwithHmacSHA-1"] = Pkcs12; + algorithmType["PBEwithHmacSHA-224"] = Pkcs12; + algorithmType["PBEwithHmacSHA-256"] = Pkcs12; + algorithmType["PBEwithHmacRipeMD128"] = Pkcs12; + algorithmType["PBEwithHmacRipeMD160"] = Pkcs12; + algorithmType["PBEwithHmacRipeMD256"] = Pkcs12; + algorithmType["PBEwithHmacTiger"] = Pkcs12; + + algorithmType["PBEwithMD5and128bitAES-CBC-OpenSSL"] = OpenSsl; + algorithmType["PBEwithMD5and192bitAES-CBC-OpenSSL"] = OpenSsl; + algorithmType["PBEwithMD5and256bitAES-CBC-OpenSSL"] = OpenSsl; + + oids["PBEwithMD2andDES-CBC"] = PkcsObjectIdentifiers.PbeWithMD2AndDesCbc; + oids["PBEwithMD2andRC2-CBC"] = PkcsObjectIdentifiers.PbeWithMD2AndRC2Cbc; + oids["PBEwithMD5andDES-CBC"] = PkcsObjectIdentifiers.PbeWithMD5AndDesCbc; + oids["PBEwithMD5andRC2-CBC"] = PkcsObjectIdentifiers.PbeWithMD5AndRC2Cbc; + oids["PBEwithSHA-1andDES-CBC"] = PkcsObjectIdentifiers.PbeWithSha1AndDesCbc; + oids["PBEwithSHA-1andRC2-CBC"] = PkcsObjectIdentifiers.PbeWithSha1AndRC2Cbc; + oids["PBEwithSHA-1and128bitRC4"] = PkcsObjectIdentifiers.PbeWithShaAnd128BitRC4; + oids["PBEwithSHA-1and40bitRC4"] = PkcsObjectIdentifiers.PbeWithShaAnd40BitRC4; + oids["PBEwithSHA-1and3-keyDESEDE-CBC"] = PkcsObjectIdentifiers.PbeWithShaAnd3KeyTripleDesCbc; + oids["PBEwithSHA-1and2-keyDESEDE-CBC"] = PkcsObjectIdentifiers.PbeWithShaAnd2KeyTripleDesCbc; + oids["PBEwithSHA-1and128bitRC2-CBC"] = PkcsObjectIdentifiers.PbeWithShaAnd128BitRC2Cbc; + oids["PBEwithSHA-1and40bitRC2-CBC"] = PkcsObjectIdentifiers.PbewithShaAnd40BitRC2Cbc; + oids["PBEwithHmacSHA-1"] = OiwObjectIdentifiers.IdSha1; + oids["PBEwithHmacSHA-224"] = NistObjectIdentifiers.IdSha224; + oids["PBEwithHmacSHA-256"] = NistObjectIdentifiers.IdSha256; + oids["PBEwithHmacRipeMD128"] = TeleTrusTObjectIdentifiers.RipeMD128; + oids["PBEwithHmacRipeMD160"] = TeleTrusTObjectIdentifiers.RipeMD160; + oids["PBEwithHmacRipeMD256"] = TeleTrusTObjectIdentifiers.RipeMD256; + oids["Pkcs5scheme2"] = PkcsObjectIdentifiers.IdPbeS2; + } + + static PbeParametersGenerator MakePbeGenerator( + string type, + IDigest digest, + byte[] key, + byte[] salt, + int iterationCount) + { + PbeParametersGenerator generator; + + if (type.Equals(Pkcs5S1)) + { + generator = new Pkcs5S1ParametersGenerator(digest); + } + else if (type.Equals(Pkcs5S2)) + { + generator = new Pkcs5S2ParametersGenerator(); + } + else if (type.Equals(Pkcs12)) + { + generator = new Pkcs12ParametersGenerator(digest); + } + else if (type.Equals(OpenSsl)) + { + generator = new OpenSslPbeParametersGenerator(); + } + else + { + throw new ArgumentException("Unknown PBE type: " + type, "type"); + } + + generator.Init(key, salt, iterationCount); + return generator; + } + + /// <summary> + /// Returns a ObjectIdentifier for a give encoding. + /// </summary> + /// <param name="mechanism">A string representation of the encoding.</param> + /// <returns>A DerObjectIdentifier, null if the Oid is not available.</returns> + public static DerObjectIdentifier GetObjectIdentifier( + string mechanism) + { + mechanism = (string) algorithms[mechanism.ToUpperInvariant()]; + if (mechanism != null) + { + return (DerObjectIdentifier)oids[mechanism]; + } + return null; + } + + public static ICollection Algorithms + { + get { return oids.Keys; } + } + + public static bool IsPkcs12( + string algorithm) + { + string mechanism = (string) algorithms[algorithm.ToUpperInvariant()]; + + return mechanism != null && Pkcs12.Equals(algorithmType[mechanism]); + } + + public static bool IsPkcs5Scheme1( + string algorithm) + { + string mechanism = (string)algorithms[algorithm.ToUpperInvariant()]; + + return mechanism != null && Pkcs5S1.Equals(algorithmType[mechanism]); + } + + public static bool IsPkcs5Scheme2( + string algorithm) + { + string mechanism = (string)algorithms[algorithm.ToUpperInvariant()]; + + return mechanism != null && Pkcs5S2.Equals(algorithmType[mechanism]); + } + + public static bool IsOpenSsl( + string algorithm) + { + string mechanism = (string)algorithms[algorithm.ToUpperInvariant()]; + + return mechanism != null && OpenSsl.Equals(algorithmType[mechanism]); + } + + public static bool IsPbeAlgorithm( + string algorithm) + { + string mechanism = (string)algorithms[algorithm.ToUpperInvariant()]; + + return mechanism != null && algorithmType[mechanism] != null; + } + + public static Asn1Encodable GenerateAlgorithmParameters( + DerObjectIdentifier algorithmOid, + byte[] salt, + int iterationCount) + { + return GenerateAlgorithmParameters(algorithmOid.Id, salt, iterationCount); + } + + public static Asn1Encodable GenerateAlgorithmParameters( + string algorithm, + byte[] salt, + int iterationCount) + { + if (IsPkcs12(algorithm)) + { + return new Pkcs12PbeParams(salt, iterationCount); + } + else if (IsPkcs5Scheme2(algorithm)) + { + return new Pbkdf2Params(salt, iterationCount); + } + else + { + return new PbeParameter(salt, iterationCount); + } + } + + public static ICipherParameters GenerateCipherParameters( + DerObjectIdentifier algorithmOid, + char[] password, + Asn1Encodable pbeParameters) + { + return GenerateCipherParameters(algorithmOid.Id, password, false, pbeParameters); + } + + public static ICipherParameters GenerateCipherParameters( + DerObjectIdentifier algorithmOid, + char[] password, + bool wrongPkcs12Zero, + Asn1Encodable pbeParameters) + { + return GenerateCipherParameters(algorithmOid.Id, password, wrongPkcs12Zero, pbeParameters); + } + + public static ICipherParameters GenerateCipherParameters( + AlgorithmIdentifier algID, + char[] password) + { + return GenerateCipherParameters(algID.ObjectID.Id, password, false, algID.Parameters); + } + + public static ICipherParameters GenerateCipherParameters( + AlgorithmIdentifier algID, + char[] password, + bool wrongPkcs12Zero) + { + return GenerateCipherParameters(algID.ObjectID.Id, password, wrongPkcs12Zero, algID.Parameters); + } + + public static ICipherParameters GenerateCipherParameters( + string algorithm, + char[] password, + Asn1Encodable pbeParameters) + { + return GenerateCipherParameters(algorithm, password, false, pbeParameters); + } + + public static ICipherParameters GenerateCipherParameters( + string algorithm, + char[] password, + bool wrongPkcs12Zero, + Asn1Encodable pbeParameters) + { + string mechanism = (string) algorithms[algorithm.ToUpperInvariant()]; + + byte[] keyBytes = null; + byte[] salt = null; + int iterationCount = 0; + + if (IsPkcs12(mechanism)) + { + Pkcs12PbeParams pbeParams = Pkcs12PbeParams.GetInstance(pbeParameters); + salt = pbeParams.GetIV(); + iterationCount = pbeParams.Iterations.IntValue; + keyBytes = PbeParametersGenerator.Pkcs12PasswordToBytes(password, wrongPkcs12Zero); + } + else if (IsPkcs5Scheme2(mechanism)) + { + // See below + } + else + { + PbeParameter pbeParams = PbeParameter.GetInstance(pbeParameters); + salt = pbeParams.GetSalt(); + iterationCount = pbeParams.IterationCount.IntValue; + keyBytes = PbeParametersGenerator.Pkcs5PasswordToBytes(password); + } + + ICipherParameters parameters = null; + + if (IsPkcs5Scheme2(mechanism)) + { + PbeS2Parameters s2p = PbeS2Parameters.GetInstance(pbeParameters.ToAsn1Object()); + AlgorithmIdentifier encScheme = s2p.EncryptionScheme; + DerObjectIdentifier encOid = encScheme.ObjectID; + Asn1Object encParams = encScheme.Parameters.ToAsn1Object(); + + // TODO What about s2p.KeyDerivationFunc.ObjectID? + Pbkdf2Params pbeParams = Pbkdf2Params.GetInstance(s2p.KeyDerivationFunc.Parameters.ToAsn1Object()); + + byte[] iv; + if (encOid.Equals(PkcsObjectIdentifiers.RC2Cbc)) // PKCS5.B.2.3 + { + RC2CbcParameter rc2Params = RC2CbcParameter.GetInstance(encParams); + iv = rc2Params.GetIV(); + } + else + { + iv = Asn1OctetString.GetInstance(encParams).GetOctets(); + } + + salt = pbeParams.GetSalt(); + iterationCount = pbeParams.IterationCount.IntValue; + keyBytes = PbeParametersGenerator.Pkcs5PasswordToBytes(password); + + int keyLength = pbeParams.KeyLength != null + ? pbeParams.KeyLength.IntValue * 8 + : GeneratorUtilities.GetDefaultKeySize(encOid); + + PbeParametersGenerator gen = MakePbeGenerator( + (string)algorithmType[mechanism], null, keyBytes, salt, iterationCount); + + parameters = gen.GenerateDerivedParameters(encOid.Id, keyLength); + + if (iv != null) + { + // FIXME? OpenSSL weirdness with IV of zeros (for ECB keys?) + if (Arrays.AreEqual(iv, new byte[iv.Length])) + { + //Console.Error.Write("***** IV all 0 (length " + iv.Length + ") *****"); + } + else + { + parameters = new ParametersWithIV(parameters, iv); + } + } + } + else if (mechanism.StartsWith("PBEwithSHA-1")) + { + PbeParametersGenerator generator = MakePbeGenerator( + (string) algorithmType[mechanism], new Sha1Digest(), keyBytes, salt, iterationCount); + + if (mechanism.Equals("PBEwithSHA-1and128bitRC4")) + { + parameters = generator.GenerateDerivedParameters("RC4", 128); + } + else if (mechanism.Equals("PBEwithSHA-1and40bitRC4")) + { + parameters = generator.GenerateDerivedParameters("RC4", 40); + } + else if (mechanism.Equals("PBEwithSHA-1and3-keyDESEDE-CBC")) + { + parameters = generator.GenerateDerivedParameters("DESEDE", 192, 64); + } + else if (mechanism.Equals("PBEwithSHA-1and2-keyDESEDE-CBC")) + { + parameters = generator.GenerateDerivedParameters("DESEDE", 128, 64); + } + else if (mechanism.Equals("PBEwithSHA-1and128bitRC2-CBC")) + { + parameters = generator.GenerateDerivedParameters("RC2", 128, 64); + } + else if (mechanism.Equals("PBEwithSHA-1and40bitRC2-CBC")) + { + parameters = generator.GenerateDerivedParameters("RC2", 40, 64); + } + else if (mechanism.Equals("PBEwithSHA-1andDES-CBC")) + { + parameters = generator.GenerateDerivedParameters("DES", 64, 64); + } + else if (mechanism.Equals("PBEwithSHA-1andRC2-CBC")) + { + parameters = generator.GenerateDerivedParameters("RC2", 64, 64); + } + else if (mechanism.Equals("PBEwithSHA-1and128bitAES-CBC-BC")) + { + parameters = generator.GenerateDerivedParameters("AES", 128, 128); + } + else if (mechanism.Equals("PBEwithSHA-1and192bitAES-CBC-BC")) + { + parameters = generator.GenerateDerivedParameters("AES", 192, 128); + } + else if (mechanism.Equals("PBEwithSHA-1and256bitAES-CBC-BC")) + { + parameters = generator.GenerateDerivedParameters("AES", 256, 128); + } + } + else if (mechanism.StartsWith("PBEwithSHA-256")) + { + PbeParametersGenerator generator = MakePbeGenerator( + (string) algorithmType[mechanism], new Sha256Digest(), keyBytes, salt, iterationCount); + + if (mechanism.Equals("PBEwithSHA-256and128bitAES-CBC-BC")) + { + parameters = generator.GenerateDerivedParameters("AES", 128, 128); + } + else if (mechanism.Equals("PBEwithSHA-256and192bitAES-CBC-BC")) + { + parameters = generator.GenerateDerivedParameters("AES", 192, 128); + } + else if (mechanism.Equals("PBEwithSHA-256and256bitAES-CBC-BC")) + { + parameters = generator.GenerateDerivedParameters("AES", 256, 128); + } + } + else if (mechanism.StartsWith("PBEwithMD5")) + { + PbeParametersGenerator generator = MakePbeGenerator( + (string)algorithmType[mechanism], new MD5Digest(), keyBytes, salt, iterationCount); + + if (mechanism.Equals("PBEwithMD5andDES-CBC")) + { + parameters = generator.GenerateDerivedParameters("DES", 64, 64); + } + else if (mechanism.Equals("PBEwithMD5andRC2-CBC")) + { + parameters = generator.GenerateDerivedParameters("RC2", 64, 64); + } + else if (mechanism.Equals("PBEwithMD5and128bitAES-CBC-OpenSSL")) + { + parameters = generator.GenerateDerivedParameters("AES", 128, 128); + } + else if (mechanism.Equals("PBEwithMD5and192bitAES-CBC-OpenSSL")) + { + parameters = generator.GenerateDerivedParameters("AES", 192, 128); + } + else if (mechanism.Equals("PBEwithMD5and256bitAES-CBC-OpenSSL")) + { + parameters = generator.GenerateDerivedParameters("AES", 256, 128); + } + } + else if (mechanism.StartsWith("PBEwithMD2")) + { + PbeParametersGenerator generator = MakePbeGenerator( + (string)algorithmType[mechanism], new MD2Digest(), keyBytes, salt, iterationCount); + if (mechanism.Equals("PBEwithMD2andDES-CBC")) + { + parameters = generator.GenerateDerivedParameters("DES", 64, 64); + } + else if (mechanism.Equals("PBEwithMD2andRC2-CBC")) + { + parameters = generator.GenerateDerivedParameters("RC2", 64, 64); + } + } + else if (mechanism.StartsWith("PBEwithHmac")) + { + string digestName = mechanism.Substring("PBEwithHmac".Length); + IDigest digest = DigestUtilities.GetDigest(digestName); + + PbeParametersGenerator generator = MakePbeGenerator( + (string) algorithmType[mechanism], digest, keyBytes, salt, iterationCount); + + int bitLen = digest.GetDigestSize() * 8; + parameters = generator.GenerateDerivedMacParameters(bitLen); + } + + Array.Clear(keyBytes, 0, keyBytes.Length); + + return FixDesParity(mechanism, parameters); + } + + public static object CreateEngine( + DerObjectIdentifier algorithmOid) + { + return CreateEngine(algorithmOid.Id); + } + + public static object CreateEngine( + AlgorithmIdentifier algID) + { + string algorithm = algID.ObjectID.Id; + + if (IsPkcs5Scheme2(algorithm)) + { + PbeS2Parameters s2p = PbeS2Parameters.GetInstance(algID.Parameters.ToAsn1Object()); + AlgorithmIdentifier encScheme = s2p.EncryptionScheme; + return CipherUtilities.GetCipher(encScheme.ObjectID); + } + + return CreateEngine(algorithm); + } + + public static object CreateEngine( + string algorithm) + { + string mechanism = (string)algorithms[algorithm.ToUpperInvariant()]; + + if (mechanism.StartsWith("PBEwithHmac")) + { + string digestName = mechanism.Substring("PBEwithHmac".Length); + + return MacUtilities.GetMac("HMAC/" + digestName); + } + + if (mechanism.StartsWith("PBEwithMD2") + || mechanism.StartsWith("PBEwithMD5") + || mechanism.StartsWith("PBEwithSHA-1")) + { + if (mechanism.EndsWith("RC2-CBC")) + { + return CipherUtilities.GetCipher("RC2/CBC"); + } + + if (mechanism.EndsWith("RC4")) + { + return CipherUtilities.GetCipher("RC4"); + } + + if (mechanism.EndsWith("DES-CBC")) + { + return CipherUtilities.GetCipher("DES/CBC"); + } + + if (mechanism.EndsWith("DESEDE-CBC")) + { + return CipherUtilities.GetCipher("DESEDE/CBC"); + } + } + + return null; + } + + public static string GetEncodingName( + DerObjectIdentifier oid) + { + return (string) algorithms[oid.Id]; + } + + private static ICipherParameters FixDesParity(string mechanism, ICipherParameters parameters) + { + if (!mechanism.EndsWith("DES-CBC") & !mechanism.EndsWith("DESEDE-CBC")) + { + return parameters; + } + + if (parameters is ParametersWithIV) + { + ParametersWithIV ivParams = (ParametersWithIV)parameters; + return new ParametersWithIV(FixDesParity(mechanism, ivParams.Parameters), ivParams.GetIV()); + } + + KeyParameter kParam = (KeyParameter)parameters; + byte[] keyBytes = kParam.GetKey(); + DesParameters.SetOddParity(keyBytes); + return new KeyParameter(keyBytes); + } + } +} diff --git a/Crypto/src/security/PrivateKeyFactory.cs b/Crypto/src/security/PrivateKeyFactory.cs new file mode 100644
index 000000000..5ebebd55a --- /dev/null +++ b/Crypto/src/security/PrivateKeyFactory.cs
@@ -0,0 +1,221 @@ +using System; +using System.Collections; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Sec; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Pkcs; + +namespace Org.BouncyCastle.Security +{ + public sealed class PrivateKeyFactory + { + private PrivateKeyFactory() + { + } + + public static AsymmetricKeyParameter CreateKey( + byte[] privateKeyInfoData) + { + return CreateKey( + PrivateKeyInfo.GetInstance( + Asn1Object.FromByteArray(privateKeyInfoData))); + } + + public static AsymmetricKeyParameter CreateKey( + Stream inStr) + { + return CreateKey( + PrivateKeyInfo.GetInstance( + Asn1Object.FromStream(inStr))); + } + + public static AsymmetricKeyParameter CreateKey( + PrivateKeyInfo keyInfo) + { + AlgorithmIdentifier algID = keyInfo.AlgorithmID; + DerObjectIdentifier algOid = algID.ObjectID; + + // TODO See RSAUtil.isRsaOid in Java build + if (algOid.Equals(PkcsObjectIdentifiers.RsaEncryption) + || algOid.Equals(X509ObjectIdentifiers.IdEARsa) + || algOid.Equals(PkcsObjectIdentifiers.IdRsassaPss) + || algOid.Equals(PkcsObjectIdentifiers.IdRsaesOaep)) + { + RsaPrivateKeyStructure keyStructure = new RsaPrivateKeyStructure( + Asn1Sequence.GetInstance(keyInfo.PrivateKey)); + + return new RsaPrivateCrtKeyParameters( + keyStructure.Modulus, + keyStructure.PublicExponent, + keyStructure.PrivateExponent, + keyStructure.Prime1, + keyStructure.Prime2, + keyStructure.Exponent1, + keyStructure.Exponent2, + keyStructure.Coefficient); + } + // TODO? +// else if (algOid.Equals(X9ObjectIdentifiers.DHPublicNumber)) + else if (algOid.Equals(PkcsObjectIdentifiers.DhKeyAgreement)) + { + DHParameter para = new DHParameter( + Asn1Sequence.GetInstance(algID.Parameters.ToAsn1Object())); + DerInteger derX = (DerInteger)keyInfo.PrivateKey; + + BigInteger lVal = para.L; + int l = lVal == null ? 0 : lVal.IntValue; + DHParameters dhParams = new DHParameters(para.P, para.G, null, l); + + return new DHPrivateKeyParameters(derX.Value, dhParams, algOid); + } + else if (algOid.Equals(OiwObjectIdentifiers.ElGamalAlgorithm)) + { + ElGamalParameter para = new ElGamalParameter( + Asn1Sequence.GetInstance(algID.Parameters.ToAsn1Object())); + DerInteger derX = (DerInteger)keyInfo.PrivateKey; + + return new ElGamalPrivateKeyParameters( + derX.Value, + new ElGamalParameters(para.P, para.G)); + } + else if (algOid.Equals(X9ObjectIdentifiers.IdDsa)) + { + DerInteger derX = (DerInteger) keyInfo.PrivateKey; + Asn1Encodable ae = algID.Parameters; + + DsaParameters parameters = null; + if (ae != null) + { + DsaParameter para = DsaParameter.GetInstance(ae.ToAsn1Object()); + parameters = new DsaParameters(para.P, para.Q, para.G); + } + + return new DsaPrivateKeyParameters(derX.Value, parameters); + } + else if (algOid.Equals(X9ObjectIdentifiers.IdECPublicKey)) + { + X962Parameters para = new X962Parameters(algID.Parameters.ToAsn1Object()); + + X9ECParameters x9; + if (para.IsNamedCurve) + { + x9 = ECKeyPairGenerator.FindECCurveByOid((DerObjectIdentifier)para.Parameters); + } + else + { + x9 = new X9ECParameters((Asn1Sequence)para.Parameters); + } + + ECPrivateKeyStructure ec = new ECPrivateKeyStructure( + Asn1Sequence.GetInstance(keyInfo.PrivateKey)); + BigInteger d = ec.GetKey(); + + if (para.IsNamedCurve) + { + return new ECPrivateKeyParameters("EC", d, (DerObjectIdentifier)para.Parameters); + } + + ECDomainParameters dParams = new ECDomainParameters(x9.Curve, x9.G, x9.N, x9.H, x9.GetSeed()); + return new ECPrivateKeyParameters(d, dParams); + } + else if (algOid.Equals(CryptoProObjectIdentifiers.GostR3410x2001)) + { + Gost3410PublicKeyAlgParameters gostParams = new Gost3410PublicKeyAlgParameters( + Asn1Sequence.GetInstance(algID.Parameters.ToAsn1Object())); + + ECPrivateKeyStructure ec = new ECPrivateKeyStructure( + Asn1Sequence.GetInstance(keyInfo.PrivateKey)); + + ECDomainParameters ecP = ECGost3410NamedCurves.GetByOid(gostParams.PublicKeyParamSet); + + if (ecP == null) + return null; + + return new ECPrivateKeyParameters("ECGOST3410", ec.GetKey(), gostParams.PublicKeyParamSet); + } + else if (algOid.Equals(CryptoProObjectIdentifiers.GostR3410x94)) + { + Gost3410PublicKeyAlgParameters gostParams = new Gost3410PublicKeyAlgParameters( + Asn1Sequence.GetInstance(algID.Parameters.ToAsn1Object())); + + DerOctetString derX = (DerOctetString) keyInfo.PrivateKey; + byte[] keyEnc = derX.GetOctets(); + byte[] keyBytes = new byte[keyEnc.Length]; + + for (int i = 0; i != keyEnc.Length; i++) + { + keyBytes[i] = keyEnc[keyEnc.Length - 1 - i]; // was little endian + } + + BigInteger x = new BigInteger(1, keyBytes); + + return new Gost3410PrivateKeyParameters(x, gostParams.PublicKeyParamSet); + } + else + { + throw new SecurityUtilityException("algorithm identifier in key not recognised"); + } + } + + public static AsymmetricKeyParameter DecryptKey( + char[] passPhrase, + EncryptedPrivateKeyInfo encInfo) + { + return CreateKey(PrivateKeyInfoFactory.CreatePrivateKeyInfo(passPhrase, encInfo)); + } + + public static AsymmetricKeyParameter DecryptKey( + char[] passPhrase, + byte[] encryptedPrivateKeyInfoData) + { + return DecryptKey(passPhrase, Asn1Object.FromByteArray(encryptedPrivateKeyInfoData)); + } + + public static AsymmetricKeyParameter DecryptKey( + char[] passPhrase, + Stream encryptedPrivateKeyInfoStream) + { + return DecryptKey(passPhrase, Asn1Object.FromStream(encryptedPrivateKeyInfoStream)); + } + + private static AsymmetricKeyParameter DecryptKey( + char[] passPhrase, + Asn1Object asn1Object) + { + return DecryptKey(passPhrase, EncryptedPrivateKeyInfo.GetInstance(asn1Object)); + } + + public static byte[] EncryptKey( + DerObjectIdentifier algorithm, + char[] passPhrase, + byte[] salt, + int iterationCount, + AsymmetricKeyParameter key) + { + return EncryptedPrivateKeyInfoFactory.CreateEncryptedPrivateKeyInfo( + algorithm, passPhrase, salt, iterationCount, key).GetEncoded(); + } + + public static byte[] EncryptKey( + string algorithm, + char[] passPhrase, + byte[] salt, + int iterationCount, + AsymmetricKeyParameter key) + { + return EncryptedPrivateKeyInfoFactory.CreateEncryptedPrivateKeyInfo( + algorithm, passPhrase, salt, iterationCount, key).GetEncoded(); + } + } +} diff --git a/Crypto/src/security/PublicKeyFactory.cs b/Crypto/src/security/PublicKeyFactory.cs new file mode 100644
index 000000000..c15e24a41 --- /dev/null +++ b/Crypto/src/security/PublicKeyFactory.cs
@@ -0,0 +1,253 @@ +using System; +using System.Collections; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Sec; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; + +namespace Org.BouncyCastle.Security +{ + public sealed class PublicKeyFactory + { + private PublicKeyFactory() + { + } + + public static AsymmetricKeyParameter CreateKey( + byte[] keyInfoData) + { + return CreateKey( + SubjectPublicKeyInfo.GetInstance( + Asn1Object.FromByteArray(keyInfoData))); + } + + public static AsymmetricKeyParameter CreateKey( + Stream inStr) + { + return CreateKey( + SubjectPublicKeyInfo.GetInstance( + Asn1Object.FromStream(inStr))); + } + + public static AsymmetricKeyParameter CreateKey( + SubjectPublicKeyInfo keyInfo) + { + AlgorithmIdentifier algID = keyInfo.AlgorithmID; + DerObjectIdentifier algOid = algID.ObjectID; + + // TODO See RSAUtil.isRsaOid in Java build + if (algOid.Equals(PkcsObjectIdentifiers.RsaEncryption) + || algOid.Equals(X509ObjectIdentifiers.IdEARsa) + || algOid.Equals(PkcsObjectIdentifiers.IdRsassaPss) + || algOid.Equals(PkcsObjectIdentifiers.IdRsaesOaep)) + { + RsaPublicKeyStructure pubKey = RsaPublicKeyStructure.GetInstance( + keyInfo.GetPublicKey()); + + return new RsaKeyParameters(false, pubKey.Modulus, pubKey.PublicExponent); + } + else if (algOid.Equals(X9ObjectIdentifiers.DHPublicNumber)) + { + Asn1Sequence seq = Asn1Sequence.GetInstance(algID.Parameters.ToAsn1Object()); + + DHPublicKey dhPublicKey = DHPublicKey.GetInstance(keyInfo.GetPublicKey()); + + BigInteger y = dhPublicKey.Y.Value; + + if (IsPkcsDHParam(seq)) + return ReadPkcsDHParam(algOid, y, seq); + + DHDomainParameters dhParams = DHDomainParameters.GetInstance(seq); + + BigInteger p = dhParams.P.Value; + BigInteger g = dhParams.G.Value; + BigInteger q = dhParams.Q.Value; + + BigInteger j = null; + if (dhParams.J != null) + { + j = dhParams.J.Value; + } + + DHValidationParameters validation = null; + DHValidationParms dhValidationParms = dhParams.ValidationParms; + if (dhValidationParms != null) + { + byte[] seed = dhValidationParms.Seed.GetBytes(); + BigInteger pgenCounter = dhValidationParms.PgenCounter.Value; + + // TODO Check pgenCounter size? + + validation = new DHValidationParameters(seed, pgenCounter.IntValue); + } + + return new DHPublicKeyParameters(y, new DHParameters(p, g, q, j, validation)); + } + else if (algOid.Equals(PkcsObjectIdentifiers.DhKeyAgreement)) + { + Asn1Sequence seq = Asn1Sequence.GetInstance(algID.Parameters.ToAsn1Object()); + + DerInteger derY = (DerInteger) keyInfo.GetPublicKey(); + + return ReadPkcsDHParam(algOid, derY.Value, seq); + } + else if (algOid.Equals(OiwObjectIdentifiers.ElGamalAlgorithm)) + { + ElGamalParameter para = new ElGamalParameter( + Asn1Sequence.GetInstance(algID.Parameters.ToAsn1Object())); + DerInteger derY = (DerInteger) keyInfo.GetPublicKey(); + + return new ElGamalPublicKeyParameters( + derY.Value, + new ElGamalParameters(para.P, para.G)); + } + else if (algOid.Equals(X9ObjectIdentifiers.IdDsa) + || algOid.Equals(OiwObjectIdentifiers.DsaWithSha1)) + { + DerInteger derY = (DerInteger) keyInfo.GetPublicKey(); + Asn1Encodable ae = algID.Parameters; + + DsaParameters parameters = null; + if (ae != null) + { + DsaParameter para = DsaParameter.GetInstance(ae.ToAsn1Object()); + parameters = new DsaParameters(para.P, para.Q, para.G); + } + + return new DsaPublicKeyParameters(derY.Value, parameters); + } + else if (algOid.Equals(X9ObjectIdentifiers.IdECPublicKey)) + { + X962Parameters para = new X962Parameters(algID.Parameters.ToAsn1Object()); + + X9ECParameters x9; + if (para.IsNamedCurve) + { + x9 = ECKeyPairGenerator.FindECCurveByOid((DerObjectIdentifier)para.Parameters); + } + else + { + x9 = new X9ECParameters((Asn1Sequence)para.Parameters); + } + + Asn1OctetString key = new DerOctetString(keyInfo.PublicKeyData.GetBytes()); + X9ECPoint derQ = new X9ECPoint(x9.Curve, key); + ECPoint q = derQ.Point; + + if (para.IsNamedCurve) + { + return new ECPublicKeyParameters("EC", q, (DerObjectIdentifier)para.Parameters); + } + + ECDomainParameters dParams = new ECDomainParameters(x9.Curve, x9.G, x9.N, x9.H, x9.GetSeed()); + return new ECPublicKeyParameters(q, dParams); + } + else if (algOid.Equals(CryptoProObjectIdentifiers.GostR3410x2001)) + { + Gost3410PublicKeyAlgParameters gostParams = new Gost3410PublicKeyAlgParameters( + (Asn1Sequence) algID.Parameters); + + Asn1OctetString key; + try + { + key = (Asn1OctetString) keyInfo.GetPublicKey(); + } + catch (IOException) + { + throw new ArgumentException("invalid info structure in GOST3410 public key"); + } + + byte[] keyEnc = key.GetOctets(); + byte[] x = new byte[32]; + byte[] y = new byte[32]; + + for (int i = 0; i != y.Length; i++) + { + x[i] = keyEnc[32 - 1 - i]; + } + + for (int i = 0; i != x.Length; i++) + { + y[i] = keyEnc[64 - 1 - i]; + } + + ECDomainParameters ecP = ECGost3410NamedCurves.GetByOid(gostParams.PublicKeyParamSet); + + if (ecP == null) + return null; + + ECPoint q = ecP.Curve.CreatePoint(new BigInteger(1, x), new BigInteger(1, y), false); + + return new ECPublicKeyParameters("ECGOST3410", q, gostParams.PublicKeyParamSet); + } + else if (algOid.Equals(CryptoProObjectIdentifiers.GostR3410x94)) + { + Gost3410PublicKeyAlgParameters algParams = new Gost3410PublicKeyAlgParameters( + (Asn1Sequence) algID.Parameters); + + DerOctetString derY; + try + { + derY = (DerOctetString) keyInfo.GetPublicKey(); + } + catch (IOException) + { + throw new ArgumentException("invalid info structure in GOST3410 public key"); + } + + byte[] keyEnc = derY.GetOctets(); + byte[] keyBytes = new byte[keyEnc.Length]; + + for (int i = 0; i != keyEnc.Length; i++) + { + keyBytes[i] = keyEnc[keyEnc.Length - 1 - i]; // was little endian + } + + BigInteger y = new BigInteger(1, keyBytes); + + return new Gost3410PublicKeyParameters(y, algParams.PublicKeyParamSet); + } + else + { + throw new SecurityUtilityException("algorithm identifier in key not recognised: " + algOid); + } + } + + private static bool IsPkcsDHParam(Asn1Sequence seq) + { + if (seq.Count == 2) + return true; + + if (seq.Count > 3) + return false; + + DerInteger l = DerInteger.GetInstance(seq[2]); + DerInteger p = DerInteger.GetInstance(seq[0]); + + return l.Value.CompareTo(BigInteger.ValueOf(p.Value.BitLength)) <= 0; + } + + private static DHPublicKeyParameters ReadPkcsDHParam(DerObjectIdentifier algOid, + BigInteger y, Asn1Sequence seq) + { + DHParameter para = new DHParameter(seq); + + BigInteger lVal = para.L; + int l = lVal == null ? 0 : lVal.IntValue; + DHParameters dhParams = new DHParameters(para.P, para.G, null, l); + + return new DHPublicKeyParameters(y, dhParams, algOid); + } + } +} diff --git a/Crypto/src/security/SecureRandom.cs b/Crypto/src/security/SecureRandom.cs new file mode 100644
index 000000000..866a2cf0d --- /dev/null +++ b/Crypto/src/security/SecureRandom.cs
@@ -0,0 +1,228 @@ +using System; +using System.Globalization; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Prng; + +namespace Org.BouncyCastle.Security +{ + public class SecureRandom + : Random + { + // Note: all objects of this class should be deriving their random data from + // a single generator appropriate to the digest being used. + private static readonly IRandomGenerator sha1Generator = new DigestRandomGenerator(new Sha1Digest()); + private static readonly IRandomGenerator sha256Generator = new DigestRandomGenerator(new Sha256Digest()); + + private static readonly SecureRandom[] master = { null }; + private static SecureRandom Master + { + get + { + if (master[0] == null) + { + IRandomGenerator gen = sha256Generator; + gen = new ReversedWindowGenerator(gen, 32); + SecureRandom sr = master[0] = new SecureRandom(gen); + + sr.SetSeed(DateTime.Now.Ticks); + sr.SetSeed(new ThreadedSeedGenerator().GenerateSeed(24, true)); + sr.GenerateSeed(1 + sr.Next(32)); + } + + return master[0]; + } + } + + public static SecureRandom GetInstance( + string algorithm) + { + // TODO Compared to JDK, we don't auto-seed if the client forgets - problem? + + // TODO Support all digests more generally, by stripping PRNG and calling DigestUtilities? + string drgName = algorithm.ToUpperInvariant(); + + IRandomGenerator drg = null; + if (drgName == "SHA1PRNG") + { + drg = sha1Generator; + } + else if (drgName == "SHA256PRNG") + { + drg = sha256Generator; + } + + if (drg != null) + { + return new SecureRandom(drg); + } + + throw new ArgumentException("Unrecognised PRNG algorithm: " + algorithm, "algorithm"); + } + + public static byte[] GetSeed( + int length) + { + return Master.GenerateSeed(length); + } + + protected IRandomGenerator generator; + + public SecureRandom() + : this(sha1Generator) + { + SetSeed(GetSeed(8)); + } + + public SecureRandom( + byte[] inSeed) + : this(sha1Generator) + { + SetSeed(inSeed); + } + + /// <summary>Use the specified instance of IRandomGenerator as random source.</summary> + /// <remarks> + /// This constructor performs no seeding of either the <c>IRandomGenerator</c> or the + /// constructed <c>SecureRandom</c>. It is the responsibility of the client to provide + /// proper seed material as necessary/appropriate for the given <c>IRandomGenerator</c> + /// implementation. + /// </remarks> + /// <param name="generator">The source to generate all random bytes from.</param> + public SecureRandom( + IRandomGenerator generator) + : base(0) + { + this.generator = generator; + } + + public virtual byte[] GenerateSeed( + int length) + { + SetSeed(DateTime.Now.Ticks); + + byte[] rv = new byte[length]; + NextBytes(rv); + return rv; + } + + public virtual void SetSeed( + byte[] inSeed) + { + generator.AddSeedMaterial(inSeed); + } + + public virtual void SetSeed( + long seed) + { + generator.AddSeedMaterial(seed); + } + + public override int Next() + { + for (;;) + { + int i = NextInt() & int.MaxValue; + + if (i != int.MaxValue) + return i; + } + } + + public override int Next( + int maxValue) + { + if (maxValue < 2) + { + if (maxValue < 0) + throw new ArgumentOutOfRangeException("maxValue < 0"); + + return 0; + } + + // Test whether maxValue is a power of 2 + if ((maxValue & -maxValue) == maxValue) + { + int val = NextInt() & int.MaxValue; + long lr = ((long) maxValue * (long) val) >> 31; + return (int) lr; + } + + int bits, result; + do + { + bits = NextInt() & int.MaxValue; + result = bits % maxValue; + } + while (bits - result + (maxValue - 1) < 0); // Ignore results near overflow + + return result; + } + + public override int Next( + int minValue, + int maxValue) + { + if (maxValue <= minValue) + { + if (maxValue == minValue) + return minValue; + + throw new ArgumentException("maxValue cannot be less than minValue"); + } + + int diff = maxValue - minValue; + if (diff > 0) + return minValue + Next(diff); + + for (;;) + { + int i = NextInt(); + + if (i >= minValue && i < maxValue) + return i; + } + } + + public override void NextBytes( + byte[] buffer) + { + generator.NextBytes(buffer); + } + + public virtual void NextBytes( + byte[] buffer, + int start, + int length) + { + generator.NextBytes(buffer, start, length); + } + + private static readonly double DoubleScale = System.Math.Pow(2.0, 64.0); + + public override double NextDouble() + { + return Convert.ToDouble((ulong) NextLong()) / DoubleScale; + } + + public virtual int NextInt() + { + byte[] intBytes = new byte[4]; + NextBytes(intBytes); + + int result = 0; + for (int i = 0; i < 4; i++) + { + result = (result << 8) + (intBytes[i] & 0xff); + } + + return result; + } + + public virtual long NextLong() + { + return ((long)(uint) NextInt() << 32) | (long)(uint) NextInt(); + } + } +} diff --git a/Crypto/src/security/SecurityUtilityException.cs b/Crypto/src/security/SecurityUtilityException.cs new file mode 100644
index 000000000..8a5ea68d0 --- /dev/null +++ b/Crypto/src/security/SecurityUtilityException.cs
@@ -0,0 +1,33 @@ +using System; + +namespace Org.BouncyCastle.Security +{ + public class SecurityUtilityException + : Exception + { + /** + * base constructor. + */ + public SecurityUtilityException() + { + } + + /** + * create a SecurityUtilityException with the given message. + * + * @param message the message to be carried with the exception. + */ + public SecurityUtilityException( + string message) + : base(message) + { + } + + public SecurityUtilityException( + string message, + Exception exception) + : base(message, exception) + { + } + } +} diff --git a/Crypto/src/security/SignatureException.cs b/Crypto/src/security/SignatureException.cs new file mode 100644
index 000000000..5ae4cbcf4 --- /dev/null +++ b/Crypto/src/security/SignatureException.cs
@@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Security +{ + public class SignatureException : GeneralSecurityException + { + public SignatureException() : base() { } + public SignatureException(string message) : base(message) { } + public SignatureException(string message, Exception exception) : base(message, exception) { } + } +} diff --git a/Crypto/src/security/SignerUtilities.cs b/Crypto/src/security/SignerUtilities.cs new file mode 100644
index 000000000..ab23ee5d5 --- /dev/null +++ b/Crypto/src/security/SignerUtilities.cs
@@ -0,0 +1,550 @@ +using System; +using System.Collections; +using System.Globalization; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Signers; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Security +{ + /// <summary> + /// Signer Utility class contains methods that can not be specifically grouped into other classes. + /// </summary> + public sealed class SignerUtilities + { + private SignerUtilities() + { + } + + internal static readonly IDictionary algorithms = Platform.CreateHashtable(); + internal static readonly IDictionary oids = Platform.CreateHashtable(); + + static SignerUtilities() + { + algorithms["MD2WITHRSA"] = "MD2withRSA"; + algorithms["MD2WITHRSAENCRYPTION"] = "MD2withRSA"; + algorithms[PkcsObjectIdentifiers.MD2WithRsaEncryption.Id] = "MD2withRSA"; + + algorithms["MD4WITHRSA"] = "MD4withRSA"; + algorithms["MD4WITHRSAENCRYPTION"] = "MD4withRSA"; + algorithms[PkcsObjectIdentifiers.MD4WithRsaEncryption.Id] = "MD4withRSA"; + + algorithms["MD5WITHRSA"] = "MD5withRSA"; + algorithms["MD5WITHRSAENCRYPTION"] = "MD5withRSA"; + algorithms[PkcsObjectIdentifiers.MD5WithRsaEncryption.Id] = "MD5withRSA"; + + algorithms["SHA1WITHRSA"] = "SHA-1withRSA"; + algorithms["SHA1WITHRSAENCRYPTION"] = "SHA-1withRSA"; + algorithms[PkcsObjectIdentifiers.Sha1WithRsaEncryption.Id] = "SHA-1withRSA"; + algorithms["SHA-1WITHRSA"] = "SHA-1withRSA"; + + algorithms["SHA224WITHRSA"] = "SHA-224withRSA"; + algorithms["SHA224WITHRSAENCRYPTION"] = "SHA-224withRSA"; + algorithms[PkcsObjectIdentifiers.Sha224WithRsaEncryption.Id] = "SHA-224withRSA"; + algorithms["SHA-224WITHRSA"] = "SHA-224withRSA"; + + algorithms["SHA256WITHRSA"] = "SHA-256withRSA"; + algorithms["SHA256WITHRSAENCRYPTION"] = "SHA-256withRSA"; + algorithms[PkcsObjectIdentifiers.Sha256WithRsaEncryption.Id] = "SHA-256withRSA"; + algorithms["SHA-256WITHRSA"] = "SHA-256withRSA"; + + algorithms["SHA384WITHRSA"] = "SHA-384withRSA"; + algorithms["SHA384WITHRSAENCRYPTION"] = "SHA-384withRSA"; + algorithms[PkcsObjectIdentifiers.Sha384WithRsaEncryption.Id] = "SHA-384withRSA"; + algorithms["SHA-384WITHRSA"] = "SHA-384withRSA"; + + algorithms["SHA512WITHRSA"] = "SHA-512withRSA"; + algorithms["SHA512WITHRSAENCRYPTION"] = "SHA-512withRSA"; + algorithms[PkcsObjectIdentifiers.Sha512WithRsaEncryption.Id] = "SHA-512withRSA"; + algorithms["SHA-512WITHRSA"] = "SHA-512withRSA"; + + algorithms["PSSWITHRSA"] = "PSSwithRSA"; + algorithms["RSASSA-PSS"] = "PSSwithRSA"; + algorithms[PkcsObjectIdentifiers.IdRsassaPss.Id] = "PSSwithRSA"; + algorithms["RSAPSS"] = "PSSwithRSA"; + + algorithms["SHA1WITHRSAANDMGF1"] = "SHA-1withRSAandMGF1"; + algorithms["SHA-1WITHRSAANDMGF1"] = "SHA-1withRSAandMGF1"; + algorithms["SHA1WITHRSA/PSS"] = "SHA-1withRSAandMGF1"; + algorithms["SHA-1WITHRSA/PSS"] = "SHA-1withRSAandMGF1"; + + algorithms["SHA224WITHRSAANDMGF1"] = "SHA-224withRSAandMGF1"; + algorithms["SHA-224WITHRSAANDMGF1"] = "SHA-224withRSAandMGF1"; + algorithms["SHA224WITHRSA/PSS"] = "SHA-224withRSAandMGF1"; + algorithms["SHA-224WITHRSA/PSS"] = "SHA-224withRSAandMGF1"; + + algorithms["SHA256WITHRSAANDMGF1"] = "SHA-256withRSAandMGF1"; + algorithms["SHA-256WITHRSAANDMGF1"] = "SHA-256withRSAandMGF1"; + algorithms["SHA256WITHRSA/PSS"] = "SHA-256withRSAandMGF1"; + algorithms["SHA-256WITHRSA/PSS"] = "SHA-256withRSAandMGF1"; + + algorithms["SHA384WITHRSAANDMGF1"] = "SHA-384withRSAandMGF1"; + algorithms["SHA-384WITHRSAANDMGF1"] = "SHA-384withRSAandMGF1"; + algorithms["SHA384WITHRSA/PSS"] = "SHA-384withRSAandMGF1"; + algorithms["SHA-384WITHRSA/PSS"] = "SHA-384withRSAandMGF1"; + + algorithms["SHA512WITHRSAANDMGF1"] = "SHA-512withRSAandMGF1"; + algorithms["SHA-512WITHRSAANDMGF1"] = "SHA-512withRSAandMGF1"; + algorithms["SHA512WITHRSA/PSS"] = "SHA-512withRSAandMGF1"; + algorithms["SHA-512WITHRSA/PSS"] = "SHA-512withRSAandMGF1"; + + algorithms["RIPEMD128WITHRSA"] = "RIPEMD128withRSA"; + algorithms["RIPEMD128WITHRSAENCRYPTION"] = "RIPEMD128withRSA"; + algorithms[TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128.Id] = "RIPEMD128withRSA"; + + algorithms["RIPEMD160WITHRSA"] = "RIPEMD160withRSA"; + algorithms["RIPEMD160WITHRSAENCRYPTION"] = "RIPEMD160withRSA"; + algorithms[TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160.Id] = "RIPEMD160withRSA"; + + algorithms["RIPEMD256WITHRSA"] = "RIPEMD256withRSA"; + algorithms["RIPEMD256WITHRSAENCRYPTION"] = "RIPEMD256withRSA"; + algorithms[TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256.Id] = "RIPEMD256withRSA"; + + algorithms["NONEWITHRSA"] = "RSA"; + algorithms["RSAWITHNONE"] = "RSA"; + algorithms["RAWRSA"] = "RSA"; + + algorithms["RAWRSAPSS"] = "RAWRSASSA-PSS"; + algorithms["NONEWITHRSAPSS"] = "RAWRSASSA-PSS"; + algorithms["NONEWITHRSASSA-PSS"] = "RAWRSASSA-PSS"; + + algorithms["NONEWITHDSA"] = "NONEwithDSA"; + algorithms["DSAWITHNONE"] = "NONEwithDSA"; + algorithms["RAWDSA"] = "NONEwithDSA"; + + algorithms["DSA"] = "SHA-1withDSA"; + algorithms["DSAWITHSHA1"] = "SHA-1withDSA"; + algorithms["DSAWITHSHA-1"] = "SHA-1withDSA"; + algorithms["SHA/DSA"] = "SHA-1withDSA"; + algorithms["SHA1/DSA"] = "SHA-1withDSA"; + algorithms["SHA-1/DSA"] = "SHA-1withDSA"; + algorithms["SHA1WITHDSA"] = "SHA-1withDSA"; + algorithms["SHA-1WITHDSA"] = "SHA-1withDSA"; + algorithms[X9ObjectIdentifiers.IdDsaWithSha1.Id] = "SHA-1withDSA"; + + algorithms["DSAWITHSHA224"] = "SHA-224withDSA"; + algorithms["DSAWITHSHA-224"] = "SHA-224withDSA"; + algorithms["SHA224/DSA"] = "SHA-224withDSA"; + algorithms["SHA-224/DSA"] = "SHA-224withDSA"; + algorithms["SHA224WITHDSA"] = "SHA-224withDSA"; + algorithms["SHA-224WITHDSA"] = "SHA-224withDSA"; + algorithms[NistObjectIdentifiers.DsaWithSha224.Id] = "SHA-224withDSA"; + + algorithms["DSAWITHSHA256"] = "SHA-256withDSA"; + algorithms["DSAWITHSHA-256"] = "SHA-256withDSA"; + algorithms["SHA256/DSA"] = "SHA-256withDSA"; + algorithms["SHA-256/DSA"] = "SHA-256withDSA"; + algorithms["SHA256WITHDSA"] = "SHA-256withDSA"; + algorithms["SHA-256WITHDSA"] = "SHA-256withDSA"; + algorithms[NistObjectIdentifiers.DsaWithSha256.Id] = "SHA-256withDSA"; + + algorithms["DSAWITHSHA384"] = "SHA-384withDSA"; + algorithms["DSAWITHSHA-384"] = "SHA-384withDSA"; + algorithms["SHA384/DSA"] = "SHA-384withDSA"; + algorithms["SHA-384/DSA"] = "SHA-384withDSA"; + algorithms["SHA384WITHDSA"] = "SHA-384withDSA"; + algorithms["SHA-384WITHDSA"] = "SHA-384withDSA"; + algorithms[NistObjectIdentifiers.DsaWithSha384.Id] = "SHA-384withDSA"; + + algorithms["DSAWITHSHA512"] = "SHA-512withDSA"; + algorithms["DSAWITHSHA-512"] = "SHA-512withDSA"; + algorithms["SHA512/DSA"] = "SHA-512withDSA"; + algorithms["SHA-512/DSA"] = "SHA-512withDSA"; + algorithms["SHA512WITHDSA"] = "SHA-512withDSA"; + algorithms["SHA-512WITHDSA"] = "SHA-512withDSA"; + algorithms[NistObjectIdentifiers.DsaWithSha512.Id] = "SHA-512withDSA"; + + algorithms["NONEWITHECDSA"] = "NONEwithECDSA"; + algorithms["ECDSAWITHNONE"] = "NONEwithECDSA"; + + algorithms["ECDSA"] = "SHA-1withECDSA"; + algorithms["SHA1/ECDSA"] = "SHA-1withECDSA"; + algorithms["SHA-1/ECDSA"] = "SHA-1withECDSA"; + algorithms["ECDSAWITHSHA1"] = "SHA-1withECDSA"; + algorithms["ECDSAWITHSHA-1"] = "SHA-1withECDSA"; + algorithms["SHA1WITHECDSA"] = "SHA-1withECDSA"; + algorithms["SHA-1WITHECDSA"] = "SHA-1withECDSA"; + algorithms[X9ObjectIdentifiers.ECDsaWithSha1.Id] = "SHA-1withECDSA"; + algorithms[TeleTrusTObjectIdentifiers.ECSignWithSha1.Id] = "SHA-1withECDSA"; + + algorithms["SHA224/ECDSA"] = "SHA-224withECDSA"; + algorithms["SHA-224/ECDSA"] = "SHA-224withECDSA"; + algorithms["ECDSAWITHSHA224"] = "SHA-224withECDSA"; + algorithms["ECDSAWITHSHA-224"] = "SHA-224withECDSA"; + algorithms["SHA224WITHECDSA"] = "SHA-224withECDSA"; + algorithms["SHA-224WITHECDSA"] = "SHA-224withECDSA"; + algorithms[X9ObjectIdentifiers.ECDsaWithSha224.Id] = "SHA-224withECDSA"; + + algorithms["SHA256/ECDSA"] = "SHA-256withECDSA"; + algorithms["SHA-256/ECDSA"] = "SHA-256withECDSA"; + algorithms["ECDSAWITHSHA256"] = "SHA-256withECDSA"; + algorithms["ECDSAWITHSHA-256"] = "SHA-256withECDSA"; + algorithms["SHA256WITHECDSA"] = "SHA-256withECDSA"; + algorithms["SHA-256WITHECDSA"] = "SHA-256withECDSA"; + algorithms[X9ObjectIdentifiers.ECDsaWithSha256.Id] = "SHA-256withECDSA"; + + algorithms["SHA384/ECDSA"] = "SHA-384withECDSA"; + algorithms["SHA-384/ECDSA"] = "SHA-384withECDSA"; + algorithms["ECDSAWITHSHA384"] = "SHA-384withECDSA"; + algorithms["ECDSAWITHSHA-384"] = "SHA-384withECDSA"; + algorithms["SHA384WITHECDSA"] = "SHA-384withECDSA"; + algorithms["SHA-384WITHECDSA"] = "SHA-384withECDSA"; + algorithms[X9ObjectIdentifiers.ECDsaWithSha384.Id] = "SHA-384withECDSA"; + + algorithms["SHA512/ECDSA"] = "SHA-512withECDSA"; + algorithms["SHA-512/ECDSA"] = "SHA-512withECDSA"; + algorithms["ECDSAWITHSHA512"] = "SHA-512withECDSA"; + algorithms["ECDSAWITHSHA-512"] = "SHA-512withECDSA"; + algorithms["SHA512WITHECDSA"] = "SHA-512withECDSA"; + algorithms["SHA-512WITHECDSA"] = "SHA-512withECDSA"; + algorithms[X9ObjectIdentifiers.ECDsaWithSha512.Id] = "SHA-512withECDSA"; + + algorithms["RIPEMD160/ECDSA"] = "RIPEMD160withECDSA"; + algorithms["SHA-512/ECDSA"] = "RIPEMD160withECDSA"; + algorithms["ECDSAWITHRIPEMD160"] = "RIPEMD160withECDSA"; + algorithms["ECDSAWITHRIPEMD160"] = "RIPEMD160withECDSA"; + algorithms["RIPEMD160WITHECDSA"] = "RIPEMD160withECDSA"; + algorithms["RIPEMD160WITHECDSA"] = "RIPEMD160withECDSA"; + algorithms[TeleTrusTObjectIdentifiers.ECSignWithRipeMD160.Id] = "RIPEMD160withECDSA"; + + algorithms["GOST-3410"] = "GOST3410"; + algorithms["GOST-3410-94"] = "GOST3410"; + algorithms["GOST3411WITHGOST3410"] = "GOST3410"; + algorithms[CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94.Id] = "GOST3410"; + + algorithms["ECGOST-3410"] = "ECGOST3410"; + algorithms["ECGOST-3410-2001"] = "ECGOST3410"; + algorithms["GOST3411WITHECGOST3410"] = "ECGOST3410"; + algorithms[CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001.Id] = "ECGOST3410"; + + + + oids["MD2withRSA"] = PkcsObjectIdentifiers.MD2WithRsaEncryption; + oids["MD4withRSA"] = PkcsObjectIdentifiers.MD4WithRsaEncryption; + oids["MD5withRSA"] = PkcsObjectIdentifiers.MD5WithRsaEncryption; + + oids["SHA-1withRSA"] = PkcsObjectIdentifiers.Sha1WithRsaEncryption; + oids["SHA-224withRSA"] = PkcsObjectIdentifiers.Sha224WithRsaEncryption; + oids["SHA-256withRSA"] = PkcsObjectIdentifiers.Sha256WithRsaEncryption; + oids["SHA-384withRSA"] = PkcsObjectIdentifiers.Sha384WithRsaEncryption; + oids["SHA-512withRSA"] = PkcsObjectIdentifiers.Sha512WithRsaEncryption; + + oids["PSSwithRSA"] = PkcsObjectIdentifiers.IdRsassaPss; + oids["SHA-1withRSAandMGF1"] = PkcsObjectIdentifiers.IdRsassaPss; + oids["SHA-224withRSAandMGF1"] = PkcsObjectIdentifiers.IdRsassaPss; + oids["SHA-256withRSAandMGF1"] = PkcsObjectIdentifiers.IdRsassaPss; + oids["SHA-384withRSAandMGF1"] = PkcsObjectIdentifiers.IdRsassaPss; + oids["SHA-512withRSAandMGF1"] = PkcsObjectIdentifiers.IdRsassaPss; + + oids["RIPEMD128withRSA"] = TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128; + oids["RIPEMD160withRSA"] = TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160; + oids["RIPEMD256withRSA"] = TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256; + + oids["SHA-1withDSA"] = X9ObjectIdentifiers.IdDsaWithSha1; + + oids["SHA-1withECDSA"] = X9ObjectIdentifiers.ECDsaWithSha1; + oids["SHA-224withECDSA"] = X9ObjectIdentifiers.ECDsaWithSha224; + oids["SHA-256withECDSA"] = X9ObjectIdentifiers.ECDsaWithSha256; + oids["SHA-384withECDSA"] = X9ObjectIdentifiers.ECDsaWithSha384; + oids["SHA-512withECDSA"] = X9ObjectIdentifiers.ECDsaWithSha512; + + oids["GOST3410"] = CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94; + oids["ECGOST3410"] = CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001; + } + + /// <summary> + /// Returns a ObjectIdentifier for a give encoding. + /// </summary> + /// <param name="mechanism">A string representation of the encoding.</param> + /// <returns>A DerObjectIdentifier, null if the Oid is not available.</returns> + // TODO Don't really want to support this + public static DerObjectIdentifier GetObjectIdentifier( + string mechanism) + { + if (mechanism == null) + throw new ArgumentNullException("mechanism"); + + mechanism = mechanism.ToUpperInvariant(); + string aliased = (string) algorithms[mechanism]; + + if (aliased != null) + mechanism = aliased; + + return (DerObjectIdentifier) oids[mechanism]; + } + + public static ICollection Algorithms + { + get { return oids.Keys; } + } + + public static Asn1Encodable GetDefaultX509Parameters( + DerObjectIdentifier id) + { + return GetDefaultX509Parameters(id.Id); + } + + public static Asn1Encodable GetDefaultX509Parameters( + string algorithm) + { + if (algorithm == null) + throw new ArgumentNullException("algorithm"); + + algorithm = algorithm.ToUpperInvariant(); + + string mechanism = (string) algorithms[algorithm]; + + if (mechanism == null) + mechanism = algorithm; + + if (mechanism == "PSSwithRSA") + { + // TODO The Sha1Digest here is a default. In JCE version, the actual digest + // to be used can be overridden by subsequent parameter settings. + return GetPssX509Parameters("SHA-1"); + } + + if (mechanism.EndsWith("withRSAandMGF1")) + { + string digestName = mechanism.Substring(0, mechanism.Length - "withRSAandMGF1".Length); + return GetPssX509Parameters(digestName); + } + + return DerNull.Instance; + } + + private static Asn1Encodable GetPssX509Parameters( + string digestName) + { + AlgorithmIdentifier hashAlgorithm = new AlgorithmIdentifier( + DigestUtilities.GetObjectIdentifier(digestName), DerNull.Instance); + + // TODO Is it possible for the MGF hash alg to be different from the PSS one? + AlgorithmIdentifier maskGenAlgorithm = new AlgorithmIdentifier( + PkcsObjectIdentifiers.IdMgf1, hashAlgorithm); + + int saltLen = DigestUtilities.GetDigest(digestName).GetDigestSize(); + return new RsassaPssParameters(hashAlgorithm, maskGenAlgorithm, + new DerInteger(saltLen), new DerInteger(1)); + } + + public static ISigner GetSigner( + DerObjectIdentifier id) + { + return GetSigner(id.Id); + } + + public static ISigner GetSigner( + string algorithm) + { + if (algorithm == null) + throw new ArgumentNullException("algorithm"); + + algorithm = algorithm.ToUpperInvariant(); + + string mechanism = (string) algorithms[algorithm]; + + if (mechanism == null) + mechanism = algorithm; + + if (mechanism.Equals("RSA")) + { + return (new RsaDigestSigner(new NullDigest())); + } + if (mechanism.Equals("MD2withRSA")) + { + return (new RsaDigestSigner(new MD2Digest())); + } + if (mechanism.Equals("MD4withRSA")) + { + return (new RsaDigestSigner(new MD4Digest())); + } + if (mechanism.Equals("MD5withRSA")) + { + return (new RsaDigestSigner(new MD5Digest())); + } + if (mechanism.Equals("SHA-1withRSA")) + { + return (new RsaDigestSigner(new Sha1Digest())); + } + if (mechanism.Equals("SHA-224withRSA")) + { + return (new RsaDigestSigner(new Sha224Digest())); + } + if (mechanism.Equals("SHA-256withRSA")) + { + return (new RsaDigestSigner(new Sha256Digest())); + } + if (mechanism.Equals("SHA-384withRSA")) + { + return (new RsaDigestSigner(new Sha384Digest())); + } + if (mechanism.Equals("SHA-512withRSA")) + { + return (new RsaDigestSigner(new Sha512Digest())); + } + if (mechanism.Equals("RIPEMD128withRSA")) + { + return (new RsaDigestSigner(new RipeMD128Digest())); + } + if (mechanism.Equals("RIPEMD160withRSA")) + { + return (new RsaDigestSigner(new RipeMD160Digest())); + } + if (mechanism.Equals("RIPEMD256withRSA")) + { + return (new RsaDigestSigner(new RipeMD256Digest())); + } + + if (mechanism.Equals("RAWRSASSA-PSS")) + { + // TODO Add support for other parameter settings + return PssSigner.CreateRawSigner(new RsaBlindedEngine(), new Sha1Digest()); + } + if (mechanism.Equals("PSSwithRSA")) + { + // TODO The Sha1Digest here is a default. In JCE version, the actual digest + // to be used can be overridden by subsequent parameter settings. + return (new PssSigner(new RsaBlindedEngine(), new Sha1Digest())); + } + if (mechanism.Equals("SHA-1withRSAandMGF1")) + { + return (new PssSigner(new RsaBlindedEngine(), new Sha1Digest())); + } + if (mechanism.Equals("SHA-224withRSAandMGF1")) + { + return (new PssSigner(new RsaBlindedEngine(), new Sha224Digest())); + } + if (mechanism.Equals("SHA-256withRSAandMGF1")) + { + return (new PssSigner(new RsaBlindedEngine(), new Sha256Digest())); + } + if (mechanism.Equals("SHA-384withRSAandMGF1")) + { + return (new PssSigner(new RsaBlindedEngine(), new Sha384Digest())); + } + if (mechanism.Equals("SHA-512withRSAandMGF1")) + { + return (new PssSigner(new RsaBlindedEngine(), new Sha512Digest())); + } + + if (mechanism.Equals("NONEwithDSA")) + { + return (new DsaDigestSigner(new DsaSigner(), new NullDigest())); + } + if (mechanism.Equals("SHA-1withDSA")) + { + return (new DsaDigestSigner(new DsaSigner(), new Sha1Digest())); + } + if (mechanism.Equals("SHA-224withDSA")) + { + return (new DsaDigestSigner(new DsaSigner(), new Sha224Digest())); + } + if (mechanism.Equals("SHA-256withDSA")) + { + return (new DsaDigestSigner(new DsaSigner(), new Sha256Digest())); + } + if (mechanism.Equals("SHA-384withDSA")) + { + return (new DsaDigestSigner(new DsaSigner(), new Sha384Digest())); + } + if (mechanism.Equals("SHA-512withDSA")) + { + return (new DsaDigestSigner(new DsaSigner(), new Sha512Digest())); + } + + if (mechanism.Equals("NONEwithECDSA")) + { + return (new DsaDigestSigner(new ECDsaSigner(), new NullDigest())); + } + if (mechanism.Equals("SHA-1withECDSA")) + { + return (new DsaDigestSigner(new ECDsaSigner(), new Sha1Digest())); + } + if (mechanism.Equals("SHA-224withECDSA")) + { + return (new DsaDigestSigner(new ECDsaSigner(), new Sha224Digest())); + } + if (mechanism.Equals("SHA-256withECDSA")) + { + return (new DsaDigestSigner(new ECDsaSigner(), new Sha256Digest())); + } + if (mechanism.Equals("SHA-384withECDSA")) + { + return (new DsaDigestSigner(new ECDsaSigner(), new Sha384Digest())); + } + if (mechanism.Equals("SHA-512withECDSA")) + { + return (new DsaDigestSigner(new ECDsaSigner(), new Sha512Digest())); + } + + if (mechanism.Equals("RIPEMD160withECDSA")) + { + return (new DsaDigestSigner(new ECDsaSigner(), new RipeMD160Digest())); + } + + if (mechanism.Equals("SHA1WITHECNR")) + { + return (new DsaDigestSigner(new ECNRSigner(), new Sha1Digest())); + } + if (mechanism.Equals("SHA224WITHECNR")) + { + return (new DsaDigestSigner(new ECNRSigner(), new Sha224Digest())); + } + if (mechanism.Equals("SHA256WITHECNR")) + { + return (new DsaDigestSigner(new ECNRSigner(), new Sha256Digest())); + } + if (mechanism.Equals("SHA384WITHECNR")) + { + return (new DsaDigestSigner(new ECNRSigner(), new Sha384Digest())); + } + if (mechanism.Equals("SHA512WITHECNR")) + { + return (new DsaDigestSigner(new ECNRSigner(), new Sha512Digest())); + } + + if (mechanism.Equals("GOST3410")) + { + return new Gost3410DigestSigner(new Gost3410Signer(), new Gost3411Digest()); + } + if (mechanism.Equals("ECGOST3410")) + { + return new Gost3410DigestSigner(new ECGost3410Signer(), new Gost3411Digest()); + } + + if (mechanism.Equals("SHA1WITHRSA/ISO9796-2")) + { + return new Iso9796d2Signer(new RsaBlindedEngine(), new Sha1Digest(), true); + } + if (mechanism.Equals("MD5WITHRSA/ISO9796-2")) + { + return new Iso9796d2Signer(new RsaBlindedEngine(), new MD5Digest(), true); + } + if (mechanism.Equals("RIPEMD160WITHRSA/ISO9796-2")) + { + return new Iso9796d2Signer(new RsaBlindedEngine(), new RipeMD160Digest(), true); + } + + throw new SecurityUtilityException("Signer " + algorithm + " not recognised."); + } + + public static string GetEncodingName( + DerObjectIdentifier oid) + { + return (string) algorithms[oid.Id]; + } + } +} diff --git a/Crypto/src/security/WrapperUtilities.cs b/Crypto/src/security/WrapperUtilities.cs new file mode 100644
index 000000000..33567c51b --- /dev/null +++ b/Crypto/src/security/WrapperUtilities.cs
@@ -0,0 +1,153 @@ +using System; +using System.Collections; +using System.Globalization; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Kisa; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Ntt; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Security +{ + /// <remarks> + /// Utility class for creating IWrapper objects from their names/Oids + /// </remarks> + public sealed class WrapperUtilities + { + internal enum WrapAlgorithm { AESWRAP, CAMELLIAWRAP, DESEDEWRAP, RC2WRAP, SEEDWRAP, + DESEDERFC3211WRAP, AESRFC3211WRAP, CAMELLIARFC3211WRAP }; + + private WrapperUtilities() + { + } + + private static readonly IDictionary algorithms = Platform.CreateHashtable(); + //private static readonly IDictionary oids = Platform.CreateHashtable(); + + static WrapperUtilities() + { + // Signal to obfuscation tools not to change enum constants + ((WrapAlgorithm)Enums.GetArbitraryValue(typeof(WrapAlgorithm))).ToString(); + + algorithms[NistObjectIdentifiers.IdAes128Wrap.Id] = "AESWRAP"; + algorithms[NistObjectIdentifiers.IdAes192Wrap.Id] = "AESWRAP"; + algorithms[NistObjectIdentifiers.IdAes256Wrap.Id] = "AESWRAP"; + + algorithms[NttObjectIdentifiers.IdCamellia128Wrap.Id] = "CAMELLIAWRAP"; + algorithms[NttObjectIdentifiers.IdCamellia192Wrap.Id] = "CAMELLIAWRAP"; + algorithms[NttObjectIdentifiers.IdCamellia256Wrap.Id] = "CAMELLIAWRAP"; + + algorithms[PkcsObjectIdentifiers.IdAlgCms3DesWrap.Id] = "DESEDEWRAP"; + + algorithms[PkcsObjectIdentifiers.IdAlgCmsRC2Wrap.Id] = "RC2WRAP"; + + algorithms[KisaObjectIdentifiers.IdNpkiAppCmsSeedWrap.Id] = "SEEDWRAP"; + } + + public static IWrapper GetWrapper( + DerObjectIdentifier oid) + { + return GetWrapper(oid.Id); + } + + public static IWrapper GetWrapper( + string algorithm) + { + string upper = algorithm.ToUpperInvariant(); + string mechanism = (string) algorithms[upper]; + + if (mechanism == null) + { + mechanism = upper; + } + + try + { + WrapAlgorithm wrapAlgorithm = (WrapAlgorithm)Enums.GetEnumValue( + typeof(WrapAlgorithm), mechanism); + + switch (wrapAlgorithm) + { + case WrapAlgorithm.AESWRAP: return new AesWrapEngine(); + case WrapAlgorithm.CAMELLIAWRAP: return new CamelliaWrapEngine(); + case WrapAlgorithm.DESEDEWRAP: return new DesEdeWrapEngine(); + case WrapAlgorithm.RC2WRAP: return new RC2WrapEngine(); + case WrapAlgorithm.SEEDWRAP: return new SeedWrapEngine(); + case WrapAlgorithm.DESEDERFC3211WRAP: return new Rfc3211WrapEngine(new DesEdeEngine()); + case WrapAlgorithm.AESRFC3211WRAP: return new Rfc3211WrapEngine(new AesFastEngine()); + case WrapAlgorithm.CAMELLIARFC3211WRAP: return new Rfc3211WrapEngine(new CamelliaEngine()); + } + } + catch (ArgumentException) + { + } + + // Create an IBufferedCipher and use it as IWrapper (via BufferedCipherWrapper) + IBufferedCipher blockCipher = CipherUtilities.GetCipher(algorithm); + + if (blockCipher != null) + return new BufferedCipherWrapper(blockCipher); + + throw new SecurityUtilityException("Wrapper " + algorithm + " not recognised."); + } + + public static string GetAlgorithmName( + DerObjectIdentifier oid) + { + return (string) algorithms[oid.Id]; + } + + private class BufferedCipherWrapper + : IWrapper + { + private readonly IBufferedCipher cipher; + private bool forWrapping; + + public BufferedCipherWrapper( + IBufferedCipher cipher) + { + this.cipher = cipher; + } + + public string AlgorithmName + { + get { return cipher.AlgorithmName; } + } + + public void Init( + bool forWrapping, + ICipherParameters parameters) + { + this.forWrapping = forWrapping; + + cipher.Init(forWrapping, parameters); + } + + public byte[] Wrap( + byte[] input, + int inOff, + int length) + { + if (!forWrapping) + throw new InvalidOperationException("Not initialised for wrapping"); + + return cipher.DoFinal(input, inOff, length); + } + + public byte[] Unwrap( + byte[] input, + int inOff, + int length) + { + if (forWrapping) + throw new InvalidOperationException("Not initialised for unwrapping"); + + return cipher.DoFinal(input, inOff, length); + } + } + } +} diff --git a/Crypto/src/security/cert/CertificateEncodingException.cs b/Crypto/src/security/cert/CertificateEncodingException.cs new file mode 100644
index 000000000..ae6a56653 --- /dev/null +++ b/Crypto/src/security/cert/CertificateEncodingException.cs
@@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Security.Certificates +{ + public class CertificateEncodingException : CertificateException + { + public CertificateEncodingException() : base() { } + public CertificateEncodingException(string msg) : base(msg) { } + public CertificateEncodingException(string msg, Exception e) : base(msg, e) { } + } +} diff --git a/Crypto/src/security/cert/CertificateException.cs b/Crypto/src/security/cert/CertificateException.cs new file mode 100644
index 000000000..fa403c7f5 --- /dev/null +++ b/Crypto/src/security/cert/CertificateException.cs
@@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Security.Certificates +{ + public class CertificateException : GeneralSecurityException + { + public CertificateException() : base() { } + public CertificateException(string message) : base(message) { } + public CertificateException(string message, Exception exception) : base(message, exception) { } + } +} diff --git a/Crypto/src/security/cert/CertificateExpiredException.cs b/Crypto/src/security/cert/CertificateExpiredException.cs new file mode 100644
index 000000000..33a54806f --- /dev/null +++ b/Crypto/src/security/cert/CertificateExpiredException.cs
@@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Security.Certificates +{ + public class CertificateExpiredException : CertificateException + { + public CertificateExpiredException() : base() { } + public CertificateExpiredException(string message) : base(message) { } + public CertificateExpiredException(string message, Exception exception) : base(message, exception) { } + } +} diff --git a/Crypto/src/security/cert/CertificateNotYetValidException.cs b/Crypto/src/security/cert/CertificateNotYetValidException.cs new file mode 100644
index 000000000..b9210e57c --- /dev/null +++ b/Crypto/src/security/cert/CertificateNotYetValidException.cs
@@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Security.Certificates +{ + public class CertificateNotYetValidException : CertificateException + { + public CertificateNotYetValidException() : base() { } + public CertificateNotYetValidException(string message) : base(message) { } + public CertificateNotYetValidException(string message, Exception exception) : base(message, exception) { } + } +} diff --git a/Crypto/src/security/cert/CertificateParsingException.cs b/Crypto/src/security/cert/CertificateParsingException.cs new file mode 100644
index 000000000..9a1d0111b --- /dev/null +++ b/Crypto/src/security/cert/CertificateParsingException.cs
@@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Security.Certificates +{ + public class CertificateParsingException : CertificateException + { + public CertificateParsingException() : base() { } + public CertificateParsingException(string message) : base(message) { } + public CertificateParsingException(string message, Exception exception) : base(message, exception) { } + } +} diff --git a/Crypto/src/security/cert/CrlException.cs b/Crypto/src/security/cert/CrlException.cs new file mode 100644
index 000000000..59e4f20ea --- /dev/null +++ b/Crypto/src/security/cert/CrlException.cs
@@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Security.Certificates +{ + public class CrlException : GeneralSecurityException + { + public CrlException() : base() { } + public CrlException(string msg) : base(msg) {} + public CrlException(string msg, Exception e) : base(msg, e) {} + } +} diff --git a/Crypto/src/tsp/GenTimeAccuracy.cs b/Crypto/src/tsp/GenTimeAccuracy.cs new file mode 100644
index 000000000..8a2f29989 --- /dev/null +++ b/Crypto/src/tsp/GenTimeAccuracy.cs
@@ -0,0 +1,33 @@ +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Tsp; + +namespace Org.BouncyCastle.Tsp +{ + public class GenTimeAccuracy + { + private Accuracy accuracy; + + public GenTimeAccuracy( + Accuracy accuracy) + { + this.accuracy = accuracy; + } + + public int Seconds { get { return GetTimeComponent(accuracy.Seconds); } } + + public int Millis { get { return GetTimeComponent(accuracy.Millis); } } + + public int Micros { get { return GetTimeComponent(accuracy.Micros); } } + + private int GetTimeComponent( + DerInteger time) + { + return time == null ? 0 : time.Value.IntValue; + } + + public override string ToString() + { + return Seconds + "." + Millis.ToString("000") + Micros.ToString("000"); + } + } +} diff --git a/Crypto/src/tsp/TSPAlgorithms.cs b/Crypto/src/tsp/TSPAlgorithms.cs new file mode 100644
index 000000000..e3dfc7916 --- /dev/null +++ b/Crypto/src/tsp/TSPAlgorithms.cs
@@ -0,0 +1,48 @@ +using System.Collections; + +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Tsp +{ + /** + * Recognised hash algorithms for the time stamp protocol. + */ + public abstract class TspAlgorithms + { + public static readonly string MD5 = PkcsObjectIdentifiers.MD5.Id; + + public static readonly string Sha1 = OiwObjectIdentifiers.IdSha1.Id; + + public static readonly string Sha224 = NistObjectIdentifiers.IdSha224.Id; + public static readonly string Sha256 = NistObjectIdentifiers.IdSha256.Id; + public static readonly string Sha384 = NistObjectIdentifiers.IdSha384.Id; + public static readonly string Sha512 = NistObjectIdentifiers.IdSha512.Id; + + public static readonly string RipeMD128 = TeleTrusTObjectIdentifiers.RipeMD128.Id; + public static readonly string RipeMD160 = TeleTrusTObjectIdentifiers.RipeMD160.Id; + public static readonly string RipeMD256 = TeleTrusTObjectIdentifiers.RipeMD256.Id; + + public static readonly string Gost3411 = CryptoProObjectIdentifiers.GostR3411.Id; + + public static readonly IList Allowed; + + static TspAlgorithms() + { + string[] algs = new string[] + { + Gost3411, MD5, Sha1, Sha224, Sha256, Sha384, Sha512, RipeMD128, RipeMD160, RipeMD256 + }; + + Allowed = Platform.CreateArrayList(); + foreach (string alg in algs) + { + Allowed.Add(alg); + } + } + } +} diff --git a/Crypto/src/tsp/TSPException.cs b/Crypto/src/tsp/TSPException.cs new file mode 100644
index 000000000..a5e24163d --- /dev/null +++ b/Crypto/src/tsp/TSPException.cs
@@ -0,0 +1,25 @@ +using System; + +namespace Org.BouncyCastle.Tsp +{ + public class TspException + : Exception + { + public TspException() + { + } + + public TspException( + string message) + : base(message) + { + } + + public TspException( + string message, + Exception e) + : base(message, e) + { + } + } +} diff --git a/Crypto/src/tsp/TSPUtil.cs b/Crypto/src/tsp/TSPUtil.cs new file mode 100644
index 000000000..1026914f4 --- /dev/null +++ b/Crypto/src/tsp/TSPUtil.cs
@@ -0,0 +1,202 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Cms; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Tsp +{ + public class TspUtil + { + private static ISet EmptySet = CollectionUtilities.ReadOnly(new HashSet()); + private static IList EmptyList = CollectionUtilities.ReadOnly(Platform.CreateArrayList()); + + private static readonly IDictionary digestLengths = Platform.CreateHashtable(); + private static readonly IDictionary digestNames = Platform.CreateHashtable(); + + static TspUtil() + { + digestLengths.Add(PkcsObjectIdentifiers.MD5.Id, 16); + digestLengths.Add(OiwObjectIdentifiers.IdSha1.Id, 20); + digestLengths.Add(NistObjectIdentifiers.IdSha224.Id, 28); + digestLengths.Add(NistObjectIdentifiers.IdSha256.Id, 32); + digestLengths.Add(NistObjectIdentifiers.IdSha384.Id, 48); + digestLengths.Add(NistObjectIdentifiers.IdSha512.Id, 64); + digestLengths.Add(TeleTrusTObjectIdentifiers.RipeMD128.Id, 16); + digestLengths.Add(TeleTrusTObjectIdentifiers.RipeMD160.Id, 20); + digestLengths.Add(TeleTrusTObjectIdentifiers.RipeMD256.Id, 32); + digestLengths.Add(CryptoProObjectIdentifiers.GostR3411.Id, 32); + + digestNames.Add(PkcsObjectIdentifiers.MD5.Id, "MD5"); + digestNames.Add(OiwObjectIdentifiers.IdSha1.Id, "SHA1"); + digestNames.Add(NistObjectIdentifiers.IdSha224.Id, "SHA224"); + digestNames.Add(NistObjectIdentifiers.IdSha256.Id, "SHA256"); + digestNames.Add(NistObjectIdentifiers.IdSha384.Id, "SHA384"); + digestNames.Add(NistObjectIdentifiers.IdSha512.Id, "SHA512"); + digestNames.Add(PkcsObjectIdentifiers.Sha1WithRsaEncryption.Id, "SHA1"); + digestNames.Add(PkcsObjectIdentifiers.Sha224WithRsaEncryption.Id, "SHA224"); + digestNames.Add(PkcsObjectIdentifiers.Sha256WithRsaEncryption.Id, "SHA256"); + digestNames.Add(PkcsObjectIdentifiers.Sha384WithRsaEncryption.Id, "SHA384"); + digestNames.Add(PkcsObjectIdentifiers.Sha512WithRsaEncryption.Id, "SHA512"); + digestNames.Add(TeleTrusTObjectIdentifiers.RipeMD128.Id, "RIPEMD128"); + digestNames.Add(TeleTrusTObjectIdentifiers.RipeMD160.Id, "RIPEMD160"); + digestNames.Add(TeleTrusTObjectIdentifiers.RipeMD256.Id, "RIPEMD256"); + digestNames.Add(CryptoProObjectIdentifiers.GostR3411.Id, "GOST3411"); + } + + + /** + * Fetches the signature time-stamp attributes from a SignerInformation object. + * Checks that the MessageImprint for each time-stamp matches the signature field. + * (see RFC 3161 Appendix A). + * + * @param signerInfo a SignerInformation to search for time-stamps + * @return a collection of TimeStampToken objects + * @throws TSPValidationException + */ + public static ICollection GetSignatureTimestamps( + SignerInformation signerInfo) + { + IList timestamps = Platform.CreateArrayList(); + + Asn1.Cms.AttributeTable unsignedAttrs = signerInfo.UnsignedAttributes; + if (unsignedAttrs != null) + { + foreach (Asn1.Cms.Attribute tsAttr in unsignedAttrs.GetAll( + PkcsObjectIdentifiers.IdAASignatureTimeStampToken)) + { + foreach (Asn1Encodable asn1 in tsAttr.AttrValues) + { + try + { + Asn1.Cms.ContentInfo contentInfo = Asn1.Cms.ContentInfo.GetInstance( + asn1.ToAsn1Object()); + TimeStampToken timeStampToken = new TimeStampToken(contentInfo); + TimeStampTokenInfo tstInfo = timeStampToken.TimeStampInfo; + + byte[] expectedDigest = DigestUtilities.CalculateDigest( + GetDigestAlgName(tstInfo.MessageImprintAlgOid), + signerInfo.GetSignature()); + + if (!Arrays.ConstantTimeAreEqual(expectedDigest, tstInfo.GetMessageImprintDigest())) + throw new TspValidationException("Incorrect digest in message imprint"); + + timestamps.Add(timeStampToken); + } + catch (SecurityUtilityException) + { + throw new TspValidationException("Unknown hash algorithm specified in timestamp"); + } + catch (Exception) + { + throw new TspValidationException("Timestamp could not be parsed"); + } + } + } + } + + return timestamps; + } + + /** + * Validate the passed in certificate as being of the correct type to be used + * for time stamping. To be valid it must have an ExtendedKeyUsage extension + * which has a key purpose identifier of id-kp-timeStamping. + * + * @param cert the certificate of interest. + * @throws TspValidationException if the certicate fails on one of the check points. + */ + public static void ValidateCertificate( + X509Certificate cert) + { + if (cert.Version != 3) + throw new ArgumentException("Certificate must have an ExtendedKeyUsage extension."); + + Asn1OctetString ext = cert.GetExtensionValue(X509Extensions.ExtendedKeyUsage); + if (ext == null) + throw new TspValidationException("Certificate must have an ExtendedKeyUsage extension."); + + if (!cert.GetCriticalExtensionOids().Contains(X509Extensions.ExtendedKeyUsage.Id)) + throw new TspValidationException("Certificate must have an ExtendedKeyUsage extension marked as critical."); + + try + { + ExtendedKeyUsage extKey = ExtendedKeyUsage.GetInstance( + Asn1Object.FromByteArray(ext.GetOctets())); + + if (!extKey.HasKeyPurposeId(KeyPurposeID.IdKPTimeStamping) || extKey.Count != 1) + throw new TspValidationException("ExtendedKeyUsage not solely time stamping."); + } + catch (IOException) + { + throw new TspValidationException("cannot process ExtendedKeyUsage extension"); + } + } + + /// <summary> + /// Return the digest algorithm using one of the standard JCA string + /// representations rather than the algorithm identifier (if possible). + /// </summary> + internal static string GetDigestAlgName( + string digestAlgOID) + { + string digestName = (string) digestNames[digestAlgOID]; + + return digestName != null ? digestName : digestAlgOID; + } + + internal static int GetDigestLength( + string digestAlgOID) + { + if (!digestLengths.Contains(digestAlgOID)) + throw new TspException("digest algorithm cannot be found."); + + return (int)digestLengths[digestAlgOID]; + } + + internal static IDigest CreateDigestInstance( + String digestAlgOID) + { + string digestName = GetDigestAlgName(digestAlgOID); + + return DigestUtilities.GetDigest(digestName); + } + + internal static ISet GetCriticalExtensionOids(X509Extensions extensions) + { + if (extensions == null) + return EmptySet; + + return CollectionUtilities.ReadOnly(new HashSet(extensions.GetCriticalExtensionOids())); + } + + internal static ISet GetNonCriticalExtensionOids(X509Extensions extensions) + { + if (extensions == null) + return EmptySet; + + // TODO: should probably produce a set that imposes correct ordering + return CollectionUtilities.ReadOnly(new HashSet(extensions.GetNonCriticalExtensionOids())); + } + + internal static IList GetExtensionOids(X509Extensions extensions) + { + if (extensions == null) + return EmptyList; + + return CollectionUtilities.ReadOnly(Platform.CreateArrayList(extensions.GetExtensionOids())); + } + } +} diff --git a/Crypto/src/tsp/TSPValidationException.cs b/Crypto/src/tsp/TSPValidationException.cs new file mode 100644
index 000000000..d8243e65f --- /dev/null +++ b/Crypto/src/tsp/TSPValidationException.cs
@@ -0,0 +1,39 @@ +namespace Org.BouncyCastle.Tsp +{ + /** + * Exception thrown if a TSP request or response fails to validate. + * <p> + * If a failure code is associated with the exception it can be retrieved using + * the getFailureCode() method.</p> + */ + public class TspValidationException + : TspException + { + private int failureCode; + + public TspValidationException( + string message) + : base(message) + { + this.failureCode = -1; + } + + public TspValidationException( + string message, + int failureCode) + : base(message) + { + this.failureCode = failureCode; + } + + /** + * Return the failure code associated with this exception - if one is set. + * + * @return the failure code if set, -1 otherwise. + */ + public int FailureCode + { + get { return failureCode; } + } + } +} diff --git a/Crypto/src/tsp/TimeStampRequest.cs b/Crypto/src/tsp/TimeStampRequest.cs new file mode 100644
index 000000000..6b9699379 --- /dev/null +++ b/Crypto/src/tsp/TimeStampRequest.cs
@@ -0,0 +1,196 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cmp; +using Org.BouncyCastle.Asn1.Tsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Tsp +{ + /** + * Base class for an RFC 3161 Time Stamp Request. + */ + public class TimeStampRequest + : X509ExtensionBase + { + private TimeStampReq req; + private X509Extensions extensions; + + public TimeStampRequest( + TimeStampReq req) + { + this.req = req; + this.extensions = req.Extensions; + } + + /** + * Create a TimeStampRequest from the past in byte array. + * + * @param req byte array containing the request. + * @throws IOException if the request is malformed. + */ + public TimeStampRequest( + byte[] req) + : this(new Asn1InputStream(req)) + { + } + + /** + * Create a TimeStampRequest from the past in input stream. + * + * @param in input stream containing the request. + * @throws IOException if the request is malformed. + */ + public TimeStampRequest( + Stream input) + : this(new Asn1InputStream(input)) + { + } + + private TimeStampRequest( + Asn1InputStream str) + { + try + { + this.req = TimeStampReq.GetInstance(str.ReadObject()); + } + catch (InvalidCastException e) + { + throw new IOException("malformed request: " + e); + } + catch (ArgumentException e) + { + throw new IOException("malformed request: " + e); + } + } + + public int Version + { + get { return req.Version.Value.IntValue; } + } + + public string MessageImprintAlgOid + { + get { return req.MessageImprint.HashAlgorithm.ObjectID.Id; } + } + + public byte[] GetMessageImprintDigest() + { + return req.MessageImprint.GetHashedMessage(); + } + + public string ReqPolicy + { + get + { + return req.ReqPolicy == null + ? null + : req.ReqPolicy.Id; + } + } + + public BigInteger Nonce + { + get + { + return req.Nonce == null + ? null + : req.Nonce.Value; + } + } + + public bool CertReq + { + get + { + return req.CertReq == null + ? false + : req.CertReq.IsTrue; + } + } + + /** + * Validate the timestamp request, checking the digest to see if it is of an + * accepted type and whether it is of the correct length for the algorithm specified. + * + * @param algorithms a set of string OIDS giving accepted algorithms. + * @param policies if non-null a set of policies we are willing to sign under. + * @param extensions if non-null a set of extensions we are willing to accept. + * @throws TspException if the request is invalid, or processing fails. + */ + public void Validate( + IList algorithms, + IList policies, + IList extensions) + { + if (!algorithms.Contains(this.MessageImprintAlgOid)) + { + throw new TspValidationException("request contains unknown algorithm.", PkiFailureInfo.BadAlg); + } + + if (policies != null && this.ReqPolicy != null && !policies.Contains(this.ReqPolicy)) + { + throw new TspValidationException("request contains unknown policy.", PkiFailureInfo.UnacceptedPolicy); + } + + if (this.Extensions != null && extensions != null) + { + foreach (DerObjectIdentifier oid in this.Extensions.ExtensionOids) + { + if (!extensions.Contains(oid.Id)) + { + throw new TspValidationException("request contains unknown extension.", + PkiFailureInfo.UnacceptedExtension); + } + } + } + + int digestLength = TspUtil.GetDigestLength(this.MessageImprintAlgOid); + + if (digestLength != this.GetMessageImprintDigest().Length) + { + throw new TspValidationException("imprint digest the wrong length.", + PkiFailureInfo.BadDataFormat); + } + } + + /** + * return the ASN.1 encoded representation of this object. + */ + public byte[] GetEncoded() + { + return req.GetEncoded(); + } + + internal X509Extensions Extensions + { + get { return req.Extensions; } + } + + public virtual bool HasExtensions + { + get { return extensions != null; } + } + + public virtual X509Extension GetExtension(DerObjectIdentifier oid) + { + return extensions == null ? null : extensions.GetExtension(oid); + } + + public virtual IList GetExtensionOids() + { + return TspUtil.GetExtensionOids(extensions); + } + + protected override X509Extensions GetX509Extensions() + { + return Extensions; + } + } +} diff --git a/Crypto/src/tsp/TimeStampRequestGenerator.cs b/Crypto/src/tsp/TimeStampRequestGenerator.cs new file mode 100644
index 000000000..2c698e476 --- /dev/null +++ b/Crypto/src/tsp/TimeStampRequestGenerator.cs
@@ -0,0 +1,139 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Tsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Math; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Tsp +{ + /** + * Generator for RFC 3161 Time Stamp Request objects. + */ + public class TimeStampRequestGenerator + { + private DerObjectIdentifier reqPolicy; + + private DerBoolean certReq; + + private IDictionary extensions = Platform.CreateHashtable(); + private IList extOrdering = Platform.CreateArrayList(); + + public void SetReqPolicy( + string reqPolicy) + { + this.reqPolicy = new DerObjectIdentifier(reqPolicy); + } + + public void SetCertReq( + bool certReq) + { + this.certReq = DerBoolean.GetInstance(certReq); + } + + /** + * add a given extension field for the standard extensions tag (tag 3) + * @throws IOException + */ + [Obsolete("Use method taking DerObjectIdentifier")] + public void AddExtension( + string oid, + bool critical, + Asn1Encodable value) + { + this.AddExtension(oid, critical, value.GetEncoded()); + } + + /** + * add a given extension field for the standard extensions tag + * The value parameter becomes the contents of the octet string associated + * with the extension. + */ + [Obsolete("Use method taking DerObjectIdentifier")] + public void AddExtension( + string oid, + bool critical, + byte[] value) + { + DerObjectIdentifier derOid = new DerObjectIdentifier(oid); + extensions[derOid] = new X509Extension(critical, new DerOctetString(value)); + extOrdering.Add(derOid); + } + + /** + * add a given extension field for the standard extensions tag (tag 3) + * @throws IOException + */ + public virtual void AddExtension( + DerObjectIdentifier oid, + bool critical, + Asn1Encodable extValue) + { + this.AddExtension(oid, critical, extValue.GetEncoded()); + } + + /** + * add a given extension field for the standard extensions tag + * The value parameter becomes the contents of the octet string associated + * with the extension. + */ + public virtual void AddExtension( + DerObjectIdentifier oid, + bool critical, + byte[] extValue) + { + extensions.Add(oid, new X509Extension(critical, new DerOctetString(extValue))); + extOrdering.Add(oid); + } + + public TimeStampRequest Generate( + string digestAlgorithm, + byte[] digest) + { + return this.Generate(digestAlgorithm, digest, null); + } + + public TimeStampRequest Generate( + string digestAlgorithmOid, + byte[] digest, + BigInteger nonce) + { + if (digestAlgorithmOid == null) + { + throw new ArgumentException("No digest algorithm specified"); + } + + DerObjectIdentifier digestAlgOid = new DerObjectIdentifier(digestAlgorithmOid); + + AlgorithmIdentifier algID = new AlgorithmIdentifier(digestAlgOid, DerNull.Instance); + MessageImprint messageImprint = new MessageImprint(algID, digest); + + X509Extensions ext = null; + + if (extOrdering.Count != 0) + { + ext = new X509Extensions(extOrdering, extensions); + } + + DerInteger derNonce = nonce == null + ? null + : new DerInteger(nonce); + + return new TimeStampRequest( + new TimeStampReq(messageImprint, reqPolicy, derNonce, certReq, ext)); + } + + public virtual TimeStampRequest Generate(DerObjectIdentifier digestAlgorithm, byte[] digest) + { + return Generate(digestAlgorithm.Id, digest); + } + + public virtual TimeStampRequest Generate(DerObjectIdentifier digestAlgorithm, byte[] digest, BigInteger nonce) + { + return Generate(digestAlgorithm.Id, digest, nonce); + } + } +} diff --git a/Crypto/src/tsp/TimeStampResponse.cs b/Crypto/src/tsp/TimeStampResponse.cs new file mode 100644
index 000000000..069521111 --- /dev/null +++ b/Crypto/src/tsp/TimeStampResponse.cs
@@ -0,0 +1,184 @@ +using System; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cmp; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Tsp; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Tsp +{ + /** + * Base class for an RFC 3161 Time Stamp Response object. + */ + public class TimeStampResponse + { + private TimeStampResp resp; + private TimeStampToken timeStampToken; + + public TimeStampResponse( + TimeStampResp resp) + { + this.resp = resp; + + if (resp.TimeStampToken != null) + { + timeStampToken = new TimeStampToken(resp.TimeStampToken); + } + } + + /** + * Create a TimeStampResponse from a byte array containing an ASN.1 encoding. + * + * @param resp the byte array containing the encoded response. + * @throws TspException if the response is malformed. + * @throws IOException if the byte array doesn't represent an ASN.1 encoding. + */ + public TimeStampResponse( + byte[] resp) + : this(readTimeStampResp(new Asn1InputStream(resp))) + { + } + + /** + * Create a TimeStampResponse from an input stream containing an ASN.1 encoding. + * + * @param input the input stream containing the encoded response. + * @throws TspException if the response is malformed. + * @throws IOException if the stream doesn't represent an ASN.1 encoding. + */ + public TimeStampResponse( + Stream input) + : this(readTimeStampResp(new Asn1InputStream(input))) + { + } + + private static TimeStampResp readTimeStampResp( + Asn1InputStream input) + { + try + { + return TimeStampResp.GetInstance(input.ReadObject()); + } + catch (ArgumentException e) + { + throw new TspException("malformed timestamp response: " + e, e); + } + catch (InvalidCastException e) + { + throw new TspException("malformed timestamp response: " + e, e); + } + } + + public int Status + { + get { return resp.Status.Status.IntValue; } + } + + public string GetStatusString() + { + if (resp.Status.StatusString == null) + { + return null; + } + + StringBuilder statusStringBuf = new StringBuilder(); + PkiFreeText text = resp.Status.StatusString; + for (int i = 0; i != text.Count; i++) + { + statusStringBuf.Append(text[i].GetString()); + } + + return statusStringBuf.ToString(); + } + + public PkiFailureInfo GetFailInfo() + { + if (resp.Status.FailInfo == null) + { + return null; + } + + return new PkiFailureInfo(resp.Status.FailInfo); + } + + public TimeStampToken TimeStampToken + { + get { return timeStampToken; } + } + + /** + * Check this response against to see if it a well formed response for + * the passed in request. Validation will include checking the time stamp + * token if the response status is GRANTED or GRANTED_WITH_MODS. + * + * @param request the request to be checked against + * @throws TspException if the request can not match this response. + */ + public void Validate( + TimeStampRequest request) + { + TimeStampToken tok = this.TimeStampToken; + + if (tok != null) + { + TimeStampTokenInfo tstInfo = tok.TimeStampInfo; + + if (request.Nonce != null && !request.Nonce.Equals(tstInfo.Nonce)) + { + throw new TspValidationException("response contains wrong nonce value."); + } + + if (this.Status != (int) PkiStatus.Granted && this.Status != (int) PkiStatus.GrantedWithMods) + { + throw new TspValidationException("time stamp token found in failed request."); + } + + if (!Arrays.ConstantTimeAreEqual(request.GetMessageImprintDigest(), tstInfo.GetMessageImprintDigest())) + { + throw new TspValidationException("response for different message imprint digest."); + } + + if (!tstInfo.MessageImprintAlgOid.Equals(request.MessageImprintAlgOid)) + { + throw new TspValidationException("response for different message imprint algorithm."); + } + + Asn1.Cms.Attribute scV1 = tok.SignedAttributes[PkcsObjectIdentifiers.IdAASigningCertificate]; + Asn1.Cms.Attribute scV2 = tok.SignedAttributes[PkcsObjectIdentifiers.IdAASigningCertificateV2]; + + if (scV1 == null && scV2 == null) + { + throw new TspValidationException("no signing certificate attribute present."); + } + + if (scV1 != null && scV2 != null) + { + /* + * RFC 5035 5.4. If both attributes exist in a single message, + * they are independently evaluated. + */ + } + + if (request.ReqPolicy != null && !request.ReqPolicy.Equals(tstInfo.Policy)) + { + throw new TspValidationException("TSA policy wrong for request."); + } + } + else if (this.Status == (int) PkiStatus.Granted || this.Status == (int) PkiStatus.GrantedWithMods) + { + throw new TspValidationException("no time stamp token found and one expected."); + } + } + + /** + * return the ASN.1 encoded representation of this object. + */ + public byte[] GetEncoded() + { + return resp.GetEncoded(); + } + } +} diff --git a/Crypto/src/tsp/TimeStampResponseGenerator.cs b/Crypto/src/tsp/TimeStampResponseGenerator.cs new file mode 100644
index 000000000..8d798de67 --- /dev/null +++ b/Crypto/src/tsp/TimeStampResponseGenerator.cs
@@ -0,0 +1,210 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cmp; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.Tsp; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities.Date; + +namespace Org.BouncyCastle.Tsp +{ + /** + * Generator for RFC 3161 Time Stamp Responses. + */ + public class TimeStampResponseGenerator + { + private PkiStatus status; + + private Asn1EncodableVector statusStrings; + + private int failInfo; + private TimeStampTokenGenerator tokenGenerator; + private IList acceptedAlgorithms; + private IList acceptedPolicies; + private IList acceptedExtensions; + + public TimeStampResponseGenerator( + TimeStampTokenGenerator tokenGenerator, + IList acceptedAlgorithms) + : this(tokenGenerator, acceptedAlgorithms, null, null) + { + } + + public TimeStampResponseGenerator( + TimeStampTokenGenerator tokenGenerator, + IList acceptedAlgorithms, + IList acceptedPolicy) + : this(tokenGenerator, acceptedAlgorithms, acceptedPolicy, null) + { + } + + public TimeStampResponseGenerator( + TimeStampTokenGenerator tokenGenerator, + IList acceptedAlgorithms, + IList acceptedPolicies, + IList acceptedExtensions) + { + this.tokenGenerator = tokenGenerator; + this.acceptedAlgorithms = acceptedAlgorithms; + this.acceptedPolicies = acceptedPolicies; + this.acceptedExtensions = acceptedExtensions; + + statusStrings = new Asn1EncodableVector(); + } + + private void AddStatusString(string statusString) + { + statusStrings.Add(new DerUtf8String(statusString)); + } + + private void SetFailInfoField(int field) + { + failInfo |= field; + } + + private PkiStatusInfo GetPkiStatusInfo() + { + Asn1EncodableVector v = new Asn1EncodableVector( + new DerInteger((int)status)); + + if (statusStrings.Count > 0) + { + v.Add(new PkiFreeText(new DerSequence(statusStrings))); + } + + if (failInfo != 0) + { + v.Add(new FailInfo(failInfo)); + } + + return new PkiStatusInfo(new DerSequence(v)); + } + + public TimeStampResponse Generate( + TimeStampRequest request, + BigInteger serialNumber, + DateTime genTime) + { + return Generate(request, serialNumber, new DateTimeObject(genTime)); + } + + /** + * Return an appropriate TimeStampResponse. + * <p> + * If genTime is null a timeNotAvailable error response will be returned. + * + * @param request the request this response is for. + * @param serialNumber serial number for the response token. + * @param genTime generation time for the response token. + * @param provider provider to use for signature calculation. + * @return + * @throws NoSuchAlgorithmException + * @throws NoSuchProviderException + * @throws TSPException + * </p> + */ + public TimeStampResponse Generate( + TimeStampRequest request, + BigInteger serialNumber, + DateTimeObject genTime) + { + TimeStampResp resp; + + try + { + if (genTime == null) + throw new TspValidationException("The time source is not available.", + PkiFailureInfo.TimeNotAvailable); + + request.Validate(acceptedAlgorithms, acceptedPolicies, acceptedExtensions); + + this.status = PkiStatus.Granted; + this.AddStatusString("Operation Okay"); + + PkiStatusInfo pkiStatusInfo = GetPkiStatusInfo(); + + ContentInfo tstTokenContentInfo; + try + { + TimeStampToken token = tokenGenerator.Generate(request, serialNumber, genTime.Value); + byte[] encoded = token.ToCmsSignedData().GetEncoded(); + + tstTokenContentInfo = ContentInfo.GetInstance(Asn1Object.FromByteArray(encoded)); + } + catch (IOException e) + { + throw new TspException("Timestamp token received cannot be converted to ContentInfo", e); + } + + resp = new TimeStampResp(pkiStatusInfo, tstTokenContentInfo); + } + catch (TspValidationException e) + { + status = PkiStatus.Rejection; + + this.SetFailInfoField(e.FailureCode); + this.AddStatusString(e.Message); + + PkiStatusInfo pkiStatusInfo = GetPkiStatusInfo(); + + resp = new TimeStampResp(pkiStatusInfo, null); + } + + try + { + return new TimeStampResponse(resp); + } + catch (IOException e) + { + throw new TspException("created badly formatted response!", e); + } + } + + class FailInfo + : DerBitString + { + internal FailInfo( + int failInfoValue) + : base(GetBytes(failInfoValue), GetPadBits(failInfoValue)) + { + } + } + + /** + * Generate a TimeStampResponse with chosen status and FailInfoField. + * + * @param status the PKIStatus to set. + * @param failInfoField the FailInfoField to set. + * @param statusString an optional string describing the failure. + * @return a TimeStampResponse with a failInfoField and optional statusString + * @throws TSPException in case the response could not be created + */ + public TimeStampResponse GenerateFailResponse(PkiStatus status, int failInfoField, string statusString) + { + this.status = status; + + this.SetFailInfoField(failInfoField); + + if (statusString != null) + { + this.AddStatusString(statusString); + } + + PkiStatusInfo pkiStatusInfo = GetPkiStatusInfo(); + + TimeStampResp resp = new TimeStampResp(pkiStatusInfo, null); + + try + { + return new TimeStampResponse(resp); + } + catch (IOException e) + { + throw new TspException("created badly formatted response!", e); + } + } + } +} diff --git a/Crypto/src/tsp/TimeStampToken.cs b/Crypto/src/tsp/TimeStampToken.cs new file mode 100644
index 000000000..51a9592dc --- /dev/null +++ b/Crypto/src/tsp/TimeStampToken.cs
@@ -0,0 +1,305 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ess; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Tsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Cms; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.Tsp +{ + public class TimeStampToken + { + private readonly CmsSignedData tsToken; + private readonly SignerInformation tsaSignerInfo; +// private readonly DateTime genTime; + private readonly TimeStampTokenInfo tstInfo; + private readonly CertID certID; + + public TimeStampToken( + Asn1.Cms.ContentInfo contentInfo) + : this(new CmsSignedData(contentInfo)) + { + } + + public TimeStampToken( + CmsSignedData signedData) + { + this.tsToken = signedData; + + if (!this.tsToken.SignedContentType.Equals(PkcsObjectIdentifiers.IdCTTstInfo)) + { + throw new TspValidationException("ContentInfo object not for a time stamp."); + } + + ICollection signers = tsToken.GetSignerInfos().GetSigners(); + + if (signers.Count != 1) + { + throw new ArgumentException("Time-stamp token signed by " + + signers.Count + + " signers, but it must contain just the TSA signature."); + } + + + IEnumerator signerEnum = signers.GetEnumerator(); + + signerEnum.MoveNext(); + tsaSignerInfo = (SignerInformation) signerEnum.Current; + + try + { + CmsProcessable content = tsToken.SignedContent; + MemoryStream bOut = new MemoryStream(); + + content.Write(bOut); + + this.tstInfo = new TimeStampTokenInfo( + TstInfo.GetInstance( + Asn1Object.FromByteArray(bOut.ToArray()))); + + Asn1.Cms.Attribute attr = tsaSignerInfo.SignedAttributes[ + PkcsObjectIdentifiers.IdAASigningCertificate]; + +// if (attr == null) +// { +// throw new TspValidationException( +// "no signing certificate attribute found, time stamp invalid."); +// } +// +// SigningCertificate signCert = SigningCertificate.GetInstance( +// attr.AttrValues[0]); +// +// this.certID = EssCertID.GetInstance(signCert.GetCerts()[0]); + + if (attr != null) + { + SigningCertificate signCert = SigningCertificate.GetInstance(attr.AttrValues[0]); + + this.certID = new CertID(EssCertID.GetInstance(signCert.GetCerts()[0])); + } + else + { + attr = tsaSignerInfo.SignedAttributes[PkcsObjectIdentifiers.IdAASigningCertificateV2]; + + if (attr == null) + throw new TspValidationException("no signing certificate attribute found, time stamp invalid."); + + SigningCertificateV2 signCertV2 = SigningCertificateV2.GetInstance(attr.AttrValues[0]); + + this.certID = new CertID(EssCertIDv2.GetInstance(signCertV2.GetCerts()[0])); + } + } + catch (CmsException e) + { + throw new TspException(e.Message, e.InnerException); + } + } + + public TimeStampTokenInfo TimeStampInfo + { + get { return tstInfo; } + } + + public SignerID SignerID + { + get { return tsaSignerInfo.SignerID; } + } + + public Asn1.Cms.AttributeTable SignedAttributes + { + get { return tsaSignerInfo.SignedAttributes; } + } + + public Asn1.Cms.AttributeTable UnsignedAttributes + { + get { return tsaSignerInfo.UnsignedAttributes; } + } + + public IX509Store GetCertificates( + string type) + { + return tsToken.GetCertificates(type); + } + + public IX509Store GetCrls( + string type) + { + return tsToken.GetCrls(type); + } + + public IX509Store GetAttributeCertificates( + string type) + { + return tsToken.GetAttributeCertificates(type); + } + + /** + * Validate the time stamp token. + * <p> + * To be valid the token must be signed by the passed in certificate and + * the certificate must be the one referred to by the SigningCertificate + * attribute included in the hashed attributes of the token. The + * certificate must also have the ExtendedKeyUsageExtension with only + * KeyPurposeID.IdKPTimeStamping and have been valid at the time the + * timestamp was created. + * </p> + * <p> + * A successful call to validate means all the above are true. + * </p> + */ + public void Validate( + X509Certificate cert) + { + try + { + byte[] hash = DigestUtilities.CalculateDigest( + certID.GetHashAlgorithmName(), cert.GetEncoded()); + + if (!Arrays.ConstantTimeAreEqual(certID.GetCertHash(), hash)) + { + throw new TspValidationException("certificate hash does not match certID hash."); + } + + if (certID.IssuerSerial != null) + { + if (!certID.IssuerSerial.Serial.Value.Equals(cert.SerialNumber)) + { + throw new TspValidationException("certificate serial number does not match certID for signature."); + } + + GeneralName[] names = certID.IssuerSerial.Issuer.GetNames(); + X509Name principal = PrincipalUtilities.GetIssuerX509Principal(cert); + bool found = false; + + for (int i = 0; i != names.Length; i++) + { + if (names[i].TagNo == 4 + && X509Name.GetInstance(names[i].Name).Equivalent(principal)) + { + found = true; + break; + } + } + + if (!found) + { + throw new TspValidationException("certificate name does not match certID for signature. "); + } + } + + TspUtil.ValidateCertificate(cert); + + cert.CheckValidity(tstInfo.GenTime); + + if (!tsaSignerInfo.Verify(cert)) + { + throw new TspValidationException("signature not created by certificate."); + } + } + catch (CmsException e) + { + if (e.InnerException != null) + { + throw new TspException(e.Message, e.InnerException); + } + + throw new TspException("CMS exception: " + e, e); + } + catch (CertificateEncodingException e) + { + throw new TspException("problem processing certificate: " + e, e); + } + catch (SecurityUtilityException e) + { + throw new TspException("cannot find algorithm: " + e.Message, e); + } + } + + /** + * Return the underlying CmsSignedData object. + * + * @return the underlying CMS structure. + */ + public CmsSignedData ToCmsSignedData() + { + return tsToken; + } + + /** + * Return a ASN.1 encoded byte stream representing the encoded object. + * + * @throws IOException if encoding fails. + */ + public byte[] GetEncoded() + { + return tsToken.GetEncoded(); + } + + + // perhaps this should be done using an interface on the ASN.1 classes... + private class CertID + { + private EssCertID certID; + private EssCertIDv2 certIDv2; + + internal CertID(EssCertID certID) + { + this.certID = certID; + this.certIDv2 = null; + } + + internal CertID(EssCertIDv2 certID) + { + this.certIDv2 = certID; + this.certID = null; + } + + public string GetHashAlgorithmName() + { + if (certID != null) + return "SHA-1"; + + if (NistObjectIdentifiers.IdSha256.Equals(certIDv2.HashAlgorithm.ObjectID)) + return "SHA-256"; + + return certIDv2.HashAlgorithm.ObjectID.Id; + } + + public AlgorithmIdentifier GetHashAlgorithm() + { + return (certID != null) + ? new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1) + : certIDv2.HashAlgorithm; + } + + public byte[] GetCertHash() + { + return certID != null + ? certID.GetCertHash() + : certIDv2.GetCertHash(); + } + + public IssuerSerial IssuerSerial + { + get + { + return certID != null + ? certID.IssuerSerial + : certIDv2.IssuerSerial; + } + } + } + } +} diff --git a/Crypto/src/tsp/TimeStampTokenGenerator.cs b/Crypto/src/tsp/TimeStampTokenGenerator.cs new file mode 100644
index 000000000..07eddd4b9 --- /dev/null +++ b/Crypto/src/tsp/TimeStampTokenGenerator.cs
@@ -0,0 +1,245 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ess; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Tsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Cms; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.Tsp +{ + public class TimeStampTokenGenerator + { + private int accuracySeconds = -1; + private int accuracyMillis = -1; + private int accuracyMicros = -1; + private bool ordering = false; + private GeneralName tsa = null; + private string tsaPolicyOID; + + private AsymmetricKeyParameter key; + private X509Certificate cert; + private string digestOID; + private Asn1.Cms.AttributeTable signedAttr; + private Asn1.Cms.AttributeTable unsignedAttr; + private IX509Store x509Certs; + private IX509Store x509Crls; + + /** + * basic creation - only the default attributes will be included here. + */ + public TimeStampTokenGenerator( + AsymmetricKeyParameter key, + X509Certificate cert, + string digestOID, + string tsaPolicyOID) + : this(key, cert, digestOID, tsaPolicyOID, null, null) + { + } + + /** + * create with a signer with extra signed/unsigned attributes. + */ + public TimeStampTokenGenerator( + AsymmetricKeyParameter key, + X509Certificate cert, + string digestOID, + string tsaPolicyOID, + Asn1.Cms.AttributeTable signedAttr, + Asn1.Cms.AttributeTable unsignedAttr) + { + this.key = key; + this.cert = cert; + this.digestOID = digestOID; + this.tsaPolicyOID = tsaPolicyOID; + this.unsignedAttr = unsignedAttr; + + TspUtil.ValidateCertificate(cert); + + // + // Add the ESSCertID attribute + // + IDictionary signedAttrs; + if (signedAttr != null) + { + signedAttrs = signedAttr.ToDictionary(); + } + else + { + signedAttrs = Platform.CreateHashtable(); + } + + try + { + byte[] hash = DigestUtilities.CalculateDigest("SHA-1", cert.GetEncoded()); + + EssCertID essCertid = new EssCertID(hash); + + Asn1.Cms.Attribute attr = new Asn1.Cms.Attribute( + PkcsObjectIdentifiers.IdAASigningCertificate, + new DerSet(new SigningCertificate(essCertid))); + + signedAttrs[attr.AttrType] = attr; + } + catch (CertificateEncodingException e) + { + throw new TspException("Exception processing certificate.", e); + } + catch (SecurityUtilityException e) + { + throw new TspException("Can't find a SHA-1 implementation.", e); + } + + this.signedAttr = new Asn1.Cms.AttributeTable(signedAttrs); + } + + public void SetCertificates( + IX509Store certificates) + { + this.x509Certs = certificates; + } + + public void SetCrls( + IX509Store crls) + { + this.x509Crls = crls; + } + + public void SetAccuracySeconds( + int accuracySeconds) + { + this.accuracySeconds = accuracySeconds; + } + + public void SetAccuracyMillis( + int accuracyMillis) + { + this.accuracyMillis = accuracyMillis; + } + + public void SetAccuracyMicros( + int accuracyMicros) + { + this.accuracyMicros = accuracyMicros; + } + + public void SetOrdering( + bool ordering) + { + this.ordering = ordering; + } + + public void SetTsa( + GeneralName tsa) + { + this.tsa = tsa; + } + + //------------------------------------------------------------------------------ + + public TimeStampToken Generate( + TimeStampRequest request, + BigInteger serialNumber, + DateTime genTime) + { + DerObjectIdentifier digestAlgOID = new DerObjectIdentifier(request.MessageImprintAlgOid); + + AlgorithmIdentifier algID = new AlgorithmIdentifier(digestAlgOID, DerNull.Instance); + MessageImprint messageImprint = new MessageImprint(algID, request.GetMessageImprintDigest()); + + Accuracy accuracy = null; + if (accuracySeconds > 0 || accuracyMillis > 0 || accuracyMicros > 0) + { + DerInteger seconds = null; + if (accuracySeconds > 0) + { + seconds = new DerInteger(accuracySeconds); + } + + DerInteger millis = null; + if (accuracyMillis > 0) + { + millis = new DerInteger(accuracyMillis); + } + + DerInteger micros = null; + if (accuracyMicros > 0) + { + micros = new DerInteger(accuracyMicros); + } + + accuracy = new Accuracy(seconds, millis, micros); + } + + DerBoolean derOrdering = null; + if (ordering) + { + derOrdering = DerBoolean.GetInstance(ordering); + } + + DerInteger nonce = null; + if (request.Nonce != null) + { + nonce = new DerInteger(request.Nonce); + } + + DerObjectIdentifier tsaPolicy = new DerObjectIdentifier(tsaPolicyOID); + if (request.ReqPolicy != null) + { + tsaPolicy = new DerObjectIdentifier(request.ReqPolicy); + } + + TstInfo tstInfo = new TstInfo(tsaPolicy, messageImprint, + new DerInteger(serialNumber), new DerGeneralizedTime(genTime), accuracy, + derOrdering, nonce, tsa, request.Extensions); + + try + { + CmsSignedDataGenerator signedDataGenerator = new CmsSignedDataGenerator(); + + byte[] derEncodedTstInfo = tstInfo.GetDerEncoded(); + + if (request.CertReq) + { + signedDataGenerator.AddCertificates(x509Certs); + } + + signedDataGenerator.AddCrls(x509Crls); + signedDataGenerator.AddSigner(key, cert, digestOID, signedAttr, unsignedAttr); + + CmsSignedData signedData = signedDataGenerator.Generate( + PkcsObjectIdentifiers.IdCTTstInfo.Id, + new CmsProcessableByteArray(derEncodedTstInfo), + true); + + return new TimeStampToken(signedData); + } + catch (CmsException cmsEx) + { + throw new TspException("Error generating time-stamp token", cmsEx); + } + catch (IOException e) + { + throw new TspException("Exception encoding info", e); + } + catch (X509StoreException e) + { + throw new TspException("Exception handling CertStore", e); + } +// catch (InvalidAlgorithmParameterException e) +// { +// throw new TspException("Exception handling CertStore CRLs", e); +// } + } + } +} diff --git a/Crypto/src/tsp/TimeStampTokenInfo.cs b/Crypto/src/tsp/TimeStampTokenInfo.cs new file mode 100644
index 000000000..5027a87c4 --- /dev/null +++ b/Crypto/src/tsp/TimeStampTokenInfo.cs
@@ -0,0 +1,107 @@ +using System; + +using Org.BouncyCastle.Asn1.Tsp; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Tsp +{ + public class TimeStampTokenInfo + { + private TstInfo tstInfo; + private DateTime genTime; + + public TimeStampTokenInfo( + TstInfo tstInfo) + { + this.tstInfo = tstInfo; + + try + { + this.genTime = tstInfo.GenTime.ToDateTime(); + } + catch (Exception e) + { + throw new TspException("unable to parse genTime field: " + e.Message); + } + } + + public bool IsOrdered + { + get { return tstInfo.Ordering.IsTrue; } + } + + public Accuracy Accuracy + { + get { return tstInfo.Accuracy; } + } + + public DateTime GenTime + { + get { return genTime; } + } + + public GenTimeAccuracy GenTimeAccuracy + { + get + { + return this.Accuracy == null + ? null + : new GenTimeAccuracy(this.Accuracy); + } + } + + public string Policy + { + get { return tstInfo.Policy.Id; } + } + + public BigInteger SerialNumber + { + get { return tstInfo.SerialNumber.Value; } + } + + public GeneralName Tsa + { + get { return tstInfo.Tsa; } + } + + /** + * @return the nonce value, null if there isn't one. + */ + public BigInteger Nonce + { + get + { + return tstInfo.Nonce == null + ? null + : tstInfo.Nonce.Value; + } + } + + public AlgorithmIdentifier HashAlgorithm + { + get { return tstInfo.MessageImprint.HashAlgorithm; } + } + + public string MessageImprintAlgOid + { + get { return tstInfo.MessageImprint.HashAlgorithm.ObjectID.Id; } + } + + public byte[] GetMessageImprintDigest() + { + return tstInfo.MessageImprint.GetHashedMessage(); + } + + public byte[] GetEncoded() + { + return tstInfo.GetEncoded(); + } + + public TstInfo TstInfo + { + get { return tstInfo; } + } + } +} diff --git a/Crypto/src/util/Arrays.cs b/Crypto/src/util/Arrays.cs new file mode 100644
index 000000000..7fa05ebee --- /dev/null +++ b/Crypto/src/util/Arrays.cs
@@ -0,0 +1,226 @@ +using System; +using System.Text; + +namespace Org.BouncyCastle.Utilities +{ + + /// <summary> General array utilities.</summary> + public sealed class Arrays + { + private Arrays() + { + } + + public static bool AreEqual( + bool[] a, + bool[] b) + { + if (a == b) + return true; + + if (a == null || b == null) + return false; + + return HaveSameContents(a, b); + } + + public static bool AreEqual( + char[] a, + char[] b) + { + if (a == b) + return true; + + if (a == null || b == null) + return false; + + return HaveSameContents(a, b); + } + + /// <summary> + /// Are two arrays equal. + /// </summary> + /// <param name="a">Left side.</param> + /// <param name="b">Right side.</param> + /// <returns>True if equal.</returns> + public static bool AreEqual( + byte[] a, + byte[] b) + { + if (a == b) + return true; + + if (a == null || b == null) + return false; + + return HaveSameContents(a, b); + } + + [Obsolete("Use 'AreEqual' method instead")] + public static bool AreSame( + byte[] a, + byte[] b) + { + return AreEqual(a, b); + } + + /// <summary> + /// A constant time equals comparison - does not terminate early if + /// test will fail. + /// </summary> + /// <param name="a">first array</param> + /// <param name="b">second array</param> + /// <returns>true if arrays equal, false otherwise.</returns> + public static bool ConstantTimeAreEqual( + byte[] a, + byte[] b) + { + int i = a.Length; + if (i != b.Length) + return false; + int cmp = 0; + while (i != 0) + { + --i; + cmp |= (a[i] ^ b[i]); + } + return cmp == 0; + } + + public static bool AreEqual( + int[] a, + int[] b) + { + if (a == b) + return true; + + if (a == null || b == null) + return false; + + return HaveSameContents(a, b); + } + + private static bool HaveSameContents( + bool[] a, + bool[] b) + { + int i = a.Length; + if (i != b.Length) + return false; + while (i != 0) + { + --i; + if (a[i] != b[i]) + return false; + } + return true; + } + + private static bool HaveSameContents( + char[] a, + char[] b) + { + int i = a.Length; + if (i != b.Length) + return false; + while (i != 0) + { + --i; + if (a[i] != b[i]) + return false; + } + return true; + } + + private static bool HaveSameContents( + byte[] a, + byte[] b) + { + int i = a.Length; + if (i != b.Length) + return false; + while (i != 0) + { + --i; + if (a[i] != b[i]) + return false; + } + return true; + } + + private static bool HaveSameContents( + int[] a, + int[] b) + { + int i = a.Length; + if (i != b.Length) + return false; + while (i != 0) + { + --i; + if (a[i] != b[i]) + return false; + } + return true; + } + + public static string ToString( + object[] a) + { + StringBuilder sb = new StringBuilder('['); + if (a.Length > 0) + { + sb.Append(a[0]); + for (int index = 1; index < a.Length; ++index) + { + sb.Append(", ").Append(a[index]); + } + } + sb.Append(']'); + return sb.ToString(); + } + + public static int GetHashCode( + 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 byte[] Clone( + byte[] data) + { + return data == null ? null : (byte[]) data.Clone(); + } + + public static int[] Clone( + int[] data) + { + return data == null ? null : (int[]) data.Clone(); + } + + public static void Fill( + byte[] buf, + byte b) + { + int i = buf.Length; + while (i > 0) + { + buf[--i] = b; + } + } + } +} diff --git a/Crypto/src/util/BigIntegers.cs b/Crypto/src/util/BigIntegers.cs new file mode 100644
index 000000000..df82e1f22 --- /dev/null +++ b/Crypto/src/util/BigIntegers.cs
@@ -0,0 +1,72 @@ +using System; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Utilities +{ + /** + * BigInteger utilities. + */ + public sealed class BigIntegers + { + private const int MaxIterations = 1000; + + private BigIntegers() + { + } + + /** + * 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 n) + { + return n.ToByteArrayUnsigned(); + } + + /** + * 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, + // TODO Should have been just Random class + SecureRandom random) + { + int cmp = min.CompareTo(max); + if (cmp >= 0) + { + if (cmp > 0) + throw new ArgumentException("'min' may not be greater than 'max'"); + + return min; + } + + if (min.BitLength > max.BitLength / 2) + { + return CreateRandomInRange(BigInteger.Zero, max.Subtract(min), random).Add(min); + } + + for (int i = 0; i < MaxIterations; ++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/Crypto/src/util/Enums.cs b/Crypto/src/util/Enums.cs new file mode 100644
index 000000000..abab5266b --- /dev/null +++ b/Crypto/src/util/Enums.cs
@@ -0,0 +1,72 @@ +using System; +using System.Text; + +#if NETCF_1_0 || NETCF_2_0 || SILVERLIGHT || PORTABLE +using System.Collections; +using System.Reflection; +#endif + +using Org.BouncyCastle.Utilities.Date; + +namespace Org.BouncyCastle.Utilities +{ + internal sealed class Enums + { + private Enums() + { + } + + internal static Enum GetEnumValue(System.Type enumType, string s) + { + if (!enumType.IsEnum) + throw new ArgumentException("Not an enumeration type", "enumType"); + + // We only want to parse single named constants + if (s.Length > 0 && char.IsLetter(s[0]) && s.IndexOf(',') < 0) + { + s = s.Replace('-', '_'); + +#if NETCF_1_0 + FieldInfo field = enumType.GetField(s, BindingFlags.Static | BindingFlags.Public); + if (field != null) + { + return (Enum)field.GetValue(null); + } +#else + return (Enum)Enum.Parse(enumType, s, false); +#endif + } + + throw new ArgumentException(); + } + + internal static Array GetEnumValues(System.Type enumType) + { + if (!enumType.IsEnum) + throw new ArgumentException("Not an enumeration type", "enumType"); + +#if NETCF_1_0 || NETCF_2_0 || SILVERLIGHT || PORTABLE + IList result = Platform.CreateArrayList(); + FieldInfo[] fields = enumType.GetFields(BindingFlags.Static | BindingFlags.Public); + foreach (FieldInfo field in fields) + { + // Note: Argument to GetValue() ignored since the fields are static, + // but Silverlight for Windows Phone throws exception if we pass null + result.Add(field.GetValue(enumType)); + } + object[] arr = new object[result.Count]; + result.CopyTo(arr, 0); + return arr; +#else + return Enum.GetValues(enumType); +#endif + } + + internal static Enum GetArbitraryValue(System.Type enumType) + { + Array values = GetEnumValues(enumType); + int pos = (int)(DateTimeUtilities.CurrentUnixMs() & int.MaxValue) % values.Length; + return (Enum)values.GetValue(pos); + } + } +} diff --git a/Crypto/src/util/Platform.cs b/Crypto/src/util/Platform.cs new file mode 100644
index 000000000..52ace89e5 --- /dev/null +++ b/Crypto/src/util/Platform.cs
@@ -0,0 +1,171 @@ +using System; +using System.IO; +using System.Text; + +#if SILVERLIGHT || PORTABLE +using System.Collections.Generic; +#else +using System.Collections; +#endif + +namespace Org.BouncyCastle.Utilities +{ + internal sealed class Platform + { + private Platform() + { + } + +#if NETCF_1_0 || NETCF_2_0 + private static string GetNewLine() + { + MemoryStream buf = new MemoryStream(); + StreamWriter w = new StreamWriter(buf, Encoding.UTF8); + w.WriteLine(); + w.Close(); + byte[] bs = buf.ToArray(); + return Encoding.UTF8.GetString(bs, 0, bs.Length); + } +#else + private static string GetNewLine() + { + return Environment.NewLine; + } +#endif + + internal static int CompareIgnoreCase(string a, string b) + { +#if (SILVERLIGHT || PORTABLE) + return String.Compare(a, b, StringComparison.OrdinalIgnoreCase); +#else + return String.Compare(a, b, true); +#endif + } + +#if NETCF_1_0 || NETCF_2_0 || SILVERLIGHT || PORTABLE + internal static string GetEnvironmentVariable( + string variable) + { + return null; + } +#else + internal static string GetEnvironmentVariable( + string variable) + { + try + { + return Environment.GetEnvironmentVariable(variable); + } + catch (System.Security.SecurityException) + { + // We don't have the required permission to read this environment variable, + // which is fine, just act as if it's not set + return null; + } + } +#endif + +#if NETCF_1_0 + internal static Exception CreateNotImplementedException( + string message) + { + return new Exception("Not implemented: " + message); + } + + internal static bool Equals( + object a, + object b) + { + return a == b || (a != null && b != null && a.Equals(b)); + } +#else + internal static Exception CreateNotImplementedException( + string message) + { + return new NotImplementedException(message); + } +#endif + +#if SILVERLIGHT || PORTABLE + internal static System.Collections.IList CreateArrayList() + { + return new List<object>(); + } + internal static System.Collections.IList CreateArrayList(int capacity) + { + return new List<object>(capacity); + } + internal static System.Collections.IList CreateArrayList(System.Collections.ICollection collection) + { + System.Collections.IList result = new List<object>(collection.Count); + foreach (object o in collection) + { + result.Add(o); + } + return result; + } + internal static System.Collections.IList CreateArrayList(System.Collections.IEnumerable collection) + { + System.Collections.IList result = new List<object>(); + foreach (object o in collection) + { + result.Add(o); + } + return result; + } + internal static System.Collections.IDictionary CreateHashtable() + { + return new Dictionary<object, object>(); + } + internal static System.Collections.IDictionary CreateHashtable(int capacity) + { + return new Dictionary<object, object>(capacity); + } + internal static System.Collections.IDictionary CreateHashtable(System.Collections.IDictionary dictionary) + { + System.Collections.IDictionary result = new Dictionary<object, object>(dictionary.Count); + foreach (System.Collections.DictionaryEntry entry in dictionary) + { + result.Add(entry.Key, entry.Value); + } + return result; + } +#else + internal static System.Collections.IList CreateArrayList() + { + return new ArrayList(); + } + internal static System.Collections.IList CreateArrayList(int capacity) + { + return new ArrayList(capacity); + } + internal static System.Collections.IList CreateArrayList(System.Collections.ICollection collection) + { + return new ArrayList(collection); + } + internal static System.Collections.IList CreateArrayList(System.Collections.IEnumerable collection) + { + ArrayList result = new ArrayList(); + foreach (object o in collection) + { + result.Add(o); + } + return result; + } + internal static System.Collections.IDictionary CreateHashtable() + { + return new Hashtable(); + } + internal static System.Collections.IDictionary CreateHashtable(int capacity) + { + return new Hashtable(capacity); + } + internal static System.Collections.IDictionary CreateHashtable(System.Collections.IDictionary dictionary) + { + return new Hashtable(dictionary); + } +#endif + + internal static readonly string NewLine = GetNewLine(); + } +} diff --git a/Crypto/src/util/Strings.cs b/Crypto/src/util/Strings.cs new file mode 100644
index 000000000..1d35920c9 --- /dev/null +++ b/Crypto/src/util/Strings.cs
@@ -0,0 +1,107 @@ +using System; +using System.Text; + +namespace Org.BouncyCastle.Utilities +{ + /// <summary> General string utilities.</summary> + public sealed class Strings + { + private Strings() + { + } + + internal static bool IsOneOf(string s, params string[] candidates) + { + foreach (string candidate in candidates) + { + if (s == candidate) + return true; + } + return false; + } + + public static string FromByteArray( + byte[] bs) + { + char[] cs = new char[bs.Length]; + for (int i = 0; i < cs.Length; ++i) + { + cs[i] = Convert.ToChar(bs[i]); + } + return new string(cs); + } + + public static byte[] ToByteArray( + char[] cs) + { + byte[] bs = new byte[cs.Length]; + for (int i = 0; i < bs.Length; ++i) + { + bs[i] = Convert.ToByte(cs[i]); + } + return bs; + } + + public static byte[] ToByteArray( + string s) + { + byte[] bs = new byte[s.Length]; + for (int i = 0; i < bs.Length; ++i) + { + bs[i] = Convert.ToByte(s[i]); + } + return bs; + } + + public static string FromAsciiByteArray( + byte[] bytes) + { +#if (SILVERLIGHT || PORTABLE) + // TODO Check for non-ASCII bytes in input? + return Encoding.UTF8.GetString(bytes, 0, bytes.Length); +#else + return Encoding.ASCII.GetString(bytes, 0, bytes.Length); +#endif + } + + public static byte[] ToAsciiByteArray( + char[] cs) + { +#if SILVERLIGHT || PORTABLE + // TODO Check for non-ASCII characters in input? + return Encoding.UTF8.GetBytes(cs); +#else + return Encoding.ASCII.GetBytes(cs); +#endif + } + + public static byte[] ToAsciiByteArray( + string s) + { +#if SILVERLIGHT || PORTABLE + // TODO Check for non-ASCII characters in input? + return Encoding.UTF8.GetBytes(s); +#else + return Encoding.ASCII.GetBytes(s); +#endif + } + + public static string FromUtf8ByteArray( + byte[] bytes) + { + return Encoding.UTF8.GetString(bytes, 0, bytes.Length); + } + + public static byte[] ToUtf8ByteArray( + char[] cs) + { + return Encoding.UTF8.GetBytes(cs); + } + + public static byte[] ToUtf8ByteArray( + string s) + { + return Encoding.UTF8.GetBytes(s); + } + } +} diff --git a/Crypto/src/util/collections/CollectionUtilities.cs b/Crypto/src/util/collections/CollectionUtilities.cs new file mode 100644
index 000000000..fd0bdcc7a --- /dev/null +++ b/Crypto/src/util/collections/CollectionUtilities.cs
@@ -0,0 +1,71 @@ +using System; +using System.Collections; +using System.Text; + +namespace Org.BouncyCastle.Utilities.Collections +{ + public sealed class CollectionUtilities + { + private CollectionUtilities() + { + } + + public static void AddRange(IList to, ICollection range) + { + foreach (object o in range) + { + to.Add(o); + } + } + + public static bool CheckElementsAreOfType( + IEnumerable e, + Type t) + { + foreach (object o in e) + { + if (!t.IsInstanceOfType(o)) + return false; + } + return true; + } + + public static IDictionary ReadOnly(IDictionary d) + { + return new UnmodifiableDictionaryProxy(d); + } + + public static IList ReadOnly(IList l) + { + return new UnmodifiableListProxy(l); + } + + public static ISet ReadOnly(ISet s) + { + return new UnmodifiableSetProxy(s); + } + + public static string ToString( + IEnumerable c) + { + StringBuilder sb = new StringBuilder("["); + + IEnumerator e = c.GetEnumerator(); + + if (e.MoveNext()) + { + sb.Append(e.Current.ToString()); + + while (e.MoveNext()) + { + sb.Append(", "); + sb.Append(e.Current.ToString()); + } + } + + sb.Append(']'); + + return sb.ToString(); + } + } +} diff --git a/Crypto/src/util/collections/EmptyEnumerable.cs b/Crypto/src/util/collections/EmptyEnumerable.cs new file mode 100644
index 000000000..a61a0789a --- /dev/null +++ b/Crypto/src/util/collections/EmptyEnumerable.cs
@@ -0,0 +1,44 @@ +using System; +using System.Collections; + +namespace Org.BouncyCastle.Utilities.Collections +{ + public sealed class EmptyEnumerable + : IEnumerable + { + public static readonly IEnumerable Instance = new EmptyEnumerable(); + + private EmptyEnumerable() + { + } + + public IEnumerator GetEnumerator() + { + return EmptyEnumerator.Instance; + } + } + + public sealed class EmptyEnumerator + : IEnumerator + { + public static readonly IEnumerator Instance = new EmptyEnumerator(); + + private EmptyEnumerator() + { + } + + public bool MoveNext() + { + return false; + } + + public void Reset() + { + } + + public object Current + { + get { throw new InvalidOperationException("No elements"); } + } + } +} diff --git a/Crypto/src/util/collections/EnumerableProxy.cs b/Crypto/src/util/collections/EnumerableProxy.cs new file mode 100644
index 000000000..9eec4af21 --- /dev/null +++ b/Crypto/src/util/collections/EnumerableProxy.cs
@@ -0,0 +1,25 @@ +using System; +using System.Collections; + +namespace Org.BouncyCastle.Utilities.Collections +{ + public sealed class EnumerableProxy + : IEnumerable + { + private readonly IEnumerable inner; + + public EnumerableProxy( + IEnumerable inner) + { + if (inner == null) + throw new ArgumentNullException("inner"); + + this.inner = inner; + } + + public IEnumerator GetEnumerator() + { + return inner.GetEnumerator(); + } + } +} diff --git a/Crypto/src/util/collections/HashSet.cs b/Crypto/src/util/collections/HashSet.cs new file mode 100644
index 000000000..1facb58e3 --- /dev/null +++ b/Crypto/src/util/collections/HashSet.cs
@@ -0,0 +1,99 @@ +using System; +using System.Collections; + +namespace Org.BouncyCastle.Utilities.Collections +{ + public class HashSet + : ISet + { + private readonly IDictionary impl = Platform.CreateHashtable(); + + public HashSet() + { + } + + public HashSet(IEnumerable s) + { + foreach (object o in s) + { + Add(o); + } + } + + public virtual void Add(object o) + { + impl[o] = null; + } + + public virtual void AddAll(IEnumerable e) + { + foreach (object o in e) + { + Add(o); + } + } + + public virtual void Clear() + { + impl.Clear(); + } + + public virtual bool Contains(object o) + { + return impl.Contains(o); + } + + public virtual void CopyTo(Array array, int index) + { + impl.Keys.CopyTo(array, index); + } + + public virtual int Count + { + get { return impl.Count; } + } + + public virtual IEnumerator GetEnumerator() + { + return impl.Keys.GetEnumerator(); + } + + public virtual bool IsEmpty + { + get { return impl.Count == 0; } + } + + public virtual bool IsFixedSize + { + get { return impl.IsFixedSize; } + } + + public virtual bool IsReadOnly + { + get { return impl.IsReadOnly; } + } + + public virtual bool IsSynchronized + { + get { return impl.IsSynchronized; } + } + + public virtual void Remove(object o) + { + impl.Remove(o); + } + + public virtual void RemoveAll(IEnumerable e) + { + foreach (object o in e) + { + Remove(o); + } + } + + public virtual object SyncRoot + { + get { return impl.SyncRoot; } + } + } +} diff --git a/Crypto/src/util/collections/ISet.cs b/Crypto/src/util/collections/ISet.cs new file mode 100644
index 000000000..1f8edba40 --- /dev/null +++ b/Crypto/src/util/collections/ISet.cs
@@ -0,0 +1,19 @@ +using System; +using System.Collections; + +namespace Org.BouncyCastle.Utilities.Collections +{ + public interface ISet + : ICollection + { + void Add(object o); + void AddAll(IEnumerable e); + void Clear(); + bool Contains(object o); + bool IsEmpty { get; } + bool IsFixedSize { get; } + bool IsReadOnly { get; } + void Remove(object o); + void RemoveAll(IEnumerable e); + } +} diff --git a/Crypto/src/util/collections/LinkedDictionary.cs b/Crypto/src/util/collections/LinkedDictionary.cs new file mode 100644
index 000000000..933d38ded --- /dev/null +++ b/Crypto/src/util/collections/LinkedDictionary.cs
@@ -0,0 +1,178 @@ +using System; +using System.Collections; + +namespace Org.BouncyCastle.Utilities.Collections +{ + public class LinkedDictionary + : IDictionary + { + internal readonly IDictionary hash = Platform.CreateHashtable(); + internal readonly IList keys = Platform.CreateArrayList(); + + public LinkedDictionary() + { + } + + public virtual void Add(object k, object v) + { + hash.Add(k, v); + keys.Add(k); + } + + public virtual void Clear() + { + hash.Clear(); + keys.Clear(); + } + + public virtual bool Contains(object k) + { + return hash.Contains(k); + } + + public virtual void CopyTo(Array array, int index) + { + foreach (object k in keys) + { + array.SetValue(hash[k], index++); + } + } + + public virtual int Count + { + get { return hash.Count; } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public virtual IDictionaryEnumerator GetEnumerator() + { + return new LinkedDictionaryEnumerator(this); + } + + public virtual void Remove(object k) + { + hash.Remove(k); + keys.Remove(k); + } + + public virtual bool IsFixedSize + { + get { return false; } + } + + public virtual bool IsReadOnly + { + get { return false; } + } + + public virtual bool IsSynchronized + { + get { return false; } + } + + public virtual object SyncRoot + { + get { return false; } + } + + public virtual ICollection Keys + { + get { return Platform.CreateArrayList(keys); } + } + + public virtual ICollection Values + { + // NB: Order has to be the same as for Keys property + get + { + IList values = Platform.CreateArrayList(keys.Count); + foreach (object k in keys) + { + values.Add(hash[k]); + } + return values; + } + } + + public virtual object this[object k] + { + get + { + return hash[k]; + } + set + { + if (!hash.Contains(k)) + keys.Add(k); + hash[k] = value; + } + } + } + + internal class LinkedDictionaryEnumerator : IDictionaryEnumerator + { + private readonly LinkedDictionary parent; + private int pos = -1; + + internal LinkedDictionaryEnumerator(LinkedDictionary parent) + { + this.parent = parent; + } + + public virtual object Current + { + get { return Entry; } + } + + public virtual DictionaryEntry Entry + { + get + { + object k = CurrentKey; + return new DictionaryEntry(k, parent.hash[k]); + } + } + + public virtual object Key + { + get + { + return CurrentKey; + } + } + + public virtual bool MoveNext() + { + if (pos >= parent.keys.Count) + return false; + return ++pos < parent.keys.Count; + } + + public virtual void Reset() + { + this.pos = -1; + } + + public virtual object Value + { + get + { + return parent.hash[CurrentKey]; + } + } + + private object CurrentKey + { + get + { + if (pos < 0 || pos >= parent.keys.Count) + throw new InvalidOperationException(); + return parent.keys[pos]; + } + } + } +} diff --git a/Crypto/src/util/collections/UnmodifiableDictionary.cs b/Crypto/src/util/collections/UnmodifiableDictionary.cs new file mode 100644
index 000000000..0bdf70ad7 --- /dev/null +++ b/Crypto/src/util/collections/UnmodifiableDictionary.cs
@@ -0,0 +1,64 @@ +using System; +using System.Collections; + +namespace Org.BouncyCastle.Utilities.Collections +{ + public abstract class UnmodifiableDictionary + : IDictionary + { + protected UnmodifiableDictionary() + { + } + + public virtual void Add(object k, object v) + { + throw new NotSupportedException(); + } + + public virtual void Clear() + { + throw new NotSupportedException(); + } + + public abstract bool Contains(object k); + + public abstract void CopyTo(Array array, int index); + + public abstract int Count { get; } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public abstract IDictionaryEnumerator GetEnumerator(); + + public virtual void Remove(object k) + { + throw new NotSupportedException(); + } + + public abstract bool IsFixedSize { get; } + + public virtual bool IsReadOnly + { + get { return true; } + } + + public abstract bool IsSynchronized { get; } + + public abstract object SyncRoot { get; } + + public abstract ICollection Keys { get; } + + public abstract ICollection Values { get; } + + public virtual object this[object k] + { + get { return GetValue(k); } + set { throw new NotSupportedException(); } + } + + protected abstract object GetValue(object k); + } +} diff --git a/Crypto/src/util/collections/UnmodifiableDictionaryProxy.cs b/Crypto/src/util/collections/UnmodifiableDictionaryProxy.cs new file mode 100644
index 000000000..0fca909a3 --- /dev/null +++ b/Crypto/src/util/collections/UnmodifiableDictionaryProxy.cs
@@ -0,0 +1,66 @@ +using System; +using System.Collections; + +namespace Org.BouncyCastle.Utilities.Collections +{ + public class UnmodifiableDictionaryProxy + : UnmodifiableDictionary + { + private readonly IDictionary d; + + public UnmodifiableDictionaryProxy(IDictionary d) + { + this.d = d; + } + + public override bool Contains(object k) + { + return d.Contains(k); + } + + public override void CopyTo(Array array, int index) + { + d.CopyTo(array, index); + } + + public override int Count + { + get { return d.Count; } + } + + public override IDictionaryEnumerator GetEnumerator() + { + return d.GetEnumerator(); + } + + public override bool IsFixedSize + { + get { return d.IsFixedSize; } + } + + public override bool IsSynchronized + { + get { return d.IsSynchronized; } + } + + public override object SyncRoot + { + get { return d.SyncRoot; } + } + + public override ICollection Keys + { + get { return d.Keys; } + } + + public override ICollection Values + { + get { return d.Values; } + } + + protected override object GetValue(object k) + { + return d[k]; + } + } +} diff --git a/Crypto/src/util/collections/UnmodifiableList.cs b/Crypto/src/util/collections/UnmodifiableList.cs new file mode 100644
index 000000000..28e49eac3 --- /dev/null +++ b/Crypto/src/util/collections/UnmodifiableList.cs
@@ -0,0 +1,67 @@ +using System; +using System.Collections; + +namespace Org.BouncyCastle.Utilities.Collections +{ + public abstract class UnmodifiableList + : IList + { + protected UnmodifiableList() + { + } + + public virtual int Add(object o) + { + throw new NotSupportedException(); + } + + public virtual void Clear() + { + throw new NotSupportedException(); + } + + public abstract bool Contains(object o); + + public abstract void CopyTo(Array array, int index); + + public abstract int Count { get; } + + public abstract IEnumerator GetEnumerator(); + + public abstract int IndexOf(object o); + + public virtual void Insert(int i, object o) + { + throw new NotSupportedException(); + } + + public abstract bool IsFixedSize { get; } + + public virtual bool IsReadOnly + { + get { return true; } + } + + public abstract bool IsSynchronized { get; } + + public virtual void Remove(object o) + { + throw new NotSupportedException(); + } + + public virtual void RemoveAt(int i) + { + throw new NotSupportedException(); + } + + public abstract object SyncRoot { get; } + + public virtual object this[int i] + { + get { return GetValue(i); } + set { throw new NotSupportedException(); } + } + + protected abstract object GetValue(int i); + } +} diff --git a/Crypto/src/util/collections/UnmodifiableListProxy.cs b/Crypto/src/util/collections/UnmodifiableListProxy.cs new file mode 100644
index 000000000..9d00737ef --- /dev/null +++ b/Crypto/src/util/collections/UnmodifiableListProxy.cs
@@ -0,0 +1,61 @@ +using System; +using System.Collections; + +namespace Org.BouncyCastle.Utilities.Collections +{ + public class UnmodifiableListProxy + : UnmodifiableList + { + private readonly IList l; + + public UnmodifiableListProxy(IList l) + { + this.l = l; + } + + public override bool Contains(object o) + { + return l.Contains(o); + } + + public override void CopyTo(Array array, int index) + { + l.CopyTo(array, index); + } + + public override int Count + { + get { return l.Count; } + } + + public override IEnumerator GetEnumerator() + { + return l.GetEnumerator(); + } + + public override int IndexOf(object o) + { + return l.IndexOf(o); + } + + public override bool IsFixedSize + { + get { return l.IsFixedSize; } + } + + public override bool IsSynchronized + { + get { return l.IsSynchronized; } + } + + public override object SyncRoot + { + get { return l.SyncRoot; } + } + + protected override object GetValue(int i) + { + return l[i]; + } + } +} diff --git a/Crypto/src/util/collections/UnmodifiableSet.cs b/Crypto/src/util/collections/UnmodifiableSet.cs new file mode 100644
index 000000000..8792815ac --- /dev/null +++ b/Crypto/src/util/collections/UnmodifiableSet.cs
@@ -0,0 +1,59 @@ +using System; +using System.Collections; + +namespace Org.BouncyCastle.Utilities.Collections +{ + public abstract class UnmodifiableSet + : ISet + { + protected UnmodifiableSet() + { + } + + public virtual void Add(object o) + { + throw new NotSupportedException(); + } + + public virtual void AddAll(IEnumerable e) + { + throw new NotSupportedException(); + } + + public virtual void Clear() + { + throw new NotSupportedException(); + } + + public abstract bool Contains(object o); + + public abstract void CopyTo(Array array, int index); + + public abstract int Count { get; } + + public abstract IEnumerator GetEnumerator(); + + public abstract bool IsEmpty { get; } + + public abstract bool IsFixedSize { get; } + + public virtual bool IsReadOnly + { + get { return true; } + } + + public abstract bool IsSynchronized { get; } + + public abstract object SyncRoot { get; } + + public virtual void Remove(object o) + { + throw new NotSupportedException(); + } + + public virtual void RemoveAll(IEnumerable e) + { + throw new NotSupportedException(); + } + } +} diff --git a/Crypto/src/util/collections/UnmodifiableSetProxy.cs b/Crypto/src/util/collections/UnmodifiableSetProxy.cs new file mode 100644
index 000000000..e119e2957 --- /dev/null +++ b/Crypto/src/util/collections/UnmodifiableSetProxy.cs
@@ -0,0 +1,56 @@ +using System; +using System.Collections; + +namespace Org.BouncyCastle.Utilities.Collections +{ + public class UnmodifiableSetProxy + : UnmodifiableSet + { + private readonly ISet s; + + public UnmodifiableSetProxy (ISet s) + { + this.s = s; + } + + public override bool Contains(object o) + { + return s.Contains(o); + } + + public override void CopyTo(Array array, int index) + { + s.CopyTo(array, index); + } + + public override int Count + { + get { return s.Count; } + } + + public override IEnumerator GetEnumerator() + { + return s.GetEnumerator(); + } + + public override bool IsEmpty + { + get { return s.IsEmpty; } + } + + public override bool IsFixedSize + { + get { return s.IsFixedSize; } + } + + public override bool IsSynchronized + { + get { return s.IsSynchronized; } + } + + public override object SyncRoot + { + get { return s.SyncRoot; } + } + } +} diff --git a/Crypto/src/util/date/DateTimeObject.cs b/Crypto/src/util/date/DateTimeObject.cs new file mode 100644
index 000000000..793376b6d --- /dev/null +++ b/Crypto/src/util/date/DateTimeObject.cs
@@ -0,0 +1,25 @@ +using System; + +namespace Org.BouncyCastle.Utilities.Date +{ + public sealed class DateTimeObject + { + private readonly DateTime dt; + + public DateTimeObject( + DateTime dt) + { + this.dt = dt; + } + + public DateTime Value + { + get { return dt; } + } + + public override string ToString() + { + return dt.ToString(); + } + } +} diff --git a/Crypto/src/util/date/DateTimeUtilities.cs b/Crypto/src/util/date/DateTimeUtilities.cs new file mode 100644
index 000000000..311ad5d37 --- /dev/null +++ b/Crypto/src/util/date/DateTimeUtilities.cs
@@ -0,0 +1,47 @@ +using System; + +namespace Org.BouncyCastle.Utilities.Date +{ + public class DateTimeUtilities + { + public static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1); + + private DateTimeUtilities() + { + } + + /// <summary> + /// Return the number of milliseconds since the Unix epoch (1 Jan., 1970 UTC) for a given DateTime value. + /// </summary> + /// <param name="dateTime">A UTC DateTime value not before epoch.</param> + /// <returns>Number of whole milliseconds after epoch.</returns> + /// <exception cref="ArgumentException">'dateTime' is before epoch.</exception> + public static long DateTimeToUnixMs( + DateTime dateTime) + { + if (dateTime.CompareTo(UnixEpoch) < 0) + throw new ArgumentException("DateTime value may not be before the epoch", "dateTime"); + + return (dateTime.Ticks - UnixEpoch.Ticks) / TimeSpan.TicksPerMillisecond; + } + + /// <summary> + /// Create a DateTime value from the number of milliseconds since the Unix epoch (1 Jan., 1970 UTC). + /// </summary> + /// <param name="unixMs">Number of milliseconds since the epoch.</param> + /// <returns>A UTC DateTime value</returns> + public static DateTime UnixMsToDateTime( + long unixMs) + { + return new DateTime(unixMs * TimeSpan.TicksPerMillisecond + UnixEpoch.Ticks); + } + + /// <summary> + /// Return the current number of milliseconds since the Unix epoch (1 Jan., 1970 UTC). + /// </summary> + public static long CurrentUnixMs() + { + return DateTimeToUnixMs(DateTime.UtcNow); + } + } +} diff --git a/Crypto/src/util/encoders/Base64.cs b/Crypto/src/util/encoders/Base64.cs new file mode 100644
index 000000000..5dc9fae63 --- /dev/null +++ b/Crypto/src/util/encoders/Base64.cs
@@ -0,0 +1,95 @@ +using System; +using System.IO; +using System.Text; + +namespace Org.BouncyCastle.Utilities.Encoders +{ + public sealed class Base64 + { + private Base64() + { + } + + /** + * 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) + { + string s = Convert.ToBase64String(data, 0, data.Length); + return Strings.ToAsciiByteArray(s); + } + + /** + * 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, + Stream outStream) + { + string s = Convert.ToBase64String(data, 0, data.Length); + byte[] encoded = Strings.ToAsciiByteArray(s); + outStream.Write(encoded, 0, encoded.Length); + return encoded.Length; + } + + /** + * 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, + Stream outStream) + { + string s = Convert.ToBase64String(data, off, length); + byte[] encoded = Strings.ToAsciiByteArray(s); + outStream.Write(encoded, 0, encoded.Length); + return encoded.Length; + } + + /** + * 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) + { + string s = Strings.FromAsciiByteArray(data); + return Convert.FromBase64String(s); + } + + /** + * 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) + { + return Convert.FromBase64String(data); + } + + /** + * 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, + Stream outStream) + { + byte[] decoded = Decode(data); + outStream.Write(decoded, 0, decoded.Length); + return decoded.Length; + } + } +} diff --git a/Crypto/src/util/encoders/Base64Encoder.cs b/Crypto/src/util/encoders/Base64Encoder.cs new file mode 100644
index 000000000..c94ce9d3c --- /dev/null +++ b/Crypto/src/util/encoders/Base64Encoder.cs
@@ -0,0 +1,307 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Utilities.Encoders +{ + public class Base64Encoder + : IEncoder + { + protected readonly 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 readonly byte[] decodingTable = new byte[128]; + + protected void InitialiseDecodingTable() + { + 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, + Stream outStream) + { + 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; + + outStream.WriteByte(encodingTable[(int) ((uint) a1 >> 2) & 0x3f]); + outStream.WriteByte(encodingTable[((a1 << 4) | (int) ((uint) a2 >> 4)) & 0x3f]); + outStream.WriteByte(encodingTable[((a2 << 2) | (int) ((uint) a3 >> 6)) & 0x3f]); + outStream.WriteByte(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; + + outStream.WriteByte(encodingTable[b1]); + outStream.WriteByte(encodingTable[b2]); + outStream.WriteByte(padding); + outStream.WriteByte(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; + + outStream.WriteByte(encodingTable[b1]); + outStream.WriteByte(encodingTable[b2]); + outStream.WriteByte(encodingTable[b3]); + outStream.WriteByte(padding); + break; + } + + return (dataLength / 3) * 4 + ((modulus == 0) ? 0 : 4); + } + + private bool 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, + Stream outStream) + { + 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++]]; + + outStream.WriteByte((byte)((b1 << 2) | (b2 >> 4))); + outStream.WriteByte((byte)((b2 << 4) | (b3 >> 2))); + outStream.WriteByte((byte)((b3 << 6) | b4)); + + outLen += 3; + + i = nextI(data, i, finish); + } + + outLen += decodeLastBlock(outStream, (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 DecodeString( + string data, + Stream outStream) + { + // Platform Implementation +// byte[] bytes = Convert.FromBase64String(data); +// outStream.Write(bytes, 0, bytes.Length); +// return bytes.Length; + + byte b1, b2, b3, b4; + int length = 0; + + int end = data.Length; + + while (end > 0) + { + if (!ignore(data[end - 1])) + { + break; + } + + end--; + } + + int i = 0; + 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++]]; + + outStream.WriteByte((byte)((b1 << 2) | (b2 >> 4))); + outStream.WriteByte((byte)((b2 << 4) | (b3 >> 2))); + outStream.WriteByte((byte)((b3 << 6) | b4)); + + length += 3; + + i = nextI(data, i, finish); + } + + length += decodeLastBlock(outStream, data[end - 4], data[end - 3], data[end - 2], data[end - 1]); + + return length; + } + + private int decodeLastBlock( + Stream outStream, + char c1, + char c2, + char c3, + char c4) + { + if (c3 == padding) + { + byte b1 = decodingTable[c1]; + byte b2 = decodingTable[c2]; + + outStream.WriteByte((byte)((b1 << 2) | (b2 >> 4))); + + return 1; + } + + if (c4 == padding) + { + byte b1 = decodingTable[c1]; + byte b2 = decodingTable[c2]; + byte b3 = decodingTable[c3]; + + outStream.WriteByte((byte)((b1 << 2) | (b2 >> 4))); + outStream.WriteByte((byte)((b2 << 4) | (b3 >> 2))); + + return 2; + } + + { + byte b1 = decodingTable[c1]; + byte b2 = decodingTable[c2]; + byte b3 = decodingTable[c3]; + byte b4 = decodingTable[c4]; + + outStream.WriteByte((byte)((b1 << 2) | (b2 >> 4))); + outStream.WriteByte((byte)((b2 << 4) | (b3 >> 2))); + outStream.WriteByte((byte)((b3 << 6) | b4)); + + return 3; + } + } + + private int nextI(string data, int i, int finish) + { + while ((i < finish) && ignore(data[i])) + { + i++; + } + return i; + } + } +} diff --git a/Crypto/src/util/encoders/BufferedDecoder.cs b/Crypto/src/util/encoders/BufferedDecoder.cs new file mode 100644
index 000000000..633cf1e97 --- /dev/null +++ b/Crypto/src/util/encoders/BufferedDecoder.cs
@@ -0,0 +1,117 @@ +using System; + +namespace Org.BouncyCastle.Utilities.Encoders +{ + /// <summary> + /// A buffering class to allow translation from one format to another to + /// be done in discrete chunks. + /// </summary> + public class BufferedDecoder + { + internal byte[] buffer; + internal int bufOff; + + internal ITranslator translator; + + /// <summary> + /// Create a buffered Decoder. + /// </summary> + /// <param name="translator">The translater to use.</param> + /// <param name="bufferSize">The size of the buffer.</param> + public BufferedDecoder( + ITranslator translator, + int bufferSize) + { + this.translator = translator; + + if ((bufferSize % translator.GetEncodedBlockSize()) != 0) + { + throw new ArgumentException("buffer size not multiple of input block size"); + } + + buffer = new byte[bufferSize]; +// bufOff = 0; + } + + /// <summary> + /// Process one byte of data. + /// </summary> + /// <param name="input">Data in.</param> + /// <param name="output">Byte array for the output.</param> + /// <param name="outOff">The offset in the output byte array to start writing from.</param> + /// <returns>The amount of output bytes.</returns> + public int ProcessByte( + byte input, + byte[] output, + int outOff) + { + int resultLen = 0; + + buffer[bufOff++] = input; + + if (bufOff == buffer.Length) + { + resultLen = translator.Decode(buffer, 0, buffer.Length, output, outOff); + bufOff = 0; + } + + return resultLen; + } + + + /// <summary> + /// Process data from a byte array. + /// </summary> + /// <param name="input">The input data.</param> + /// <param name="inOff">Start position within input data array.</param> + /// <param name="len">Amount of data to process from input data array.</param> + /// <param name="outBytes">Array to store output.</param> + /// <param name="outOff">Position in output array to start writing from.</param> + /// <returns>The amount of output bytes.</returns> + public int ProcessBytes( + byte[] input, + int inOff, + int len, + byte[] outBytes, + int outOff) + { + if (len < 0) + { + throw new ArgumentException("Can't have a negative input length!"); + } + + int resultLen = 0; + int gapLen = buffer.Length - bufOff; + + if (len > gapLen) + { + Array.Copy(input, inOff, buffer, bufOff, gapLen); + + resultLen += translator.Decode(buffer, 0, buffer.Length, outBytes, outOff); + + bufOff = 0; + + len -= gapLen; + inOff += gapLen; + outOff += resultLen; + + int chunkSize = len - (len % buffer.Length); + + resultLen += translator.Decode(input, inOff, chunkSize, outBytes, outOff); + + len -= chunkSize; + inOff += chunkSize; + } + + if (len != 0) + { + Array.Copy(input, inOff, buffer, bufOff, len); + + bufOff += len; + } + + return resultLen; + } + } + +} diff --git a/Crypto/src/util/encoders/BufferedEncoder.cs b/Crypto/src/util/encoders/BufferedEncoder.cs new file mode 100644
index 000000000..5c3b1ab46 --- /dev/null +++ b/Crypto/src/util/encoders/BufferedEncoder.cs
@@ -0,0 +1,117 @@ +using System; + +namespace Org.BouncyCastle.Utilities.Encoders +{ + /// <summary> + /// A class that allows encoding of data using a specific encoder to be processed in chunks. + /// </summary> + public class BufferedEncoder + { + internal byte[] Buffer; + internal int bufOff; + + internal ITranslator translator; + + + /// <summary> + /// Create. + /// </summary> + /// <param name="translator">The translator to use.</param> + /// <param name="bufferSize">Size of the chunks.</param> + public BufferedEncoder( + ITranslator translator, + int bufferSize) + { + this.translator = translator; + + if ((bufferSize % translator.GetEncodedBlockSize()) != 0) + { + throw new ArgumentException("buffer size not multiple of input block size"); + } + + Buffer = new byte[bufferSize]; +// bufOff = 0; + } + + + /// <summary> + /// Process one byte of data. + /// </summary> + /// <param name="input">The byte.</param> + /// <param name="outBytes">An array to store output in.</param> + /// <param name="outOff">Offset within output array to start writing from.</param> + /// <returns></returns> + public int ProcessByte( + byte input, + byte[] outBytes, + int outOff) + { + int resultLen = 0; + + Buffer[bufOff++] = input; + + if (bufOff == Buffer.Length) + { + resultLen = translator.Encode(Buffer, 0, Buffer.Length, outBytes, outOff); + bufOff = 0; + } + + return resultLen; + } + + /// <summary> + /// Process data from a byte array. + /// </summary> + /// <param name="input">Input data Byte array containing data to be processed.</param> + /// <param name="inOff">Start position within input data array.</param> + /// <param name="len">Amount of input data to be processed.</param> + /// <param name="outBytes">Output data array.</param> + /// <param name="outOff">Offset within output data array to start writing to.</param> + /// <returns>The amount of data written.</returns> + public int ProcessBytes( + byte[] input, + int inOff, + int len, + byte[] outBytes, + int outOff) + { + if (len < 0) + { + throw new ArgumentException("Can't have a negative input length!"); + } + + int resultLen = 0; + int gapLen = Buffer.Length - bufOff; + + if (len > gapLen) + { + Array.Copy(input, inOff, Buffer, bufOff, gapLen); + + resultLen += translator.Encode(Buffer, 0, Buffer.Length, outBytes, outOff); + + bufOff = 0; + + len -= gapLen; + inOff += gapLen; + outOff += resultLen; + + int chunkSize = len - (len % Buffer.Length); + + resultLen += translator.Encode(input, inOff, chunkSize, outBytes, outOff); + + len -= chunkSize; + inOff += chunkSize; + } + + if (len != 0) + { + Array.Copy(input, inOff, Buffer, bufOff, len); + + bufOff += len; + } + + return resultLen; + } + } + +} diff --git a/Crypto/src/util/encoders/Hex.cs b/Crypto/src/util/encoders/Hex.cs new file mode 100644
index 000000000..fbe475991 --- /dev/null +++ b/Crypto/src/util/encoders/Hex.cs
@@ -0,0 +1,131 @@ +using System; +using System.IO; +using System.Text; + +namespace Org.BouncyCastle.Utilities.Encoders +{ + /// <summary> + /// Class to decode and encode Hex. + /// </summary> + public sealed class Hex + { + private static readonly IEncoder encoder = new HexEncoder(); + + private Hex() + { + } + + public static string ToHexString( + byte[] data) + { + byte[] hex = Encode(data, 0, data.Length); + return Strings.FromAsciiByteArray(hex); + } + + public static string ToHexString( + byte[] data, + int off, + int length) + { + byte[] hex = Encode(data, off, length); + return Strings.FromAsciiByteArray(hex); + } + + /** + * 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) + { + MemoryStream bOut = new MemoryStream(length * 2); + + encoder.Encode(data, off, length, bOut); + + return bOut.ToArray(); + } + + /** + * Hex encode the byte data writing it to the given output stream. + * + * @return the number of bytes produced. + */ + public static int Encode( + byte[] data, + Stream outStream) + { + return encoder.Encode(data, 0, data.Length, outStream); + } + + /** + * 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, + Stream outStream) + { + return encoder.Encode(data, off, length, outStream); + } + + /** + * 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) + { + MemoryStream bOut = new MemoryStream((data.Length + 1) / 2); + + encoder.Decode(data, 0, data.Length, bOut); + + return bOut.ToArray(); + } + + /** + * decode the Hex encoded string data - whitespace will be ignored. + * + * @return a byte array representing the decoded data. + */ + public static byte[] Decode( + string data) + { + MemoryStream bOut = new MemoryStream((data.Length + 1) / 2); + + encoder.DecodeString(data, bOut); + + return bOut.ToArray(); + } + + /** + * 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, + Stream outStream) + { + return encoder.DecodeString(data, outStream); + } + } +} diff --git a/Crypto/src/util/encoders/HexEncoder.cs b/Crypto/src/util/encoders/HexEncoder.cs new file mode 100644
index 000000000..c47d6219b --- /dev/null +++ b/Crypto/src/util/encoders/HexEncoder.cs
@@ -0,0 +1,164 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Utilities.Encoders +{ + public class HexEncoder + : IEncoder + { + private static readonly 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. + */ + internal static readonly byte[] decodingTable = new byte[128]; + + static HexEncoder() + { + 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']; + } + + /** + * encode the input data producing a Hex output stream. + * + * @return the number of bytes produced. + */ + public int Encode( + byte[] data, + int off, + int length, + Stream outStream) + { + for (int i = off; i < (off + length); i++) + { + int v = data[i]; + + outStream.WriteByte(encodingTable[v >> 4]); + outStream.WriteByte(encodingTable[v & 0xf]); + } + + return length * 2; + } + + private bool 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, + Stream outStream) + { + 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++]]; + + outStream.WriteByte((byte)((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 DecodeString( + string data, + Stream outStream) + { + byte b1, b2; + int length = 0; + + int end = data.Length; + + while (end > 0) + { + if (!ignore(data[end - 1])) + { + break; + } + + end--; + } + + int i = 0; + while (i < end) + { + while (i < end && ignore(data[i])) + { + i++; + } + + b1 = decodingTable[data[i++]]; + + while (i < end && ignore(data[i])) + { + i++; + } + + b2 = decodingTable[data[i++]]; + + outStream.WriteByte((byte)((b1 << 4) | b2)); + + length++; + } + + return length; + } + } +} diff --git a/Crypto/src/util/encoders/HexTranslator.cs b/Crypto/src/util/encoders/HexTranslator.cs new file mode 100644
index 000000000..9775b6948 --- /dev/null +++ b/Crypto/src/util/encoders/HexTranslator.cs
@@ -0,0 +1,108 @@ +using System; + +namespace Org.BouncyCastle.Utilities.Encoders +{ + /// <summary> + /// A hex translator. + /// </summary> + public class HexTranslator : ITranslator + { + private static readonly 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' + }; + + /// <summary> + /// Return encoded block size. + /// </summary> + /// <returns>2</returns> + public int GetEncodedBlockSize() + { + return 2; + } + + /// <summary> + /// Encode some data. + /// </summary> + /// <param name="input">Input data array.</param> + /// <param name="inOff">Start position within input data array.</param> + /// <param name="length">The amount of data to process.</param> + /// <param name="outBytes">The output data array.</param> + /// <param name="outOff">The offset within the output data array to start writing from.</param> + /// <returns>Amount of data encoded.</returns> + public int Encode( + byte[] input, + int inOff, + int length, + byte[] outBytes, + int outOff) + { + for (int i = 0, j = 0; i < length; i++, j += 2) + { + outBytes[outOff + j] = hexTable[(input[inOff] >> 4) & 0x0f]; + outBytes[outOff + j + 1] = hexTable[input[inOff] & 0x0f]; + + inOff++; + } + + return length * 2; + } + + /// <summary> + /// Returns the decoded block size. + /// </summary> + /// <returns>1</returns> + public int GetDecodedBlockSize() + { + return 1; + } + + /// <summary> + /// Decode data from a byte array. + /// </summary> + /// <param name="input">The input data array.</param> + /// <param name="inOff">Start position within input data array.</param> + /// <param name="length">The amounty of data to process.</param> + /// <param name="outBytes">The output data array.</param> + /// <param name="outOff">The position within the output data array to start writing from.</param> + /// <returns>The amount of data written.</returns> + public int Decode( + byte[] input, + int inOff, + int length, + byte[] outBytes, + int outOff) + { + int halfLength = length / 2; + byte left, right; + for (int i = 0; i < halfLength; i++) + { + left = input[inOff + i * 2]; + right = input[inOff + i * 2 + 1]; + + if (left < (byte)'a') + { + outBytes[outOff] = (byte)((left - '0') << 4); + } + else + { + outBytes[outOff] = (byte)((left - 'a' + 10) << 4); + } + if (right < (byte)'a') + { + outBytes[outOff] += (byte)(right - '0'); + } + else + { + outBytes[outOff] += (byte)(right - 'a' + 10); + } + + outOff++; + } + + return halfLength; + } + } + +} diff --git a/Crypto/src/util/encoders/IEncoder.cs b/Crypto/src/util/encoders/IEncoder.cs new file mode 100644
index 000000000..5887d5daa --- /dev/null +++ b/Crypto/src/util/encoders/IEncoder.cs
@@ -0,0 +1,18 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Utilities.Encoders +{ + /** + * Encode and decode byte arrays (typically from binary to 7-bit ASCII + * encodings). + */ + public interface IEncoder + { + int Encode(byte[] data, int off, int length, Stream outStream); + + int Decode(byte[] data, int off, int length, Stream outStream); + + int DecodeString(string data, Stream outStream); + } +} diff --git a/Crypto/src/util/encoders/Translator.cs b/Crypto/src/util/encoders/Translator.cs new file mode 100644
index 000000000..10bd24b63 --- /dev/null +++ b/Crypto/src/util/encoders/Translator.cs
@@ -0,0 +1,19 @@ +using System; + +namespace Org.BouncyCastle.Utilities.Encoders +{ + /// <summary> + /// Translator interface. + /// </summary> + public interface ITranslator + { + int GetEncodedBlockSize(); + + int Encode(byte[] input, int inOff, int length, byte[] outBytes, int outOff); + + int GetDecodedBlockSize(); + + int Decode(byte[] input, int inOff, int length, byte[] outBytes, int outOff); + } + +} diff --git a/Crypto/src/util/encoders/UrlBase64.cs b/Crypto/src/util/encoders/UrlBase64.cs new file mode 100644
index 000000000..94195ef5e --- /dev/null +++ b/Crypto/src/util/encoders/UrlBase64.cs
@@ -0,0 +1,127 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Utilities.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. + * </p> + */ + public class UrlBase64 + { + private static readonly IEncoder 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) + { + MemoryStream bOut = new MemoryStream(); + + try + { + encoder.Encode(data, 0, data.Length, bOut); + } + catch (IOException e) + { + throw new Exception("exception encoding URL safe base64 string: " + e.Message, e); + } + + return bOut.ToArray(); + } + + /** + * Encode the byte data writing it to the given output stream. + * + * @return the number of bytes produced. + */ + public static int Encode( + byte[] data, + Stream outStr) + { + return encoder.Encode(data, 0, data.Length, outStr); + } + + /** + * 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) + { + MemoryStream bOut = new MemoryStream(); + + try + { + encoder.Decode(data, 0, data.Length, bOut); + } + catch (IOException e) + { + throw new Exception("exception decoding URL safe base64 string: " + e.Message, e); + } + + return bOut.ToArray(); + } + + /** + * 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, + Stream outStr) + { + return encoder.Decode(data, 0, data.Length, outStr); + } + + /** + * 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) + { + MemoryStream bOut = new MemoryStream(); + + try + { + encoder.DecodeString(data, bOut); + } + catch (IOException e) + { + throw new Exception("exception decoding URL safe base64 string: " + e.Message, e); + } + + return bOut.ToArray(); + } + + /** + * 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, + Stream outStr) + { + return encoder.DecodeString(data, outStr); + } + } +} diff --git a/Crypto/src/util/encoders/UrlBase64Encoder.cs b/Crypto/src/util/encoders/UrlBase64Encoder.cs new file mode 100644
index 000000000..5611a831c --- /dev/null +++ b/Crypto/src/util/encoders/UrlBase64Encoder.cs
@@ -0,0 +1,31 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Utilities.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. + * </p> + */ + public class UrlBase64Encoder + : 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(); + } + } +} \ No newline at end of file diff --git a/Crypto/src/util/io/BaseInputStream.cs b/Crypto/src/util/io/BaseInputStream.cs new file mode 100644
index 000000000..08eedb160 --- /dev/null +++ b/Crypto/src/util/io/BaseInputStream.cs
@@ -0,0 +1,54 @@ +using System; +using System.Diagnostics; +using System.IO; + +namespace Org.BouncyCastle.Utilities.IO +{ + public abstract class BaseInputStream : Stream + { + private bool closed; + + public sealed override bool CanRead { get { return !closed; } } + public sealed override bool CanSeek { get { return false; } } + public sealed override bool CanWrite { get { return false; } } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + closed = true; + } + + public sealed override void Flush() {} + public sealed override long Length { get { throw new NotSupportedException(); } } + public sealed override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + public override int Read(byte[] buffer, int offset, int count) + { + int pos = offset; + try + { + int end = offset + count; + while (pos < end) + { + int b = ReadByte(); + if (b == -1) break; + buffer[pos++] = (byte) b; + } + } + catch (IOException) + { + if (pos == offset) throw; + } + return pos - offset; + } + + public sealed override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } + public sealed override void SetLength(long value) { throw new NotSupportedException(); } + public sealed override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } + } +} diff --git a/Crypto/src/util/io/BaseOutputStream.cs b/Crypto/src/util/io/BaseOutputStream.cs new file mode 100644
index 000000000..77233f68c --- /dev/null +++ b/Crypto/src/util/io/BaseOutputStream.cs
@@ -0,0 +1,54 @@ +using System; +using System.Diagnostics; +using System.IO; + +namespace Org.BouncyCastle.Utilities.IO +{ + public abstract class BaseOutputStream : Stream + { + private bool closed; + + public sealed override bool CanRead { get { return false; } } + public sealed override bool CanSeek { get { return false; } } + public sealed override bool CanWrite { get { return !closed; } } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + closed = true; + } + + public override void Flush() {} + public sealed override long Length { get { throw new NotSupportedException(); } } + public sealed override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + public sealed override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } + public sealed override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } + public sealed override void SetLength(long value) { throw new NotSupportedException(); } + + public override void Write(byte[] buffer, int offset, int count) + { + Debug.Assert(buffer != null); + Debug.Assert(0 <= offset && offset <= buffer.Length); + Debug.Assert(count >= 0); + + int end = offset + count; + + Debug.Assert(0 <= end && end <= buffer.Length); + + for (int i = offset; i < end; ++i) + { + this.WriteByte(buffer[i]); + } + } + + public virtual void Write(params byte[] buffer) + { + Write(buffer, 0, buffer.Length); + } + } +} diff --git a/Crypto/src/util/io/PushbackStream.cs b/Crypto/src/util/io/PushbackStream.cs new file mode 100644
index 000000000..954694259 --- /dev/null +++ b/Crypto/src/util/io/PushbackStream.cs
@@ -0,0 +1,52 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1.Utilities; + +namespace Org.BouncyCastle.Utilities.IO +{ + public class PushbackStream + : FilterStream + { + private int buf = -1; + + public PushbackStream( + Stream s) + : base(s) + { + } + + public override int ReadByte() + { + if (buf != -1) + { + int tmp = buf; + buf = -1; + return tmp; + } + + return base.ReadByte(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (buf != -1 && count > 0) + { + // TODO Can this case be made more efficient? + buffer[offset] = (byte) buf; + buf = -1; + return 1; + } + + return base.Read(buffer, offset, count); + } + + public virtual void Unread(int b) + { + if (buf != -1) + throw new InvalidOperationException("Can only push back one byte"); + + buf = b & 0xFF; + } + } +} diff --git a/Crypto/src/util/io/StreamOverflowException.cs b/Crypto/src/util/io/StreamOverflowException.cs new file mode 100644
index 000000000..a8e7432fa --- /dev/null +++ b/Crypto/src/util/io/StreamOverflowException.cs
@@ -0,0 +1,27 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Utilities.IO +{ + public class StreamOverflowException + : IOException + { + public StreamOverflowException() + : base() + { + } + + public StreamOverflowException( + string message) + : base(message) + { + } + + public StreamOverflowException( + string message, + Exception exception) + : base(message, exception) + { + } + } +} diff --git a/Crypto/src/util/io/Streams.cs b/Crypto/src/util/io/Streams.cs new file mode 100644
index 000000000..ee95d3b01 --- /dev/null +++ b/Crypto/src/util/io/Streams.cs
@@ -0,0 +1,94 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Utilities.IO +{ + public sealed class Streams + { + private const int BufferSize = 512; + + private Streams() + { + } + + public static void Drain(Stream inStr) + { + byte[] bs = new byte[BufferSize]; + while (inStr.Read(bs, 0, bs.Length) > 0) + { + } + } + + public static byte[] ReadAll(Stream inStr) + { + MemoryStream buf = new MemoryStream(); + PipeAll(inStr, buf); + return buf.ToArray(); + } + + public static byte[] ReadAllLimited(Stream inStr, int limit) + { + MemoryStream buf = new MemoryStream(); + PipeAllLimited(inStr, limit, buf); + return buf.ToArray(); + } + + public static int ReadFully(Stream inStr, byte[] buf) + { + return ReadFully(inStr, buf, 0, buf.Length); + } + + public static int ReadFully(Stream inStr, byte[] buf, int off, int len) + { + int totalRead = 0; + while (totalRead < len) + { + int numRead = inStr.Read(buf, off + totalRead, len - totalRead); + if (numRead < 1) + break; + totalRead += numRead; + } + return totalRead; + } + + public static void PipeAll(Stream inStr, Stream outStr) + { + byte[] bs = new byte[BufferSize]; + int numRead; + while ((numRead = inStr.Read(bs, 0, bs.Length)) > 0) + { + outStr.Write(bs, 0, numRead); + } + } + + /// <summary> + /// Pipe all bytes from <c>inStr</c> to <c>outStr</c>, throwing <c>StreamFlowException</c> if greater + /// than <c>limit</c> bytes in <c>inStr</c>. + /// </summary> + /// <param name="inStr"> + /// A <see cref="Stream"/> + /// </param> + /// <param name="limit"> + /// A <see cref="System.Int64"/> + /// </param> + /// <param name="outStr"> + /// A <see cref="Stream"/> + /// </param> + /// <returns>The number of bytes actually transferred, if not greater than <c>limit</c></returns> + /// <exception cref="IOException"></exception> + public static long PipeAllLimited(Stream inStr, long limit, Stream outStr) + { + byte[] bs = new byte[BufferSize]; + long total = 0; + 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/Crypto/src/util/io/TeeInputStream.cs b/Crypto/src/util/io/TeeInputStream.cs new file mode 100644
index 000000000..fed9823f0 --- /dev/null +++ b/Crypto/src/util/io/TeeInputStream.cs
@@ -0,0 +1,54 @@ +using System; +using System.Diagnostics; +using System.IO; + +namespace Org.BouncyCastle.Utilities.IO +{ + public class TeeInputStream + : BaseInputStream + { + private readonly Stream input, tee; + + public TeeInputStream(Stream input, Stream tee) + { + Debug.Assert(input.CanRead); + Debug.Assert(tee.CanWrite); + + this.input = input; + this.tee = tee; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + input.Dispose(); + tee.Dispose(); + } + } + + public override int Read(byte[] buf, int off, int len) + { + int i = input.Read(buf, off, len); + + if (i > 0) + { + tee.Write(buf, off, i); + } + + return i; + } + + public override int ReadByte() + { + int i = input.ReadByte(); + + if (i >= 0) + { + tee.WriteByte((byte)i); + } + + return i; + } + } +} diff --git a/Crypto/src/util/io/TeeOutputStream.cs b/Crypto/src/util/io/TeeOutputStream.cs new file mode 100644
index 000000000..965ef23c8 --- /dev/null +++ b/Crypto/src/util/io/TeeOutputStream.cs
@@ -0,0 +1,42 @@ +using System; +using System.Diagnostics; +using System.IO; + +namespace Org.BouncyCastle.Utilities.IO +{ + public class TeeOutputStream + : BaseOutputStream + { + private readonly Stream output, tee; + + public TeeOutputStream(Stream output, Stream tee) + { + Debug.Assert(output.CanWrite); + Debug.Assert(tee.CanWrite); + + this.output = output; + this.tee = tee; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + output.Dispose(); + tee.Dispose(); + } + } + + public override void Write(byte[] buffer, int offset, int count) + { + output.Write(buffer, offset, count); + tee.Write(buffer, offset, count); + } + + public override void WriteByte(byte b) + { + output.WriteByte(b); + tee.WriteByte(b); + } + } +} diff --git a/Crypto/src/util/io/pem/PemGenerationException.cs b/Crypto/src/util/io/pem/PemGenerationException.cs new file mode 100644
index 000000000..22e83ac2d --- /dev/null +++ b/Crypto/src/util/io/pem/PemGenerationException.cs
@@ -0,0 +1,26 @@ +using System; + +namespace Org.BouncyCastle.Utilities.IO.Pem +{ + public class PemGenerationException + : Exception + { + public PemGenerationException() + : base() + { + } + + public PemGenerationException( + string message) + : base(message) + { + } + + public PemGenerationException( + string message, + Exception exception) + : base(message, exception) + { + } + } +} diff --git a/Crypto/src/util/io/pem/PemHeader.cs b/Crypto/src/util/io/pem/PemHeader.cs new file mode 100644
index 000000000..72da8a4f7 --- /dev/null +++ b/Crypto/src/util/io/pem/PemHeader.cs
@@ -0,0 +1,55 @@ +using System; + +namespace Org.BouncyCastle.Utilities.IO.Pem +{ + public class PemHeader + { + private string name; + private string val; + + public PemHeader(string name, string val) + { + this.name = name; + this.val = val; + } + + public virtual string Name + { + get { return name; } + } + + public virtual string Value + { + get { return val; } + } + + public override int GetHashCode() + { + return GetHashCode(this.name) + 31 * GetHashCode(this.val); + } + + public override bool Equals(object obj) + { + if (obj == this) + return true; + + if (!(obj is PemHeader)) + return false; + + PemHeader other = (PemHeader)obj; + + return Platform.Equals(this.name, other.name) + && Platform.Equals(this.val, other.val); + } + + private int GetHashCode(string s) + { + if (s == null) + { + return 1; + } + + return s.GetHashCode(); + } + } +} diff --git a/Crypto/src/util/io/pem/PemObject.cs b/Crypto/src/util/io/pem/PemObject.cs new file mode 100644
index 000000000..41212f997 --- /dev/null +++ b/Crypto/src/util/io/pem/PemObject.cs
@@ -0,0 +1,47 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Utilities.IO.Pem +{ + public class PemObject + : PemObjectGenerator + { + private string type; + private IList headers; + private byte[] content; + + public PemObject(string type, byte[] content) + : this(type, Platform.CreateArrayList(), content) + { + } + + public PemObject(String type, IList headers, byte[] content) + { + this.type = type; + this.headers = Platform.CreateArrayList(headers); + this.content = content; + } + + public string Type + { + get { return type; } + } + + public IList Headers + { + get { return headers; } + } + + public byte[] Content + { + get { return content; } + } + + public PemObject Generate() + { + return this; + } + } +} diff --git a/Crypto/src/util/io/pem/PemObjectGenerator.cs b/Crypto/src/util/io/pem/PemObjectGenerator.cs new file mode 100644
index 000000000..6f9bfc191 --- /dev/null +++ b/Crypto/src/util/io/pem/PemObjectGenerator.cs
@@ -0,0 +1,13 @@ +using System; + +namespace Org.BouncyCastle.Utilities.IO.Pem +{ + public interface PemObjectGenerator + { + /// <returns> + /// A <see cref="PemObject"/> + /// </returns> + /// <exception cref="PemGenerationException"></exception> + PemObject Generate(); + } +} diff --git a/Crypto/src/util/io/pem/PemObjectParser.cs b/Crypto/src/util/io/pem/PemObjectParser.cs new file mode 100644
index 000000000..91d26dc3a --- /dev/null +++ b/Crypto/src/util/io/pem/PemObjectParser.cs
@@ -0,0 +1,17 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Utilities.IO.Pem +{ + public interface PemObjectParser + { + /// <param name="obj"> + /// A <see cref="PemObject"/> + /// </param> + /// <returns> + /// A <see cref="System.Object"/> + /// </returns> + /// <exception cref="IOException"></exception> + object ParseObject(PemObject obj); + } +} diff --git a/Crypto/src/util/io/pem/PemReader.cs b/Crypto/src/util/io/pem/PemReader.cs new file mode 100644
index 000000000..b3284705d --- /dev/null +++ b/Crypto/src/util/io/pem/PemReader.cs
@@ -0,0 +1,94 @@ +using System; +using System.Collections; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Utilities.IO.Pem +{ + public class PemReader + { + private const string BeginString = "-----BEGIN "; + private const string EndString = "-----END "; + + private readonly TextReader reader; + + public PemReader(TextReader reader) + { + if (reader == null) + throw new ArgumentNullException("reader"); + + this.reader = reader; + } + + public TextReader Reader + { + get { return reader; } + } + + /// <returns> + /// A <see cref="PemObject"/> + /// </returns> + /// <exception cref="IOException"></exception> + public PemObject ReadPemObject() + { + string line = reader.ReadLine(); + + if (line != null && line.StartsWith(BeginString)) + { + line = line.Substring(BeginString.Length); + int index = line.IndexOf('-'); + string type = line.Substring(0, index); + + if (index > 0) + return LoadObject(type); + } + + return null; + } + + private PemObject LoadObject(string type) + { + string endMarker = EndString + type; + IList headers = Platform.CreateArrayList(); + StringBuilder buf = new StringBuilder(); + + string line; + while ((line = reader.ReadLine()) != null + && line.IndexOf(endMarker) == -1) + { + int colonPos = line.IndexOf(':'); + + if (colonPos == -1) + { + buf.Append(line.Trim()); + } + else + { + // Process field + string fieldName = line.Substring(0, colonPos).Trim(); + + if (fieldName.StartsWith("X-")) + fieldName = fieldName.Substring(2); + + string fieldValue = line.Substring(colonPos + 1).Trim(); + + headers.Add(new PemHeader(fieldName, fieldValue)); + } + } + + if (line == null) + { + throw new IOException(endMarker + " not found"); + } + + if (buf.Length % 4 != 0) + { + throw new IOException("base64 data appears to be truncated"); + } + + return new PemObject(type, headers, Base64.Decode(buf.ToString())); + } + } +} diff --git a/Crypto/src/util/io/pem/PemWriter.cs b/Crypto/src/util/io/pem/PemWriter.cs new file mode 100644
index 000000000..e85b31543 --- /dev/null +++ b/Crypto/src/util/io/pem/PemWriter.cs
@@ -0,0 +1,120 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Utilities.IO.Pem +{ + /** + * A generic PEM writer, based on RFC 1421 + */ + public class PemWriter + { + private const int LineLength = 64; + + private readonly TextWriter writer; + private readonly int nlLength; + private char[] buf = new char[LineLength]; + + /** + * Base constructor. + * + * @param out output stream to use. + */ + public PemWriter(TextWriter writer) + { + if (writer == null) + throw new ArgumentNullException("writer"); + + this.writer = writer; + this.nlLength = Platform.NewLine.Length; + } + + public TextWriter Writer + { + get { return writer; } + } + + /** + * 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.Type.Length + 10 + nlLength)) + 6 + 4; + + if (obj.Headers.Count > 0) + { + foreach (PemHeader header in obj.Headers) + { + size += header.Name.Length + ": ".Length + header.Value.Length + nlLength; + } + + size += nlLength; + } + + // base64 encoding + int dataLen = ((obj.Content.Length + 2) / 3) * 4; + + size += dataLen + (((dataLen + LineLength - 1) / LineLength) * nlLength); + + return size; + } + + public void WriteObject(PemObjectGenerator objGen) + { + PemObject obj = objGen.Generate(); + + WritePreEncapsulationBoundary(obj.Type); + + if (obj.Headers.Count > 0) + { + foreach (PemHeader header in obj.Headers) + { + writer.Write(header.Name); + writer.Write(": "); + writer.WriteLine(header.Value); + } + + writer.WriteLine(); + } + + WriteEncoded(obj.Content); + WritePostEncapsulationBoundary(obj.Type); + } + + private void WriteEncoded(byte[] bytes) + { + 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++; + } + writer.WriteLine(buf, 0, index); + } + } + + private void WritePreEncapsulationBoundary(string type) + { + writer.WriteLine("-----BEGIN " + type + "-----"); + } + + private void WritePostEncapsulationBoundary(string type) + { + writer.WriteLine("-----END " + type + "-----"); + } + } +} diff --git a/Crypto/src/util/net/IPAddress.cs b/Crypto/src/util/net/IPAddress.cs new file mode 100644
index 000000000..2a30a15f0 --- /dev/null +++ b/Crypto/src/util/net/IPAddress.cs
@@ -0,0 +1,197 @@ +using System; +using System.Globalization; + +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Utilities.Net +{ + 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 bool 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 bool 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 bool IsValidIPv4( + string address) + { + try + { + return unsafeIsValidIPv4(address); + } + catch (FormatException) {} + catch (OverflowException) {} + return false; + } + + private static bool unsafeIsValidIPv4( + string address) + { + if (address.Length == 0) + return false; + + 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; + + string octetStr = temp.Substring(start, pos - start); + int octet = Int32.Parse(octetStr); + + if (octet < 0 || octet > 255) + return false; + + start = pos + 1; + octets++; + } + + return octets == 4; + } + + public static bool 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 bool 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 bool IsMaskValue( + string component, + int size) + { + int val = Int32.Parse(component); + try + { + return val >= 0 && val <= size; + } + catch (FormatException) {} + catch (OverflowException) {} + 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 bool IsValidIPv6( + string address) + { + try + { + return unsafeIsValidIPv6(address); + } + catch (FormatException) {} + catch (OverflowException) {} + return false; + } + + private static bool unsafeIsValidIPv6( + string address) + { + if (address.Length == 0) + { + return false; + } + + int octets = 0; + + string temp = address + ":"; + bool 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 - start); + + if (pos == (temp.Length - 1) && value.IndexOf('.') > 0) + { + if (!IsValidIPv4(value)) + { + return false; + } + + octets++; // add an extra one as address covers 2 words. + } + else + { + string octetStr = temp.Substring(start, pos - start); + int octet = Int32.Parse(octetStr, NumberStyles.AllowHexSpecifier); + + 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/Crypto/src/util/zlib/Adler32.cs b/Crypto/src/util/zlib/Adler32.cs new file mode 100644
index 000000000..c38258f2a --- /dev/null +++ b/Crypto/src/util/zlib/Adler32.cs
@@ -0,0 +1,88 @@ +using System; +/* + * $Id: Adler32.cs,v 1.1 2006-07-31 13:59:25 bouncy Exp $ + * +Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +namespace Org.BouncyCastle.Utilities.Zlib { + + internal sealed class Adler32{ + + // largest prime smaller than 65536 + private const int BASE=65521; + // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + private const int NMAX=5552; + + internal long adler32(long adler, byte[] buf, int index, int len){ + if(buf == null){ return 1L; } + + long s1=adler&0xffff; + long s2=(adler>>16)&0xffff; + int k; + + while(len > 0) { + k=len<NMAX?len:NMAX; + len-=k; + while(k>=16){ + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + s1+=buf[index++]&0xff; s2+=s1; + k-=16; + } + if(k!=0){ + do{ + s1+=buf[index++]&0xff; s2+=s1; + } + while(--k!=0); + } + s1%=BASE; + s2%=BASE; + } + return (s2<<16)|s1; + } + + } +} \ No newline at end of file diff --git a/Crypto/src/util/zlib/Deflate.cs b/Crypto/src/util/zlib/Deflate.cs new file mode 100644
index 000000000..ca0430939 --- /dev/null +++ b/Crypto/src/util/zlib/Deflate.cs
@@ -0,0 +1,1640 @@ +using System; +/* + * $Id: Deflate.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +namespace Org.BouncyCastle.Utilities.Zlib { + + public sealed class Deflate{ + + private const int MAX_MEM_LEVEL=9; + + private const int Z_DEFAULT_COMPRESSION=-1; + + private const int MAX_WBITS=15; // 32K LZ77 window + private const int DEF_MEM_LEVEL=8; + + internal class Config{ + internal int good_length; // reduce lazy search above this match length + internal int max_lazy; // do not perform lazy search above this match length + internal int nice_length; // quit search above this match length + internal int max_chain; + internal int func; + internal Config(int good_length, int max_lazy, + int nice_length, int max_chain, int func){ + this.good_length=good_length; + this.max_lazy=max_lazy; + this.nice_length=nice_length; + this.max_chain=max_chain; + this.func=func; + } + } + + private const int STORED=0; + private const int FAST=1; + private const int SLOW=2; + private static readonly Config[] config_table; + + static Deflate(){ + config_table=new Config[10]; + // good lazy nice chain + config_table[0]=new Config(0, 0, 0, 0, STORED); + config_table[1]=new Config(4, 4, 8, 4, FAST); + config_table[2]=new Config(4, 5, 16, 8, FAST); + config_table[3]=new Config(4, 6, 32, 32, FAST); + + config_table[4]=new Config(4, 4, 16, 16, SLOW); + config_table[5]=new Config(8, 16, 32, 32, SLOW); + config_table[6]=new Config(8, 16, 128, 128, SLOW); + config_table[7]=new Config(8, 32, 128, 256, SLOW); + config_table[8]=new Config(32, 128, 258, 1024, SLOW); + config_table[9]=new Config(32, 258, 258, 4096, SLOW); + } + + private static readonly String[] z_errmsg = { + "need dictionary", // Z_NEED_DICT 2 + "stream end", // Z_STREAM_END 1 + "", // Z_OK 0 + "file error", // Z_ERRNO (-1) + "stream error", // Z_STREAM_ERROR (-2) + "data error", // Z_DATA_ERROR (-3) + "insufficient memory", // Z_MEM_ERROR (-4) + "buffer error", // Z_BUF_ERROR (-5) + "incompatible version",// Z_VERSION_ERROR (-6) + "" + }; + + // block not completed, need more input or more output + private const int NeedMore=0; + + // block flush performed + private const int BlockDone=1; + + // finish started, need only more output at next deflate + private const int FinishStarted=2; + + // finish done, accept no more input or output + private const int FinishDone=3; + + // preset dictionary flag in zlib header + private const int PRESET_DICT=0x20; + + private const int Z_FILTERED=1; + private const int Z_HUFFMAN_ONLY=2; + private const int Z_DEFAULT_STRATEGY=0; + + private const int Z_NO_FLUSH=0; + private const int Z_PARTIAL_FLUSH=1; + private const int Z_SYNC_FLUSH=2; + private const int Z_FULL_FLUSH=3; + private const int Z_FINISH=4; + + private const int Z_OK=0; + private const int Z_STREAM_END=1; + private const int Z_NEED_DICT=2; + private const int Z_ERRNO=-1; + private const int Z_STREAM_ERROR=-2; + private const int Z_DATA_ERROR=-3; + private const int Z_MEM_ERROR=-4; + private const int Z_BUF_ERROR=-5; + private const int Z_VERSION_ERROR=-6; + + private const int INIT_STATE=42; + private const int BUSY_STATE=113; + private const int FINISH_STATE=666; + + // The deflate compression method + private const int Z_DEFLATED=8; + + private const int STORED_BLOCK=0; + private const int STATIC_TREES=1; + private const int DYN_TREES=2; + + // The three kinds of block type + private const int Z_BINARY=0; + private const int Z_ASCII=1; + private const int Z_UNKNOWN=2; + + private const int Buf_size=8*2; + + // repeat previous bit length 3-6 times (2 bits of repeat count) + private const int REP_3_6=16; + + // repeat a zero length 3-10 times (3 bits of repeat count) + private const int REPZ_3_10=17; + + // repeat a zero length 11-138 times (7 bits of repeat count) + private const int REPZ_11_138=18; + + private const int MIN_MATCH=3; + private const int MAX_MATCH=258; + private const int MIN_LOOKAHEAD=(MAX_MATCH+MIN_MATCH+1); + + private const int MAX_BITS=15; + private const int D_CODES=30; + private const int BL_CODES=19; + private const int LENGTH_CODES=29; + private const int LITERALS=256; + private const int L_CODES=(LITERALS+1+LENGTH_CODES); + private const int HEAP_SIZE=(2*L_CODES+1); + + private const int END_BLOCK=256; + + internal ZStream strm; // pointer back to this zlib stream + internal int status; // as the name implies + internal byte[] pending_buf; // output still pending + internal int pending_buf_size; // size of pending_buf + internal int pending_out; // next pending byte to output to the stream + internal int pending; // nb of bytes in the pending buffer + internal int noheader; // suppress zlib header and adler32 + internal byte data_type; // UNKNOWN, BINARY or ASCII + internal byte method; // STORED (for zip only) or DEFLATED + internal int last_flush; // value of flush param for previous deflate call + + internal int w_size; // LZ77 window size (32K by default) + internal int w_bits; // log2(w_size) (8..16) + internal int w_mask; // w_size - 1 + + internal byte[] window; + // Sliding window. Input bytes are read into the second half of the window, + // and move to the first half later to keep a dictionary of at least wSize + // bytes. With this organization, matches are limited to a distance of + // wSize-MAX_MATCH bytes, but this ensures that IO is always + // performed with a length multiple of the block size. Also, it limits + // the window size to 64K, which is quite useful on MSDOS. + // To do: use the user input buffer as sliding window. + + internal int window_size; + // Actual size of window: 2*wSize, except when the user input buffer + // is directly used as sliding window. + + internal short[] prev; + // Link to older string with same hash index. To limit the size of this + // array to 64K, this link is maintained only for the last 32K strings. + // An index in this array is thus a window index modulo 32K. + + internal short[] head; // Heads of the hash chains or NIL. + + internal int ins_h; // hash index of string to be inserted + internal int hash_size; // number of elements in hash table + internal int hash_bits; // log2(hash_size) + internal int hash_mask; // hash_size-1 + + // Number of bits by which ins_h must be shifted at each input + // step. It must be such that after MIN_MATCH steps, the oldest + // byte no longer takes part in the hash key, that is: + // hash_shift * MIN_MATCH >= hash_bits + internal int hash_shift; + + // Window position at the beginning of the current output block. Gets + // negative when the window is moved backwards. + + internal int block_start; + + internal int match_length; // length of best match + internal int prev_match; // previous match + internal int match_available; // set if previous match exists + internal int strstart; // start of string to insert + internal int match_start; // start of matching string + internal int lookahead; // number of valid bytes ahead in window + + // Length of the best match at previous step. Matches not greater than this + // are discarded. This is used in the lazy match evaluation. + internal int prev_length; + + // To speed up deflation, hash chains are never searched beyond this + // length. A higher limit improves compression ratio but degrades the speed. + internal int max_chain_length; + + // Attempt to find a better match only when the current match is strictly + // smaller than this value. This mechanism is used only for compression + // levels >= 4. + internal int max_lazy_match; + + // Insert new strings in the hash table only if the match length is not + // greater than this length. This saves time but degrades compression. + // max_insert_length is used only for compression levels <= 3. + + internal int level; // compression level (1..9) + internal int strategy; // favor or force Huffman coding + + // Use a faster search when the previous match is longer than this + internal int good_match; + + // Stop searching when current match exceeds this + internal int nice_match; + + internal short[] dyn_ltree; // literal and length tree + internal short[] dyn_dtree; // distance tree + internal short[] bl_tree; // Huffman tree for bit lengths + + internal Tree l_desc=new Tree(); // desc for literal tree + internal Tree d_desc=new Tree(); // desc for distance tree + internal Tree bl_desc=new Tree(); // desc for bit length tree + + // number of codes at each bit length for an optimal tree + internal short[] bl_count=new short[MAX_BITS+1]; + + // heap used to build the Huffman trees + internal int[] heap=new int[2*L_CODES+1]; + + internal int heap_len; // number of elements in the heap + internal int heap_max; // element of largest frequency + // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + // The same heap array is used to build all trees. + + // Depth of each subtree used as tie breaker for trees of equal frequency + internal byte[] depth=new byte[2*L_CODES+1]; + + internal int l_buf; // index for literals or lengths */ + + // Size of match buffer for literals/lengths. There are 4 reasons for + // limiting lit_bufsize to 64K: + // - frequencies can be kept in 16 bit counters + // - if compression is not successful for the first block, all input + // data is still in the window so we can still emit a stored block even + // when input comes from standard input. (This can also be done for + // all blocks if lit_bufsize is not greater than 32K.) + // - if compression is not successful for a file smaller than 64K, we can + // even emit a stored file instead of a stored block (saving 5 bytes). + // This is applicable only for zip (not gzip or zlib). + // - creating new Huffman trees less frequently may not provide fast + // adaptation to changes in the input data statistics. (Take for + // example a binary file with poorly compressible code followed by + // a highly compressible string table.) Smaller buffer sizes give + // fast adaptation but have of course the overhead of transmitting + // trees more frequently. + // - I can't count above 4 + internal int lit_bufsize; + + internal int last_lit; // running index in l_buf + + // Buffer for distances. To simplify the code, d_buf and l_buf have + // the same number of elements. To use different lengths, an extra flag + // array would be necessary. + + internal int d_buf; // index of pendig_buf + + internal int opt_len; // bit length of current block with optimal trees + internal int static_len; // bit length of current block with static trees + internal int matches; // number of string matches in current block + internal int last_eob_len; // bit length of EOB code for last block + + // Output buffer. bits are inserted starting at the bottom (least + // significant bits). + internal uint bi_buf; + + // Number of valid bits in bi_buf. All bits above the last valid bit + // are always zero. + internal int bi_valid; + + internal Deflate(){ + dyn_ltree=new short[HEAP_SIZE*2]; + dyn_dtree=new short[(2*D_CODES+1)*2]; // distance tree + bl_tree=new short[(2*BL_CODES+1)*2]; // Huffman tree for bit lengths + } + + internal void lm_init() { + window_size=2*w_size; + + head[hash_size-1]=0; + for(int i=0; i<hash_size-1; i++){ + head[i]=0; + } + + // Set the default configuration parameters: + max_lazy_match = Deflate.config_table[level].max_lazy; + good_match = Deflate.config_table[level].good_length; + nice_match = Deflate.config_table[level].nice_length; + max_chain_length = Deflate.config_table[level].max_chain; + + strstart = 0; + block_start = 0; + lookahead = 0; + match_length = prev_length = MIN_MATCH-1; + match_available = 0; + ins_h = 0; + } + + // Initialize the tree data structures for a new zlib stream. + internal void tr_init(){ + + l_desc.dyn_tree = dyn_ltree; + l_desc.stat_desc = StaticTree.static_l_desc; + + d_desc.dyn_tree = dyn_dtree; + d_desc.stat_desc = StaticTree.static_d_desc; + + bl_desc.dyn_tree = bl_tree; + bl_desc.stat_desc = StaticTree.static_bl_desc; + + bi_buf = 0; + bi_valid = 0; + last_eob_len = 8; // enough lookahead for inflate + + // Initialize the first block of the first file: + init_block(); + } + + internal void init_block(){ + // Initialize the trees. + for(int i = 0; i < L_CODES; i++) dyn_ltree[i*2] = 0; + for(int i= 0; i < D_CODES; i++) dyn_dtree[i*2] = 0; + for(int i= 0; i < BL_CODES; i++) bl_tree[i*2] = 0; + + dyn_ltree[END_BLOCK*2] = 1; + opt_len = static_len = 0; + last_lit = matches = 0; + } + + // Restore the heap property by moving down the tree starting at node k, + // exchanging a node with the smallest of its two sons if necessary, stopping + // when the heap property is re-established (each father smaller than its + // two sons). + internal void pqdownheap(short[] tree, // the tree to restore + int k // node to move down + ){ + int v = heap[k]; + int j = k << 1; // left son of k + while (j <= heap_len) { + // Set j to the smallest of the two sons: + if (j < heap_len && + smaller(tree, heap[j+1], heap[j], depth)){ + j++; + } + // Exit if v is smaller than both sons + if(smaller(tree, v, heap[j], depth)) break; + + // Exchange v with the smallest son + heap[k]=heap[j]; k = j; + // And continue down the tree, setting j to the left son of k + j <<= 1; + } + heap[k] = v; + } + + internal static bool smaller(short[] tree, int n, int m, byte[] depth){ + short tn2=tree[n*2]; + short tm2=tree[m*2]; + return (tn2<tm2 || + (tn2==tm2 && depth[n] <= depth[m])); + } + + // Scan a literal or distance tree to determine the frequencies of the codes + // in the bit length tree. + internal void scan_tree (short[] tree,// the tree to be scanned + int max_code // and its largest code of non zero frequency + ){ + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0*2+1]; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if (nextlen == 0){ max_count = 138; min_count = 3; } + tree[(max_code+1)*2+1] = -1; // guard + + for(n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[(n+1)*2+1]; + if(++count < max_count && curlen == nextlen) { + continue; + } + else if(count < min_count) { + bl_tree[curlen*2] += (short)count; + } + else if(curlen != 0) { + if(curlen != prevlen) bl_tree[curlen*2]++; + bl_tree[REP_3_6*2]++; + } + else if(count <= 10) { + bl_tree[REPZ_3_10*2]++; + } + else{ + bl_tree[REPZ_11_138*2]++; + } + count = 0; prevlen = curlen; + if(nextlen == 0) { + max_count = 138; min_count = 3; + } + else if(curlen == nextlen) { + max_count = 6; min_count = 3; + } + else{ + max_count = 7; min_count = 4; + } + } + } + + // Construct the Huffman tree for the bit lengths and return the index in + // bl_order of the last bit length code to send. + internal int build_bl_tree(){ + int max_blindex; // index of last bit length code of non zero freq + + // Determine the bit length frequencies for literal and distance trees + scan_tree(dyn_ltree, l_desc.max_code); + scan_tree(dyn_dtree, d_desc.max_code); + + // Build the bit length tree: + bl_desc.build_tree(this); + // opt_len now includes the length of the tree representations, except + // the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + + // Determine the number of bit length codes to send. The pkzip format + // requires that at least 4 bit length codes be sent. (appnote.txt says + // 3 but the actual value used is 4.) + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (bl_tree[Tree.bl_order[max_blindex]*2+1] != 0) break; + } + // Update opt_len to include the bit length tree and counts + opt_len += 3*(max_blindex+1) + 5+5+4; + + return max_blindex; + } + + + // Send the header for a block using dynamic Huffman trees: the counts, the + // lengths of the bit length codes, the literal tree and the distance tree. + // IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + internal void send_all_trees(int lcodes, int dcodes, int blcodes){ + int rank; // index in bl_order + + send_bits(lcodes-257, 5); // not +255 as stated in appnote.txt + send_bits(dcodes-1, 5); + send_bits(blcodes-4, 4); // not -3 as stated in appnote.txt + for (rank = 0; rank < blcodes; rank++) { + send_bits(bl_tree[Tree.bl_order[rank]*2+1], 3); + } + send_tree(dyn_ltree, lcodes-1); // literal tree + send_tree(dyn_dtree, dcodes-1); // distance tree + } + + // Send a literal or distance tree in compressed form, using the codes in + // bl_tree. + internal void send_tree (short[] tree,// the tree to be sent + int max_code // and its largest code of non zero frequency + ){ + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0*2+1]; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if (nextlen == 0){ max_count = 138; min_count = 3; } + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[(n+1)*2+1]; + if(++count < max_count && curlen == nextlen) { + continue; + } + else if(count < min_count) { + do { send_code(curlen, bl_tree); } while (--count != 0); + } + else if(curlen != 0){ + if(curlen != prevlen){ + send_code(curlen, bl_tree); count--; + } + send_code(REP_3_6, bl_tree); + send_bits(count-3, 2); + } + else if(count <= 10){ + send_code(REPZ_3_10, bl_tree); + send_bits(count-3, 3); + } + else{ + send_code(REPZ_11_138, bl_tree); + send_bits(count-11, 7); + } + count = 0; prevlen = curlen; + if(nextlen == 0){ + max_count = 138; min_count = 3; + } + else if(curlen == nextlen){ + max_count = 6; min_count = 3; + } + else{ + max_count = 7; min_count = 4; + } + } + } + + // Output a byte on the stream. + // IN assertion: there is enough room in pending_buf. + internal void put_byte(byte[] p, int start, int len){ + System.Array.Copy(p, start, pending_buf, pending, len); + pending+=len; + } + + internal void put_byte(byte c){ + pending_buf[pending++]=c; + } + internal void put_short(int w) { + pending_buf[pending++]=(byte)(w/*&0xff*/); + pending_buf[pending++]=(byte)(w>>8); + } + internal void putShortMSB(int b){ + pending_buf[pending++]=(byte)(b>>8); + pending_buf[pending++]=(byte)(b/*&0xff*/); + } + + internal void send_code(int c, short[] tree){ + int c2=c*2; + send_bits((tree[c2]&0xffff), (tree[c2+1]&0xffff)); + } + + internal void send_bits(int val, int length){ + if (bi_valid > Buf_size - length) { + bi_buf |= (uint)(val << bi_valid); + pending_buf[pending++]=(byte)(bi_buf/*&0xff*/); + pending_buf[pending++]=(byte)(bi_buf>>8); + bi_buf = ((uint)val) >> (Buf_size - bi_valid); + bi_valid += length - Buf_size; + } else { + bi_buf |= (uint)(val << bi_valid); + bi_valid += length; + } +// int len = length; +// if (bi_valid > (int)Buf_size - len) { +// int val = value; +// // bi_buf |= (val << bi_valid); +// bi_buf = (short)((ushort)bi_buf | (ushort)((val << bi_valid)&0xffff)); +// put_short(bi_buf); +// bi_buf = (short)(((uint)val) >> (Buf_size - bi_valid)); +// bi_valid += len - Buf_size; +// } else { +// // bi_buf |= (value) << bi_valid; +// bi_buf = (short)((ushort)bi_buf | (ushort)(((value) << bi_valid)&0xffff)); +// bi_valid += len; +// } + } + + // Send one empty static block to give enough lookahead for inflate. + // This takes 10 bits, of which 7 may remain in the bit buffer. + // The current inflate code requires 9 bits of lookahead. If the + // last two codes for the previous block (real code plus EOB) were coded + // on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + // the last real code. In this case we send two empty static blocks instead + // of one. (There are no problems if the previous block is stored or fixed.) + // To simplify the code, we assume the worst case of last real code encoded + // on one bit only. + internal void _tr_align(){ + send_bits(STATIC_TREES<<1, 3); + send_code(END_BLOCK, StaticTree.static_ltree); + + bi_flush(); + + // Of the 10 bits for the empty block, we have already sent + // (10 - bi_valid) bits. The lookahead for the last real code (before + // the EOB of the previous block) was thus at least one plus the length + // of the EOB plus what we have just sent of the empty static block. + if (1 + last_eob_len + 10 - bi_valid < 9) { + send_bits(STATIC_TREES<<1, 3); + send_code(END_BLOCK, StaticTree.static_ltree); + bi_flush(); + } + last_eob_len = 7; + } + + + // Save the match info and tally the frequency counts. Return true if + // the current block must be flushed. + internal bool _tr_tally (int dist, // distance of matched string + int lc // match length-MIN_MATCH or unmatched char (if dist==0) + ){ + + pending_buf[d_buf+last_lit*2] = (byte)(dist>>8); + pending_buf[d_buf+last_lit*2+1] = (byte)dist; + + pending_buf[l_buf+last_lit] = (byte)lc; last_lit++; + + if (dist == 0) { + // lc is the unmatched char + dyn_ltree[lc*2]++; + } + else { + matches++; + // Here, lc is the match length - MIN_MATCH + dist--; // dist = match distance - 1 + dyn_ltree[(Tree._length_code[lc]+LITERALS+1)*2]++; + dyn_dtree[Tree.d_code(dist)*2]++; + } + + if ((last_lit & 0x1fff) == 0 && level > 2) { + // Compute an upper bound for the compressed length + int out_length = last_lit*8; + int in_length = strstart - block_start; + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (int)((int)dyn_dtree[dcode*2] * + (5L+Tree.extra_dbits[dcode])); + } + out_length >>= 3; + if ((matches < (last_lit/2)) && out_length < in_length/2) return true; + } + + return (last_lit == lit_bufsize-1); + // We avoid equality with lit_bufsize because of wraparound at 64K + // on 16 bit machines and because stored blocks are restricted to + // 64K-1 bytes. + } + + // Send the block data compressed using the given Huffman trees + internal void compress_block(short[] ltree, short[] dtree){ + int dist; // distance of matched string + int lc; // match length or unmatched char (if dist == 0) + int lx = 0; // running index in l_buf + int code; // the code to send + int extra; // number of extra bits to send + + if (last_lit != 0){ + do{ + dist=((pending_buf[d_buf+lx*2]<<8)&0xff00)| + (pending_buf[d_buf+lx*2+1]&0xff); + lc=(pending_buf[l_buf+lx])&0xff; lx++; + + if(dist == 0){ + send_code(lc, ltree); // send a literal byte + } + else{ + // Here, lc is the match length - MIN_MATCH + code = Tree._length_code[lc]; + + send_code(code+LITERALS+1, ltree); // send the length code + extra = Tree.extra_lbits[code]; + if(extra != 0){ + lc -= Tree.base_length[code]; + send_bits(lc, extra); // send the extra length bits + } + dist--; // dist is now the match distance - 1 + code = Tree.d_code(dist); + + send_code(code, dtree); // send the distance code + extra = Tree.extra_dbits[code]; + if (extra != 0) { + dist -= Tree.base_dist[code]; + send_bits(dist, extra); // send the extra distance bits + } + } // literal or match pair ? + + // Check that the overlay between pending_buf and d_buf+l_buf is ok: + } + while (lx < last_lit); + } + + send_code(END_BLOCK, ltree); + last_eob_len = ltree[END_BLOCK*2+1]; + } + + // Set the data type to ASCII or BINARY, using a crude approximation: + // binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + // IN assertion: the fields freq of dyn_ltree are set and the total of all + // frequencies does not exceed 64K (to fit in an int on 16 bit machines). + internal void set_data_type(){ + int n = 0; + int ascii_freq = 0; + int bin_freq = 0; + while(n<7){ bin_freq += dyn_ltree[n*2]; n++;} + while(n<128){ ascii_freq += dyn_ltree[n*2]; n++;} + while(n<LITERALS){ bin_freq += dyn_ltree[n*2]; n++;} + data_type=(byte)(bin_freq > (ascii_freq >> 2) ? Z_BINARY : Z_ASCII); + } + + // Flush the bit buffer, keeping at most 7 bits in it. + internal void bi_flush(){ + if (bi_valid == 16) { + pending_buf[pending++]=(byte)(bi_buf/*&0xff*/); + pending_buf[pending++]=(byte)(bi_buf>>8); + bi_buf=0; + bi_valid=0; + } + else if (bi_valid >= 8) { + pending_buf[pending++]=(byte)(bi_buf); + bi_buf>>=8; + bi_buf &= 0x00ff; + bi_valid-=8; + } + } + + // Flush the bit buffer and align the output on a byte boundary + internal void bi_windup(){ + if (bi_valid > 8) { + pending_buf[pending++]=(byte)(bi_buf); + pending_buf[pending++]=(byte)(bi_buf>>8); + } else if (bi_valid > 0) { + pending_buf[pending++]=(byte)(bi_buf); + } + bi_buf = 0; + bi_valid = 0; + } + + // Copy a stored block, storing first the length and its + // one's complement if requested. + internal void copy_block(int buf, // the input data + int len, // its length + bool header // true if block header must be written + ){ + //int index=0; + bi_windup(); // align on byte boundary + last_eob_len = 8; // enough lookahead for inflate + + if (header) { + put_short((short)len); + put_short((short)~len); + } + + // while(len--!=0) { + // put_byte(window[buf+index]); + // index++; + // } + put_byte(window, buf, len); + } + + internal void flush_block_only(bool eof){ + _tr_flush_block(block_start>=0 ? block_start : -1, + strstart-block_start, + eof); + block_start=strstart; + strm.flush_pending(); + } + + // Copy without compression as much as possible from the input stream, return + // the current block state. + // This function does not insert new strings in the dictionary since + // uncompressible data is probably not useful. This function is used + // only for the level=0 compression option. + // NOTE: this function should be optimized to avoid extra copying from + // window to pending_buf. + internal int deflate_stored(int flush){ + // Stored blocks are limited to 0xffff bytes, pending_buf is limited + // to pending_buf_size, and each stored block has a 5 byte header: + + int max_block_size = 0xffff; + int max_start; + + if(max_block_size > pending_buf_size - 5) { + max_block_size = pending_buf_size - 5; + } + + // Copy as much as possible from input to output: + while(true){ + // Fill the window as much as possible: + if(lookahead<=1){ + fill_window(); + if(lookahead==0 && flush==Z_NO_FLUSH) return NeedMore; + if(lookahead==0) break; // flush the current block + } + + strstart+=lookahead; + lookahead=0; + + // Emit a stored block if pending_buf will be full: + max_start=block_start+max_block_size; + if(strstart==0|| strstart>=max_start) { + // strstart == 0 is possible when wraparound on 16-bit machine + lookahead = (int)(strstart-max_start); + strstart = (int)max_start; + + flush_block_only(false); + if(strm.avail_out==0) return NeedMore; + + } + + // Flush if we may have to slide, otherwise block_start may become + // negative and the data will be gone: + if(strstart-block_start >= w_size-MIN_LOOKAHEAD) { + flush_block_only(false); + if(strm.avail_out==0) return NeedMore; + } + } + + flush_block_only(flush == Z_FINISH); + if(strm.avail_out==0) + return (flush == Z_FINISH) ? FinishStarted : NeedMore; + + return flush == Z_FINISH ? FinishDone : BlockDone; + } + + // Send a stored block + internal void _tr_stored_block(int buf, // input block + int stored_len, // length of input block + bool eof // true if this is the last block for a file + ){ + send_bits((STORED_BLOCK<<1)+(eof?1:0), 3); // send block type + copy_block(buf, stored_len, true); // with header + } + + // Determine the best encoding for the current block: dynamic trees, static + // trees or store, and output the encoded block to the zip file. + internal void _tr_flush_block(int buf, // input block, or NULL if too old + int stored_len, // length of input block + bool eof // true if this is the last block for a file + ) { + int opt_lenb, static_lenb;// opt_len and static_len in bytes + int max_blindex = 0; // index of last bit length code of non zero freq + + // Build the Huffman trees unless a stored block is forced + if(level > 0) { + // Check if the file is ascii or binary + if(data_type == Z_UNKNOWN) set_data_type(); + + // Construct the literal and distance trees + l_desc.build_tree(this); + + d_desc.build_tree(this); + + // At this point, opt_len and static_len are the total bit lengths of + // the compressed block data, excluding the tree representations. + + // Build the bit length tree for the above two trees, and get the index + // in bl_order of the last bit length code to send. + max_blindex=build_bl_tree(); + + // Determine the best encoding. Compute first the block length in bytes + opt_lenb=(opt_len+3+7)>>3; + static_lenb=(static_len+3+7)>>3; + + if(static_lenb<=opt_lenb) opt_lenb=static_lenb; + } + else { + opt_lenb=static_lenb=stored_len+5; // force a stored block + } + + if(stored_len+4<=opt_lenb && buf != -1){ + // 4: two words for the lengths + // The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + // Otherwise we can't have processed more than WSIZE input bytes since + // the last block flush, because compression would have been + // successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + // transform a block into a stored block. + _tr_stored_block(buf, stored_len, eof); + } + else if(static_lenb == opt_lenb){ + send_bits((STATIC_TREES<<1)+(eof?1:0), 3); + compress_block(StaticTree.static_ltree, StaticTree.static_dtree); + } + else{ + send_bits((DYN_TREES<<1)+(eof?1:0), 3); + send_all_trees(l_desc.max_code+1, d_desc.max_code+1, max_blindex+1); + compress_block(dyn_ltree, dyn_dtree); + } + + // The above check is made mod 2^32, for files larger than 512 MB + // and uLong implemented on 32 bits. + + init_block(); + + if(eof){ + bi_windup(); + } + } + + // Fill the window when the lookahead becomes insufficient. + // Updates strstart and lookahead. + // + // IN assertion: lookahead < MIN_LOOKAHEAD + // OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + // At least one byte has been read, or avail_in == 0; reads are + // performed for at least two bytes (required for the zip translate_eol + // option -- not supported here). + internal void fill_window(){ + int n, m; + int p; + int more; // Amount of free space at the end of the window. + + do{ + more = (window_size-lookahead-strstart); + + // Deal with !@#$% 64K limit: + if(more==0 && strstart==0 && lookahead==0){ + more = w_size; + } + else if(more==-1) { + // Very unlikely, but possible on 16 bit machine if strstart == 0 + // and lookahead == 1 (input done one byte at time) + more--; + + // If the window is almost full and there is insufficient lookahead, + // move the upper half to the lower one to make room in the upper half. + } + else if(strstart >= w_size+ w_size-MIN_LOOKAHEAD) { + System.Array.Copy(window, w_size, window, 0, w_size); + match_start-=w_size; + strstart-=w_size; // we now have strstart >= MAX_DIST + block_start-=w_size; + + // Slide the hash table (could be avoided with 32 bit values + // at the expense of memory usage). We slide even when level == 0 + // to keep the hash table consistent if we switch back to level > 0 + // later. (Using level 0 permanently is not an optimal usage of + // zlib, so we don't care about this pathological case.) + + n = hash_size; + p=n; + do { + m = (head[--p]&0xffff); + head[p]=(short)(m>=w_size ? (m-w_size) : 0); + } + while (--n != 0); + + n = w_size; + p = n; + do { + m = (prev[--p]&0xffff); + prev[p] = (short)(m >= w_size ? (m-w_size) : 0); + // If n is not on any hash chain, prev[n] is garbage but + // its value will never be used. + } + while (--n!=0); + more += w_size; + } + + if (strm.avail_in == 0) return; + + // If there was no sliding: + // strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + // more == window_size - lookahead - strstart + // => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + // => more >= window_size - 2*WSIZE + 2 + // In the BIG_MEM or MMAP case (not yet supported), + // window_size == input_size + MIN_LOOKAHEAD && + // strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + // Otherwise, window_size == 2*WSIZE so more >= 2. + // If there was sliding, more >= WSIZE. So in all cases, more >= 2. + + n = strm.read_buf(window, strstart + lookahead, more); + lookahead += n; + + // Initialize the hash value now that we have some input: + if(lookahead >= MIN_MATCH) { + ins_h = window[strstart]&0xff; + ins_h=(((ins_h)<<hash_shift)^(window[strstart+1]&0xff))&hash_mask; + } + // If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + // but this is not important since only literal bytes will be emitted. + } + while (lookahead < MIN_LOOKAHEAD && strm.avail_in != 0); + } + + // Compress as much as possible from the input stream, return the current + // block state. + // This function does not perform lazy evaluation of matches and inserts + // new strings in the dictionary only for unmatched strings or for short + // matches. It is used only for the fast compression options. + internal int deflate_fast(int flush){ + // short hash_head = 0; // head of the hash chain + int hash_head = 0; // head of the hash chain + bool bflush; // set if current block must be flushed + + while(true){ + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + if(lookahead < MIN_LOOKAHEAD){ + fill_window(); + if(lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH){ + return NeedMore; + } + if(lookahead == 0) break; // flush the current block + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + if(lookahead >= MIN_MATCH){ + ins_h=(((ins_h)<<hash_shift)^(window[(strstart)+(MIN_MATCH-1)]&0xff))&hash_mask; + + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head=(head[ins_h]&0xffff); + prev[strstart&w_mask]=head[ins_h]; + head[ins_h]=(short)strstart; + } + + // Find the longest match, discarding those <= prev_length. + // At this point we have always match_length < MIN_MATCH + + if(hash_head!=0L && + ((strstart-hash_head)&0xffff) <= w_size-MIN_LOOKAHEAD + ){ + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + if(strategy != Z_HUFFMAN_ONLY){ + match_length=longest_match (hash_head); + } + // longest_match() sets match_start + } + if(match_length>=MIN_MATCH){ + // check_match(strstart, match_start, match_length); + + bflush=_tr_tally(strstart-match_start, match_length-MIN_MATCH); + + lookahead -= match_length; + + // Insert new strings in the hash table only if the match length + // is not too large. This saves time but degrades compression. + if(match_length <= max_lazy_match && + lookahead >= MIN_MATCH) { + match_length--; // string at strstart already in hash table + do{ + strstart++; + + ins_h=((ins_h<<hash_shift)^(window[(strstart)+(MIN_MATCH-1)]&0xff))&hash_mask; + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head=(head[ins_h]&0xffff); + prev[strstart&w_mask]=head[ins_h]; + head[ins_h]=(short)strstart; + + // strstart never exceeds WSIZE-MAX_MATCH, so there are + // always MIN_MATCH bytes ahead. + } + while (--match_length != 0); + strstart++; + } + else{ + strstart += match_length; + match_length = 0; + ins_h = window[strstart]&0xff; + + ins_h=(((ins_h)<<hash_shift)^(window[strstart+1]&0xff))&hash_mask; + // If lookahead < MIN_MATCH, ins_h is garbage, but it does not + // matter since it will be recomputed at next deflate call. + } + } + else { + // No match, output a literal byte + + bflush=_tr_tally(0, window[strstart]&0xff); + lookahead--; + strstart++; + } + if (bflush){ + + flush_block_only(false); + if(strm.avail_out==0) return NeedMore; + } + } + + flush_block_only(flush == Z_FINISH); + if(strm.avail_out==0){ + if(flush == Z_FINISH) return FinishStarted; + else return NeedMore; + } + return flush==Z_FINISH ? FinishDone : BlockDone; + } + + // Same as above, but achieves better compression. We use a lazy + // evaluation for matches: a match is finally adopted only if there is + // no better match at the next window position. + internal int deflate_slow(int flush){ + // short hash_head = 0; // head of hash chain + int hash_head = 0; // head of hash chain + bool bflush; // set if current block must be flushed + + // Process the input block. + while(true){ + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + + if (lookahead < MIN_LOOKAHEAD) { + fill_window(); + if(lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return NeedMore; + } + if(lookahead == 0) break; // flush the current block + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + + if(lookahead >= MIN_MATCH) { + ins_h=(((ins_h)<<hash_shift)^(window[(strstart)+(MIN_MATCH-1)]&0xff)) & hash_mask; + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head=(head[ins_h]&0xffff); + prev[strstart&w_mask]=head[ins_h]; + head[ins_h]=(short)strstart; + } + + // Find the longest match, discarding those <= prev_length. + prev_length = match_length; prev_match = match_start; + match_length = MIN_MATCH-1; + + if (hash_head != 0 && prev_length < max_lazy_match && + ((strstart-hash_head)&0xffff) <= w_size-MIN_LOOKAHEAD + ){ + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + + if(strategy != Z_HUFFMAN_ONLY) { + match_length = longest_match(hash_head); + } + // longest_match() sets match_start + + if (match_length <= 5 && (strategy == Z_FILTERED || + (match_length == MIN_MATCH && + strstart - match_start > 4096))) { + + // If prev_match is also MIN_MATCH, match_start is garbage + // but we will ignore the current match anyway. + match_length = MIN_MATCH-1; + } + } + + // If there was a match at the previous step and the current + // match is not better, output the previous match: + if(prev_length >= MIN_MATCH && match_length <= prev_length) { + int max_insert = strstart + lookahead - MIN_MATCH; + // Do not insert strings in hash table beyond this. + + // check_match(strstart-1, prev_match, prev_length); + + bflush=_tr_tally(strstart-1-prev_match, prev_length - MIN_MATCH); + + // Insert in hash table all strings up to the end of the match. + // strstart-1 and strstart are already inserted. If there is not + // enough lookahead, the last two strings are not inserted in + // the hash table. + lookahead -= prev_length-1; + prev_length -= 2; + do{ + if(++strstart <= max_insert) { + ins_h=(((ins_h)<<hash_shift)^(window[(strstart)+(MIN_MATCH-1)]&0xff))&hash_mask; + //prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head=(head[ins_h]&0xffff); + prev[strstart&w_mask]=head[ins_h]; + head[ins_h]=(short)strstart; + } + } + while(--prev_length != 0); + match_available = 0; + match_length = MIN_MATCH-1; + strstart++; + + if (bflush){ + flush_block_only(false); + if(strm.avail_out==0) return NeedMore; + } + } else if (match_available!=0) { + + // If there was no match at the previous position, output a + // single literal. If there was a match but the current match + // is longer, truncate the previous match to a single literal. + + bflush=_tr_tally(0, window[strstart-1]&0xff); + + if (bflush) { + flush_block_only(false); + } + strstart++; + lookahead--; + if(strm.avail_out == 0) return NeedMore; + } else { + // There is no previous match to compare with, wait for + // the next step to decide. + + match_available = 1; + strstart++; + lookahead--; + } + } + + if(match_available!=0) { + bflush=_tr_tally(0, window[strstart-1]&0xff); + match_available = 0; + } + flush_block_only(flush == Z_FINISH); + + if(strm.avail_out==0){ + if(flush == Z_FINISH) return FinishStarted; + else return NeedMore; + } + + return flush == Z_FINISH ? FinishDone : BlockDone; + } + + internal int longest_match(int cur_match){ + int chain_length = max_chain_length; // max hash chain length + int scan = strstart; // current string + int match; // matched string + int len; // length of current match + int best_len = prev_length; // best match length so far + int limit = strstart>(w_size-MIN_LOOKAHEAD) ? + strstart-(w_size-MIN_LOOKAHEAD) : 0; + int nice_match=this.nice_match; + + // Stop when cur_match becomes <= limit. To simplify the code, + // we prevent matches with the string of window index 0. + + int wmask = w_mask; + + int strend = strstart + MAX_MATCH; + byte scan_end1 = window[scan+best_len-1]; + byte scan_end = window[scan+best_len]; + + // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + // It is easy to get rid of this optimization if necessary. + + // Do not waste too much time if we already have a good match: + if (prev_length >= good_match) { + chain_length >>= 2; + } + + // Do not look for matches beyond the end of the input. This is necessary + // to make deflate deterministic. + if (nice_match > lookahead) nice_match = lookahead; + + do { + match = cur_match; + + // Skip to next match if the match length cannot increase + // or if the match length is less than 2: + if (window[match+best_len] != scan_end || + window[match+best_len-1] != scan_end1 || + window[match] != window[scan] || + window[++match] != window[scan+1]) continue; + + // The check at best_len-1 can be removed because it will be made + // again later. (This heuristic is not always a win.) + // It is not necessary to compare scan[2] and match[2] since they + // are always equal when the other bytes match, given that + // the hash keys are equal and that HASH_BITS >= 8. + scan += 2; match++; + + // We check for insufficient lookahead only every 8th comparison; + // the 256th check will be made at strstart+258. + do { + } while (window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + scan < strend); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + + if(len>best_len) { + match_start = cur_match; + best_len = len; + if (len >= nice_match) break; + scan_end1 = window[scan+best_len-1]; + scan_end = window[scan+best_len]; + } + + } while ((cur_match = (prev[cur_match & wmask]&0xffff)) > limit + && --chain_length != 0); + + if (best_len <= lookahead) return best_len; + return lookahead; + } + + internal int deflateInit(ZStream strm, int level, int bits){ + return deflateInit2(strm, level, Z_DEFLATED, bits, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY); + } + internal int deflateInit(ZStream strm, int level){ + return deflateInit(strm, level, MAX_WBITS); + } + internal int deflateInit2(ZStream strm, int level, int method, int windowBits, + int memLevel, int strategy){ + int noheader = 0; + // byte[] my_version=ZLIB_VERSION; + + // + // if (version == null || version[0] != my_version[0] + // || stream_size != sizeof(z_stream)) { + // return Z_VERSION_ERROR; + // } + + strm.msg = null; + + if (level == Z_DEFAULT_COMPRESSION) level = 6; + + if (windowBits < 0) { // undocumented feature: suppress zlib header + noheader = 1; + windowBits = -windowBits; + } + + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || + method != Z_DEFLATED || + windowBits < 9 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_HUFFMAN_ONLY) { + return Z_STREAM_ERROR; + } + + strm.dstate = (Deflate)this; + + this.noheader = noheader; + w_bits = windowBits; + w_size = 1 << w_bits; + w_mask = w_size - 1; + + hash_bits = memLevel + 7; + hash_size = 1 << hash_bits; + hash_mask = hash_size - 1; + hash_shift = ((hash_bits+MIN_MATCH-1)/MIN_MATCH); + + window = new byte[w_size*2]; + prev = new short[w_size]; + head = new short[hash_size]; + + lit_bufsize = 1 << (memLevel + 6); // 16K elements by default + + // We overlay pending_buf and d_buf+l_buf. This works since the average + // output size for (length,distance) codes is <= 24 bits. + pending_buf = new byte[lit_bufsize*4]; + pending_buf_size = lit_bufsize*4; + + d_buf = lit_bufsize/2; + l_buf = (1+2)*lit_bufsize; + + this.level = level; + + //System.out.println("level="+level); + + this.strategy = strategy; + this.method = (byte)method; + + return deflateReset(strm); + } + + internal int deflateReset(ZStream strm){ + strm.total_in = strm.total_out = 0; + strm.msg = null; // + strm.data_type = Z_UNKNOWN; + + pending = 0; + pending_out = 0; + + if(noheader < 0) { + noheader = 0; // was set to -1 by deflate(..., Z_FINISH); + } + status = (noheader!=0) ? BUSY_STATE : INIT_STATE; + strm.adler=strm._adler.adler32(0, null, 0, 0); + + last_flush = Z_NO_FLUSH; + + tr_init(); + lm_init(); + return Z_OK; + } + + internal int deflateEnd(){ + if(status!=INIT_STATE && status!=BUSY_STATE && status!=FINISH_STATE){ + return Z_STREAM_ERROR; + } + // Deallocate in reverse order of allocations: + pending_buf=null; + head=null; + prev=null; + window=null; + // free + // dstate=null; + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; + } + + internal int deflateParams(ZStream strm, int _level, int _strategy){ + int err=Z_OK; + + if(_level == Z_DEFAULT_COMPRESSION){ + _level = 6; + } + if(_level < 0 || _level > 9 || + _strategy < 0 || _strategy > Z_HUFFMAN_ONLY) { + return Z_STREAM_ERROR; + } + + if(config_table[level].func!=config_table[_level].func && + strm.total_in != 0) { + // Flush the last buffer: + err = strm.deflate(Z_PARTIAL_FLUSH); + } + + if(level != _level) { + level = _level; + max_lazy_match = config_table[level].max_lazy; + good_match = config_table[level].good_length; + nice_match = config_table[level].nice_length; + max_chain_length = config_table[level].max_chain; + } + strategy = _strategy; + return err; + } + + internal int deflateSetDictionary (ZStream strm, byte[] dictionary, int dictLength){ + int length = dictLength; + int index=0; + + if(dictionary == null || status != INIT_STATE) + return Z_STREAM_ERROR; + + strm.adler=strm._adler.adler32(strm.adler, dictionary, 0, dictLength); + + if(length < MIN_MATCH) return Z_OK; + if(length > w_size-MIN_LOOKAHEAD){ + length = w_size-MIN_LOOKAHEAD; + index=dictLength-length; // use the tail of the dictionary + } + System.Array.Copy(dictionary, index, window, 0, length); + strstart = length; + block_start = length; + + // Insert all strings in the hash table (except for the last two bytes). + // s->lookahead stays null, so s->ins_h will be recomputed at the next + // call of fill_window. + + ins_h = window[0]&0xff; + ins_h=(((ins_h)<<hash_shift)^(window[1]&0xff))&hash_mask; + + for(int n=0; n<=length-MIN_MATCH; n++){ + ins_h=(((ins_h)<<hash_shift)^(window[(n)+(MIN_MATCH-1)]&0xff))&hash_mask; + prev[n&w_mask]=head[ins_h]; + head[ins_h]=(short)n; + } + return Z_OK; + } + + internal int deflate(ZStream strm, int flush){ + int old_flush; + + if(flush>Z_FINISH || flush<0){ + return Z_STREAM_ERROR; + } + + if(strm.next_out == null || + (strm.next_in == null && strm.avail_in != 0) || + (status == FINISH_STATE && flush != Z_FINISH)) { + strm.msg=z_errmsg[Z_NEED_DICT-(Z_STREAM_ERROR)]; + return Z_STREAM_ERROR; + } + if(strm.avail_out == 0){ + strm.msg=z_errmsg[Z_NEED_DICT-(Z_BUF_ERROR)]; + return Z_BUF_ERROR; + } + + this.strm = strm; // just in case + old_flush = last_flush; + last_flush = flush; + + // Write the zlib header + if(status == INIT_STATE) { + int header = (Z_DEFLATED+((w_bits-8)<<4))<<8; + int level_flags=((level-1)&0xff)>>1; + + if(level_flags>3) level_flags=3; + header |= (level_flags<<6); + if(strstart!=0) header |= PRESET_DICT; + header+=31-(header % 31); + + status=BUSY_STATE; + putShortMSB(header); + + + // Save the adler32 of the preset dictionary: + if(strstart!=0){ + putShortMSB((int)(strm.adler>>16)); + putShortMSB((int)(strm.adler&0xffff)); + } + strm.adler=strm._adler.adler32(0, null, 0, 0); + } + + // Flush as much pending output as possible + if(pending != 0) { + strm.flush_pending(); + if(strm.avail_out == 0) { + //System.out.println(" avail_out==0"); + // Since avail_out is 0, deflate will be called again with + // more output space, but possibly with both pending and + // avail_in equal to zero. There won't be anything to do, + // but this is not an error situation so make sure we + // return OK instead of BUF_ERROR at next call of deflate: + last_flush = -1; + return Z_OK; + } + + // Make sure there is something to do and avoid duplicate consecutive + // flushes. For repeated and useless calls with Z_FINISH, we keep + // returning Z_STREAM_END instead of Z_BUFF_ERROR. + } + else if(strm.avail_in==0 && flush <= old_flush && + flush != Z_FINISH) { + strm.msg=z_errmsg[Z_NEED_DICT-(Z_BUF_ERROR)]; + return Z_BUF_ERROR; + } + + // User must not provide more input after the first FINISH: + if(status == FINISH_STATE && strm.avail_in != 0) { + strm.msg=z_errmsg[Z_NEED_DICT-(Z_BUF_ERROR)]; + return Z_BUF_ERROR; + } + + // Start a new block or continue the current one. + if(strm.avail_in!=0 || lookahead!=0 || + (flush != Z_NO_FLUSH && status != FINISH_STATE)) { + int bstate=-1; + switch(config_table[level].func){ + case STORED: + bstate = deflate_stored(flush); + break; + case FAST: + bstate = deflate_fast(flush); + break; + case SLOW: + bstate = deflate_slow(flush); + break; + default: + break; + } + + if (bstate==FinishStarted || bstate==FinishDone) { + status = FINISH_STATE; + } + if (bstate==NeedMore || bstate==FinishStarted) { + if(strm.avail_out == 0) { + last_flush = -1; // avoid BUF_ERROR next call, see above + } + return Z_OK; + // If flush != Z_NO_FLUSH && avail_out == 0, the next call + // of deflate should use the same flush parameter to make sure + // that the flush is complete. So we don't have to output an + // empty block here, this will be done at next call. This also + // ensures that for a very small output buffer, we emit at most + // one empty block. + } + + if (bstate==BlockDone) { + if(flush == Z_PARTIAL_FLUSH) { + _tr_align(); + } + else { // FULL_FLUSH or SYNC_FLUSH + _tr_stored_block(0, 0, false); + // For a full flush, this empty block will be recognized + // as a special marker by inflate_sync(). + if(flush == Z_FULL_FLUSH) { + //state.head[s.hash_size-1]=0; + for(int i=0; i<hash_size/*-1*/; i++) // forget history + head[i]=0; + } + } + strm.flush_pending(); + if(strm.avail_out == 0) { + last_flush = -1; // avoid BUF_ERROR at next call, see above + return Z_OK; + } + } + } + + if(flush!=Z_FINISH) return Z_OK; + if(noheader!=0) return Z_STREAM_END; + + // Write the zlib trailer (adler32) + putShortMSB((int)(strm.adler>>16)); + putShortMSB((int)(strm.adler&0xffff)); + strm.flush_pending(); + + // If avail_out is zero, the application will call deflate again + // to flush the rest. + noheader = -1; // write the trailer only once! + return pending != 0 ? Z_OK : Z_STREAM_END; + } + } +} \ No newline at end of file diff --git a/Crypto/src/util/zlib/InfBlocks.cs b/Crypto/src/util/zlib/InfBlocks.cs new file mode 100644
index 000000000..479d9b5c9 --- /dev/null +++ b/Crypto/src/util/zlib/InfBlocks.cs
@@ -0,0 +1,618 @@ +using System; +/* + * $Id: InfBlocks.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +namespace Org.BouncyCastle.Utilities.Zlib { + + internal sealed class InfBlocks{ + private const int MANY=1440; + + // And'ing with mask[n] masks the lower n bits + private static readonly int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + // Table for deflate from PKZIP's appnote.txt. + static readonly int[] border = { // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + }; + + private const int Z_OK=0; + private const int Z_STREAM_END=1; + private const int Z_NEED_DICT=2; + private const int Z_ERRNO=-1; + private const int Z_STREAM_ERROR=-2; + private const int Z_DATA_ERROR=-3; + private const int Z_MEM_ERROR=-4; + private const int Z_BUF_ERROR=-5; + private const int Z_VERSION_ERROR=-6; + + private const int TYPE=0; // get type bits (3, including end bit) + private const int LENS=1; // get lengths for stored + private const int STORED=2;// processing stored block + private const int TABLE=3; // get table lengths + private const int BTREE=4; // get bit lengths tree for a dynamic block + private const int DTREE=5; // get length, distance trees for a dynamic block + private const int CODES=6; // processing fixed or dynamic block + private const int DRY=7; // output remaining window bytes + private const int DONE=8; // finished last block, done + private const int BAD=9; // ot a data error--stuck here + + internal int mode; // current inflate_block mode + + internal int left; // if STORED, bytes left to copy + + internal int table; // table lengths (14 bits) + internal int index; // index into blens (or border) + internal int[] blens; // bit lengths of codes + internal int[] bb=new int[1]; // bit length tree depth + internal int[] tb=new int[1]; // bit length decoding tree + + internal InfCodes codes=new InfCodes(); // if CODES, current state + + int last; // true if this block is the last block + + // mode independent information + internal int bitk; // bits in bit buffer + internal int bitb; // bit buffer + internal int[] hufts; // single malloc for tree space + internal byte[] window; // sliding window + internal int end; // one byte after sliding window + internal int read; // window read pointer + internal int write; // window write pointer + internal Object checkfn; // check function + internal long check; // check on output + + internal InfTree inftree=new InfTree(); + + internal InfBlocks(ZStream z, Object checkfn, int w){ + hufts=new int[MANY*3]; + window=new byte[w]; + end=w; + this.checkfn = checkfn; + mode = TYPE; + reset(z, null); + } + + internal void reset(ZStream z, long[] c){ + if(c!=null) c[0]=check; + if(mode==BTREE || mode==DTREE){ + } + if(mode==CODES){ + codes.free(z); + } + mode=TYPE; + bitk=0; + bitb=0; + read=write=0; + + if(checkfn != null) + z.adler=check=z._adler.adler32(0L, null, 0, 0); + } + + internal int proc(ZStream z, int r){ + int t; // temporary storage + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; { // bytes to end of window or read pointer + + // copy input/output information to locals (UPDATE macro restores) + p=z.next_in_index;n=z.avail_in;b=bitb;k=bitk;} { + q=write;m=(int)(q<read?read-q-1:end-q);} + + // process input based on current state + while(true){ + switch (mode){ + case TYPE: + + while(k<(3)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + }; + n--; + b|=(z.next_in[p++]&0xff)<<k; + k+=8; + } + t = (int)(b & 7); + last = t & 1; + + switch (t >> 1){ + case 0: { // stored + b>>=(3);k-=(3);} + t = k & 7; { // go to byte boundary + + b>>=(t);k-=(t);} + mode = LENS; // get length of stored block + break; + case 1: { // fixed + int[] bl=new int[1]; + int[] bd=new int[1]; + int[][] tl=new int[1][]; + int[][] td=new int[1][]; + + InfTree.inflate_trees_fixed(bl, bd, tl, td, z); + codes.init(bl[0], bd[0], tl[0], 0, td[0], 0, z); + } { + + b>>=(3);k-=(3);} + + mode = CODES; + break; + case 2: { // dynamic + + b>>=(3);k-=(3);} + + mode = TABLE; + break; + case 3: { // illegal + + b>>=(3);k-=(3);} + mode = BAD; + z.msg = "invalid block type"; + r = Z_DATA_ERROR; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + } + break; + case LENS: + + while(k<(32)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + }; + n--; + b|=(z.next_in[p++]&0xff)<<k; + k+=8; + } + + if ((((~b) >> 16) & 0xffff) != (b & 0xffff)){ + mode = BAD; + z.msg = "invalid stored block lengths"; + r = Z_DATA_ERROR; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + } + left = (b & 0xffff); + b = k = 0; // dump bits + mode = left!=0 ? STORED : (last!=0 ? DRY : TYPE); + break; + case STORED: + if (n == 0){ + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + } + + if(m==0){ + if(q==end&&read!=0){ + q=0; m=(int)(q<read?read-q-1:end-q); + } + if(m==0){ + write=q; + r=inflate_flush(z,r); + q=write;m=(int)(q<read?read-q-1:end-q); + if(q==end&&read!=0){ + q=0; m=(int)(q<read?read-q-1:end-q); + } + if(m==0){ + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + } + } + } + r=Z_OK; + + t = left; + if(t>n) t = n; + if(t>m) t = m; + System.Array.Copy(z.next_in, p, window, q, t); + p += t; n -= t; + q += t; m -= t; + if ((left -= t) != 0) + break; + mode = last!=0 ? DRY : TYPE; + break; + case TABLE: + + while(k<(14)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + }; + n--; + b|=(z.next_in[p++]&0xff)<<k; + k+=8; + } + + table = t = (b & 0x3fff); + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) { + mode = BAD; + z.msg = "too many length or distance symbols"; + r = Z_DATA_ERROR; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + } + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if(blens==null || blens.Length<t){ + blens=new int[t]; + } + else{ + for(int i=0; i<t; i++){blens[i]=0;} + } { + + b>>=(14);k-=(14);} + + index = 0; + mode = BTREE; + goto case BTREE; + case BTREE: + while (index < 4 + (table >> 10)){ + while(k<(3)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + }; + n--; + b|=(z.next_in[p++]&0xff)<<k; + k+=8; + } + + blens[border[index++]] = b&7; { + + b>>=(3);k-=(3);} + } + + while(index < 19){ + blens[border[index++]] = 0; + } + + bb[0] = 7; + t = inftree.inflate_trees_bits(blens, bb, tb, hufts, z); + if (t != Z_OK){ + r = t; + if (r == Z_DATA_ERROR){ + blens=null; + mode = BAD; + } + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + } + + index = 0; + mode = DTREE; + goto case DTREE; + case DTREE: + while (true){ + t = table; + if(!(index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))){ + break; + } + + int i, j, c; + + t = bb[0]; + + while(k<(t)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + }; + n--; + b|=(z.next_in[p++]&0xff)<<k; + k+=8; + } + + if(tb[0]==-1){ + //System.err.println("null..."); + } + + t=hufts[(tb[0]+(b&inflate_mask[t]))*3+1]; + c=hufts[(tb[0]+(b&inflate_mask[t]))*3+2]; + + if (c < 16){ + b>>=(t);k-=(t); + blens[index++] = c; + } + else { // c == 16..18 + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + + while(k<(t+i)){ + if(n!=0){ + r=Z_OK; + } + else{ + bitb=b; bitk=k; + z.avail_in=n; + z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + }; + n--; + b|=(z.next_in[p++]&0xff)<<k; + k+=8; + } + + b>>=(t);k-=(t); + + j += (b & inflate_mask[i]); + + b>>=(i);k-=(i); + + i = index; + t = table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)){ + blens=null; + mode = BAD; + z.msg = "invalid bit length repeat"; + r = Z_DATA_ERROR; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + } + + c = c == 16 ? blens[i-1] : 0; + do{ + blens[i++] = c; + } + while (--j!=0); + index = i; + } + } + + tb[0]=-1; { + int[] bl=new int[1]; + int[] bd=new int[1]; + int[] tl=new int[1]; + int[] td=new int[1]; + bl[0] = 9; // must be <= 9 for lookahead assumptions + bd[0] = 6; // must be <= 9 for lookahead assumptions + + t = table; + t = inftree.inflate_trees_dynamic(257 + (t & 0x1f), + 1 + ((t >> 5) & 0x1f), + blens, bl, bd, tl, td, hufts, z); + + if (t != Z_OK){ + if (t == Z_DATA_ERROR){ + blens=null; + mode = BAD; + } + r = t; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z,r); + } + codes.init(bl[0], bd[0], hufts, tl[0], hufts, td[0], z); + } + mode = CODES; + goto case CODES; + case CODES: + bitb=b; bitk=k; + z.avail_in=n; z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + + if ((r = codes.proc(this, z, r)) != Z_STREAM_END){ + return inflate_flush(z, r); + } + r = Z_OK; + codes.free(z); + + p=z.next_in_index; n=z.avail_in;b=bitb;k=bitk; + q=write;m=(int)(q<read?read-q-1:end-q); + + if (last==0){ + mode = TYPE; + break; + } + mode = DRY; + goto case DRY; + case DRY: + write=q; + r=inflate_flush(z, r); + q=write; m=(int)(q<read?read-q-1:end-q); + if (read != write){ + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z, r); + } + mode = DONE; + goto case DONE; + case DONE: + r = Z_STREAM_END; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z, r); + case BAD: + r = Z_DATA_ERROR; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z, r); + + default: + r = Z_STREAM_ERROR; + + bitb=b; bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + write=q; + return inflate_flush(z, r); + } + } + } + + internal void free(ZStream z){ + reset(z, null); + window=null; + hufts=null; + //ZFREE(z, s); + } + + internal void set_dictionary(byte[] d, int start, int n){ + System.Array.Copy(d, start, window, 0, n); + read = write = n; + } + + // Returns true if inflate is currently at the end of a block generated + // by Z_SYNC_FLUSH or Z_FULL_FLUSH. + internal int sync_point(){ + return mode == LENS ? 1 : 0; + } + + // copy as much as possible from the sliding window to the output area + internal int inflate_flush(ZStream z, int r){ + int n; + int p; + int q; + + // local copies of source and destination pointers + p = z.next_out_index; + q = read; + + // compute number of bytes to copy as far as end of window + n = (int)((q <= write ? write : end) - q); + if (n > z.avail_out) n = z.avail_out; + if (n!=0 && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if(checkfn != null) + z.adler=check=z._adler.adler32(check, window, q, n); + + // copy as far as end of window + System.Array.Copy(window, q, z.next_out, p, n); + p += n; + q += n; + + // see if more to copy at beginning of window + if (q == end){ + // wrap pointers + q = 0; + if (write == end) + write = 0; + + // compute bytes to copy + n = write - q; + if (n > z.avail_out) n = z.avail_out; + if (n!=0 && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if(checkfn != null) + z.adler=check=z._adler.adler32(check, window, q, n); + + // copy + System.Array.Copy(window, q, z.next_out, p, n); + p += n; + q += n; + } + + // update pointers + z.next_out_index = p; + read = q; + + // done + return r; + } + } +} \ No newline at end of file diff --git a/Crypto/src/util/zlib/InfCodes.cs b/Crypto/src/util/zlib/InfCodes.cs new file mode 100644
index 000000000..6fcafe458 --- /dev/null +++ b/Crypto/src/util/zlib/InfCodes.cs
@@ -0,0 +1,611 @@ +using System; +/* + * $Id: InfCodes.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +namespace Org.BouncyCastle.Utilities.Zlib { + + internal sealed class InfCodes{ + + private static readonly int[] inflate_mask = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, + 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, + 0x00007fff, 0x0000ffff + }; + + private const int Z_OK=0; + private const int Z_STREAM_END=1; + private const int Z_NEED_DICT=2; + private const int Z_ERRNO=-1; + private const int Z_STREAM_ERROR=-2; + private const int Z_DATA_ERROR=-3; + private const int Z_MEM_ERROR=-4; + private const int Z_BUF_ERROR=-5; + private const int Z_VERSION_ERROR=-6; + + // waiting for "i:"=input, + // "o:"=output, + // "x:"=nothing + private const int START=0; // x: set up for LEN + private const int LEN=1; // i: get length/literal/eob next + private const int LENEXT=2; // i: getting length extra (have base) + private const int DIST=3; // i: get distance next + private const int DISTEXT=4;// i: getting distance extra + private const int COPY=5; // o: copying bytes in window, waiting for space + private const int LIT=6; // o: got literal, waiting for output space + private const int WASH=7; // o: got eob, possibly still output waiting + private const int END=8; // x: got eob and all data flushed + private const int BADCODE=9;// x: got error + + int mode; // current inflate_codes mode + + // mode dependent information + int len; + + int[] tree; // pointer into tree + int tree_index=0; + int need; // bits needed + + int lit; + + // if EXT or COPY, where and how much + int get; // bits to get for extra + int dist; // distance back to copy from + + byte lbits; // ltree bits decoded per branch + byte dbits; // dtree bits decoder per branch + int[] ltree; // literal/length/eob tree + int ltree_index; // literal/length/eob tree + int[] dtree; // distance tree + int dtree_index; // distance tree + + internal InfCodes(){ + } + internal void init(int bl, int bd, + int[] tl, int tl_index, + int[] td, int td_index, ZStream z){ + mode=START; + lbits=(byte)bl; + dbits=(byte)bd; + ltree=tl; + ltree_index=tl_index; + dtree = td; + dtree_index=td_index; + tree=null; + } + + internal int proc(InfBlocks s, ZStream z, int r){ + int j; // temporary storage + int tindex; // temporary pointer + int e; // extra bits or operation + int b=0; // bit buffer + int k=0; // bits in bit buffer + int p=0; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int f; // pointer to copy strings from + + // copy input/output information to locals (UPDATE macro restores) + p=z.next_in_index;n=z.avail_in;b=s.bitb;k=s.bitk; + q=s.write;m=q<s.read?s.read-q-1:s.end-q; + + // process input and output based on current state + while (true){ + switch (mode){ + // waiting for "i:"=input, "o:"=output, "x:"=nothing + case START: // x: set up for LEN + if (m >= 258 && n >= 10){ + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + r = inflate_fast(lbits, dbits, + ltree, ltree_index, + dtree, dtree_index, + s, z); + + p=z.next_in_index;n=z.avail_in;b=s.bitb;k=s.bitk; + q=s.write;m=q<s.read?s.read-q-1:s.end-q; + + if (r != Z_OK){ + mode = r == Z_STREAM_END ? WASH : BADCODE; + break; + } + } + need = lbits; + tree = ltree; + tree_index=ltree_index; + + mode = LEN; + goto case LEN; + case LEN: // i: get length/literal/eob next + j = need; + + while(k<(j)){ + if(n!=0)r=Z_OK; + else{ + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(z,r); + } + n--; + b|=(z.next_in[p++]&0xff)<<k; + k+=8; + } + + tindex=(tree_index+(b&inflate_mask[j]))*3; + + b>>=(tree[tindex+1]); + k-=(tree[tindex+1]); + + e=tree[tindex]; + + if(e == 0){ // literal + lit = tree[tindex+2]; + mode = LIT; + break; + } + if((e & 16)!=0 ){ // length + get = e & 15; + len = tree[tindex+2]; + mode = LENEXT; + break; + } + if ((e & 64) == 0){ // next table + need = e; + tree_index = tindex/3+tree[tindex+2]; + break; + } + if ((e & 32)!=0){ // end of block + mode = WASH; + break; + } + mode = BADCODE; // invalid code + z.msg = "invalid literal/length code"; + r = Z_DATA_ERROR; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(z,r); + + case LENEXT: // i: getting length extra (have base) + j = get; + + while(k<(j)){ + if(n!=0)r=Z_OK; + else{ + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(z,r); + } + n--; b|=(z.next_in[p++]&0xff)<<k; + k+=8; + } + + len += (b & inflate_mask[j]); + + b>>=j; + k-=j; + + need = dbits; + tree = dtree; + tree_index=dtree_index; + mode = DIST; + goto case DIST; + case DIST: // i: get distance next + j = need; + + while(k<(j)){ + if(n!=0)r=Z_OK; + else{ + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(z,r); + } + n--; b|=(z.next_in[p++]&0xff)<<k; + k+=8; + } + + tindex=(tree_index+(b & inflate_mask[j]))*3; + + b>>=tree[tindex+1]; + k-=tree[tindex+1]; + + e = (tree[tindex]); + if((e & 16)!=0){ // distance + get = e & 15; + dist = tree[tindex+2]; + mode = DISTEXT; + break; + } + if ((e & 64) == 0){ // next table + need = e; + tree_index = tindex/3 + tree[tindex+2]; + break; + } + mode = BADCODE; // invalid code + z.msg = "invalid distance code"; + r = Z_DATA_ERROR; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(z,r); + + case DISTEXT: // i: getting distance extra + j = get; + + while(k<(j)){ + if(n!=0)r=Z_OK; + else{ + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(z,r); + } + n--; b|=(z.next_in[p++]&0xff)<<k; + k+=8; + } + + dist += (b & inflate_mask[j]); + + b>>=j; + k-=j; + + mode = COPY; + goto case COPY; + case COPY: // o: copying bytes in window, waiting for space + f = q - dist; + while(f < 0){ // modulo window size-"while" instead + f += s.end; // of "if" handles invalid distances + } + while (len!=0){ + + if(m==0){ + if(q==s.end&&s.read!=0){q=0;m=q<s.read?s.read-q-1:s.end-q;} + if(m==0){ + s.write=q; r=s.inflate_flush(z,r); + q=s.write;m=q<s.read?s.read-q-1:s.end-q; + + if(q==s.end&&s.read!=0){q=0;m=q<s.read?s.read-q-1:s.end-q;} + + if(m==0){ + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(z,r); + } + } + } + + s.window[q++]=s.window[f++]; m--; + + if (f == s.end) + f = 0; + len--; + } + mode = START; + break; + case LIT: // o: got literal, waiting for output space + if(m==0){ + if(q==s.end&&s.read!=0){q=0;m=q<s.read?s.read-q-1:s.end-q;} + if(m==0){ + s.write=q; r=s.inflate_flush(z,r); + q=s.write;m=q<s.read?s.read-q-1:s.end-q; + + if(q==s.end&&s.read!=0){q=0;m=q<s.read?s.read-q-1:s.end-q;} + if(m==0){ + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(z,r); + } + } + } + r=Z_OK; + + s.window[q++]=(byte)lit; m--; + + mode = START; + break; + case WASH: // o: got eob, possibly more output + if (k > 7){ // return unused byte, if any + k -= 8; + n++; + p--; // can always return one + } + + s.write=q; r=s.inflate_flush(z,r); + q=s.write;m=q<s.read?s.read-q-1:s.end-q; + + if (s.read != s.write){ + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(z,r); + } + mode = END; + goto case END; + case END: + r = Z_STREAM_END; + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(z,r); + + case BADCODE: // x: got error + + r = Z_DATA_ERROR; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(z,r); + + default: + r = Z_STREAM_ERROR; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + return s.inflate_flush(z,r); + } + } + } + + internal void free(ZStream z){ + // ZFREE(z, c); + } + + // Called with number of bytes left to write in window at least 258 + // (the maximum string length) and number of input bytes available + // at least ten. The ten bytes are six bytes for the longest length/ + // distance pair plus four bytes for overloading the bit buffer. + + internal int inflate_fast(int bl, int bd, + int[] tl, int tl_index, + int[] td, int td_index, + InfBlocks s, ZStream z){ + int t; // temporary pointer + int[] tp; // temporary pointer + int tp_index; // temporary pointer + int e; // extra bits or operation + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int ml; // mask for literal/length tree + int md; // mask for distance tree + int c; // bytes to copy + int d; // distance back to copy from + int r; // copy source pointer + + int tp_index_t_3; // (tp_index+t)*3 + + // load input, output, bit values + p=z.next_in_index;n=z.avail_in;b=s.bitb;k=s.bitk; + q=s.write;m=q<s.read?s.read-q-1:s.end-q; + + // initialize masks + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + // do until not enough input or output space for fast loop + do { // assume called with m >= 258 && n >= 10 + // get literal/length code + while(k<(20)){ // max bits for literal/length code + n--; + b|=(z.next_in[p++]&0xff)<<k;k+=8; + } + + t= b&ml; + tp=tl; + tp_index=tl_index; + tp_index_t_3=(tp_index+t)*3; + if ((e = tp[tp_index_t_3]) == 0){ + b>>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]); + + s.window[q++] = (byte)tp[tp_index_t_3+2]; + m--; + continue; + } + do { + + b>>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]); + + if((e&16)!=0){ + e &= 15; + c = tp[tp_index_t_3+2] + ((int)b & inflate_mask[e]); + + b>>=e; k-=e; + + // decode distance base of block to copy + while(k<(15)){ // max bits for distance code + n--; + b|=(z.next_in[p++]&0xff)<<k;k+=8; + } + + t= b&md; + tp=td; + tp_index=td_index; + tp_index_t_3=(tp_index+t)*3; + e = tp[tp_index_t_3]; + + do { + + b>>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]); + + if((e&16)!=0){ + // get extra bits to add to distance base + e &= 15; + while(k<(e)){ // get extra bits (up to 13) + n--; + b|=(z.next_in[p++]&0xff)<<k;k+=8; + } + + d = tp[tp_index_t_3+2] + (b&inflate_mask[e]); + + b>>=(e); k-=(e); + + // do the copy + m -= c; + if (q >= d){ // offset before dest + // just copy + r=q-d; + if(q-r>0 && 2>(q-r)){ + s.window[q++]=s.window[r++]; // minimum count is three, + s.window[q++]=s.window[r++]; // so unroll loop a little + c-=2; + } + else{ + System.Array.Copy(s.window, r, s.window, q, 2); + q+=2; r+=2; c-=2; + } + } + else{ // else offset after destination + r=q-d; + do{ + r+=s.end; // force pointer in window + }while(r<0); // covers invalid distances + e=s.end-r; + if(c>e){ // if source crosses, + c-=e; // wrapped copy + if(q-r>0 && e>(q-r)){ + do{s.window[q++] = s.window[r++];} + while(--e!=0); + } + else{ + System.Array.Copy(s.window, r, s.window, q, e); + q+=e; r+=e; e=0; + } + r = 0; // copy rest from start of window + } + + } + + // copy all or what's left + if(q-r>0 && c>(q-r)){ + do{s.window[q++] = s.window[r++];} + while(--c!=0); + } + else{ + System.Array.Copy(s.window, r, s.window, q, c); + q+=c; r+=c; c=0; + } + break; + } + else if((e&64)==0){ + t+=tp[tp_index_t_3+2]; + t+=(b&inflate_mask[e]); + tp_index_t_3=(tp_index+t)*3; + e=tp[tp_index_t_3]; + } + else{ + z.msg = "invalid distance code"; + + c=z.avail_in-n;c=(k>>3)<c?k>>3:c;n+=c;p-=c;k-=c<<3; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + + return Z_DATA_ERROR; + } + } + while(true); + break; + } + + if((e&64)==0){ + t+=tp[tp_index_t_3+2]; + t+=(b&inflate_mask[e]); + tp_index_t_3=(tp_index+t)*3; + if((e=tp[tp_index_t_3])==0){ + + b>>=(tp[tp_index_t_3+1]); k-=(tp[tp_index_t_3+1]); + + s.window[q++]=(byte)tp[tp_index_t_3+2]; + m--; + break; + } + } + else if((e&32)!=0){ + + c=z.avail_in-n;c=(k>>3)<c?k>>3:c;n+=c;p-=c;k-=c<<3; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + + return Z_STREAM_END; + } + else{ + z.msg="invalid literal/length code"; + + c=z.avail_in-n;c=(k>>3)<c?k>>3:c;n+=c;p-=c;k-=c<<3; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + + return Z_DATA_ERROR; + } + } + while(true); + } + while(m>=258 && n>= 10); + + // not enough input or output--restore pointers and return + c=z.avail_in-n;c=(k>>3)<c?k>>3:c;n+=c;p-=c;k-=c<<3; + + s.bitb=b;s.bitk=k; + z.avail_in=n;z.total_in+=p-z.next_in_index;z.next_in_index=p; + s.write=q; + + return Z_OK; + } + } +} \ No newline at end of file diff --git a/Crypto/src/util/zlib/InfTree.cs b/Crypto/src/util/zlib/InfTree.cs new file mode 100644
index 000000000..6ed7d1920 --- /dev/null +++ b/Crypto/src/util/zlib/InfTree.cs
@@ -0,0 +1,523 @@ +using System; +/* + * $Id: InfTree.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +namespace Org.BouncyCastle.Utilities.Zlib { + + internal sealed class InfTree{ + + private const int MANY=1440; + + private const int Z_OK=0; + private const int Z_STREAM_END=1; + private const int Z_NEED_DICT=2; + private const int Z_ERRNO=-1; + private const int Z_STREAM_ERROR=-2; + private const int Z_DATA_ERROR=-3; + private const int Z_MEM_ERROR=-4; + private const int Z_BUF_ERROR=-5; + private const int Z_VERSION_ERROR=-6; + + private const int fixed_bl = 9; + private const int fixed_bd = 5; + + static readonly int[] fixed_tl = { + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,192, + 80,7,10, 0,8,96, 0,8,32, 0,9,160, + 0,8,0, 0,8,128, 0,8,64, 0,9,224, + 80,7,6, 0,8,88, 0,8,24, 0,9,144, + 83,7,59, 0,8,120, 0,8,56, 0,9,208, + 81,7,17, 0,8,104, 0,8,40, 0,9,176, + 0,8,8, 0,8,136, 0,8,72, 0,9,240, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,200, + 81,7,13, 0,8,100, 0,8,36, 0,9,168, + 0,8,4, 0,8,132, 0,8,68, 0,9,232, + 80,7,8, 0,8,92, 0,8,28, 0,9,152, + 84,7,83, 0,8,124, 0,8,60, 0,9,216, + 82,7,23, 0,8,108, 0,8,44, 0,9,184, + 0,8,12, 0,8,140, 0,8,76, 0,9,248, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,196, + 81,7,11, 0,8,98, 0,8,34, 0,9,164, + 0,8,2, 0,8,130, 0,8,66, 0,9,228, + 80,7,7, 0,8,90, 0,8,26, 0,9,148, + 84,7,67, 0,8,122, 0,8,58, 0,9,212, + 82,7,19, 0,8,106, 0,8,42, 0,9,180, + 0,8,10, 0,8,138, 0,8,74, 0,9,244, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,204, + 81,7,15, 0,8,102, 0,8,38, 0,9,172, + 0,8,6, 0,8,134, 0,8,70, 0,9,236, + 80,7,9, 0,8,94, 0,8,30, 0,9,156, + 84,7,99, 0,8,126, 0,8,62, 0,9,220, + 82,7,27, 0,8,110, 0,8,46, 0,9,188, + 0,8,14, 0,8,142, 0,8,78, 0,9,252, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,194, + 80,7,10, 0,8,97, 0,8,33, 0,9,162, + 0,8,1, 0,8,129, 0,8,65, 0,9,226, + 80,7,6, 0,8,89, 0,8,25, 0,9,146, + 83,7,59, 0,8,121, 0,8,57, 0,9,210, + 81,7,17, 0,8,105, 0,8,41, 0,9,178, + 0,8,9, 0,8,137, 0,8,73, 0,9,242, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,202, + 81,7,13, 0,8,101, 0,8,37, 0,9,170, + 0,8,5, 0,8,133, 0,8,69, 0,9,234, + 80,7,8, 0,8,93, 0,8,29, 0,9,154, + 84,7,83, 0,8,125, 0,8,61, 0,9,218, + 82,7,23, 0,8,109, 0,8,45, 0,9,186, + 0,8,13, 0,8,141, 0,8,77, 0,9,250, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,198, + 81,7,11, 0,8,99, 0,8,35, 0,9,166, + 0,8,3, 0,8,131, 0,8,67, 0,9,230, + 80,7,7, 0,8,91, 0,8,27, 0,9,150, + 84,7,67, 0,8,123, 0,8,59, 0,9,214, + 82,7,19, 0,8,107, 0,8,43, 0,9,182, + 0,8,11, 0,8,139, 0,8,75, 0,9,246, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,206, + 81,7,15, 0,8,103, 0,8,39, 0,9,174, + 0,8,7, 0,8,135, 0,8,71, 0,9,238, + 80,7,9, 0,8,95, 0,8,31, 0,9,158, + 84,7,99, 0,8,127, 0,8,63, 0,9,222, + 82,7,27, 0,8,111, 0,8,47, 0,9,190, + 0,8,15, 0,8,143, 0,8,79, 0,9,254, + 96,7,256, 0,8,80, 0,8,16, 84,8,115, + 82,7,31, 0,8,112, 0,8,48, 0,9,193, + + 80,7,10, 0,8,96, 0,8,32, 0,9,161, + 0,8,0, 0,8,128, 0,8,64, 0,9,225, + 80,7,6, 0,8,88, 0,8,24, 0,9,145, + 83,7,59, 0,8,120, 0,8,56, 0,9,209, + 81,7,17, 0,8,104, 0,8,40, 0,9,177, + 0,8,8, 0,8,136, 0,8,72, 0,9,241, + 80,7,4, 0,8,84, 0,8,20, 85,8,227, + 83,7,43, 0,8,116, 0,8,52, 0,9,201, + 81,7,13, 0,8,100, 0,8,36, 0,9,169, + 0,8,4, 0,8,132, 0,8,68, 0,9,233, + 80,7,8, 0,8,92, 0,8,28, 0,9,153, + 84,7,83, 0,8,124, 0,8,60, 0,9,217, + 82,7,23, 0,8,108, 0,8,44, 0,9,185, + 0,8,12, 0,8,140, 0,8,76, 0,9,249, + 80,7,3, 0,8,82, 0,8,18, 85,8,163, + 83,7,35, 0,8,114, 0,8,50, 0,9,197, + 81,7,11, 0,8,98, 0,8,34, 0,9,165, + 0,8,2, 0,8,130, 0,8,66, 0,9,229, + 80,7,7, 0,8,90, 0,8,26, 0,9,149, + 84,7,67, 0,8,122, 0,8,58, 0,9,213, + 82,7,19, 0,8,106, 0,8,42, 0,9,181, + 0,8,10, 0,8,138, 0,8,74, 0,9,245, + 80,7,5, 0,8,86, 0,8,22, 192,8,0, + 83,7,51, 0,8,118, 0,8,54, 0,9,205, + 81,7,15, 0,8,102, 0,8,38, 0,9,173, + 0,8,6, 0,8,134, 0,8,70, 0,9,237, + 80,7,9, 0,8,94, 0,8,30, 0,9,157, + 84,7,99, 0,8,126, 0,8,62, 0,9,221, + 82,7,27, 0,8,110, 0,8,46, 0,9,189, + 0,8,14, 0,8,142, 0,8,78, 0,9,253, + 96,7,256, 0,8,81, 0,8,17, 85,8,131, + 82,7,31, 0,8,113, 0,8,49, 0,9,195, + 80,7,10, 0,8,97, 0,8,33, 0,9,163, + 0,8,1, 0,8,129, 0,8,65, 0,9,227, + 80,7,6, 0,8,89, 0,8,25, 0,9,147, + 83,7,59, 0,8,121, 0,8,57, 0,9,211, + 81,7,17, 0,8,105, 0,8,41, 0,9,179, + 0,8,9, 0,8,137, 0,8,73, 0,9,243, + 80,7,4, 0,8,85, 0,8,21, 80,8,258, + 83,7,43, 0,8,117, 0,8,53, 0,9,203, + 81,7,13, 0,8,101, 0,8,37, 0,9,171, + 0,8,5, 0,8,133, 0,8,69, 0,9,235, + 80,7,8, 0,8,93, 0,8,29, 0,9,155, + 84,7,83, 0,8,125, 0,8,61, 0,9,219, + 82,7,23, 0,8,109, 0,8,45, 0,9,187, + 0,8,13, 0,8,141, 0,8,77, 0,9,251, + 80,7,3, 0,8,83, 0,8,19, 85,8,195, + 83,7,35, 0,8,115, 0,8,51, 0,9,199, + 81,7,11, 0,8,99, 0,8,35, 0,9,167, + 0,8,3, 0,8,131, 0,8,67, 0,9,231, + 80,7,7, 0,8,91, 0,8,27, 0,9,151, + 84,7,67, 0,8,123, 0,8,59, 0,9,215, + 82,7,19, 0,8,107, 0,8,43, 0,9,183, + 0,8,11, 0,8,139, 0,8,75, 0,9,247, + 80,7,5, 0,8,87, 0,8,23, 192,8,0, + 83,7,51, 0,8,119, 0,8,55, 0,9,207, + 81,7,15, 0,8,103, 0,8,39, 0,9,175, + 0,8,7, 0,8,135, 0,8,71, 0,9,239, + 80,7,9, 0,8,95, 0,8,31, 0,9,159, + 84,7,99, 0,8,127, 0,8,63, 0,9,223, + 82,7,27, 0,8,111, 0,8,47, 0,9,191, + 0,8,15, 0,8,143, 0,8,79, 0,9,255 + }; + static readonly int[] fixed_td = { + 80,5,1, 87,5,257, 83,5,17, 91,5,4097, + 81,5,5, 89,5,1025, 85,5,65, 93,5,16385, + 80,5,3, 88,5,513, 84,5,33, 92,5,8193, + 82,5,9, 90,5,2049, 86,5,129, 192,5,24577, + 80,5,2, 87,5,385, 83,5,25, 91,5,6145, + 81,5,7, 89,5,1537, 85,5,97, 93,5,24577, + 80,5,4, 88,5,769, 84,5,49, 92,5,12289, + 82,5,13, 90,5,3073, 86,5,193, 192,5,24577 + }; + + // Tables for deflate from PKZIP's appnote.txt. + static readonly int[] cplens = { // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 + }; + + // see note #13 above about 258 + static readonly int[] cplext = { // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112 // 112==invalid + }; + + static readonly int[] cpdist = { // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577 + }; + + static readonly int[] cpdext = { // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + + // If BMAX needs to be larger than 16, then h and x[] should be uLong. + const int BMAX=15; // maximum bit length of any code + + int[] hn = null; // hufts used in space + int[] v = null; // work area for huft_build + int[] c = null; // bit length count table + int[] r = null; // table entry for structure assignment + int[] u = null; // table stack + int[] x = null; // bit offsets, then code stack + + private int huft_build(int[] b, // code lengths in bits (all assumed <= BMAX) + int bindex, + int n, // number of codes (assumed <= 288) + int s, // number of simple-valued codes (0..s-1) + int[] d, // list of base values for non-simple codes + int[] e, // list of extra bits for non-simple codes + int[] t, // result: starting table + int[] m, // maximum lookup bits, returns actual + int[] hp,// space for trees + int[] hn,// hufts used in space + int[] v // working area: values in order of bit length + ){ + // Given a list of code lengths and a maximum table size, make a set of + // tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + // if the given code set is incomplete (the tables are still built in this + // case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of + // lengths), or Z_MEM_ERROR if not enough memory. + + int a; // counter for codes of length k + int f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + int i; // counter, current code + int j; // counter + int k; // number of bits in current code + int l; // bits per table (returned in m) + int mask; // (1 << w) - 1, to avoid cc -O bug on HP + int p; // pointer into c[], b[], or v[] + int q; // points to current table + int w; // bits before this table == (l * h) + int xp; // pointer into x + int y; // number of dummy codes added + int z; // number of entries in current table + + // Generate counts for each bit length + + p = 0; i = n; + do { + c[b[bindex+p]]++; p++; i--; // assume all entries <= BMAX + }while(i!=0); + + if(c[0] == n){ // null input--all zero length codes + t[0] = -1; + m[0] = 0; + return Z_OK; + } + + // Find minimum and maximum length, bound *m by those + l = m[0]; + for (j = 1; j <= BMAX; j++) + if(c[j]!=0) break; + k = j; // minimum code length + if(l < j){ + l = j; + } + for (i = BMAX; i!=0; i--){ + if(c[i]!=0) break; + } + g = i; // maximum code length + if(l > i){ + l = i; + } + m[0] = l; + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1){ + if ((y -= c[j]) < 0){ + return Z_DATA_ERROR; + } + } + if ((y -= c[i]) < 0){ + return Z_DATA_ERROR; + } + c[i] += y; + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = 1; xp = 2; + while (--i!=0) { // note that i == g from above + x[xp] = (j += c[p]); + xp++; + p++; + } + + // Make a table of values in order of bit lengths + i = 0; p = 0; + do { + if ((j = b[bindex+p]) != 0){ + v[x[j]++] = i; + } + p++; + } + while (++i < n); + n = x[g]; // set n to length of v + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = 0; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = 0; // just to keep compilers happy + q = 0; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++){ + a = c[k]; + while (a--!=0){ + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l){ + h++; + w += l; // previous table always l bits + // compute minimum size table less than or equal to l bits + z = g - w; + z = (z > l) ? l : z; // table size upper limit + if((f=1<<(j=k-w))>a+1){ // try a k-w bit table + // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = k; + if(j < z){ + while (++j < z){ // try smaller tables up to z bits + if((f <<= 1) <= c[++xp]) + break; // enough codes to use up j bits + f -= c[xp]; // else deduct codes from patterns + } + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (hn[0] + z > MANY){ // (note: doesn't matter for fixed) + return Z_DATA_ERROR; // overflow of MANY + } + u[h] = q = /*hp+*/ hn[0]; // DEBUG + hn[0] += z; + + // connect to last table, if there is one + if(h!=0){ + x[h]=i; // save pattern for backing up + r[0]=(byte)j; // bits in this table + r[1]=(byte)l; // bits to dump before this table + j=i>>(w - l); + r[2] = (int)(q - u[h-1] - j); // offset to this table + System.Array.Copy(r, 0, hp, (u[h-1]+j)*3, 3); // connect to last table + } + else{ + t[0] = q; // first table is returned result + } + } + + // set up table entry in r + r[1] = (byte)(k - w); + if (p >= n){ + r[0] = 128 + 64; // out of values--invalid code + } + else if (v[p] < s){ + r[0] = (byte)(v[p] < 256 ? 0 : 32 + 64); // 256 is end-of-block + r[2] = v[p++]; // simple code is just the value + } + else{ + r[0]=(byte)(e[v[p]-s]+16+64); // non-simple--look up in lists + r[2]=d[v[p++] - s]; + } + + // fill code-like entries with r + f=1<<(k-w); + for (j=i>>w;j<z;j+=f){ + System.Array.Copy(r, 0, hp, (q+j)*3, 3); + } + + // backwards increment the k-bit code i + for (j = 1 << (k - 1); (i & j)!=0; j >>= 1){ + i ^= j; + } + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]){ + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + // Return Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; + } + + internal int inflate_trees_bits(int[] c, // 19 code lengths + int[] bb, // bits tree desired/actual depth + int[] tb, // bits tree result + int[] hp, // space for trees + ZStream z // for messages + ){ + int result; + initWorkArea(19); + hn[0]=0; + result = huft_build(c, 0, 19, 19, null, null, tb, bb, hp, hn, v); + + if(result == Z_DATA_ERROR){ + z.msg = "oversubscribed dynamic bit lengths tree"; + } + else if(result == Z_BUF_ERROR || bb[0] == 0){ + z.msg = "incomplete dynamic bit lengths tree"; + result = Z_DATA_ERROR; + } + return result; + } + + internal int inflate_trees_dynamic(int nl, // number of literal/length codes + int nd, // number of distance codes + int[] c, // that many (total) code lengths + int[] bl, // literal desired/actual bit depth + int[] bd, // distance desired/actual bit depth + int[] tl, // literal/length tree result + int[] td, // distance tree result + int[] hp, // space for trees + ZStream z // for messages + ){ + int result; + + // build literal/length tree + initWorkArea(288); + hn[0]=0; + result = huft_build(c, 0, nl, 257, cplens, cplext, tl, bl, hp, hn, v); + if (result != Z_OK || bl[0] == 0){ + if(result == Z_DATA_ERROR){ + z.msg = "oversubscribed literal/length tree"; + } + else if (result != Z_MEM_ERROR){ + z.msg = "incomplete literal/length tree"; + result = Z_DATA_ERROR; + } + return result; + } + + // build distance tree + initWorkArea(288); + result = huft_build(c, nl, nd, 0, cpdist, cpdext, td, bd, hp, hn, v); + + if (result != Z_OK || (bd[0] == 0 && nl > 257)){ + if (result == Z_DATA_ERROR){ + z.msg = "oversubscribed distance tree"; + } + else if (result == Z_BUF_ERROR) { + z.msg = "incomplete distance tree"; + result = Z_DATA_ERROR; + } + else if (result != Z_MEM_ERROR){ + z.msg = "empty distance tree with lengths"; + result = Z_DATA_ERROR; + } + return result; + } + + return Z_OK; + } + + internal static int inflate_trees_fixed(int[] bl, //literal desired/actual bit depth + int[] bd, //distance desired/actual bit depth + int[][] tl,//literal/length tree result + int[][] td,//distance tree result + ZStream z //for memory allocation + ){ + bl[0]=fixed_bl; + bd[0]=fixed_bd; + tl[0]=fixed_tl; + td[0]=fixed_td; + return Z_OK; + } + + private void initWorkArea(int vsize){ + if(hn==null){ + hn=new int[1]; + v=new int[vsize]; + c=new int[BMAX+1]; + r=new int[3]; + u=new int[BMAX]; + x=new int[BMAX+1]; + } + if(v.Length<vsize){ v=new int[vsize]; } + for(int i=0; i<vsize; i++){v[i]=0;} + for(int i=0; i<BMAX+1; i++){c[i]=0;} + for(int i=0; i<3; i++){r[i]=0;} + // for(int i=0; i<BMAX; i++){u[i]=0;} + System.Array.Copy(c, 0, u, 0, BMAX); + // for(int i=0; i<BMAX+1; i++){x[i]=0;} + System.Array.Copy(c, 0, x, 0, BMAX+1); + } + } +} \ No newline at end of file diff --git a/Crypto/src/util/zlib/Inflate.cs b/Crypto/src/util/zlib/Inflate.cs new file mode 100644
index 000000000..ac8789332 --- /dev/null +++ b/Crypto/src/util/zlib/Inflate.cs
@@ -0,0 +1,387 @@ +using System; +/* + * $Id: Inflate.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +namespace Org.BouncyCastle.Utilities.Zlib { + + internal sealed class Inflate{ + + private const int MAX_WBITS=15; // 32K LZ77 window + + // preset dictionary flag in zlib header + private const int PRESET_DICT=0x20; + + internal const int Z_NO_FLUSH=0; + internal const int Z_PARTIAL_FLUSH=1; + internal const int Z_SYNC_FLUSH=2; + internal const int Z_FULL_FLUSH=3; + internal const int Z_FINISH=4; + + private const int Z_DEFLATED=8; + + private const int Z_OK=0; + private const int Z_STREAM_END=1; + private const int Z_NEED_DICT=2; + private const int Z_ERRNO=-1; + private const int Z_STREAM_ERROR=-2; + private const int Z_DATA_ERROR=-3; + private const int Z_MEM_ERROR=-4; + private const int Z_BUF_ERROR=-5; + private const int Z_VERSION_ERROR=-6; + + private const int METHOD=0; // waiting for method byte + private const int FLAG=1; // waiting for flag byte + private const int DICT4=2; // four dictionary check bytes to go + private const int DICT3=3; // three dictionary check bytes to go + private const int DICT2=4; // two dictionary check bytes to go + private const int DICT1=5; // one dictionary check byte to go + private const int DICT0=6; // waiting for inflateSetDictionary + private const int BLOCKS=7; // decompressing blocks + private const int CHECK4=8; // four check bytes to go + private const int CHECK3=9; // three check bytes to go + private const int CHECK2=10; // two check bytes to go + private const int CHECK1=11; // one check byte to go + private const int DONE=12; // finished check, done + private const int BAD=13; // got an error--stay here + + internal int mode; // current inflate mode + + // mode dependent information + internal int method; // if FLAGS, method byte + + // if CHECK, check values to compare + internal long[] was=new long[1] ; // computed check value + internal long need; // stream check value + + // if BAD, inflateSync's marker bytes count + internal int marker; + + // mode independent information + internal int nowrap; // flag for no wrapper + internal int wbits; // log2(window size) (8..15, defaults to 15) + + internal InfBlocks blocks; // current inflate_blocks state + + internal int inflateReset(ZStream z){ + if(z == null || z.istate == null) return Z_STREAM_ERROR; + + z.total_in = z.total_out = 0; + z.msg = null; + z.istate.mode = z.istate.nowrap!=0 ? BLOCKS : METHOD; + z.istate.blocks.reset(z, null); + return Z_OK; + } + + internal int inflateEnd(ZStream z){ + if(blocks != null) + blocks.free(z); + blocks=null; + // ZFREE(z, z->state); + return Z_OK; + } + + internal int inflateInit(ZStream z, int w){ + z.msg = null; + blocks = null; + + // handle undocumented nowrap option (no zlib header or check) + nowrap = 0; + if(w < 0){ + w = - w; + nowrap = 1; + } + + // set window size + if(w<8 ||w>15){ + inflateEnd(z); + return Z_STREAM_ERROR; + } + wbits=w; + + z.istate.blocks=new InfBlocks(z, + z.istate.nowrap!=0 ? null : this, + 1<<w); + + // reset state + inflateReset(z); + return Z_OK; + } + + internal int inflate(ZStream z, int f){ + int r; + int b; + + if(z == null || z.istate == null || z.next_in == null) + return Z_STREAM_ERROR; + f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK; + r = Z_BUF_ERROR; + while (true){ + //System.out.println("mode: "+z.istate.mode); + switch (z.istate.mode){ + case METHOD: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + if(((z.istate.method = z.next_in[z.next_in_index++])&0xf)!=Z_DEFLATED){ + z.istate.mode = BAD; + z.msg="unknown compression method"; + z.istate.marker = 5; // can't try inflateSync + break; + } + if((z.istate.method>>4)+8>z.istate.wbits){ + z.istate.mode = BAD; + z.msg="invalid window size"; + z.istate.marker = 5; // can't try inflateSync + break; + } + z.istate.mode=FLAG; + goto case FLAG; + case FLAG: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + b = (z.next_in[z.next_in_index++])&0xff; + + if((((z.istate.method << 8)+b) % 31)!=0){ + z.istate.mode = BAD; + z.msg = "incorrect header check"; + z.istate.marker = 5; // can't try inflateSync + break; + } + + if((b&PRESET_DICT)==0){ + z.istate.mode = BLOCKS; + break; + } + z.istate.mode = DICT4; + goto case DICT4; + case DICT4: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + z.istate.need=((z.next_in[z.next_in_index++]&0xff)<<24)&0xff000000L; + z.istate.mode=DICT3; + goto case DICT3; + case DICT3: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + z.istate.need+=((z.next_in[z.next_in_index++]&0xff)<<16)&0xff0000L; + z.istate.mode=DICT2; + goto case DICT2; + case DICT2: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + z.istate.need+=((z.next_in[z.next_in_index++]&0xff)<<8)&0xff00L; + z.istate.mode=DICT1; + goto case DICT1; + case DICT1: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + z.istate.need += (z.next_in[z.next_in_index++]&0xffL); + z.adler = z.istate.need; + z.istate.mode = DICT0; + return Z_NEED_DICT; + case DICT0: + z.istate.mode = BAD; + z.msg = "need dictionary"; + z.istate.marker = 0; // can try inflateSync + return Z_STREAM_ERROR; + case BLOCKS: + + r = z.istate.blocks.proc(z, r); + if(r == Z_DATA_ERROR){ + z.istate.mode = BAD; + z.istate.marker = 0; // can try inflateSync + break; + } + if(r == Z_OK){ + r = f; + } + if(r != Z_STREAM_END){ + return r; + } + r = f; + z.istate.blocks.reset(z, z.istate.was); + if(z.istate.nowrap!=0){ + z.istate.mode=DONE; + break; + } + z.istate.mode=CHECK4; + goto case CHECK4; + case CHECK4: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + z.istate.need=((z.next_in[z.next_in_index++]&0xff)<<24)&0xff000000L; + z.istate.mode=CHECK3; + goto case CHECK3; + case CHECK3: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + z.istate.need+=((z.next_in[z.next_in_index++]&0xff)<<16)&0xff0000L; + z.istate.mode = CHECK2; + goto case CHECK2; + case CHECK2: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + z.istate.need+=((z.next_in[z.next_in_index++]&0xff)<<8)&0xff00L; + z.istate.mode = CHECK1; + goto case CHECK1; + case CHECK1: + + if(z.avail_in==0)return r;r=f; + + z.avail_in--; z.total_in++; + z.istate.need+=(z.next_in[z.next_in_index++]&0xffL); + + if(((int)(z.istate.was[0])) != ((int)(z.istate.need))){ + z.istate.mode = BAD; + z.msg = "incorrect data check"; + z.istate.marker = 5; // can't try inflateSync + break; + } + + z.istate.mode = DONE; + goto case DONE; + case DONE: + return Z_STREAM_END; + case BAD: + return Z_DATA_ERROR; + default: + return Z_STREAM_ERROR; + } + } + } + + + internal int inflateSetDictionary(ZStream z, byte[] dictionary, int dictLength){ + int index=0; + int length = dictLength; + if(z==null || z.istate == null|| z.istate.mode != DICT0) + return Z_STREAM_ERROR; + + if(z._adler.adler32(1L, dictionary, 0, dictLength)!=z.adler){ + return Z_DATA_ERROR; + } + + z.adler = z._adler.adler32(0, null, 0, 0); + + if(length >= (1<<z.istate.wbits)){ + length = (1<<z.istate.wbits)-1; + index=dictLength - length; + } + z.istate.blocks.set_dictionary(dictionary, index, length); + z.istate.mode = BLOCKS; + return Z_OK; + } + + private static readonly byte[] mark = {(byte)0, (byte)0, (byte)0xff, (byte)0xff}; + + internal int inflateSync(ZStream z){ + int n; // number of bytes to look at + int p; // pointer to bytes + int m; // number of marker bytes found in a row + long r, w; // temporaries to save total_in and total_out + + // set up + if(z == null || z.istate == null) + return Z_STREAM_ERROR; + if(z.istate.mode != BAD){ + z.istate.mode = BAD; + z.istate.marker = 0; + } + if((n=z.avail_in)==0) + return Z_BUF_ERROR; + p=z.next_in_index; + m=z.istate.marker; + + // search + while (n!=0 && m < 4){ + if(z.next_in[p] == mark[m]){ + m++; + } + else if(z.next_in[p]!=0){ + m = 0; + } + else{ + m = 4 - m; + } + p++; n--; + } + + // restore + z.total_in += p-z.next_in_index; + z.next_in_index = p; + z.avail_in = n; + z.istate.marker = m; + + // return no joy or set up to restart on a new block + if(m != 4){ + return Z_DATA_ERROR; + } + r=z.total_in; w=z.total_out; + inflateReset(z); + z.total_in=r; z.total_out = w; + z.istate.mode = BLOCKS; + return Z_OK; + } + + // Returns true if inflate is currently at the end of a block generated + // by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + // implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH + // but removes the length bytes of the resulting empty stored block. When + // decompressing, PPP checks that at the end of input packet, inflate is + // waiting for these length bytes. + internal int inflateSyncPoint(ZStream z){ + if(z == null || z.istate == null || z.istate.blocks == null) + return Z_STREAM_ERROR; + return z.istate.blocks.sync_point(); + } + } +} \ No newline at end of file diff --git a/Crypto/src/util/zlib/JZlib.cs b/Crypto/src/util/zlib/JZlib.cs new file mode 100644
index 000000000..4f2cfdaa9 --- /dev/null +++ b/Crypto/src/util/zlib/JZlib.cs
@@ -0,0 +1,73 @@ +using System; +/* + * $Id: JZlib.cs,v 1.3 2011-02-15 05:46:04 bouncy Exp $ + * +Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +namespace Org.BouncyCastle.Utilities.Zlib { + + public sealed class JZlib{ + private const String _version="1.0.7"; + public static String version() + { + return _version; + } + + // compression levels + public const int Z_NO_COMPRESSION=0; + public const int Z_BEST_SPEED=1; + public const int Z_BEST_COMPRESSION=9; + public const int Z_DEFAULT_COMPRESSION=-1; + + // compression strategy + public const int Z_FILTERED=1; + public const int Z_HUFFMAN_ONLY=2; + public const int Z_DEFAULT_STRATEGY=0; + + public const int Z_NO_FLUSH=0; + public const int Z_PARTIAL_FLUSH=1; + public const int Z_SYNC_FLUSH=2; + public const int Z_FULL_FLUSH=3; + public const int Z_FINISH=4; + + public const int Z_OK=0; + public const int Z_STREAM_END=1; + public const int Z_NEED_DICT=2; + public const int Z_ERRNO=-1; + public const int Z_STREAM_ERROR=-2; + public const int Z_DATA_ERROR=-3; + public const int Z_MEM_ERROR=-4; + public const int Z_BUF_ERROR=-5; + public const int Z_VERSION_ERROR=-6; + } +} \ No newline at end of file diff --git a/Crypto/src/util/zlib/StaticTree.cs b/Crypto/src/util/zlib/StaticTree.cs new file mode 100644
index 000000000..097735db5 --- /dev/null +++ b/Crypto/src/util/zlib/StaticTree.cs
@@ -0,0 +1,152 @@ +using System; +/* + * $Id: StaticTree.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +namespace Org.BouncyCastle.Utilities.Zlib { + + internal sealed class StaticTree{ + private const int MAX_BITS=15; + + private const int BL_CODES=19; + private const int D_CODES=30; + private const int LITERALS=256; + private const int LENGTH_CODES=29; + private const int L_CODES=(LITERALS+1+LENGTH_CODES); + + // Bit length codes must not exceed MAX_BL_BITS bits + internal const int MAX_BL_BITS=7; + + internal static readonly short[] static_ltree = { + 12, 8, 140, 8, 76, 8, 204, 8, 44, 8, + 172, 8, 108, 8, 236, 8, 28, 8, 156, 8, + 92, 8, 220, 8, 60, 8, 188, 8, 124, 8, + 252, 8, 2, 8, 130, 8, 66, 8, 194, 8, + 34, 8, 162, 8, 98, 8, 226, 8, 18, 8, + 146, 8, 82, 8, 210, 8, 50, 8, 178, 8, + 114, 8, 242, 8, 10, 8, 138, 8, 74, 8, + 202, 8, 42, 8, 170, 8, 106, 8, 234, 8, + 26, 8, 154, 8, 90, 8, 218, 8, 58, 8, + 186, 8, 122, 8, 250, 8, 6, 8, 134, 8, + 70, 8, 198, 8, 38, 8, 166, 8, 102, 8, + 230, 8, 22, 8, 150, 8, 86, 8, 214, 8, + 54, 8, 182, 8, 118, 8, 246, 8, 14, 8, + 142, 8, 78, 8, 206, 8, 46, 8, 174, 8, + 110, 8, 238, 8, 30, 8, 158, 8, 94, 8, + 222, 8, 62, 8, 190, 8, 126, 8, 254, 8, + 1, 8, 129, 8, 65, 8, 193, 8, 33, 8, + 161, 8, 97, 8, 225, 8, 17, 8, 145, 8, + 81, 8, 209, 8, 49, 8, 177, 8, 113, 8, + 241, 8, 9, 8, 137, 8, 73, 8, 201, 8, + 41, 8, 169, 8, 105, 8, 233, 8, 25, 8, + 153, 8, 89, 8, 217, 8, 57, 8, 185, 8, + 121, 8, 249, 8, 5, 8, 133, 8, 69, 8, + 197, 8, 37, 8, 165, 8, 101, 8, 229, 8, + 21, 8, 149, 8, 85, 8, 213, 8, 53, 8, + 181, 8, 117, 8, 245, 8, 13, 8, 141, 8, + 77, 8, 205, 8, 45, 8, 173, 8, 109, 8, + 237, 8, 29, 8, 157, 8, 93, 8, 221, 8, + 61, 8, 189, 8, 125, 8, 253, 8, 19, 9, + 275, 9, 147, 9, 403, 9, 83, 9, 339, 9, + 211, 9, 467, 9, 51, 9, 307, 9, 179, 9, + 435, 9, 115, 9, 371, 9, 243, 9, 499, 9, + 11, 9, 267, 9, 139, 9, 395, 9, 75, 9, + 331, 9, 203, 9, 459, 9, 43, 9, 299, 9, + 171, 9, 427, 9, 107, 9, 363, 9, 235, 9, + 491, 9, 27, 9, 283, 9, 155, 9, 411, 9, + 91, 9, 347, 9, 219, 9, 475, 9, 59, 9, + 315, 9, 187, 9, 443, 9, 123, 9, 379, 9, + 251, 9, 507, 9, 7, 9, 263, 9, 135, 9, + 391, 9, 71, 9, 327, 9, 199, 9, 455, 9, + 39, 9, 295, 9, 167, 9, 423, 9, 103, 9, + 359, 9, 231, 9, 487, 9, 23, 9, 279, 9, + 151, 9, 407, 9, 87, 9, 343, 9, 215, 9, + 471, 9, 55, 9, 311, 9, 183, 9, 439, 9, + 119, 9, 375, 9, 247, 9, 503, 9, 15, 9, + 271, 9, 143, 9, 399, 9, 79, 9, 335, 9, + 207, 9, 463, 9, 47, 9, 303, 9, 175, 9, + 431, 9, 111, 9, 367, 9, 239, 9, 495, 9, + 31, 9, 287, 9, 159, 9, 415, 9, 95, 9, + 351, 9, 223, 9, 479, 9, 63, 9, 319, 9, + 191, 9, 447, 9, 127, 9, 383, 9, 255, 9, + 511, 9, 0, 7, 64, 7, 32, 7, 96, 7, + 16, 7, 80, 7, 48, 7, 112, 7, 8, 7, + 72, 7, 40, 7, 104, 7, 24, 7, 88, 7, + 56, 7, 120, 7, 4, 7, 68, 7, 36, 7, + 100, 7, 20, 7, 84, 7, 52, 7, 116, 7, + 3, 8, 131, 8, 67, 8, 195, 8, 35, 8, + 163, 8, 99, 8, 227, 8 + }; + + internal static readonly short[] static_dtree = { + 0, 5, 16, 5, 8, 5, 24, 5, 4, 5, + 20, 5, 12, 5, 28, 5, 2, 5, 18, 5, + 10, 5, 26, 5, 6, 5, 22, 5, 14, 5, + 30, 5, 1, 5, 17, 5, 9, 5, 25, 5, + 5, 5, 21, 5, 13, 5, 29, 5, 3, 5, + 19, 5, 11, 5, 27, 5, 7, 5, 23, 5 + }; + + internal static readonly StaticTree static_l_desc = + new StaticTree(static_ltree, Tree.extra_lbits, + LITERALS+1, L_CODES, MAX_BITS); + + internal static readonly StaticTree static_d_desc = + new StaticTree(static_dtree, Tree.extra_dbits, + 0, D_CODES, MAX_BITS); + + internal static readonly StaticTree static_bl_desc = + new StaticTree(null, Tree.extra_blbits, + 0, BL_CODES, MAX_BL_BITS); + + internal short[] static_tree; // static tree or null + internal int[] extra_bits; // extra bits for each code or null + internal int extra_base; // base index for extra_bits + internal int elems; // max number of elements in the tree + internal int max_length; // max bit length for the codes + + internal StaticTree(short[] static_tree, + int[] extra_bits, + int extra_base, + int elems, + int max_length + ){ + this.static_tree=static_tree; + this.extra_bits=extra_bits; + this.extra_base=extra_base; + this.elems=elems; + this.max_length=max_length; + } + } +} \ No newline at end of file diff --git a/Crypto/src/util/zlib/Tree.cs b/Crypto/src/util/zlib/Tree.cs new file mode 100644
index 000000000..29d0a30d4 --- /dev/null +++ b/Crypto/src/util/zlib/Tree.cs
@@ -0,0 +1,367 @@ +using System; +/* + * $Id: Tree.cs,v 1.2 2008-05-10 09:35:40 bouncy Exp $ + * +Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +namespace Org.BouncyCastle.Utilities.Zlib { + + internal sealed class Tree{ + private const int MAX_BITS=15; + private const int BL_CODES=19; + private const int D_CODES=30; + private const int LITERALS=256; + private const int LENGTH_CODES=29; + private const int L_CODES=(LITERALS+1+LENGTH_CODES); + private const int HEAP_SIZE=(2*L_CODES+1); + + // Bit length codes must not exceed MAX_BL_BITS bits + internal const int MAX_BL_BITS=7; + + // end of block literal code + internal const int END_BLOCK=256; + + // repeat previous bit length 3-6 times (2 bits of repeat count) + internal const int REP_3_6=16; + + // repeat a zero length 3-10 times (3 bits of repeat count) + internal const int REPZ_3_10=17; + + // repeat a zero length 11-138 times (7 bits of repeat count) + internal const int REPZ_11_138=18; + + // extra bits for each length code + internal static readonly int[] extra_lbits={ + 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0 + }; + + // extra bits for each distance code + internal static readonly int[] extra_dbits={ + 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 + }; + + // extra bits for each bit length code + internal static readonly int[] extra_blbits={ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7 + }; + + internal static readonly byte[] bl_order={ + 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; + + + // The lengths of the bit length codes are sent in order of decreasing + // probability, to avoid transmitting the lengths for unused bit + // length codes. + + internal const int Buf_size=8*2; + + // see definition of array dist_code below + internal const int DIST_CODE_LEN=512; + + internal static readonly byte[] _dist_code = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, + 18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, + 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 + }; + + internal static readonly byte[] _length_code={ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, + 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, + 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, + 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 + }; + + internal static readonly int[] base_length = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, + 64, 80, 96, 112, 128, 160, 192, 224, 0 + }; + + internal static readonly int[] base_dist = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 + }; + + // Mapping from a distance to a distance code. dist is the distance - 1 and + // must not have side effects. _dist_code[256] and _dist_code[257] are never + // used. + internal static int d_code(int dist){ + return ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]); + } + + internal short[] dyn_tree; // the dynamic tree + internal int max_code; // largest code with non zero frequency + internal StaticTree stat_desc; // the corresponding static tree + + // Compute the optimal bit lengths for a tree and update the total bit length + // for the current block. + // IN assertion: the fields freq and dad are set, heap[heap_max] and + // above are the tree nodes sorted by increasing frequency. + // OUT assertions: the field len is set to the optimal bit length, the + // array bl_count contains the frequencies for each bit length. + // The length opt_len is updated; static_len is also updated if stree is + // not null. + internal void gen_bitlen(Deflate s){ + short[] tree = dyn_tree; + short[] stree = stat_desc.static_tree; + int[] extra = stat_desc.extra_bits; + int based = stat_desc.extra_base; + int max_length = stat_desc.max_length; + int h; // heap index + int n, m; // iterate over the tree elements + int bits; // bit length + int xbits; // extra bits + short f; // frequency + int overflow = 0; // number of elements with bit length too large + + for (bits = 0; bits <= MAX_BITS; bits++) s.bl_count[bits] = 0; + + // In a first pass, compute the optimal bit lengths (which may + // overflow in the case of the bit length tree). + tree[s.heap[s.heap_max]*2+1] = 0; // root of the heap + + for(h=s.heap_max+1; h<HEAP_SIZE; h++){ + n = s.heap[h]; + bits = tree[tree[n*2+1]*2+1] + 1; + if (bits > max_length){ bits = max_length; overflow++; } + tree[n*2+1] = (short)bits; + // We overwrite tree[n*2+1] which is no longer needed + + if (n > max_code) continue; // not a leaf node + + s.bl_count[bits]++; + xbits = 0; + if (n >= based) xbits = extra[n-based]; + f = tree[n*2]; + s.opt_len += f * (bits + xbits); + if (stree!=null) s.static_len += f * (stree[n*2+1] + xbits); + } + if (overflow == 0) return; + + // This happens for example on obj2 and pic of the Calgary corpus + // Find the first bit length which could increase: + do { + bits = max_length-1; + while(s.bl_count[bits]==0) bits--; + s.bl_count[bits]--; // move one leaf down the tree + s.bl_count[bits+1]+=2; // move one overflow item as its brother + s.bl_count[max_length]--; + // The brother of the overflow item also moves one step up, + // but this does not affect bl_count[max_length] + overflow -= 2; + } + while (overflow > 0); + + for (bits = max_length; bits != 0; bits--) { + n = s.bl_count[bits]; + while (n != 0) { + m = s.heap[--h]; + if (m > max_code) continue; + if (tree[m*2+1] != bits) { + s.opt_len += (int)(((long)bits - (long)tree[m*2+1])*(long)tree[m*2]); + tree[m*2+1] = (short)bits; + } + n--; + } + } + } + + // Construct one Huffman tree and assigns the code bit strings and lengths. + // Update the total bit length for the current block. + // IN assertion: the field freq is set for all tree elements. + // OUT assertions: the fields len and code are set to the optimal bit length + // and corresponding code. The length opt_len is updated; static_len is + // also updated if stree is not null. The field max_code is set. + internal void build_tree(Deflate s){ + short[] tree=dyn_tree; + short[] stree=stat_desc.static_tree; + int elems=stat_desc.elems; + int n, m; // iterate over heap elements + int max_code=-1; // largest code with non zero frequency + int node; // new node being created + + // Construct the initial heap, with least frequent element in + // heap[1]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + // heap[0] is not used. + s.heap_len = 0; + s.heap_max = HEAP_SIZE; + + for(n=0; n<elems; n++) { + if(tree[n*2] != 0) { + s.heap[++s.heap_len] = max_code = n; + s.depth[n] = 0; + } + else{ + tree[n*2+1] = 0; + } + } + + // The pkzip format requires that at least one distance code exists, + // and that at least one bit should be sent even if there is only one + // possible code. So to avoid special checks later on we force at least + // two codes of non zero frequency. + while (s.heap_len < 2) { + node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0); + tree[node*2] = 1; + s.depth[node] = 0; + s.opt_len--; if (stree!=null) s.static_len -= stree[node*2+1]; + // node is 0 or 1 so it does not have extra bits + } + this.max_code = max_code; + + // The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + // establish sub-heaps of increasing lengths: + + for(n=s.heap_len/2;n>=1; n--) + s.pqdownheap(tree, n); + + // Construct the Huffman tree by repeatedly combining the least two + // frequent nodes. + + node=elems; // next internal node of the tree + do{ + // n = node of least frequency + n=s.heap[1]; + s.heap[1]=s.heap[s.heap_len--]; + s.pqdownheap(tree, 1); + m=s.heap[1]; // m = node of next least frequency + + s.heap[--s.heap_max] = n; // keep the nodes sorted by frequency + s.heap[--s.heap_max] = m; + + // Create a new node father of n and m + tree[node*2] = (short)(tree[n*2] + tree[m*2]); + s.depth[node] = (byte)(System.Math.Max(s.depth[n],s.depth[m])+1); + tree[n*2+1] = tree[m*2+1] = (short)node; + + // and insert the new node in the heap + s.heap[1] = node++; + s.pqdownheap(tree, 1); + } + while(s.heap_len>=2); + + s.heap[--s.heap_max] = s.heap[1]; + + // At this point, the fields freq and dad are set. We can now + // generate the bit lengths. + + gen_bitlen(s); + + // The field len is now set, we can generate the bit codes + gen_codes(tree, max_code, s.bl_count); + } + + // Generate the codes for a given tree and bit counts (which need not be + // optimal). + // IN assertion: the array bl_count contains the bit length statistics for + // the given tree and the field len is set for all tree elements. + // OUT assertion: the field code is set for all tree elements of non + // zero code length. + internal static void gen_codes(short[] tree, // the tree to decorate + int max_code, // largest code with non zero frequency + short[] bl_count // number of codes at each bit length + ){ + short[] next_code=new short[MAX_BITS+1]; // next code value for each bit length + short code = 0; // running code value + int bits; // bit index + int n; // code index + + // The distribution counts are first used to generate the code values + // without bit reversal. + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (short)((code + bl_count[bits-1]) << 1); + } + + // Check that the bit counts in bl_count are consistent. The last code + // must be all ones. + //Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1, + // "inconsistent bit counts"); + //Tracev((stderr,"\ngen_codes: max_code %d ", max_code)); + + for (n = 0; n <= max_code; n++) { + int len = tree[n*2+1]; + if (len == 0) continue; + // Now reverse the bits + tree[n*2] = (short)(bi_reverse(next_code[len]++, len)); + } + } + + // Reverse the first len bits of a code, using straightforward code (a faster + // method would use a table) + // IN assertion: 1 <= len <= 15 + internal static int bi_reverse(int code, // the value to invert + int len // its bit length + ){ + int res = 0; + do{ + res|=code&1; + code>>=1; + res<<=1; + } + while(--len>0); + return res>>1; + } + } +} \ No newline at end of file diff --git a/Crypto/src/util/zlib/ZDeflaterOutputStream.cs b/Crypto/src/util/zlib/ZDeflaterOutputStream.cs new file mode 100644
index 000000000..6dea74223 --- /dev/null +++ b/Crypto/src/util/zlib/ZDeflaterOutputStream.cs
@@ -0,0 +1,155 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Utilities.Zlib { + /// <summary> + /// Summary description for DeflaterOutputStream. + /// </summary> + [Obsolete("Use 'ZOutputStream' instead")] + public class ZDeflaterOutputStream : Stream { + protected ZStream z=new ZStream(); + protected int flushLevel=JZlib.Z_NO_FLUSH; + private const int BUFSIZE = 4192; + protected byte[] buf=new byte[BUFSIZE]; + private byte[] buf1=new byte[1]; + + protected Stream outp; + + public ZDeflaterOutputStream(Stream outp) : this(outp, 6, false) { + } + + public ZDeflaterOutputStream(Stream outp, int level) : this(outp, level, false) { + } + + public ZDeflaterOutputStream(Stream outp, int level, bool nowrap) { + this.outp=outp; + z.deflateInit(level, nowrap); + } + + + public override bool CanRead { + get { + // TODO: Add DeflaterOutputStream.CanRead getter implementation + return false; + } + } + + public override bool CanSeek { + get { + // TODO: Add DeflaterOutputStream.CanSeek getter implementation + return false; + } + } + + public override bool CanWrite { + get { + // TODO: Add DeflaterOutputStream.CanWrite getter implementation + return true; + } + } + + public override long Length { + get { + // TODO: Add DeflaterOutputStream.Length getter implementation + return 0; + } + } + + public override long Position { + get { + // TODO: Add DeflaterOutputStream.Position getter implementation + return 0; + } + set { + // TODO: Add DeflaterOutputStream.Position setter implementation + } + } + + public override void Write(byte[] b, int off, int len) { + if(len==0) + return; + int err; + z.next_in=b; + z.next_in_index=off; + z.avail_in=len; + do{ + z.next_out=buf; + z.next_out_index=0; + z.avail_out=BUFSIZE; + err=z.deflate(flushLevel); + if(err!=JZlib.Z_OK) + throw new IOException("deflating: "+z.msg); + if (z.avail_out < BUFSIZE) + { + outp.Write(buf, 0, BUFSIZE-z.avail_out); + } + } + while(z.avail_in>0 || z.avail_out==0); + } + + public override long Seek(long offset, SeekOrigin origin) { + // TODO: Add DeflaterOutputStream.Seek implementation + return 0; + } + + public override void SetLength(long value) { + // TODO: Add DeflaterOutputStream.SetLength implementation + + } + + public override int Read(byte[] buffer, int offset, int count) { + // TODO: Add DeflaterOutputStream.Read implementation + return 0; + } + + public override void Flush() { + outp.Flush(); + } + + public override void WriteByte(byte b) { + buf1[0]=(byte)b; + Write(buf1, 0, 1); + } + + public void Finish() { + int err; + do{ + z.next_out=buf; + z.next_out_index=0; + z.avail_out=BUFSIZE; + err=z.deflate(JZlib.Z_FINISH); + if(err!=JZlib.Z_STREAM_END && err != JZlib.Z_OK) + throw new IOException("deflating: "+z.msg); + if(BUFSIZE-z.avail_out>0){ + outp.Write(buf, 0, BUFSIZE-z.avail_out); + } + } + while(z.avail_in>0 || z.avail_out==0); + Flush(); + } + + public void End() { + if(z==null) + return; + z.deflateEnd(); + z.free(); + z=null; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + try{ + try { Finish(); } + catch (IOException) { } + } + finally{ + End(); + outp.Dispose(); + outp = null; + } + } + } + } +} diff --git a/Crypto/src/util/zlib/ZInflaterInputStream.cs b/Crypto/src/util/zlib/ZInflaterInputStream.cs new file mode 100644
index 000000000..d2d70ebd1 --- /dev/null +++ b/Crypto/src/util/zlib/ZInflaterInputStream.cs
@@ -0,0 +1,131 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Utilities.Zlib { + /// <summary> + /// Summary description for DeflaterOutputStream. + /// </summary> + [Obsolete("Use 'ZInputStream' instead")] + public class ZInflaterInputStream : Stream { + protected ZStream z=new ZStream(); + protected int flushLevel=JZlib.Z_NO_FLUSH; + private const int BUFSIZE = 4192; + protected byte[] buf=new byte[BUFSIZE]; + private byte[] buf1=new byte[1]; + + protected Stream inp=null; + private bool nomoreinput=false; + + public ZInflaterInputStream(Stream inp) : this(inp, false) { + } + + public ZInflaterInputStream(Stream inp, bool nowrap) { + this.inp=inp; + z.inflateInit(nowrap); + z.next_in=buf; + z.next_in_index=0; + z.avail_in=0; + } + + public override bool CanRead { + get { + // TODO: Add DeflaterOutputStream.CanRead getter implementation + return true; + } + } + + public override bool CanSeek { + get { + // TODO: Add DeflaterOutputStream.CanSeek getter implementation + return false; + } + } + + public override bool CanWrite { + get { + // TODO: Add DeflaterOutputStream.CanWrite getter implementation + return false; + } + } + + public override long Length { + get { + // TODO: Add DeflaterOutputStream.Length getter implementation + return 0; + } + } + + public override long Position { + get { + // TODO: Add DeflaterOutputStream.Position getter implementation + return 0; + } + set { + // TODO: Add DeflaterOutputStream.Position setter implementation + } + } + + public override void Write(byte[] b, int off, int len) { + } + + public override long Seek(long offset, SeekOrigin origin) { + // TODO: Add DeflaterOutputStream.Seek implementation + return 0; + } + + public override void SetLength(long value) { + // TODO: Add DeflaterOutputStream.SetLength implementation + + } + + public override int Read(byte[] b, int off, int len) { + if(len==0) + return(0); + int err; + z.next_out=b; + z.next_out_index=off; + z.avail_out=len; + do { + if((z.avail_in==0)&&(!nomoreinput)) { // if buffer is empty and more input is avaiable, refill it + z.next_in_index=0; + z.avail_in=inp.Read(buf, 0, BUFSIZE);//(BUFSIZE<z.avail_out ? BUFSIZE : z.avail_out)); + if(z.avail_in<=0) { + z.avail_in=0; + nomoreinput=true; + } + } + err=z.inflate(flushLevel); + if(nomoreinput&&(err==JZlib.Z_BUF_ERROR)) + return(0); + if(err!=JZlib.Z_OK && err!=JZlib.Z_STREAM_END) + throw new IOException("inflating: "+z.msg); + if((nomoreinput||err==JZlib.Z_STREAM_END)&&(z.avail_out==len)) + return(0); + } + while(z.avail_out==len&&err==JZlib.Z_OK); + //System.err.print("("+(len-z.avail_out)+")"); + return(len-z.avail_out); + } + + public override void Flush() { + inp.Flush(); + } + + public override void WriteByte(byte b) { + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + inp.Dispose(); + } + } + + public override int ReadByte() { + if(Read(buf1, 0, 1)<=0) + return -1; + return(buf1[0]&0xFF); + } + } +} diff --git a/Crypto/src/util/zlib/ZInputStream.cs b/Crypto/src/util/zlib/ZInputStream.cs new file mode 100644
index 000000000..179c133bf --- /dev/null +++ b/Crypto/src/util/zlib/ZInputStream.cs
@@ -0,0 +1,196 @@ +/* +Copyright (c) 2001 Lapo Luchini. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS +OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ +/* This file is a port of jzlib v1.0.7, com.jcraft.jzlib.ZInputStream.java + */ + +using System; +using System.Diagnostics; +using System.IO; + +namespace Org.BouncyCastle.Utilities.Zlib +{ + public class ZInputStream + : Stream + { + private const int BufferSize = 512; + + protected ZStream z = new ZStream(); + protected int flushLevel = JZlib.Z_NO_FLUSH; + // TODO Allow custom buf + protected byte[] buf = new byte[BufferSize]; + protected byte[] buf1 = new byte[1]; + protected bool compress; + + protected Stream input; + protected bool closed; + + private bool nomoreinput = false; + + public ZInputStream(Stream input) + : this(input, false) + { + } + + public ZInputStream(Stream input, bool nowrap) + { + Debug.Assert(input.CanRead); + + this.input = input; + this.z.inflateInit(nowrap); + this.compress = false; + this.z.next_in = buf; + this.z.next_in_index = 0; + this.z.avail_in = 0; + } + + public ZInputStream(Stream input, int level) + { + Debug.Assert(input.CanRead); + + this.input = input; + this.z.deflateInit(level); + this.compress = true; + this.z.next_in = buf; + this.z.next_in_index = 0; + this.z.avail_in = 0; + } + + /*public int available() throws IOException { + return inf.finished() ? 0 : 1; + }*/ + + public sealed override bool CanRead { get { return !closed; } } + public sealed override bool CanSeek { get { return false; } } + public sealed override bool CanWrite { get { return false; } } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (!closed) + { + closed = true; + input.Dispose(); + } + } + } + + public sealed override void Flush() {} + + public virtual int FlushMode + { + get { return flushLevel; } + set { this.flushLevel = value; } + } + + public sealed override long Length { get { throw new NotSupportedException(); } } + public sealed override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + public override int Read(byte[] b, int off, int len) + { + if (len==0) + return 0; + + z.next_out = b; + z.next_out_index = off; + z.avail_out = len; + + int err; + do + { + if (z.avail_in == 0 && !nomoreinput) + { + // if buffer is empty and more input is available, refill it + z.next_in_index = 0; + z.avail_in = input.Read(buf, 0, buf.Length); //(bufsize<z.avail_out ? bufsize : z.avail_out)); + + if (z.avail_in <= 0) + { + z.avail_in = 0; + nomoreinput = true; + } + } + + err = compress + ? z.deflate(flushLevel) + : z.inflate(flushLevel); + + if (nomoreinput && err == JZlib.Z_BUF_ERROR) + return 0; + if (err != JZlib.Z_OK && err != JZlib.Z_STREAM_END) + // TODO +// throw new ZStreamException((compress ? "de" : "in") + "flating: " + z.msg); + throw new IOException((compress ? "de" : "in") + "flating: " + z.msg); + if ((nomoreinput || err == JZlib.Z_STREAM_END) && z.avail_out == len) + return 0; + } + while(z.avail_out == len && err == JZlib.Z_OK); + //Console.Error.WriteLine("("+(len-z.avail_out)+")"); + return len - z.avail_out; + } + + public override int ReadByte() + { + if (Read(buf1, 0, 1) <= 0) + return -1; + return buf1[0]; + } + +// public long skip(long n) throws IOException { +// int len=512; +// if(n<len) +// len=(int)n; +// byte[] tmp=new byte[len]; +// return((long)read(tmp)); +// } + + public sealed override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } + public sealed override void SetLength(long value) { throw new NotSupportedException(); } + + public virtual long TotalIn + { + get { return z.total_in; } + } + + public virtual long TotalOut + { + get { return z.total_out; } + } + + public sealed override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } + } +} diff --git a/Crypto/src/util/zlib/ZOutputStream.cs b/Crypto/src/util/zlib/ZOutputStream.cs new file mode 100644
index 000000000..7ea7cbb33 --- /dev/null +++ b/Crypto/src/util/zlib/ZOutputStream.cs
@@ -0,0 +1,220 @@ +/* +Copyright (c) 2001 Lapo Luchini. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS +OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ +/* This file is a port of jzlib v1.0.7, com.jcraft.jzlib.ZOutputStream.java + */ + +using System; +using System.Diagnostics; +using System.IO; + +namespace Org.BouncyCastle.Utilities.Zlib +{ + public class ZOutputStream + : Stream + { + private const int BufferSize = 512; + + protected ZStream z = new ZStream(); + protected int flushLevel = JZlib.Z_NO_FLUSH; + // TODO Allow custom buf + protected byte[] buf = new byte[BufferSize]; + protected byte[] buf1 = new byte[1]; + protected bool compress; + + protected Stream output; + protected bool closed; + + public ZOutputStream(Stream output) + : base() + { + Debug.Assert(output.CanWrite); + + this.output = output; + this.z.inflateInit(); + this.compress = false; + } + + public ZOutputStream(Stream output, int level) + : this(output, level, false) + { + } + + public ZOutputStream(Stream output, int level, bool nowrap) + : base() + { + Debug.Assert(output.CanWrite); + + this.output = output; + this.z.deflateInit(level, nowrap); + this.compress = true; + } + + public sealed override bool CanRead { get { return false; } } + public sealed override bool CanSeek { get { return false; } } + public sealed override bool CanWrite { get { return !closed; } } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (this.closed) + return; + + try + { + try + { + Finish(); + } + catch (IOException) + { + // Ignore + } + } + finally + { + this.closed = true; + End(); + output.Dispose(); + output = null; + } + } + } + + public virtual void End() + { + if (z == null) + return; + if (compress) + z.deflateEnd(); + else + z.inflateEnd(); + z.free(); + z = null; + } + + public virtual void Finish() + { + do + { + z.next_out = buf; + z.next_out_index = 0; + z.avail_out = buf.Length; + + int err = compress + ? z.deflate(JZlib.Z_FINISH) + : z.inflate(JZlib.Z_FINISH); + + if (err != JZlib.Z_STREAM_END && err != JZlib.Z_OK) + // TODO +// throw new ZStreamException((compress?"de":"in")+"flating: "+z.msg); + throw new IOException((compress ? "de" : "in") + "flating: " + z.msg); + + int count = buf.Length - z.avail_out; + if (count > 0) + { + output.Write(buf, 0, count); + } + } + while (z.avail_in > 0 || z.avail_out == 0); + + Flush(); + } + + public override void Flush() + { + output.Flush(); + } + + public virtual int FlushMode + { + get { return flushLevel; } + set { this.flushLevel = value; } + } + + public sealed override long Length { get { throw new NotSupportedException(); } } + public sealed override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + public sealed override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } + public sealed override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } + public sealed override void SetLength(long value) { throw new NotSupportedException(); } + + public virtual long TotalIn + { + get { return z.total_in; } + } + + public virtual long TotalOut + { + get { return z.total_out; } + } + + public override void Write(byte[] b, int off, int len) + { + if (len == 0) + return; + + z.next_in = b; + z.next_in_index = off; + z.avail_in = len; + + do + { + z.next_out = buf; + z.next_out_index = 0; + z.avail_out = buf.Length; + + int err = compress + ? z.deflate(flushLevel) + : z.inflate(flushLevel); + + if (err != JZlib.Z_OK) + // TODO +// throw new ZStreamException((compress ? "de" : "in") + "flating: " + z.msg); + throw new IOException((compress ? "de" : "in") + "flating: " + z.msg); + + output.Write(buf, 0, buf.Length - z.avail_out); + } + while (z.avail_in > 0 || z.avail_out == 0); + } + + public override void WriteByte(byte b) + { + buf1[0] = b; + Write(buf1, 0, 1); + } + } +} diff --git a/Crypto/src/util/zlib/ZStream.cs b/Crypto/src/util/zlib/ZStream.cs new file mode 100644
index 000000000..7ff961462 --- /dev/null +++ b/Crypto/src/util/zlib/ZStream.cs
@@ -0,0 +1,214 @@ +using System; +/* + * $Id: ZStream.cs,v 1.1 2006-07-31 13:59:26 bouncy Exp $ + * +Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors + * Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) + * and contributors of zlib. + */ + +namespace Org.BouncyCastle.Utilities.Zlib { + + public sealed class ZStream{ + + private const int MAX_WBITS=15; // 32K LZ77 window + private const int DEF_WBITS=MAX_WBITS; + + private const int Z_NO_FLUSH=0; + private const int Z_PARTIAL_FLUSH=1; + private const int Z_SYNC_FLUSH=2; + private const int Z_FULL_FLUSH=3; + private const int Z_FINISH=4; + + private const int MAX_MEM_LEVEL=9; + + private const int Z_OK=0; + private const int Z_STREAM_END=1; + private const int Z_NEED_DICT=2; + private const int Z_ERRNO=-1; + private const int Z_STREAM_ERROR=-2; + private const int Z_DATA_ERROR=-3; + private const int Z_MEM_ERROR=-4; + private const int Z_BUF_ERROR=-5; + private const int Z_VERSION_ERROR=-6; + + public byte[] next_in; // next input byte + public int next_in_index; + public int avail_in; // number of bytes available at next_in + public long total_in; // total nb of input bytes read so far + + public byte[] next_out; // next output byte should be put there + public int next_out_index; + public int avail_out; // remaining free space at next_out + public long total_out; // total nb of bytes output so far + + public String msg; + + internal Deflate dstate; + internal Inflate istate; + + internal int data_type; // best guess about the data type: ascii or binary + + public long adler; + internal Adler32 _adler=new Adler32(); + + public int inflateInit(){ + return inflateInit(DEF_WBITS); + } + public int inflateInit(bool nowrap){ + return inflateInit(DEF_WBITS, nowrap); + } + public int inflateInit(int w){ + return inflateInit(w, false); + } + + public int inflateInit(int w, bool nowrap){ + istate=new Inflate(); + return istate.inflateInit(this, nowrap?-w:w); + } + + public int inflate(int f){ + if(istate==null) return Z_STREAM_ERROR; + return istate.inflate(this, f); + } + public int inflateEnd(){ + if(istate==null) return Z_STREAM_ERROR; + int ret=istate.inflateEnd(this); + istate = null; + return ret; + } + public int inflateSync(){ + if(istate == null) + return Z_STREAM_ERROR; + return istate.inflateSync(this); + } + public int inflateSetDictionary(byte[] dictionary, int dictLength){ + if(istate == null) + return Z_STREAM_ERROR; + return istate.inflateSetDictionary(this, dictionary, dictLength); + } + + public int deflateInit(int level){ + return deflateInit(level, MAX_WBITS); + } + public int deflateInit(int level, bool nowrap){ + return deflateInit(level, MAX_WBITS, nowrap); + } + public int deflateInit(int level, int bits){ + return deflateInit(level, bits, false); + } + public int deflateInit(int level, int bits, bool nowrap){ + dstate=new Deflate(); + return dstate.deflateInit(this, level, nowrap?-bits:bits); + } + public int deflate(int flush){ + if(dstate==null){ + return Z_STREAM_ERROR; + } + return dstate.deflate(this, flush); + } + public int deflateEnd(){ + if(dstate==null) return Z_STREAM_ERROR; + int ret=dstate.deflateEnd(); + dstate=null; + return ret; + } + public int deflateParams(int level, int strategy){ + if(dstate==null) return Z_STREAM_ERROR; + return dstate.deflateParams(this, level, strategy); + } + public int deflateSetDictionary (byte[] dictionary, int dictLength){ + if(dstate == null) + return Z_STREAM_ERROR; + return dstate.deflateSetDictionary(this, dictionary, dictLength); + } + + // Flush as much pending output as possible. All deflate() output goes + // through this function so some applications may wish to modify it + // to avoid allocating a large strm->next_out buffer and copying into it. + // (See also read_buf()). + internal void flush_pending(){ + int len=dstate.pending; + + if(len>avail_out) len=avail_out; + if(len==0) return; + + if(dstate.pending_buf.Length<=dstate.pending_out || + next_out.Length<=next_out_index || + dstate.pending_buf.Length<(dstate.pending_out+len) || + next_out.Length<(next_out_index+len)){ + // System.out.println(dstate.pending_buf.length+", "+dstate.pending_out+ + // ", "+next_out.length+", "+next_out_index+", "+len); + // System.out.println("avail_out="+avail_out); + } + + System.Array.Copy(dstate.pending_buf, dstate.pending_out, + next_out, next_out_index, len); + + next_out_index+=len; + dstate.pending_out+=len; + total_out+=len; + avail_out-=len; + dstate.pending-=len; + if(dstate.pending==0){ + dstate.pending_out=0; + } + } + + // Read a new buffer from the current input stream, update the adler32 + // and total number of bytes read. All deflate() input goes through + // this function so some applications may wish to modify it to avoid + // allocating a large strm->next_in buffer and copying from it. + // (See also flush_pending()). + internal int read_buf(byte[] buf, int start, int size) { + int len=avail_in; + + if(len>size) len=size; + if(len==0) return 0; + + avail_in-=len; + + if(dstate.noheader==0) { + adler=_adler.adler32(adler, next_in, next_in_index, len); + } + System.Array.Copy(next_in, next_in_index, buf, start, len); + next_in_index += len; + total_in += len; + return len; + } + + public void free(){ + next_in=null; + next_out=null; + msg=null; + _adler=null; + } + } +} \ No newline at end of file diff --git a/Crypto/src/x509/AttributeCertificateHolder.cs b/Crypto/src/x509/AttributeCertificateHolder.cs new file mode 100644
index 000000000..3a6af4c20 --- /dev/null +++ b/Crypto/src/x509/AttributeCertificateHolder.cs
@@ -0,0 +1,442 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.X509 +{ + /// <remarks> + /// The Holder object. + /// <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> + /// </remarks> + public class AttributeCertificateHolder + //: CertSelector, Selector + : IX509Selector + { + internal readonly Holder holder; + + internal AttributeCertificateHolder( + Asn1Sequence seq) + { + holder = Holder.GetInstance(seq); + } + + public AttributeCertificateHolder( + X509Name issuerName, + BigInteger serialNumber) + { + holder = new Holder( + new IssuerSerial( + GenerateGeneralNames(issuerName), + new DerInteger(serialNumber))); + } + + public AttributeCertificateHolder( + X509Certificate cert) + { + X509Name name; + try + { + name = PrincipalUtilities.GetIssuerX509Principal(cert); + } + catch (Exception e) + { + throw new CertificateParsingException(e.Message); + } + + holder = new Holder(new IssuerSerial(GenerateGeneralNames(name), new DerInteger(cert.SerialNumber))); + } + + public AttributeCertificateHolder( + X509Name principal) + { + holder = new Holder(GenerateGeneralNames(principal)); + } + + /** + * Constructs a holder for v2 attribute certificates with a hash value for + * some type of object. + * <p> + * <code>digestedObjectType</code> can be one of the following: + * <ul> + * <li>0 - publicKey - A hash of the public key of the holder must be + * passed.</li> + * <li>1 - publicKeyCert - A hash of the public key certificate of the + * holder must be passed.</li> + * <li>2 - otherObjectDigest - A hash of some other object type must be + * passed. <code>otherObjectTypeID</code> must not be empty.</li> + * </ul> + * </p> + * <p>This cannot be used if a v1 attribute certificate is used.</p> + * + * @param digestedObjectType The digest object type. + * @param digestAlgorithm The algorithm identifier for the hash. + * @param otherObjectTypeID The object type ID if + * <code>digestedObjectType</code> is + * <code>otherObjectDigest</code>. + * @param objectDigest The hash value. + */ + public AttributeCertificateHolder( + int digestedObjectType, + string digestAlgorithm, + string otherObjectTypeID, + byte[] objectDigest) + { + // TODO Allow 'objectDigest' to be null? + + holder = new Holder(new ObjectDigestInfo(digestedObjectType, otherObjectTypeID, + new AlgorithmIdentifier(digestAlgorithm), Arrays.Clone(objectDigest))); + } + + /** + * Returns the digest object type if an object digest info is used. + * <p> + * <ul> + * <li>0 - publicKey - A hash of the public key of the holder must be + * passed.</li> + * <li>1 - publicKeyCert - A hash of the public key certificate of the + * holder must be passed.</li> + * <li>2 - otherObjectDigest - A hash of some other object type must be + * passed. <code>otherObjectTypeID</code> must not be empty.</li> + * </ul> + * </p> + * + * @return The digest object type or -1 if no object digest info is set. + */ + public int DigestedObjectType + { + get + { + ObjectDigestInfo odi = holder.ObjectDigestInfo; + + return odi == null + ? -1 + : odi.DigestedObjectType.Value.IntValue; + } + } + + /** + * Returns the other object type ID if an object digest info is used. + * + * @return The other object type ID or <code>null</code> if no object + * digest info is set. + */ + public string DigestAlgorithm + { + get + { + ObjectDigestInfo odi = holder.ObjectDigestInfo; + + return odi == null + ? null + : odi.DigestAlgorithm.ObjectID.Id; + } + } + + /** + * Returns the hash if an object digest info is used. + * + * @return The hash or <code>null</code> if no object digest info is set. + */ + public byte[] GetObjectDigest() + { + ObjectDigestInfo odi = holder.ObjectDigestInfo; + + return odi == null + ? null + : odi.ObjectDigest.GetBytes(); + } + + /** + * Returns the digest algorithm ID if an object digest info is used. + * + * @return The digest algorithm ID or <code>null</code> if no object + * digest info is set. + */ + public string OtherObjectTypeID + { + get + { + ObjectDigestInfo odi = holder.ObjectDigestInfo; + + return odi == null + ? null + : odi.OtherObjectTypeID.Id; + } + } + + private GeneralNames GenerateGeneralNames( + X509Name principal) + { +// return GeneralNames.GetInstance(new DerSequence(new GeneralName(principal))); + return new GeneralNames(new GeneralName(principal)); + } + + private bool MatchesDN( + X509Name subject, + GeneralNames targets) + { + GeneralName[] names = targets.GetNames(); + + for (int i = 0; i != names.Length; i++) + { + GeneralName gn = names[i]; + + if (gn.TagNo == GeneralName.DirectoryName) + { + try + { + if (X509Name.GetInstance(gn.Name).Equivalent(subject)) + { + return true; + } + } + catch (Exception) + { + } + } + } + + return false; + } + + private object[] GetNames( + GeneralName[] names) + { + int count = 0; + for (int i = 0; i != names.Length; i++) + { + if (names[i].TagNo == GeneralName.DirectoryName) + { + ++count; + } + } + + object[] result = new object[count]; + + int pos = 0; + for (int i = 0; i != names.Length; i++) + { + if (names[i].TagNo == GeneralName.DirectoryName) + { + result[pos++] = X509Name.GetInstance(names[i].Name); + } + } + + return result; + } + + private X509Name[] GetPrincipals( + GeneralNames names) + { + object[] p = this.GetNames(names.GetNames()); + + int count = 0; + + for (int i = 0; i != p.Length; i++) + { + if (p[i] is X509Name) + { + ++count; + } + } + + X509Name[] result = new X509Name[count]; + + int pos = 0; + for (int i = 0; i != p.Length; i++) + { + if (p[i] is X509Name) + { + result[pos++] = (X509Name)p[i]; + } + } + + return result; + } + + /** + * Return any principal objects inside the attribute certificate holder entity names field. + * + * @return an array of IPrincipal objects (usually X509Name), null if no entity names field is set. + */ + public X509Name[] GetEntityNames() + { + if (holder.EntityName != null) + { + return GetPrincipals(holder.EntityName); + } + + return null; + } + + /** + * Return the principals associated with the issuer attached to this holder + * + * @return an array of principals, null if no BaseCertificateID is set. + */ + public X509Name[] GetIssuer() + { + if (holder.BaseCertificateID != null) + { + return GetPrincipals(holder.BaseCertificateID.Issuer); + } + + return null; + } + + /** + * Return the serial number associated with the issuer attached to this holder. + * + * @return the certificate serial number, null if no BaseCertificateID is set. + */ + public BigInteger SerialNumber + { + get + { + if (holder.BaseCertificateID != null) + { + return holder.BaseCertificateID.Serial.Value; + } + + return null; + } + } + + public object Clone() + { + return new AttributeCertificateHolder((Asn1Sequence)holder.ToAsn1Object()); + } + + public bool Match( +// Certificate cert) + X509Certificate x509Cert) + { +// if (!(cert is X509Certificate)) +// { +// return false; +// } +// +// X509Certificate x509Cert = (X509Certificate)cert; + + try + { + if (holder.BaseCertificateID != null) + { + return holder.BaseCertificateID.Serial.Value.Equals(x509Cert.SerialNumber) + && MatchesDN(PrincipalUtilities.GetIssuerX509Principal(x509Cert), holder.BaseCertificateID.Issuer); + } + + if (holder.EntityName != null) + { + if (MatchesDN(PrincipalUtilities.GetSubjectX509Principal(x509Cert), holder.EntityName)) + { + return true; + } + } + + if (holder.ObjectDigestInfo != null) + { + IDigest md = null; + try + { + md = DigestUtilities.GetDigest(DigestAlgorithm); + } + catch (Exception) + { + return false; + } + + switch (DigestedObjectType) + { + case ObjectDigestInfo.PublicKey: + { + // TODO: DSA Dss-parms + + //byte[] b = x509Cert.GetPublicKey().getEncoded(); + // TODO Is this the right way to encode? + byte[] b = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo( + x509Cert.GetPublicKey()).GetEncoded(); + md.BlockUpdate(b, 0, b.Length); + break; + } + + case ObjectDigestInfo.PublicKeyCert: + { + byte[] b = x509Cert.GetEncoded(); + md.BlockUpdate(b, 0, b.Length); + break; + } + + // TODO Default handler? + } + + // TODO Shouldn't this be the other way around? + if (!Arrays.AreEqual(DigestUtilities.DoFinal(md), GetObjectDigest())) + { + return false; + } + } + } + catch (CertificateEncodingException) + { + return false; + } + + return false; + } + + public override bool Equals( + object obj) + { + if (obj == this) + { + return true; + } + + if (!(obj is AttributeCertificateHolder)) + { + return false; + } + + AttributeCertificateHolder other = (AttributeCertificateHolder)obj; + + return this.holder.Equals(other.holder); + } + + public override int GetHashCode() + { + return this.holder.GetHashCode(); + } + + public bool Match( + object obj) + { + if (!(obj is X509Certificate)) + { + return false; + } + +// return Match((Certificate)obj); + return Match((X509Certificate)obj); + } + } +} diff --git a/Crypto/src/x509/AttributeCertificateIssuer.cs b/Crypto/src/x509/AttributeCertificateIssuer.cs new file mode 100644
index 000000000..7df1416d3 --- /dev/null +++ b/Crypto/src/x509/AttributeCertificateIssuer.cs
@@ -0,0 +1,199 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.X509 +{ + /** + * Carrying class for an attribute certificate issuer. + */ + public class AttributeCertificateIssuer + //: CertSelector, Selector + : IX509Selector + { + internal readonly Asn1Encodable form; + + /** + * Set the issuer directly with the ASN.1 structure. + * + * @param issuer The issuer + */ + public AttributeCertificateIssuer( + AttCertIssuer issuer) + { + form = issuer.Issuer; + } + + public AttributeCertificateIssuer( + X509Name principal) + { +// form = new V2Form(GeneralNames.GetInstance(new DerSequence(new GeneralName(principal)))); + form = new V2Form(new GeneralNames(new GeneralName(principal))); + } + + private object[] GetNames() + { + GeneralNames name; + if (form is V2Form) + { + name = ((V2Form)form).IssuerName; + } + else + { + name = (GeneralNames)form; + } + + GeneralName[] names = name.GetNames(); + + int count = 0; + for (int i = 0; i != names.Length; i++) + { + if (names[i].TagNo == GeneralName.DirectoryName) + { + ++count; + } + } + + object[] result = new object[count]; + + int pos = 0; + for (int i = 0; i != names.Length; i++) + { + if (names[i].TagNo == GeneralName.DirectoryName) + { + result[pos++] = X509Name.GetInstance(names[i].Name); + } + } + + return result; + } + + /// <summary>Return any principal objects inside the attribute certificate issuer object.</summary> + /// <returns>An array of IPrincipal objects (usually X509Principal).</returns> + public X509Name[] GetPrincipals() + { + object[] p = this.GetNames(); + + int count = 0; + for (int i = 0; i != p.Length; i++) + { + if (p[i] is X509Name) + { + ++count; + } + } + + X509Name[] result = new X509Name[count]; + + int pos = 0; + for (int i = 0; i != p.Length; i++) + { + if (p[i] is X509Name) + { + result[pos++] = (X509Name)p[i]; + } + } + + return result; + } + + private bool MatchesDN( + X509Name subject, + GeneralNames targets) + { + GeneralName[] names = targets.GetNames(); + + for (int i = 0; i != names.Length; i++) + { + GeneralName gn = names[i]; + + if (gn.TagNo == GeneralName.DirectoryName) + { + try + { + if (X509Name.GetInstance(gn.Name).Equivalent(subject)) + { + return true; + } + } + catch (Exception) + { + } + } + } + + return false; + } + + public object Clone() + { + return new AttributeCertificateIssuer(AttCertIssuer.GetInstance(form)); + } + + public bool Match( +// Certificate cert) + X509Certificate x509Cert) + { +// if (!(cert is X509Certificate)) +// { +// return false; +// } +// +// X509Certificate x509Cert = (X509Certificate)cert; + + if (form is V2Form) + { + V2Form issuer = (V2Form) form; + if (issuer.BaseCertificateID != null) + { + return issuer.BaseCertificateID.Serial.Value.Equals(x509Cert.SerialNumber) + && MatchesDN(x509Cert.IssuerDN, issuer.BaseCertificateID.Issuer); + } + + return MatchesDN(x509Cert.SubjectDN, issuer.IssuerName); + } + + return MatchesDN(x509Cert.SubjectDN, (GeneralNames) form); + } + + public override bool Equals( + object obj) + { + if (obj == this) + { + return true; + } + + if (!(obj is AttributeCertificateIssuer)) + { + return false; + } + + AttributeCertificateIssuer other = (AttributeCertificateIssuer)obj; + + return this.form.Equals(other.form); + } + + public override int GetHashCode() + { + return this.form.GetHashCode(); + } + + public bool Match( + object obj) + { + if (!(obj is X509Certificate)) + { + return false; + } + + //return Match((Certificate)obj); + return Match((X509Certificate)obj); + } + } +} diff --git a/Crypto/src/x509/IX509AttributeCertificate.cs b/Crypto/src/x509/IX509AttributeCertificate.cs new file mode 100644
index 000000000..9a3004e01 --- /dev/null +++ b/Crypto/src/x509/IX509AttributeCertificate.cs
@@ -0,0 +1,57 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.X509 +{ + /// <remarks>Interface for an X.509 Attribute Certificate.</remarks> + public interface IX509AttributeCertificate + : IX509Extension + { + /// <summary>The version number for the certificate.</summary> + int Version { get; } + + /// <summary>The serial number for the certificate.</summary> + BigInteger SerialNumber { get; } + + /// <summary>The UTC DateTime before which the certificate is not valid.</summary> + DateTime NotBefore { get; } + + /// <summary>The UTC DateTime after which the certificate is not valid.</summary> + DateTime NotAfter { get; } + + /// <summary>The holder of the certificate.</summary> + AttributeCertificateHolder Holder { get; } + + /// <summary>The issuer details for the certificate.</summary> + AttributeCertificateIssuer Issuer { get; } + + /// <summary>Return the attributes contained in the attribute block in the certificate.</summary> + /// <returns>An array of attributes.</returns> + X509Attribute[] GetAttributes(); + + /// <summary>Return the attributes with the same type as the passed in oid.</summary> + /// <param name="oid">The object identifier we wish to match.</param> + /// <returns>An array of matched attributes, null if there is no match.</returns> + X509Attribute[] GetAttributes(string oid); + + bool[] GetIssuerUniqueID(); + + bool IsValidNow { get; } + bool IsValid(DateTime date); + + void CheckValidity(); + void CheckValidity(DateTime date); + + byte[] GetSignature(); + + void Verify(AsymmetricKeyParameter publicKey); + + /// <summary>Return an ASN.1 encoded byte array representing the attribute certificate.</summary> + /// <returns>An ASN.1 encoded byte array.</returns> + /// <exception cref="IOException">If the certificate cannot be encoded.</exception> + byte[] GetEncoded(); + } +} diff --git a/Crypto/src/x509/IX509Extension.cs b/Crypto/src/x509/IX509Extension.cs new file mode 100644
index 000000000..e861e8736 --- /dev/null +++ b/Crypto/src/x509/IX509Extension.cs
@@ -0,0 +1,27 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.X509 +{ + public interface IX509Extension + { + /// <summary> + /// Get all critical extension values, by oid + /// </summary> + /// <returns>IDictionary with string (OID) keys and Asn1OctetString values</returns> + ISet GetCriticalExtensionOids(); + + /// <summary> + /// Get all non-critical extension values, by oid + /// </summary> + /// <returns>IDictionary with string (OID) keys and Asn1OctetString values</returns> + ISet GetNonCriticalExtensionOids(); + + [Obsolete("Use version taking a DerObjectIdentifier instead")] + Asn1OctetString GetExtensionValue(string oid); + + Asn1OctetString GetExtensionValue(DerObjectIdentifier oid); + } +} diff --git a/Crypto/src/x509/PEMParser.cs b/Crypto/src/x509/PEMParser.cs new file mode 100644
index 000000000..8c117f323 --- /dev/null +++ b/Crypto/src/x509/PEMParser.cs
@@ -0,0 +1,94 @@ +using System; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.X509 +{ + class PemParser + { + private readonly string _header1; + private readonly string _header2; + private readonly string _footer1; + private readonly string _footer2; + + internal PemParser( + string type) + { + _header1 = "-----BEGIN " + type + "-----"; + _header2 = "-----BEGIN X509 " + type + "-----"; + _footer1 = "-----END " + type + "-----"; + _footer2 = "-----END X509 " + type + "-----"; + } + + private string ReadLine( + Stream inStream) + { + int c; + StringBuilder l = new StringBuilder(); + + do + { + while (((c = inStream.ReadByte()) != '\r') && c != '\n' && (c >= 0)) + { + if (c == '\r') + { + continue; + } + + l.Append((char)c); + } + } + while (c >= 0 && l.Length == 0); + + if (c < 0) + { + return null; + } + + return l.ToString(); + } + + internal Asn1Sequence ReadPemObject( + Stream inStream) + { + string line; + StringBuilder pemBuf = new StringBuilder(); + + while ((line = ReadLine(inStream)) != null) + { + if (line.StartsWith(_header1) || line.StartsWith(_header2)) + { + break; + } + } + + while ((line = ReadLine(inStream)) != null) + { + if (line.StartsWith(_footer1) || line.StartsWith(_footer2)) + { + break; + } + + pemBuf.Append(line); + } + + if (pemBuf.Length != 0) + { + Asn1Object o = Asn1Object.FromByteArray(Base64.Decode(pemBuf.ToString())); + + if (!(o is Asn1Sequence)) + { + throw new IOException("malformed PEM data encountered"); + } + + return (Asn1Sequence) o; + } + + return null; + } + } +} + diff --git a/Crypto/src/x509/PrincipalUtil.cs b/Crypto/src/x509/PrincipalUtil.cs new file mode 100644
index 000000000..0edc4a395 --- /dev/null +++ b/Crypto/src/x509/PrincipalUtil.cs
@@ -0,0 +1,70 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Security.Certificates; + +namespace Org.BouncyCastle.X509 +{ + /// <remarks> + /// A utility class that will extract X509Principal objects from X.509 certificates. + /// <p> + /// Use this in preference to trying to recreate a principal from a string, not all + /// DNs are what they should be, so it's best to leave them encoded where they + /// can be.</p> + /// </remarks> + public class PrincipalUtilities + { + /// <summary>Return the issuer of the given cert as an X509Principal.</summary> + public static X509Name GetIssuerX509Principal( + X509Certificate cert) + { + try + { + TbsCertificateStructure tbsCert = TbsCertificateStructure.GetInstance( + Asn1Object.FromByteArray(cert.GetTbsCertificate())); + + return tbsCert.Issuer; + } + catch (Exception e) + { + throw new CertificateEncodingException("Could not extract issuer", e); + } + } + + /// <summary>Return the subject of the given cert as an X509Principal.</summary> + public static X509Name GetSubjectX509Principal( + X509Certificate cert) + { + try + { + TbsCertificateStructure tbsCert = TbsCertificateStructure.GetInstance( + Asn1Object.FromByteArray(cert.GetTbsCertificate())); + + return tbsCert.Subject; + } + catch (Exception e) + { + throw new CertificateEncodingException("Could not extract subject", e); + } + } + + /// <summary>Return the issuer of the given CRL as an X509Principal.</summary> + public static X509Name GetIssuerX509Principal( + X509Crl crl) + { + try + { + TbsCertificateList tbsCertList = TbsCertificateList.GetInstance( + Asn1Object.FromByteArray(crl.GetTbsCertList())); + + return tbsCertList.Issuer; + } + catch (Exception e) + { + throw new CrlException("Could not extract issuer", e); + } + } + } +} diff --git a/Crypto/src/x509/SubjectPublicKeyInfoFactory.cs b/Crypto/src/x509/SubjectPublicKeyInfoFactory.cs new file mode 100644
index 000000000..54ca78090 --- /dev/null +++ b/Crypto/src/x509/SubjectPublicKeyInfoFactory.cs
@@ -0,0 +1,187 @@ +using System; +using System.IO; +using System.Collections; +using System.Text; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.X509 +{ + /// <summary> + /// A factory to produce Public Key Info Objects. + /// </summary> + public sealed class SubjectPublicKeyInfoFactory + { + private SubjectPublicKeyInfoFactory() + { + } + + /// <summary> + /// Create a Subject Public Key Info object for a given public key. + /// </summary> + /// <param name="key">One of ElGammalPublicKeyParameters, DSAPublicKeyParameter, DHPublicKeyParameters, RsaKeyParameters or ECPublicKeyParameters</param> + /// <returns>A subject public key info object.</returns> + /// <exception cref="Exception">Throw exception if object provided is not one of the above.</exception> + public static SubjectPublicKeyInfo CreateSubjectPublicKeyInfo( + AsymmetricKeyParameter key) + { + if (key == null) + throw new ArgumentNullException("key"); + if (key.IsPrivate) + throw new ArgumentException("Private key passed - public key expected.", "key"); + + if (key is ElGamalPublicKeyParameters) + { + ElGamalPublicKeyParameters _key = (ElGamalPublicKeyParameters)key; + ElGamalParameters kp = _key.Parameters; + + SubjectPublicKeyInfo info = new SubjectPublicKeyInfo( + new AlgorithmIdentifier( + OiwObjectIdentifiers.ElGamalAlgorithm, + new ElGamalParameter(kp.P, kp.G).ToAsn1Object()), + new DerInteger(_key.Y)); + + return info; + } + + if (key is DsaPublicKeyParameters) + { + DsaPublicKeyParameters _key = (DsaPublicKeyParameters) key; + DsaParameters kp = _key.Parameters; + Asn1Encodable ae = kp == null + ? null + : new DsaParameter(kp.P, kp.Q, kp.G).ToAsn1Object(); + + return new SubjectPublicKeyInfo( + new AlgorithmIdentifier(X9ObjectIdentifiers.IdDsa, ae), + new DerInteger(_key.Y)); + } + + if (key is DHPublicKeyParameters) + { + DHPublicKeyParameters _key = (DHPublicKeyParameters) key; + DHParameters kp = _key.Parameters; + + SubjectPublicKeyInfo info = new SubjectPublicKeyInfo( + new AlgorithmIdentifier( + _key.AlgorithmOid, + new DHParameter(kp.P, kp.G, kp.L).ToAsn1Object()), + new DerInteger(_key.Y)); + + return info; + } // End of DH + + if (key is RsaKeyParameters) + { + RsaKeyParameters _key = (RsaKeyParameters) key; + + SubjectPublicKeyInfo info = new SubjectPublicKeyInfo( + new AlgorithmIdentifier(PkcsObjectIdentifiers.RsaEncryption, DerNull.Instance), + new RsaPublicKeyStructure(_key.Modulus, _key.Exponent).ToAsn1Object()); + + return info; + } // End of RSA. + + if (key is ECPublicKeyParameters) + { + ECPublicKeyParameters _key = (ECPublicKeyParameters) key; + + if (_key.AlgorithmName == "ECGOST3410") + { + if (_key.PublicKeyParamSet == null) + throw Platform.CreateNotImplementedException("Not a CryptoPro parameter set"); + + ECPoint q = _key.Q; + BigInteger bX = q.X.ToBigInteger(); + BigInteger bY = q.Y.ToBigInteger(); + + byte[] encKey = new byte[64]; + ExtractBytes(encKey, 0, bX); + ExtractBytes(encKey, 32, bY); + + Gost3410PublicKeyAlgParameters gostParams = new Gost3410PublicKeyAlgParameters( + _key.PublicKeyParamSet, CryptoProObjectIdentifiers.GostR3411x94CryptoProParamSet); + + AlgorithmIdentifier algID = new AlgorithmIdentifier( + CryptoProObjectIdentifiers.GostR3410x2001, + gostParams.ToAsn1Object()); + + return new SubjectPublicKeyInfo(algID, new DerOctetString(encKey)); + } + else + { + X962Parameters x962; + if (_key.PublicKeyParamSet == null) + { + ECDomainParameters kp = _key.Parameters; + X9ECParameters ecP = new X9ECParameters(kp.Curve, kp.G, kp.N, kp.H, kp.GetSeed()); + + x962 = new X962Parameters(ecP); + } + else + { + x962 = new X962Parameters(_key.PublicKeyParamSet); + } + + Asn1OctetString p = (Asn1OctetString)(new X9ECPoint(_key.Q).ToAsn1Object()); + + AlgorithmIdentifier algID = new AlgorithmIdentifier( + X9ObjectIdentifiers.IdECPublicKey, x962.ToAsn1Object()); + + return new SubjectPublicKeyInfo(algID, p.GetOctets()); + } + } // End of EC + + if (key is Gost3410PublicKeyParameters) + { + Gost3410PublicKeyParameters _key = (Gost3410PublicKeyParameters) key; + + if (_key.PublicKeyParamSet == null) + throw Platform.CreateNotImplementedException("Not a CryptoPro parameter set"); + + byte[] keyEnc = _key.Y.ToByteArrayUnsigned(); + byte[] keyBytes = new byte[keyEnc.Length]; + + for (int i = 0; i != keyBytes.Length; i++) + { + keyBytes[i] = keyEnc[keyEnc.Length - 1 - i]; // must be little endian + } + + Gost3410PublicKeyAlgParameters algParams = new Gost3410PublicKeyAlgParameters( + _key.PublicKeyParamSet, CryptoProObjectIdentifiers.GostR3411x94CryptoProParamSet); + + AlgorithmIdentifier algID = new AlgorithmIdentifier( + CryptoProObjectIdentifiers.GostR3410x94, + algParams.ToAsn1Object()); + + return new SubjectPublicKeyInfo(algID, new DerOctetString(keyBytes)); + } + + throw new ArgumentException("Class provided no convertible: " + key.GetType().FullName); + } + + private static void ExtractBytes( + byte[] encKey, + int offset, + BigInteger bI) + { + byte[] val = bI.ToByteArray(); + int n = (bI.BitLength + 7) / 8; + + for (int i = 0; i < n; ++i) + { + encKey[offset + i] = val[val.Length - 1 - i]; + } + } + } +} diff --git a/Crypto/src/x509/X509AttrCertParser.cs b/Crypto/src/x509/X509AttrCertParser.cs new file mode 100644
index 000000000..a5c07362e --- /dev/null +++ b/Crypto/src/x509/X509AttrCertParser.cs
@@ -0,0 +1,173 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.X509 +{ + public class X509AttrCertParser + { + private static readonly PemParser PemAttrCertParser = new PemParser("ATTRIBUTE CERTIFICATE"); + + private Asn1Set sData; + private int sDataObjectCount; + private Stream currentStream; + + private IX509AttributeCertificate ReadDerCertificate( + Asn1InputStream dIn) + { + Asn1Sequence seq = (Asn1Sequence)dIn.ReadObject(); + + if (seq.Count > 1 && seq[0] is DerObjectIdentifier) + { + if (seq[0].Equals(PkcsObjectIdentifiers.SignedData)) + { + sData = SignedData.GetInstance( + Asn1Sequence.GetInstance((Asn1TaggedObject) seq[1], true)).Certificates; + + return GetCertificate(); + } + } + +// return new X509V2AttributeCertificate(seq.getEncoded()); + return new X509V2AttributeCertificate(AttributeCertificate.GetInstance(seq)); + } + + private IX509AttributeCertificate GetCertificate() + { + if (sData != null) + { + while (sDataObjectCount < sData.Count) + { + object obj = sData[sDataObjectCount++]; + + if (obj is Asn1TaggedObject && ((Asn1TaggedObject)obj).TagNo == 2) + { + //return new X509V2AttributeCertificate( + // Asn1Sequence.GetInstance((Asn1TaggedObject)obj, false).GetEncoded()); + return new X509V2AttributeCertificate( + AttributeCertificate.GetInstance( + Asn1Sequence.GetInstance((Asn1TaggedObject)obj, false))); + } + } + } + + return null; + } + + private IX509AttributeCertificate ReadPemCertificate( + Stream inStream) + { + Asn1Sequence seq = PemAttrCertParser.ReadPemObject(inStream); + + return seq == null + ? null + //: new X509V2AttributeCertificate(seq.getEncoded()); + : new X509V2AttributeCertificate(AttributeCertificate.GetInstance(seq)); + } + + /// <summary> + /// Create loading data from byte array. + /// </summary> + /// <param name="input"></param> + public IX509AttributeCertificate ReadAttrCert( + byte[] input) + { + return ReadAttrCert(new MemoryStream(input, false)); + } + + /// <summary> + /// Create loading data from byte array. + /// </summary> + /// <param name="input"></param> + public ICollection ReadAttrCerts( + byte[] input) + { + return ReadAttrCerts(new MemoryStream(input, false)); + } + + /** + * Generates a certificate object and initializes it with the data + * read from the input stream inStream. + */ + public IX509AttributeCertificate ReadAttrCert( + Stream inStream) + { + if (inStream == null) + throw new ArgumentNullException("inStream"); + if (!inStream.CanRead) + throw new ArgumentException("inStream must be read-able", "inStream"); + + if (currentStream == null) + { + currentStream = inStream; + sData = null; + sDataObjectCount = 0; + } + else if (currentStream != inStream) // reset if input stream has changed + { + currentStream = inStream; + sData = null; + sDataObjectCount = 0; + } + + try + { + if (sData != null) + { + if (sDataObjectCount != sData.Count) + { + return GetCertificate(); + } + + sData = null; + sDataObjectCount = 0; + return null; + } + + PushbackStream pis = new PushbackStream(inStream); + int tag = pis.ReadByte(); + + if (tag < 0) + return null; + + pis.Unread(tag); + + if (tag != 0x30) // assume ascii PEM encoded. + { + return ReadPemCertificate(pis); + } + + return ReadDerCertificate(new Asn1InputStream(pis)); + } + catch (Exception e) + { + throw new CertificateException(e.ToString()); + } + } + + /** + * Returns a (possibly empty) collection view of the certificates + * read from the given input stream inStream. + */ + public ICollection ReadAttrCerts( + Stream inStream) + { + IX509AttributeCertificate attrCert; + IList attrCerts = Platform.CreateArrayList(); + + while ((attrCert = ReadAttrCert(inStream)) != null) + { + attrCerts.Add(attrCert); + } + + return attrCerts; + } + } +} \ No newline at end of file diff --git a/Crypto/src/x509/X509Attribute.cs b/Crypto/src/x509/X509Attribute.cs new file mode 100644
index 000000000..248d66cc4 --- /dev/null +++ b/Crypto/src/x509/X509Attribute.cs
@@ -0,0 +1,76 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.X509 +{ + /** + * Class for carrying the values in an X.509 Attribute. + */ + public class X509Attribute + : Asn1Encodable + { + private readonly AttributeX509 attr; + + /** + * @param at an object representing an attribute. + */ + internal X509Attribute( + Asn1Encodable at) + { + this.attr = AttributeX509.GetInstance(at); + } + + /** + * Create an X.509 Attribute with the type given by the passed in oid and + * the value represented by an ASN.1 Set containing value. + * + * @param oid type of the attribute + * @param value value object to go into the atribute's value set. + */ + public X509Attribute( + string oid, + Asn1Encodable value) + { + this.attr = new AttributeX509(new DerObjectIdentifier(oid), new DerSet(value)); + } + + /** + * Create an X.59 Attribute with the type given by the passed in oid and the + * value represented by an ASN.1 Set containing the objects in value. + * + * @param oid type of the attribute + * @param value vector of values to go in the attribute's value set. + */ + public X509Attribute( + string oid, + Asn1EncodableVector value) + { + this.attr = new AttributeX509(new DerObjectIdentifier(oid), new DerSet(value)); + } + + public string Oid + { + get { return attr.AttrType.Id; } + } + + public Asn1Encodable[] GetValues() + { + Asn1Set s = attr.AttrValues; + Asn1Encodable[] values = new Asn1Encodable[s.Count]; + + for (int i = 0; i != s.Count; i++) + { + values[i] = (Asn1Encodable)s[i]; + } + + return values; + } + + public override Asn1Object ToAsn1Object() + { + return attr.ToAsn1Object(); + } + } +} diff --git a/Crypto/src/x509/X509CertPairParser.cs b/Crypto/src/x509/X509CertPairParser.cs new file mode 100644
index 000000000..82612599b --- /dev/null +++ b/Crypto/src/x509/X509CertPairParser.cs
@@ -0,0 +1,95 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.X509 +{ + public class X509CertPairParser + { + private Stream currentStream; + + private X509CertificatePair ReadDerCrossCertificatePair( + Stream inStream) + { + Asn1InputStream dIn = new Asn1InputStream(inStream);//, ProviderUtil.getReadLimit(in)); + Asn1Sequence seq = (Asn1Sequence)dIn.ReadObject(); + CertificatePair pair = CertificatePair.GetInstance(seq); + return new X509CertificatePair(pair); + } + + /// <summary> + /// Create loading data from byte array. + /// </summary> + /// <param name="input"></param> + public X509CertificatePair ReadCertPair( + byte[] input) + { + return ReadCertPair(new MemoryStream(input, false)); + } + + /// <summary> + /// Create loading data from byte array. + /// </summary> + /// <param name="input"></param> + public ICollection ReadCertPairs( + byte[] input) + { + return ReadCertPairs(new MemoryStream(input, false)); + } + + public X509CertificatePair ReadCertPair( + Stream inStream) + { + if (inStream == null) + throw new ArgumentNullException("inStream"); + if (!inStream.CanRead) + throw new ArgumentException("inStream must be read-able", "inStream"); + + if (currentStream == null) + { + currentStream = inStream; + } + else if (currentStream != inStream) // reset if input stream has changed + { + currentStream = inStream; + } + + try + { + PushbackStream pis = new PushbackStream(inStream); + int tag = pis.ReadByte(); + + if (tag < 0) + return null; + + pis.Unread(tag); + + return ReadDerCrossCertificatePair(pis); + } + catch (Exception e) + { + throw new CertificateException(e.ToString()); + } + } + + public ICollection ReadCertPairs( + Stream inStream) + { + X509CertificatePair certPair; + IList certPairs = Platform.CreateArrayList(); + + while ((certPair = ReadCertPair(inStream)) != null) + { + certPairs.Add(certPair); + } + + return certPairs; + } + } +} diff --git a/Crypto/src/x509/X509Certificate.cs b/Crypto/src/x509/X509Certificate.cs new file mode 100644
index 000000000..f156f3147 --- /dev/null +++ b/Crypto/src/x509/X509Certificate.cs
@@ -0,0 +1,595 @@ +using System; +using System.Collections; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Misc; +using Org.BouncyCastle.Asn1.Utilities; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.X509.Extension; + +namespace Org.BouncyCastle.X509 +{ + /// <summary> + /// An Object representing an X509 Certificate. + /// Has static methods for loading Certificates encoded in many forms that return X509Certificate Objects. + /// </summary> + public class X509Certificate + : X509ExtensionBase +// , PKCS12BagAttributeCarrier + { + private readonly X509CertificateStructure c; +// private Hashtable pkcs12Attributes = new Hashtable(); +// private ArrayList pkcs12Ordering = new ArrayList(); + private readonly BasicConstraints basicConstraints; + private readonly bool[] keyUsage; + + private bool hashValueSet; + private int hashValue; + + protected X509Certificate() + { + } + + public X509Certificate( + X509CertificateStructure c) + { + this.c = c; + + try + { + Asn1OctetString str = this.GetExtensionValue(new DerObjectIdentifier("2.5.29.19")); + + if (str != null) + { + basicConstraints = BasicConstraints.GetInstance( + X509ExtensionUtilities.FromExtensionValue(str)); + } + } + catch (Exception e) + { + throw new CertificateParsingException("cannot construct BasicConstraints: " + e); + } + + try + { + Asn1OctetString str = this.GetExtensionValue(new DerObjectIdentifier("2.5.29.15")); + + if (str != null) + { + DerBitString bits = DerBitString.GetInstance( + X509ExtensionUtilities.FromExtensionValue(str)); + + byte[] bytes = bits.GetBytes(); + int length = (bytes.Length * 8) - bits.PadBits; + + keyUsage = new bool[(length < 9) ? 9 : length]; + + for (int i = 0; i != length; i++) + { +// keyUsage[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0; + keyUsage[i] = (bytes[i / 8] & (0x80 >> (i % 8))) != 0; + } + } + else + { + keyUsage = null; + } + } + catch (Exception e) + { + throw new CertificateParsingException("cannot construct KeyUsage: " + e); + } + } + +// internal X509Certificate( +// Asn1Sequence seq) +// { +// this.c = X509CertificateStructure.GetInstance(seq); +// } + +// /// <summary> +// /// Load certificate from byte array. +// /// </summary> +// /// <param name="encoded">Byte array containing encoded X509Certificate.</param> +// public X509Certificate( +// byte[] encoded) +// : this((Asn1Sequence) new Asn1InputStream(encoded).ReadObject()) +// { +// } +// +// /// <summary> +// /// Load certificate from Stream. +// /// Must be positioned at start of certificate. +// /// </summary> +// /// <param name="input"></param> +// public X509Certificate( +// Stream input) +// : this((Asn1Sequence) new Asn1InputStream(input).ReadObject()) +// { +// } + + public virtual X509CertificateStructure CertificateStructure + { + get { return c; } + } + + /// <summary> + /// Return true if the current time is within the start and end times nominated on the certificate. + /// </summary> + /// <returns>true id certificate is valid for the current time.</returns> + public virtual bool IsValidNow + { + get { return IsValid(DateTime.UtcNow); } + } + + /// <summary> + /// Return true if the nominated time is within the start and end times nominated on the certificate. + /// </summary> + /// <param name="time">The time to test validity against.</param> + /// <returns>True if certificate is valid for nominated time.</returns> + public virtual bool IsValid( + DateTime time) + { + return time.CompareTo(NotBefore) >= 0 && time.CompareTo(NotAfter) <= 0; + } + + /// <summary> + /// Checks if the current date is within certificate's validity period. + /// </summary> + public virtual void CheckValidity() + { + this.CheckValidity(DateTime.UtcNow); + } + + /// <summary> + /// Checks if the given date is within certificate's validity period. + /// </summary> + /// <exception cref="CertificateExpiredException">if the certificate is expired by given date</exception> + /// <exception cref="CertificateNotYetValidException">if the certificate is not yet valid on given date</exception> + public virtual void CheckValidity( + DateTime time) + { + if (time.CompareTo(NotAfter) > 0) + throw new CertificateExpiredException("certificate expired on " + c.EndDate.GetTime()); + if (time.CompareTo(NotBefore) < 0) + throw new CertificateNotYetValidException("certificate not valid until " + c.StartDate.GetTime()); + } + + /// <summary> + /// Return the certificate's version. + /// </summary> + /// <returns>An integer whose value Equals the version of the cerficate.</returns> + public virtual int Version + { + get { return c.Version; } + } + + /// <summary> + /// Return a <see cref="Org.BouncyCastle.Math.BigInteger">BigInteger</see> containing the serial number. + /// </summary> + /// <returns>The Serial number.</returns> + public virtual BigInteger SerialNumber + { + get { return c.SerialNumber.Value; } + } + + /// <summary> + /// Get the Issuer Distinguished Name. (Who signed the certificate.) + /// </summary> + /// <returns>And X509Object containing name and value pairs.</returns> +// public IPrincipal IssuerDN + public virtual X509Name IssuerDN + { + get { return c.Issuer; } + } + + /// <summary> + /// Get the subject of this certificate. + /// </summary> + /// <returns>An X509Name object containing name and value pairs.</returns> +// public IPrincipal SubjectDN + public virtual X509Name SubjectDN + { + get { return c.Subject; } + } + + /// <summary> + /// The time that this certificate is valid from. + /// </summary> + /// <returns>A DateTime object representing that time in the local time zone.</returns> + public virtual DateTime NotBefore + { + get { return c.StartDate.ToDateTime(); } + } + + /// <summary> + /// The time that this certificate is valid up to. + /// </summary> + /// <returns>A DateTime object representing that time in the local time zone.</returns> + public virtual DateTime NotAfter + { + get { return c.EndDate.ToDateTime(); } + } + + /// <summary> + /// Return the Der encoded TbsCertificate data. + /// This is the certificate component less the signature. + /// To Get the whole certificate call the GetEncoded() member. + /// </summary> + /// <returns>A byte array containing the Der encoded Certificate component.</returns> + public virtual byte[] GetTbsCertificate() + { + return c.TbsCertificate.GetDerEncoded(); + } + + /// <summary> + /// The signature. + /// </summary> + /// <returns>A byte array containg the signature of the certificate.</returns> + public virtual byte[] GetSignature() + { + return c.Signature.GetBytes(); + } + + /// <summary> + /// A meaningful version of the Signature Algorithm. (EG SHA1WITHRSA) + /// </summary> + /// <returns>A sting representing the signature algorithm.</returns> + public virtual string SigAlgName + { + get { return SignerUtilities.GetEncodingName(c.SignatureAlgorithm.ObjectID); } + } + + /// <summary> + /// Get the Signature Algorithms Object ID. + /// </summary> + /// <returns>A string containg a '.' separated object id.</returns> + public virtual string SigAlgOid + { + get { return c.SignatureAlgorithm.ObjectID.Id; } + } + + /// <summary> + /// Get the signature algorithms parameters. (EG DSA Parameters) + /// </summary> + /// <returns>A byte array containing the Der encoded version of the parameters or null if there are none.</returns> + public virtual byte[] GetSigAlgParams() + { + if (c.SignatureAlgorithm.Parameters != null) + { + return c.SignatureAlgorithm.Parameters.GetDerEncoded(); + } + + return null; + } + + /// <summary> + /// Get the issuers UID. + /// </summary> + /// <returns>A DerBitString.</returns> + public virtual DerBitString IssuerUniqueID + { + get { return c.TbsCertificate.IssuerUniqueID; } + } + + /// <summary> + /// Get the subjects UID. + /// </summary> + /// <returns>A DerBitString.</returns> + public virtual DerBitString SubjectUniqueID + { + get { return c.TbsCertificate.SubjectUniqueID; } + } + + /// <summary> + /// Get a key usage guidlines. + /// </summary> + public virtual bool[] GetKeyUsage() + { + return keyUsage == null ? null : (bool[]) keyUsage.Clone(); + } + + // TODO Replace with something that returns a list of DerObjectIdentifier + public virtual IList GetExtendedKeyUsage() + { + Asn1OctetString str = this.GetExtensionValue(new DerObjectIdentifier("2.5.29.37")); + + if (str == null) + return null; + + try + { + Asn1Sequence seq = Asn1Sequence.GetInstance( + X509ExtensionUtilities.FromExtensionValue(str)); + + IList list = Platform.CreateArrayList(); + + foreach (DerObjectIdentifier oid in seq) + { + list.Add(oid.Id); + } + + return list; + } + catch (Exception e) + { + throw new CertificateParsingException("error processing extended key usage extension", e); + } + } + + public virtual int GetBasicConstraints() + { + if (basicConstraints != null && basicConstraints.IsCA()) + { + if (basicConstraints.PathLenConstraint == null) + { + return int.MaxValue; + } + + return basicConstraints.PathLenConstraint.IntValue; + } + + return -1; + } + + public virtual ICollection GetSubjectAlternativeNames() + { + return GetAlternativeNames("2.5.29.17"); + } + + public virtual ICollection GetIssuerAlternativeNames() + { + return GetAlternativeNames("2.5.29.18"); + } + + protected virtual ICollection GetAlternativeNames( + string oid) + { + Asn1OctetString altNames = GetExtensionValue(new DerObjectIdentifier(oid)); + + if (altNames == null) + return null; + + Asn1Object asn1Object = X509ExtensionUtilities.FromExtensionValue(altNames); + + GeneralNames gns = GeneralNames.GetInstance(asn1Object); + + IList result = Platform.CreateArrayList(); + foreach (GeneralName gn in gns.GetNames()) + { + IList entry = Platform.CreateArrayList(); + entry.Add(gn.TagNo); + entry.Add(gn.Name.ToString()); + result.Add(entry); + } + return result; + } + + protected override X509Extensions GetX509Extensions() + { + return c.Version == 3 + ? c.TbsCertificate.Extensions + : null; + } + + /// <summary> + /// Get the public key of the subject of the certificate. + /// </summary> + /// <returns>The public key parameters.</returns> + public virtual AsymmetricKeyParameter GetPublicKey() + { + return PublicKeyFactory.CreateKey(c.SubjectPublicKeyInfo); + } + + /// <summary> + /// Return a Der encoded version of this certificate. + /// </summary> + /// <returns>A byte array.</returns> + public virtual byte[] GetEncoded() + { + return c.GetDerEncoded(); + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + X509Certificate other = obj as X509Certificate; + + if (other == null) + return false; + + return c.Equals(other.c); + + // NB: May prefer this implementation of Equals if more than one certificate implementation in play +// return Arrays.AreEqual(this.GetEncoded(), other.GetEncoded()); + } + + public override int GetHashCode() + { + lock (this) + { + if (!hashValueSet) + { + hashValue = c.GetHashCode(); + hashValueSet = true; + } + } + + return hashValue; + } + +// public void setBagAttribute( +// DERObjectIdentifier oid, +// DEREncodable attribute) +// { +// pkcs12Attributes.put(oid, attribute); +// pkcs12Ordering.addElement(oid); +// } +// +// public DEREncodable getBagAttribute( +// DERObjectIdentifier oid) +// { +// return (DEREncodable)pkcs12Attributes.get(oid); +// } +// +// public Enumeration getBagAttributeKeys() +// { +// return pkcs12Ordering.elements(); +// } + + public override string ToString() + { + StringBuilder buf = new StringBuilder(); + string nl = Platform.NewLine; + + buf.Append(" [0] Version: ").Append(this.Version).Append(nl); + buf.Append(" SerialNumber: ").Append(this.SerialNumber).Append(nl); + buf.Append(" IssuerDN: ").Append(this.IssuerDN).Append(nl); + buf.Append(" Start Date: ").Append(this.NotBefore).Append(nl); + buf.Append(" Final Date: ").Append(this.NotAfter).Append(nl); + buf.Append(" SubjectDN: ").Append(this.SubjectDN).Append(nl); + buf.Append(" Public Key: ").Append(this.GetPublicKey()).Append(nl); + buf.Append(" Signature Algorithm: ").Append(this.SigAlgName).Append(nl); + + byte[] sig = this.GetSignature(); + buf.Append(" Signature: ").Append(Hex.ToHexString(sig, 0, 20)).Append(nl); + + for (int i = 20; i < sig.Length; i += 20) + { + int len = System.Math.Min(20, sig.Length - i); + buf.Append(" ").Append(Hex.ToHexString(sig, i, len)).Append(nl); + } + + X509Extensions extensions = c.TbsCertificate.Extensions; + + if (extensions != null) + { + IEnumerator e = extensions.ExtensionOids.GetEnumerator(); + + if (e.MoveNext()) + { + buf.Append(" Extensions: \n"); + } + + do + { + DerObjectIdentifier oid = (DerObjectIdentifier)e.Current; + X509Extension ext = extensions.GetExtension(oid); + + if (ext.Value != null) + { + byte[] octs = ext.Value.GetOctets(); + Asn1Object obj = Asn1Object.FromByteArray(octs); + buf.Append(" critical(").Append(ext.IsCritical).Append(") "); + try + { + if (oid.Equals(X509Extensions.BasicConstraints)) + { + buf.Append(BasicConstraints.GetInstance(obj)); + } + else if (oid.Equals(X509Extensions.KeyUsage)) + { + buf.Append(KeyUsage.GetInstance(obj)); + } + else if (oid.Equals(MiscObjectIdentifiers.NetscapeCertType)) + { + buf.Append(new NetscapeCertType((DerBitString) obj)); + } + else if (oid.Equals(MiscObjectIdentifiers.NetscapeRevocationUrl)) + { + buf.Append(new NetscapeRevocationUrl((DerIA5String) obj)); + } + else if (oid.Equals(MiscObjectIdentifiers.VerisignCzagExtension)) + { + buf.Append(new VerisignCzagExtension((DerIA5String) obj)); + } + else + { + buf.Append(oid.Id); + buf.Append(" value = ").Append(Asn1Dump.DumpAsString(obj)); + //buf.Append(" value = ").Append("*****").Append(nl); + } + } + catch (Exception) + { + buf.Append(oid.Id); + //buf.Append(" value = ").Append(new string(Hex.encode(ext.getValue().getOctets()))).Append(nl); + buf.Append(" value = ").Append("*****"); + } + } + + buf.Append(nl); + } + while (e.MoveNext()); + } + + return buf.ToString(); + } + + /// <summary> + /// Verify the certificate's signature using the nominated public key. + /// </summary> + /// <param name="key">An appropriate public key parameter object, RsaPublicKeyParameters, DsaPublicKeyParameters or ECDsaPublicKeyParameters</param> + /// <returns>True if the signature is valid.</returns> + /// <exception cref="Exception">If key submitted is not of the above nominated types.</exception> + public virtual void Verify( + AsymmetricKeyParameter key) + { + string sigName = X509SignatureUtilities.GetSignatureName(c.SignatureAlgorithm); + ISigner signature = SignerUtilities.GetSigner(sigName); + + CheckSignature(key, signature); + } + + protected virtual void CheckSignature( + AsymmetricKeyParameter publicKey, + ISigner signature) + { + if (!IsAlgIDEqual(c.SignatureAlgorithm, c.TbsCertificate.Signature)) + throw new CertificateException("signature algorithm in TBS cert not same as outer cert"); + + Asn1Encodable parameters = c.SignatureAlgorithm.Parameters; + + X509SignatureUtilities.SetSignatureParameters(signature, parameters); + + signature.Init(false, publicKey); + + byte[] b = this.GetTbsCertificate(); + signature.BlockUpdate(b, 0, b.Length); + + byte[] sig = this.GetSignature(); + if (!signature.VerifySignature(sig)) + { + throw new InvalidKeyException("Public key presented not for certificate signature"); + } + } + + private static bool IsAlgIDEqual(AlgorithmIdentifier id1, AlgorithmIdentifier id2) + { + if (!id1.ObjectID.Equals(id2.ObjectID)) + return false; + + Asn1Encodable p1 = id1.Parameters; + Asn1Encodable p2 = id2.Parameters; + + if ((p1 == null) == (p2 == null)) + return Platform.Equals(p1, p2); + + // Exactly one of p1, p2 is null at this point + return p1 == null + ? p2.ToAsn1Object() is Asn1Null + : p1.ToAsn1Object() is Asn1Null; + } + } +} diff --git a/Crypto/src/x509/X509CertificatePair.cs b/Crypto/src/x509/X509CertificatePair.cs new file mode 100644
index 000000000..fbeba4dc6 --- /dev/null +++ b/Crypto/src/x509/X509CertificatePair.cs
@@ -0,0 +1,123 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.X509 +{ + /// <remarks> + /// This class contains a cross certificate pair. Cross certificates pairs may + /// contain two cross signed certificates from two CAs. A certificate from the + /// other CA to this CA is contained in the forward certificate, the certificate + /// from this CA to the other CA is contained in the reverse certificate. + /// </remarks> + public class X509CertificatePair + { + private readonly X509Certificate forward; + private readonly X509Certificate reverse; + + /// <summary>Constructor</summary> + /// <param name="forward">Certificate from the other CA to this CA.</param> + /// <param name="reverse">Certificate from this CA to the other CA.</param> + public X509CertificatePair( + X509Certificate forward, + X509Certificate reverse) + { + this.forward = forward; + this.reverse = reverse; + } + + /// <summary>Constructor from a ASN.1 CertificatePair structure.</summary> + /// <param name="pair">The <c>CertificatePair</c> ASN.1 object.</param> + public X509CertificatePair( + CertificatePair pair) + { + if (pair.Forward != null) + { + this.forward = new X509Certificate(pair.Forward); + } + if (pair.Reverse != null) + { + this.reverse = new X509Certificate(pair.Reverse); + } + } + + public byte[] GetEncoded() + { + try + { + X509CertificateStructure f = null, r = null; + + if (forward != null) + { + f = X509CertificateStructure.GetInstance( + Asn1Object.FromByteArray(forward.GetEncoded())); + + if (f == null) + throw new CertificateEncodingException("unable to get encoding for forward"); + } + + if (reverse != null) + { + r = X509CertificateStructure.GetInstance( + Asn1Object.FromByteArray(reverse.GetEncoded())); + + if (r == null) + throw new CertificateEncodingException("unable to get encoding for reverse"); + } + + return new CertificatePair(f, r).GetDerEncoded(); + } + catch (Exception e) + { + // TODO +// throw new ExtCertificateEncodingException(e.toString(), e); + throw new CertificateEncodingException(e.Message, e); + } + } + + /// <summary>Returns the certificate from the other CA to this CA.</summary> + public X509Certificate Forward + { + get { return forward; } + } + + /// <summary>Returns the certificate from this CA to the other CA.</summary> + public X509Certificate Reverse + { + get { return reverse; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + X509CertificatePair other = obj as X509CertificatePair; + + if (other == null) + return false; + + return Platform.Equals(this.forward, other.forward) + && Platform.Equals(this.reverse, other.reverse); + } + + public override int GetHashCode() + { + int hash = -1; + if (forward != null) + { + hash ^= forward.GetHashCode(); + } + if (reverse != null) + { + hash *= 17; + hash ^= reverse.GetHashCode(); + } + return hash; + } + } +} diff --git a/Crypto/src/x509/X509CertificateParser.cs b/Crypto/src/x509/X509CertificateParser.cs new file mode 100644
index 000000000..8f0e7406c --- /dev/null +++ b/Crypto/src/x509/X509CertificateParser.cs
@@ -0,0 +1,183 @@ +using System; +using System.Collections; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.X509 +{ + /** + * class for dealing with X509 certificates. + * <p> + * At the moment this will deal with "-----BEGIN CERTIFICATE-----" to "-----END CERTIFICATE-----" + * base 64 encoded certs, as well as the BER binaries of certificates and some classes of PKCS#7 + * objects.</p> + */ + public class X509CertificateParser + { + private static readonly PemParser PemCertParser = new PemParser("CERTIFICATE"); + + private Asn1Set sData; + private int sDataObjectCount; + private Stream currentStream; + + private X509Certificate ReadDerCertificate( + Asn1InputStream dIn) + { + Asn1Sequence seq = (Asn1Sequence)dIn.ReadObject(); + + if (seq.Count > 1 && seq[0] is DerObjectIdentifier) + { + if (seq[0].Equals(PkcsObjectIdentifiers.SignedData)) + { + sData = SignedData.GetInstance( + Asn1Sequence.GetInstance((Asn1TaggedObject) seq[1], true)).Certificates; + + return GetCertificate(); + } + } + + return CreateX509Certificate(X509CertificateStructure.GetInstance(seq)); + } + + private X509Certificate GetCertificate() + { + if (sData != null) + { + while (sDataObjectCount < sData.Count) + { + object obj = sData[sDataObjectCount++]; + + if (obj is Asn1Sequence) + { + return CreateX509Certificate( + X509CertificateStructure.GetInstance(obj)); + } + } + } + + return null; + } + + private X509Certificate ReadPemCertificate( + Stream inStream) + { + Asn1Sequence seq = PemCertParser.ReadPemObject(inStream); + + return seq == null + ? null + : CreateX509Certificate(X509CertificateStructure.GetInstance(seq)); + } + + protected virtual X509Certificate CreateX509Certificate( + X509CertificateStructure c) + { + return new X509Certificate(c); + } + + /// <summary> + /// Create loading data from byte array. + /// </summary> + /// <param name="input"></param> + public X509Certificate ReadCertificate( + byte[] input) + { + return ReadCertificate(new MemoryStream(input, false)); + } + + /// <summary> + /// Create loading data from byte array. + /// </summary> + /// <param name="input"></param> + public ICollection ReadCertificates( + byte[] input) + { + return ReadCertificates(new MemoryStream(input, false)); + } + + /** + * Generates a certificate object and initializes it with the data + * read from the input stream inStream. + */ + public X509Certificate ReadCertificate( + Stream inStream) + { + if (inStream == null) + throw new ArgumentNullException("inStream"); + if (!inStream.CanRead) + throw new ArgumentException("inStream must be read-able", "inStream"); + + if (currentStream == null) + { + currentStream = inStream; + sData = null; + sDataObjectCount = 0; + } + else if (currentStream != inStream) // reset if input stream has changed + { + currentStream = inStream; + sData = null; + sDataObjectCount = 0; + } + + try + { + if (sData != null) + { + if (sDataObjectCount != sData.Count) + { + return GetCertificate(); + } + + sData = null; + sDataObjectCount = 0; + return null; + } + + PushbackStream pis = new PushbackStream(inStream); + int tag = pis.ReadByte(); + + if (tag < 0) + return null; + + pis.Unread(tag); + + if (tag != 0x30) // assume ascii PEM encoded. + { + return ReadPemCertificate(pis); + } + + return ReadDerCertificate(new Asn1InputStream(pis)); + } + catch (Exception e) + { + throw new CertificateException("Failed to read certificate", e); + } + } + + /** + * Returns a (possibly empty) collection view of the certificates + * read from the given input stream inStream. + */ + public ICollection ReadCertificates( + Stream inStream) + { + X509Certificate cert; + IList certs = Platform.CreateArrayList(); + + while ((cert = ReadCertificate(inStream)) != null) + { + certs.Add(cert); + } + + return certs; + } + } +} diff --git a/Crypto/src/x509/X509Crl.cs b/Crypto/src/x509/X509Crl.cs new file mode 100644
index 000000000..7d0e7aa72 --- /dev/null +++ b/Crypto/src/x509/X509Crl.cs
@@ -0,0 +1,403 @@ +using System; +using System.Collections; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Utilities; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.Utilities.Date; +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.X509.Extension; + +namespace Org.BouncyCastle.X509 +{ + /** + * The following extensions are listed in RFC 2459 as relevant to CRLs + * + * Authority Key Identifier + * Issuer Alternative Name + * CRL Number + * Delta CRL Indicator (critical) + * Issuing Distribution Point (critical) + */ + public class X509Crl + : X509ExtensionBase + // TODO Add interface Crl? + { + private readonly CertificateList c; + private readonly string sigAlgName; + private readonly byte[] sigAlgParams; + private readonly bool isIndirect; + + public X509Crl( + CertificateList c) + { + this.c = c; + + try + { + this.sigAlgName = X509SignatureUtilities.GetSignatureName(c.SignatureAlgorithm); + + if (c.SignatureAlgorithm.Parameters != null) + { + this.sigAlgParams = ((Asn1Encodable)c.SignatureAlgorithm.Parameters).GetDerEncoded(); + } + else + { + this.sigAlgParams = null; + } + + this.isIndirect = IsIndirectCrl; + } + catch (Exception e) + { + throw new CrlException("CRL contents invalid: " + e); + } + } + + protected override X509Extensions GetX509Extensions() + { + return Version == 2 + ? c.TbsCertList.Extensions + : null; + } + + public virtual byte[] GetEncoded() + { + try + { + return c.GetDerEncoded(); + } + catch (Exception e) + { + throw new CrlException(e.ToString()); + } + } + + public virtual void Verify( + AsymmetricKeyParameter publicKey) + { + if (!c.SignatureAlgorithm.Equals(c.TbsCertList.Signature)) + { + throw new CrlException("Signature algorithm on CertificateList does not match TbsCertList."); + } + + ISigner sig = SignerUtilities.GetSigner(SigAlgName); + sig.Init(false, publicKey); + + byte[] encoded = this.GetTbsCertList(); + sig.BlockUpdate(encoded, 0, encoded.Length); + + if (!sig.VerifySignature(this.GetSignature())) + { + throw new SignatureException("CRL does not verify with supplied public key."); + } + } + + public virtual int Version + { + get { return c.Version; } + } + + public virtual X509Name IssuerDN + { + get { return c.Issuer; } + } + + public virtual DateTime ThisUpdate + { + get { return c.ThisUpdate.ToDateTime(); } + } + + public virtual DateTimeObject NextUpdate + { + get + { + return c.NextUpdate == null + ? null + : new DateTimeObject(c.NextUpdate.ToDateTime()); + } + } + + private ISet LoadCrlEntries() + { + ISet entrySet = new HashSet(); + IEnumerable certs = c.GetRevokedCertificateEnumeration(); + + X509Name previousCertificateIssuer = IssuerDN; + foreach (CrlEntry entry in certs) + { + X509CrlEntry crlEntry = new X509CrlEntry(entry, isIndirect, previousCertificateIssuer); + entrySet.Add(crlEntry); + previousCertificateIssuer = crlEntry.GetCertificateIssuer(); + } + + return entrySet; + } + + public virtual X509CrlEntry GetRevokedCertificate( + BigInteger serialNumber) + { + IEnumerable certs = c.GetRevokedCertificateEnumeration(); + + X509Name previousCertificateIssuer = IssuerDN; + foreach (CrlEntry entry in certs) + { + X509CrlEntry crlEntry = new X509CrlEntry(entry, isIndirect, previousCertificateIssuer); + + if (serialNumber.Equals(entry.UserCertificate.Value)) + { + return crlEntry; + } + + previousCertificateIssuer = crlEntry.GetCertificateIssuer(); + } + + return null; + } + + public virtual ISet GetRevokedCertificates() + { + ISet entrySet = LoadCrlEntries(); + + if (entrySet.Count > 0) + { + return entrySet; // TODO? Collections.unmodifiableSet(entrySet); + } + + return null; + } + + public virtual byte[] GetTbsCertList() + { + try + { + return c.TbsCertList.GetDerEncoded(); + } + catch (Exception e) + { + throw new CrlException(e.ToString()); + } + } + + public virtual byte[] GetSignature() + { + return c.Signature.GetBytes(); + } + + public virtual string SigAlgName + { + get { return sigAlgName; } + } + + public virtual string SigAlgOid + { + get { return c.SignatureAlgorithm.ObjectID.Id; } + } + + public virtual byte[] GetSigAlgParams() + { + return Arrays.Clone(sigAlgParams); + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + X509Crl other = obj as X509Crl; + + if (other == null) + return false; + + return c.Equals(other.c); + + // NB: May prefer this implementation of Equals if more than one certificate implementation in play + //return Arrays.AreEqual(this.GetEncoded(), other.GetEncoded()); + } + + public override int GetHashCode() + { + return c.GetHashCode(); + } + + /** + * Returns a string representation of this CRL. + * + * @return a string representation of this CRL. + */ + public override string ToString() + { + StringBuilder buf = new StringBuilder(); + string nl = Platform.NewLine; + + buf.Append(" Version: ").Append(this.Version).Append(nl); + buf.Append(" IssuerDN: ").Append(this.IssuerDN).Append(nl); + buf.Append(" This update: ").Append(this.ThisUpdate).Append(nl); + buf.Append(" Next update: ").Append(this.NextUpdate).Append(nl); + buf.Append(" Signature Algorithm: ").Append(this.SigAlgName).Append(nl); + + byte[] sig = this.GetSignature(); + + buf.Append(" Signature: "); + buf.Append(Hex.ToHexString(sig, 0, 20)).Append(nl); + + for (int i = 20; i < sig.Length; i += 20) + { + int count = System.Math.Min(20, sig.Length - i); + buf.Append(" "); + buf.Append(Hex.ToHexString(sig, i, count)).Append(nl); + } + + X509Extensions extensions = c.TbsCertList.Extensions; + + if (extensions != null) + { + IEnumerator e = extensions.ExtensionOids.GetEnumerator(); + + if (e.MoveNext()) + { + buf.Append(" Extensions: ").Append(nl); + } + + do + { + DerObjectIdentifier oid = (DerObjectIdentifier) e.Current; + X509Extension ext = extensions.GetExtension(oid); + + if (ext.Value != null) + { + Asn1Object asn1Value = X509ExtensionUtilities.FromExtensionValue(ext.Value); + + buf.Append(" critical(").Append(ext.IsCritical).Append(") "); + try + { + if (oid.Equals(X509Extensions.CrlNumber)) + { + buf.Append(new CrlNumber(DerInteger.GetInstance(asn1Value).PositiveValue)).Append(nl); + } + else if (oid.Equals(X509Extensions.DeltaCrlIndicator)) + { + buf.Append( + "Base CRL: " + + new CrlNumber(DerInteger.GetInstance( + asn1Value).PositiveValue)) + .Append(nl); + } + else if (oid.Equals(X509Extensions.IssuingDistributionPoint)) + { + buf.Append(IssuingDistributionPoint.GetInstance((Asn1Sequence) asn1Value)).Append(nl); + } + else if (oid.Equals(X509Extensions.CrlDistributionPoints)) + { + buf.Append(CrlDistPoint.GetInstance((Asn1Sequence) asn1Value)).Append(nl); + } + else if (oid.Equals(X509Extensions.FreshestCrl)) + { + buf.Append(CrlDistPoint.GetInstance((Asn1Sequence) asn1Value)).Append(nl); + } + else + { + buf.Append(oid.Id); + buf.Append(" value = ").Append( + Asn1Dump.DumpAsString(asn1Value)) + .Append(nl); + } + } + catch (Exception) + { + buf.Append(oid.Id); + buf.Append(" value = ").Append("*****").Append(nl); + } + } + else + { + buf.Append(nl); + } + } + while (e.MoveNext()); + } + + ISet certSet = GetRevokedCertificates(); + if (certSet != null) + { + foreach (X509CrlEntry entry in certSet) + { + buf.Append(entry); + buf.Append(nl); + } + } + + return buf.ToString(); + } + + /** + * Checks whether the given certificate is on this CRL. + * + * @param cert the certificate to check for. + * @return true if the given certificate is on this CRL, + * false otherwise. + */ +// public bool IsRevoked( +// Certificate cert) +// { +// if (!cert.getType().Equals("X.509")) +// { +// throw new RuntimeException("X.509 CRL used with non X.509 Cert"); +// } + public virtual bool IsRevoked( + X509Certificate cert) + { + CrlEntry[] certs = c.GetRevokedCertificates(); + + if (certs != null) + { +// BigInteger serial = ((X509Certificate)cert).SerialNumber; + BigInteger serial = cert.SerialNumber; + + for (int i = 0; i < certs.Length; i++) + { + if (certs[i].UserCertificate.Value.Equals(serial)) + { + return true; + } + } + } + + return false; + } + + protected virtual bool IsIndirectCrl + { + get + { + Asn1OctetString idp = GetExtensionValue(X509Extensions.IssuingDistributionPoint); + bool isIndirect = false; + + try + { + if (idp != null) + { + isIndirect = IssuingDistributionPoint.GetInstance( + X509ExtensionUtilities.FromExtensionValue(idp)).IsIndirectCrl; + } + } + catch (Exception e) + { + // TODO +// throw new ExtCrlException("Exception reading IssuingDistributionPoint", e); + throw new CrlException("Exception reading IssuingDistributionPoint" + e); + } + + return isIndirect; + } + } + } +} diff --git a/Crypto/src/x509/X509CrlEntry.cs b/Crypto/src/x509/X509CrlEntry.cs new file mode 100644
index 000000000..caca29470 --- /dev/null +++ b/Crypto/src/x509/X509CrlEntry.cs
@@ -0,0 +1,201 @@ +using System; +using System.Collections; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Utilities; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509.Extension; + +namespace Org.BouncyCastle.X509 +{ + /** + * The following extensions are listed in RFC 2459 as relevant to CRL Entries + * + * ReasonCode Hode Instruction Code Invalidity Date Certificate Issuer + * (critical) + */ + public class X509CrlEntry + : X509ExtensionBase + { + private CrlEntry c; + private bool isIndirect; + private X509Name previousCertificateIssuer; + private X509Name certificateIssuer; + + public X509CrlEntry( + CrlEntry c) + { + this.c = c; + this.certificateIssuer = loadCertificateIssuer(); + } + + /** + * Constructor for CRLEntries of indirect CRLs. If <code>isIndirect</code> + * is <code>false</code> {@link #getCertificateIssuer()} will always + * return <code>null</code>, <code>previousCertificateIssuer</code> is + * ignored. If this <code>isIndirect</code> is specified and this CrlEntry + * has no certificate issuer CRL entry extension + * <code>previousCertificateIssuer</code> is returned by + * {@link #getCertificateIssuer()}. + * + * @param c + * TbsCertificateList.CrlEntry object. + * @param isIndirect + * <code>true</code> if the corresponding CRL is a indirect + * CRL. + * @param previousCertificateIssuer + * Certificate issuer of the previous CrlEntry. + */ + public X509CrlEntry( + CrlEntry c, + bool isIndirect, + X509Name previousCertificateIssuer) + { + this.c = c; + this.isIndirect = isIndirect; + this.previousCertificateIssuer = previousCertificateIssuer; + this.certificateIssuer = loadCertificateIssuer(); + } + + private X509Name loadCertificateIssuer() + { + if (!isIndirect) + { + return null; + } + + Asn1OctetString ext = GetExtensionValue(X509Extensions.CertificateIssuer); + if (ext == null) + { + return previousCertificateIssuer; + } + + try + { + GeneralName[] names = GeneralNames.GetInstance( + X509ExtensionUtilities.FromExtensionValue(ext)).GetNames(); + + for (int i = 0; i < names.Length; i++) + { + if (names[i].TagNo == GeneralName.DirectoryName) + { + return X509Name.GetInstance(names[i].Name); + } + } + } + catch (Exception) + { + } + + return null; + } + + public X509Name GetCertificateIssuer() + { + return certificateIssuer; + } + + protected override X509Extensions GetX509Extensions() + { + return c.Extensions; + } + + public byte[] GetEncoded() + { + try + { + return c.GetDerEncoded(); + } + catch (Exception e) + { + throw new CrlException(e.ToString()); + } + } + + public BigInteger SerialNumber + { + get { return c.UserCertificate.Value; } + } + + public DateTime RevocationDate + { + get { return c.RevocationDate.ToDateTime(); } + } + + public bool HasExtensions + { + get { return c.Extensions != null; } + } + + public override string ToString() + { + StringBuilder buf = new StringBuilder(); + string nl = Platform.NewLine; + + buf.Append(" userCertificate: ").Append(this.SerialNumber).Append(nl); + buf.Append(" revocationDate: ").Append(this.RevocationDate).Append(nl); + buf.Append(" certificateIssuer: ").Append(this.GetCertificateIssuer()).Append(nl); + + X509Extensions extensions = c.Extensions; + + if (extensions != null) + { + IEnumerator e = extensions.ExtensionOids.GetEnumerator(); + if (e.MoveNext()) + { + buf.Append(" crlEntryExtensions:").Append(nl); + + do + { + DerObjectIdentifier oid = (DerObjectIdentifier)e.Current; + X509Extension ext = extensions.GetExtension(oid); + + if (ext.Value != null) + { + Asn1Object obj = Asn1Object.FromByteArray(ext.Value.GetOctets()); + + buf.Append(" critical(") + .Append(ext.IsCritical) + .Append(") "); + try + { + if (oid.Equals(X509Extensions.ReasonCode)) + { + buf.Append(new CrlReason(DerEnumerated.GetInstance(obj))); + } + else if (oid.Equals(X509Extensions.CertificateIssuer)) + { + buf.Append("Certificate issuer: ").Append( + GeneralNames.GetInstance((Asn1Sequence)obj)); + } + else + { + buf.Append(oid.Id); + buf.Append(" value = ").Append(Asn1Dump.DumpAsString(obj)); + } + buf.Append(nl); + } + catch (Exception) + { + buf.Append(oid.Id); + buf.Append(" value = ").Append("*****").Append(nl); + } + } + else + { + buf.Append(nl); + } + } + while (e.MoveNext()); + } + } + + return buf.ToString(); + } + } +} diff --git a/Crypto/src/x509/X509CrlParser.cs b/Crypto/src/x509/X509CrlParser.cs new file mode 100644
index 000000000..d830bb9a6 --- /dev/null +++ b/Crypto/src/x509/X509CrlParser.cs
@@ -0,0 +1,195 @@ +using System; +using System.Collections; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.X509 +{ + public class X509CrlParser + { + private static readonly PemParser PemCrlParser = new PemParser("CRL"); + + private readonly bool lazyAsn1; + + private Asn1Set sCrlData; + private int sCrlDataObjectCount; + private Stream currentCrlStream; + + public X509CrlParser() + : this(false) + { + } + + public X509CrlParser( + bool lazyAsn1) + { + this.lazyAsn1 = lazyAsn1; + } + + private X509Crl ReadPemCrl( + Stream inStream) + { + Asn1Sequence seq = PemCrlParser.ReadPemObject(inStream); + + return seq == null + ? null + : CreateX509Crl(CertificateList.GetInstance(seq)); + } + + private X509Crl ReadDerCrl( + Asn1InputStream dIn) + { + Asn1Sequence seq = (Asn1Sequence)dIn.ReadObject(); + + if (seq.Count > 1 && seq[0] is DerObjectIdentifier) + { + if (seq[0].Equals(PkcsObjectIdentifiers.SignedData)) + { + sCrlData = SignedData.GetInstance( + Asn1Sequence.GetInstance((Asn1TaggedObject) seq[1], true)).Crls; + + return GetCrl(); + } + } + + return CreateX509Crl(CertificateList.GetInstance(seq)); + } + + private X509Crl GetCrl() + { + if (sCrlData == null || sCrlDataObjectCount >= sCrlData.Count) + { + return null; + } + + return CreateX509Crl( + CertificateList.GetInstance( + sCrlData[sCrlDataObjectCount++])); + } + + protected virtual X509Crl CreateX509Crl( + CertificateList c) + { + return new X509Crl(c); + } + + /// <summary> + /// Create loading data from byte array. + /// </summary> + /// <param name="input"></param> + public X509Crl ReadCrl( + byte[] input) + { + return ReadCrl(new MemoryStream(input, false)); + } + + /// <summary> + /// Create loading data from byte array. + /// </summary> + /// <param name="input"></param> + public ICollection ReadCrls( + byte[] input) + { + return ReadCrls(new MemoryStream(input, false)); + } + + /** + * Generates a certificate revocation list (CRL) object and initializes + * it with the data read from the input stream inStream. + */ + public X509Crl ReadCrl( + Stream inStream) + { + if (inStream == null) + throw new ArgumentNullException("inStream"); + if (!inStream.CanRead) + throw new ArgumentException("inStream must be read-able", "inStream"); + + if (currentCrlStream == null) + { + currentCrlStream = inStream; + sCrlData = null; + sCrlDataObjectCount = 0; + } + else if (currentCrlStream != inStream) // reset if input stream has changed + { + currentCrlStream = inStream; + sCrlData = null; + sCrlDataObjectCount = 0; + } + + try + { + if (sCrlData != null) + { + if (sCrlDataObjectCount != sCrlData.Count) + { + return GetCrl(); + } + + sCrlData = null; + sCrlDataObjectCount = 0; + return null; + } + + PushbackStream pis = new PushbackStream(inStream); + int tag = pis.ReadByte(); + + if (tag < 0) + return null; + + pis.Unread(tag); + + if (tag != 0x30) // assume ascii PEM encoded. + { + return ReadPemCrl(pis); + } + + Asn1InputStream asn1 = lazyAsn1 + ? new LazyAsn1InputStream(pis) + : new Asn1InputStream(pis); + + return ReadDerCrl(asn1); + } + catch (CrlException e) + { + throw e; + } + catch (Exception e) + { + throw new CrlException(e.ToString()); + } + } + + /** + * Returns a (possibly empty) collection view of the CRLs read from + * the given input stream inStream. + * + * The inStream may contain a sequence of DER-encoded CRLs, or + * a PKCS#7 CRL set. This is a PKCS#7 SignedData object, with the + * only significant field being crls. In particular the signature + * and the contents are ignored. + */ + public ICollection ReadCrls( + Stream inStream) + { + X509Crl crl; + IList crls = Platform.CreateArrayList(); + + while ((crl = ReadCrl(inStream)) != null) + { + crls.Add(crl); + } + + return crls; + } + } +} diff --git a/Crypto/src/x509/X509ExtensionBase.cs b/Crypto/src/x509/X509ExtensionBase.cs new file mode 100644
index 000000000..aaf6695c0 --- /dev/null +++ b/Crypto/src/x509/X509ExtensionBase.cs
@@ -0,0 +1,82 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.X509 +{ + public abstract class X509ExtensionBase + : IX509Extension + { + protected abstract X509Extensions GetX509Extensions(); + + protected virtual ISet GetExtensionOids( + bool critical) + { + X509Extensions extensions = GetX509Extensions(); + if (extensions != null) + { + HashSet set = new HashSet(); + foreach (DerObjectIdentifier oid in extensions.ExtensionOids) + { + X509Extension ext = extensions.GetExtension(oid); + if (ext.IsCritical == critical) + { + set.Add(oid.Id); + } + } + + return set; + } + + return null; + } + + /// <summary> + /// Get non critical extensions. + /// </summary> + /// <returns>A set of non critical extension oids.</returns> + public virtual ISet GetNonCriticalExtensionOids() + { + return GetExtensionOids(false); + } + + /// <summary> + /// Get any critical extensions. + /// </summary> + /// <returns>A sorted list of critical entension.</returns> + public virtual ISet GetCriticalExtensionOids() + { + return GetExtensionOids(true); + } + + /// <summary> + /// Get the value of a given extension. + /// </summary> + /// <param name="oid">The object ID of the extension. </param> + /// <returns>An Asn1OctetString object if that extension is found or null if not.</returns> + [Obsolete("Use version taking a DerObjectIdentifier instead")] + public Asn1OctetString GetExtensionValue( + string oid) + { + return GetExtensionValue(new DerObjectIdentifier(oid)); + } + + public virtual Asn1OctetString GetExtensionValue( + DerObjectIdentifier oid) + { + X509Extensions exts = GetX509Extensions(); + if (exts != null) + { + X509Extension ext = exts.GetExtension(oid); + if (ext != null) + { + return ext.Value; + } + } + + return null; + } + } +} diff --git a/Crypto/src/x509/X509KeyUsage.cs b/Crypto/src/x509/X509KeyUsage.cs new file mode 100644
index 000000000..e0a7b4939 --- /dev/null +++ b/Crypto/src/x509/X509KeyUsage.cs
@@ -0,0 +1,59 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.X509 +{ + /** + * A holding class for constructing an X509 Key Usage extension. + * + * <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 X509KeyUsage + : Asn1Encodable + { + public const int DigitalSignature = 1 << 7; + public const int NonRepudiation = 1 << 6; + public const int KeyEncipherment = 1 << 5; + public const int DataEncipherment = 1 << 4; + public const int KeyAgreement = 1 << 3; + public const int KeyCertSign = 1 << 2; + public const int CrlSign = 1 << 1; + public const int EncipherOnly = 1 << 0; + public const int DecipherOnly = 1 << 15; + + private readonly int usage; + + /** + * Basic constructor. + * + * @param usage - the bitwise OR of the Key Usage flags giving the + * allowed uses for the key. + * e.g. (X509KeyUsage.keyEncipherment | X509KeyUsage.dataEncipherment) + */ + public X509KeyUsage( + int usage) + { + this.usage = usage; + } + + public override Asn1Object ToAsn1Object() + { + return new KeyUsage(usage); + } + } +} diff --git a/Crypto/src/x509/X509SignatureUtil.cs b/Crypto/src/x509/X509SignatureUtil.cs new file mode 100644
index 000000000..7a4ab1448 --- /dev/null +++ b/Crypto/src/x509/X509SignatureUtil.cs
@@ -0,0 +1,128 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.X509 +{ + internal class X509SignatureUtilities + { + private static readonly Asn1Null derNull = DerNull.Instance; + + internal static void SetSignatureParameters( + ISigner signature, + Asn1Encodable parameters) + { + if (parameters != null && !derNull.Equals(parameters)) + { + // TODO Put back in +// AlgorithmParameters sigParams = AlgorithmParameters.GetInstance(signature.getAlgorithm()); +// +// try +// { +// sigParams.Init(parameters.ToAsn1Object().GetDerEncoded()); +// } +// catch (IOException e) +// { +// throw new SignatureException("IOException decoding parameters: " + e.Message); +// } +// +// if (signature.getAlgorithm().EndsWith("MGF1")) +// { +// try +// { +// signature.setParameter(sigParams.getParameterSpec(PSSParameterSpec.class)); +// } +// catch (GeneralSecurityException e) +// { +// throw new SignatureException("Exception extracting parameters: " + e.Message); +// } +// } + } + } + + internal static string GetSignatureName( + AlgorithmIdentifier sigAlgId) + { + Asn1Encodable parameters = sigAlgId.Parameters; + + if (parameters != null && !derNull.Equals(parameters)) + { + if (sigAlgId.ObjectID.Equals(PkcsObjectIdentifiers.IdRsassaPss)) + { + RsassaPssParameters rsaParams = RsassaPssParameters.GetInstance(parameters); + + return GetDigestAlgName(rsaParams.HashAlgorithm.ObjectID) + "withRSAandMGF1"; + } + if (sigAlgId.ObjectID.Equals(X9ObjectIdentifiers.ECDsaWithSha2)) + { + Asn1Sequence ecDsaParams = Asn1Sequence.GetInstance(parameters); + + return GetDigestAlgName((DerObjectIdentifier)ecDsaParams[0]) + "withECDSA"; + } + } + + return sigAlgId.ObjectID.Id; + } + + /** + * Return the digest algorithm using one of the standard JCA string + * representations rather than the algorithm identifier (if possible). + */ + private static string GetDigestAlgName( + DerObjectIdentifier digestAlgOID) + { + if (PkcsObjectIdentifiers.MD5.Equals(digestAlgOID)) + { + return "MD5"; + } + else if (OiwObjectIdentifiers.IdSha1.Equals(digestAlgOID)) + { + return "SHA1"; + } + else if (NistObjectIdentifiers.IdSha224.Equals(digestAlgOID)) + { + return "SHA224"; + } + else if (NistObjectIdentifiers.IdSha256.Equals(digestAlgOID)) + { + return "SHA256"; + } + else if (NistObjectIdentifiers.IdSha384.Equals(digestAlgOID)) + { + return "SHA384"; + } + else if (NistObjectIdentifiers.IdSha512.Equals(digestAlgOID)) + { + return "SHA512"; + } + else if (TeleTrusTObjectIdentifiers.RipeMD128.Equals(digestAlgOID)) + { + return "RIPEMD128"; + } + else if (TeleTrusTObjectIdentifiers.RipeMD160.Equals(digestAlgOID)) + { + return "RIPEMD160"; + } + else if (TeleTrusTObjectIdentifiers.RipeMD256.Equals(digestAlgOID)) + { + return "RIPEMD256"; + } + else if (CryptoProObjectIdentifiers.GostR3411.Equals(digestAlgOID)) + { + return "GOST3411"; + } + else + { + return digestAlgOID.Id; + } + } + } +} diff --git a/Crypto/src/x509/X509Utilities.cs b/Crypto/src/x509/X509Utilities.cs new file mode 100644
index 000000000..000958340 --- /dev/null +++ b/Crypto/src/x509/X509Utilities.cs
@@ -0,0 +1,188 @@ +using System; +using System.Collections; +using System.Globalization; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.X509 +{ + internal class X509Utilities + { + private static readonly IDictionary algorithms = Platform.CreateHashtable(); + private static readonly IDictionary exParams = Platform.CreateHashtable(); + private static readonly ISet noParams = new HashSet(); + + static X509Utilities() + { + algorithms.Add("MD2WITHRSAENCRYPTION", PkcsObjectIdentifiers.MD2WithRsaEncryption); + algorithms.Add("MD2WITHRSA", PkcsObjectIdentifiers.MD2WithRsaEncryption); + algorithms.Add("MD5WITHRSAENCRYPTION", PkcsObjectIdentifiers.MD5WithRsaEncryption); + algorithms.Add("MD5WITHRSA", PkcsObjectIdentifiers.MD5WithRsaEncryption); + algorithms.Add("SHA1WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha1WithRsaEncryption); + algorithms.Add("SHA1WITHRSA", PkcsObjectIdentifiers.Sha1WithRsaEncryption); + algorithms.Add("SHA224WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha224WithRsaEncryption); + algorithms.Add("SHA224WITHRSA", PkcsObjectIdentifiers.Sha224WithRsaEncryption); + algorithms.Add("SHA256WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha256WithRsaEncryption); + algorithms.Add("SHA256WITHRSA", PkcsObjectIdentifiers.Sha256WithRsaEncryption); + algorithms.Add("SHA384WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha384WithRsaEncryption); + algorithms.Add("SHA384WITHRSA", PkcsObjectIdentifiers.Sha384WithRsaEncryption); + algorithms.Add("SHA512WITHRSAENCRYPTION", PkcsObjectIdentifiers.Sha512WithRsaEncryption); + algorithms.Add("SHA512WITHRSA", PkcsObjectIdentifiers.Sha512WithRsaEncryption); + algorithms.Add("SHA1WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss); + algorithms.Add("SHA224WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss); + algorithms.Add("SHA256WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss); + algorithms.Add("SHA384WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss); + algorithms.Add("SHA512WITHRSAANDMGF1", PkcsObjectIdentifiers.IdRsassaPss); + algorithms.Add("RIPEMD160WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160); + algorithms.Add("RIPEMD160WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160); + algorithms.Add("RIPEMD128WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128); + algorithms.Add("RIPEMD128WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128); + algorithms.Add("RIPEMD256WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256); + algorithms.Add("RIPEMD256WITHRSA", TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256); + algorithms.Add("SHA1WITHDSA", X9ObjectIdentifiers.IdDsaWithSha1); + algorithms.Add("DSAWITHSHA1", X9ObjectIdentifiers.IdDsaWithSha1); + algorithms.Add("SHA224WITHDSA", NistObjectIdentifiers.DsaWithSha224); + algorithms.Add("SHA256WITHDSA", NistObjectIdentifiers.DsaWithSha256); + algorithms.Add("SHA384WITHDSA", NistObjectIdentifiers.DsaWithSha384); + algorithms.Add("SHA512WITHDSA", NistObjectIdentifiers.DsaWithSha512); + algorithms.Add("SHA1WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha1); + algorithms.Add("ECDSAWITHSHA1", X9ObjectIdentifiers.ECDsaWithSha1); + algorithms.Add("SHA224WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha224); + algorithms.Add("SHA256WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha256); + algorithms.Add("SHA384WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha384); + algorithms.Add("SHA512WITHECDSA", X9ObjectIdentifiers.ECDsaWithSha512); + algorithms.Add("GOST3411WITHGOST3410", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94); + algorithms.Add("GOST3411WITHGOST3410-94", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94); + algorithms.Add("GOST3411WITHECGOST3410", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001); + algorithms.Add("GOST3411WITHECGOST3410-2001", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001); + algorithms.Add("GOST3411WITHGOST3410-2001", CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001); + + // + // According to RFC 3279, the ASN.1 encoding SHALL (id-dsa-with-sha1) or MUST (ecdsa-with-SHA*) omit the parameters field. + // The parameters field SHALL be NULL for RSA based signature algorithms. + // + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha1); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha224); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha256); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha384); + noParams.Add(X9ObjectIdentifiers.ECDsaWithSha512); + noParams.Add(X9ObjectIdentifiers.IdDsaWithSha1); + noParams.Add(NistObjectIdentifiers.DsaWithSha224); + noParams.Add(NistObjectIdentifiers.DsaWithSha256); + noParams.Add(NistObjectIdentifiers.DsaWithSha384); + noParams.Add(NistObjectIdentifiers.DsaWithSha512); + + // + // RFC 4491 + // + noParams.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94); + noParams.Add(CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001); + + // + // explicit params + // + AlgorithmIdentifier sha1AlgId = new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1, DerNull.Instance); + exParams.Add("SHA1WITHRSAANDMGF1", CreatePssParams(sha1AlgId, 20)); + + AlgorithmIdentifier sha224AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha224, DerNull.Instance); + exParams.Add("SHA224WITHRSAANDMGF1", CreatePssParams(sha224AlgId, 28)); + + AlgorithmIdentifier sha256AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha256, DerNull.Instance); + exParams.Add("SHA256WITHRSAANDMGF1", CreatePssParams(sha256AlgId, 32)); + + AlgorithmIdentifier sha384AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha384, DerNull.Instance); + exParams.Add("SHA384WITHRSAANDMGF1", CreatePssParams(sha384AlgId, 48)); + + AlgorithmIdentifier sha512AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha512, DerNull.Instance); + exParams.Add("SHA512WITHRSAANDMGF1", CreatePssParams(sha512AlgId, 64)); + } + + private static RsassaPssParameters CreatePssParams( + AlgorithmIdentifier hashAlgId, + int saltSize) + { + return new RsassaPssParameters( + hashAlgId, + new AlgorithmIdentifier(PkcsObjectIdentifiers.IdMgf1, hashAlgId), + new DerInteger(saltSize), + new DerInteger(1)); + } + + internal static DerObjectIdentifier GetAlgorithmOid( + string algorithmName) + { + algorithmName = algorithmName.ToUpperInvariant(); + + if (algorithms.Contains(algorithmName)) + { + return (DerObjectIdentifier) algorithms[algorithmName]; + } + + return new DerObjectIdentifier(algorithmName); + } + + internal static AlgorithmIdentifier GetSigAlgID( + DerObjectIdentifier sigOid, + string algorithmName) + { + if (noParams.Contains(sigOid)) + { + return new AlgorithmIdentifier(sigOid); + } + + algorithmName = algorithmName.ToUpperInvariant(); + + if (exParams.Contains(algorithmName)) + { + return new AlgorithmIdentifier(sigOid, (Asn1Encodable) exParams[algorithmName]); + } + + return new AlgorithmIdentifier(sigOid, DerNull.Instance); + } + + internal static IEnumerable GetAlgNames() + { + return new EnumerableProxy(algorithms.Keys); + } + + internal static byte[] GetSignatureForObject( + DerObjectIdentifier sigOid, // TODO Redundant now? + string sigName, + AsymmetricKeyParameter privateKey, + SecureRandom random, + Asn1Encodable ae) + { + if (sigOid == null) + throw new ArgumentNullException("sigOid"); + + ISigner sig = SignerUtilities.GetSigner(sigName); + + if (random != null) + { + sig.Init(true, new ParametersWithRandom(privateKey, random)); + } + else + { + sig.Init(true, privateKey); + } + + byte[] encoded = ae.GetDerEncoded(); + sig.BlockUpdate(encoded, 0, encoded.Length); + + return sig.GenerateSignature(); + } + } +} diff --git a/Crypto/src/x509/X509V1CertificateGenerator.cs b/Crypto/src/x509/X509V1CertificateGenerator.cs new file mode 100644
index 000000000..02b58a198 --- /dev/null +++ b/Crypto/src/x509/X509V1CertificateGenerator.cs
@@ -0,0 +1,205 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; + +namespace Org.BouncyCastle.X509 +{ + /// <summary> + /// Class to Generate X509V1 Certificates. + /// </summary> + public class X509V1CertificateGenerator + { + private V1TbsCertificateGenerator tbsGen; + private DerObjectIdentifier sigOID; + private AlgorithmIdentifier sigAlgId; + private string signatureAlgorithm; + + /// <summary> + /// Default Constructor. + /// </summary> + public X509V1CertificateGenerator() + { + tbsGen = new V1TbsCertificateGenerator(); + } + + /// <summary> + /// Reset the generator. + /// </summary> + public void Reset() + { + tbsGen = new V1TbsCertificateGenerator(); + } + + /// <summary> + /// Set the certificate's serial number. + /// </summary> + /// <remarks>Make serial numbers long, if you have no serial number policy make sure the number is at least 16 bytes of secure random data. + /// You will be surprised how ugly a serial number collision can get.</remarks> + /// <param name="serialNumber">The serial number.</param> + public void SetSerialNumber( + BigInteger serialNumber) + { + if (serialNumber.SignValue <= 0) + { + throw new ArgumentException("serial number must be a positive integer", "serialNumber"); + } + + tbsGen.SetSerialNumber(new DerInteger(serialNumber)); + } + + /// <summary> + /// Set the issuer distinguished name. + /// The issuer is the entity whose private key is used to sign the certificate. + /// </summary> + /// <param name="issuer">The issuers DN.</param> + public void SetIssuerDN( + X509Name issuer) + { + tbsGen.SetIssuer(issuer); + } + + /// <summary> + /// Set the date that this certificate is to be valid from. + /// </summary> + /// <param name="date"/> + public void SetNotBefore( + DateTime date) + { + tbsGen.SetStartDate(new Time(date)); + } + + /// <summary> + /// Set the date after which this certificate will no longer be valid. + /// </summary> + /// <param name="date"/> + public void SetNotAfter( + DateTime date) + { + tbsGen.SetEndDate(new Time(date)); + } + + /// <summary> + /// Set the subject distinguished name. + /// The subject describes the entity associated with the public key. + /// </summary> + /// <param name="subject"/> + public void SetSubjectDN( + X509Name subject) + { + tbsGen.SetSubject(subject); + } + + /// <summary> + /// Set the public key that this certificate identifies. + /// </summary> + /// <param name="publicKey"/> + public void SetPublicKey( + AsymmetricKeyParameter publicKey) + { + try + { + tbsGen.SetSubjectPublicKeyInfo( + SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(publicKey)); + } + catch (Exception e) + { + throw new ArgumentException("unable to process key - " + e.ToString()); + } + } + + /// <summary> + /// Set the signature algorithm that will be used to sign this certificate. + /// This can be either a name or an OID, names are treated as case insensitive. + /// </summary> + /// <param name="signatureAlgorithm">string representation of the algorithm name</param> + public void SetSignatureAlgorithm( + string signatureAlgorithm) + { + this.signatureAlgorithm = signatureAlgorithm; + + try + { + sigOID = X509Utilities.GetAlgorithmOid(signatureAlgorithm); + } + catch (Exception) + { + throw new ArgumentException("Unknown signature type requested", "signatureAlgorithm"); + } + + sigAlgId = X509Utilities.GetSigAlgID(sigOID, signatureAlgorithm); + + tbsGen.SetSignature(sigAlgId); + } + + /// <summary> + /// Generate a new X509Certificate. + /// </summary> + /// <param name="privateKey">The private key of the issuer used to sign this certificate.</param> + /// <returns>An X509Certificate.</returns> + public X509Certificate Generate( + AsymmetricKeyParameter privateKey) + { + return Generate(privateKey, null); + } + + /// <summary> + /// Generate a new X509Certificate specifying a SecureRandom instance that you would like to use. + /// </summary> + /// <param name="privateKey">The private key of the issuer used to sign this certificate.</param> + /// <param name="random">The Secure Random you want to use.</param> + /// <returns>An X509Certificate.</returns> + public X509Certificate Generate( + AsymmetricKeyParameter privateKey, + SecureRandom random) + { + TbsCertificateStructure tbsCert = tbsGen.GenerateTbsCertificate(); + byte[] signature; + + try + { + signature = X509Utilities.GetSignatureForObject( + sigOID, signatureAlgorithm, privateKey, random, tbsCert); + } + catch (Exception e) + { + // TODO +// throw new ExtCertificateEncodingException("exception encoding TBS cert", e); + throw new CertificateEncodingException("exception encoding TBS cert", e); + } + + try + { + return GenerateJcaObject(tbsCert, signature); + } + catch (CertificateParsingException e) + { + // TODO + // throw new ExtCertificateEncodingException("exception producing certificate object", e); + throw new CertificateEncodingException("exception producing certificate object", e); + } + } + + private X509Certificate GenerateJcaObject( + TbsCertificateStructure tbsCert, + byte[] signature) + { + return new X509Certificate( + new X509CertificateStructure(tbsCert, sigAlgId, new DerBitString(signature))); + } + + /// <summary> + /// Allows enumeration of the signature names supported by the generator. + /// </summary> + public IEnumerable SignatureAlgNames + { + get { return X509Utilities.GetAlgNames(); } + } + } +} diff --git a/Crypto/src/x509/X509V2AttributeCertificate.cs b/Crypto/src/x509/X509V2AttributeCertificate.cs new file mode 100644
index 000000000..117ac4cc2 --- /dev/null +++ b/Crypto/src/x509/X509V2AttributeCertificate.cs
@@ -0,0 +1,255 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.X509 +{ + /// <summary>An implementation of a version 2 X.509 Attribute Certificate.</summary> + public class X509V2AttributeCertificate + : X509ExtensionBase, IX509AttributeCertificate + { + private readonly AttributeCertificate cert; + private readonly DateTime notBefore; + private readonly DateTime notAfter; + + private static AttributeCertificate GetObject(Stream input) + { + try + { + return AttributeCertificate.GetInstance(Asn1Object.FromStream(input)); + } + catch (IOException e) + { + throw e; + } + catch (Exception e) + { + throw new IOException("exception decoding certificate structure", e); + } + } + + public X509V2AttributeCertificate( + Stream encIn) + : this(GetObject(encIn)) + { + } + + public X509V2AttributeCertificate( + byte[] encoded) + : this(new MemoryStream(encoded, false)) + { + } + + internal X509V2AttributeCertificate( + AttributeCertificate cert) + { + this.cert = cert; + + try + { + this.notAfter = cert.ACInfo.AttrCertValidityPeriod.NotAfterTime.ToDateTime(); + this.notBefore = cert.ACInfo.AttrCertValidityPeriod.NotBeforeTime.ToDateTime(); + } + catch (Exception e) + { + throw new IOException("invalid data structure in certificate!", e); + } + } + + public virtual int Version + { + get { return cert.ACInfo.Version.Value.IntValue + 1; } + } + + public virtual BigInteger SerialNumber + { + get { return cert.ACInfo.SerialNumber.Value; } + } + + public virtual AttributeCertificateHolder Holder + { + get + { + return new AttributeCertificateHolder((Asn1Sequence)cert.ACInfo.Holder.ToAsn1Object()); + } + } + + public virtual AttributeCertificateIssuer Issuer + { + get + { + return new AttributeCertificateIssuer(cert.ACInfo.Issuer); + } + } + + public virtual DateTime NotBefore + { + get { return notBefore; } + } + + public virtual DateTime NotAfter + { + get { return notAfter; } + } + + public virtual bool[] GetIssuerUniqueID() + { + DerBitString id = cert.ACInfo.IssuerUniqueID; + + if (id != null) + { + byte[] bytes = id.GetBytes(); + bool[] boolId = new bool[bytes.Length * 8 - id.PadBits]; + + for (int i = 0; i != boolId.Length; i++) + { + //boolId[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0; + boolId[i] = (bytes[i / 8] & (0x80 >> (i % 8))) != 0; + } + + return boolId; + } + + return null; + } + + public virtual bool IsValidNow + { + get { return IsValid(DateTime.UtcNow); } + } + + public virtual bool IsValid( + DateTime date) + { + return date.CompareTo(NotBefore) >= 0 && date.CompareTo(NotAfter) <= 0; + } + + public virtual void CheckValidity() + { + this.CheckValidity(DateTime.UtcNow); + } + + public virtual void CheckValidity( + DateTime date) + { + if (date.CompareTo(NotAfter) > 0) + throw new CertificateExpiredException("certificate expired on " + NotAfter); + if (date.CompareTo(NotBefore) < 0) + throw new CertificateNotYetValidException("certificate not valid until " + NotBefore); + } + + public virtual byte[] GetSignature() + { + return cert.SignatureValue.GetBytes(); + } + + public virtual void Verify( + AsymmetricKeyParameter publicKey) + { + if (!cert.SignatureAlgorithm.Equals(cert.ACInfo.Signature)) + { + throw new CertificateException("Signature algorithm in certificate info not same as outer certificate"); + } + + ISigner signature = SignerUtilities.GetSigner(cert.SignatureAlgorithm.ObjectID.Id); + + signature.Init(false, publicKey); + + try + { + byte[] b = cert.ACInfo.GetEncoded(); + signature.BlockUpdate(b, 0, b.Length); + } + catch (IOException e) + { + throw new SignatureException("Exception encoding certificate info object", e); + } + + if (!signature.VerifySignature(this.GetSignature())) + { + throw new InvalidKeyException("Public key presented not for certificate signature"); + } + } + + public virtual byte[] GetEncoded() + { + return cert.GetEncoded(); + } + + protected override X509Extensions GetX509Extensions() + { + return cert.ACInfo.Extensions; + } + + public virtual X509Attribute[] GetAttributes() + { + Asn1Sequence seq = cert.ACInfo.Attributes; + X509Attribute[] attrs = new X509Attribute[seq.Count]; + + for (int i = 0; i != seq.Count; i++) + { + attrs[i] = new X509Attribute((Asn1Encodable)seq[i]); + } + + return attrs; + } + + public virtual X509Attribute[] GetAttributes( + string oid) + { + Asn1Sequence seq = cert.ACInfo.Attributes; + IList list = Platform.CreateArrayList(); + + for (int i = 0; i != seq.Count; i++) + { + X509Attribute attr = new X509Attribute((Asn1Encodable)seq[i]); + if (attr.Oid.Equals(oid)) + { + list.Add(attr); + } + } + + if (list.Count < 1) + { + return null; + } + + X509Attribute[] result = new X509Attribute[list.Count]; + for (int i = 0; i < list.Count; ++i) + { + result[i] = (X509Attribute)list[i]; + } + return result; + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + X509V2AttributeCertificate other = obj as X509V2AttributeCertificate; + + if (other == null) + return false; + + return cert.Equals(other.cert); + + // NB: May prefer this implementation of Equals if more than one certificate implementation in play + //return Arrays.AreEqual(this.GetEncoded(), other.GetEncoded()); + } + + public override int GetHashCode() + { + return cert.GetHashCode(); + } + } +} diff --git a/Crypto/src/x509/X509V2AttributeCertificateGenerator.cs b/Crypto/src/x509/X509V2AttributeCertificateGenerator.cs new file mode 100644
index 000000000..a683d5e20 --- /dev/null +++ b/Crypto/src/x509/X509V2AttributeCertificateGenerator.cs
@@ -0,0 +1,180 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.X509 +{ + /// <remarks>Class to produce an X.509 Version 2 AttributeCertificate.</remarks> + public class X509V2AttributeCertificateGenerator + { + private readonly X509ExtensionsGenerator extGenerator = new X509ExtensionsGenerator(); + + private V2AttributeCertificateInfoGenerator acInfoGen; + private DerObjectIdentifier sigOID; + private AlgorithmIdentifier sigAlgId; + private string signatureAlgorithm; + + public X509V2AttributeCertificateGenerator() + { + acInfoGen = new V2AttributeCertificateInfoGenerator(); + } + + /// <summary>Reset the generator</summary> + public void Reset() + { + acInfoGen = new V2AttributeCertificateInfoGenerator(); + extGenerator.Reset(); + } + + /// <summary>Set the Holder of this Attribute Certificate.</summary> + public void SetHolder( + AttributeCertificateHolder holder) + { + acInfoGen.SetHolder(holder.holder); + } + + /// <summary>Set the issuer.</summary> + public void SetIssuer( + AttributeCertificateIssuer issuer) + { + acInfoGen.SetIssuer(AttCertIssuer.GetInstance(issuer.form)); + } + + /// <summary>Set the serial number for the certificate.</summary> + public void SetSerialNumber( + BigInteger serialNumber) + { + acInfoGen.SetSerialNumber(new DerInteger(serialNumber)); + } + + public void SetNotBefore( + DateTime date) + { + acInfoGen.SetStartDate(new DerGeneralizedTime(date)); + } + + public void SetNotAfter( + DateTime date) + { + acInfoGen.SetEndDate(new DerGeneralizedTime(date)); + } + + /// <summary> + /// Set the signature algorithm. This can be either a name or an OID, names + /// are treated as case insensitive. + /// </summary> + /// <param name="signatureAlgorithm">The algorithm name.</param> + public void SetSignatureAlgorithm( + string signatureAlgorithm) + { + this.signatureAlgorithm = signatureAlgorithm; + + try + { + sigOID = X509Utilities.GetAlgorithmOid(signatureAlgorithm); + } + catch (Exception) + { + throw new ArgumentException("Unknown signature type requested"); + } + + sigAlgId = X509Utilities.GetSigAlgID(sigOID, signatureAlgorithm); + + acInfoGen.SetSignature(sigAlgId); + } + + /// <summary>Add an attribute.</summary> + public void AddAttribute( + X509Attribute attribute) + { + acInfoGen.AddAttribute(AttributeX509.GetInstance(attribute.ToAsn1Object())); + } + + public void SetIssuerUniqueId( + bool[] iui) + { + // TODO convert bool array to bit string + //acInfoGen.SetIssuerUniqueID(iui); + throw Platform.CreateNotImplementedException("SetIssuerUniqueId()"); + } + + /// <summary>Add a given extension field for the standard extensions tag.</summary> + public void AddExtension( + string oid, + bool critical, + Asn1Encodable extensionValue) + { + extGenerator.AddExtension(new DerObjectIdentifier(oid), critical, extensionValue); + } + + /// <summary> + /// Add a given extension field for the standard extensions tag. + /// The value parameter becomes the contents of the octet string associated + /// with the extension. + /// </summary> + public void AddExtension( + string oid, + bool critical, + byte[] extensionValue) + { + extGenerator.AddExtension(new DerObjectIdentifier(oid), critical, extensionValue); + } + + /// <summary> + /// Generate an X509 certificate, based on the current issuer and subject. + /// </summary> + public IX509AttributeCertificate Generate( + AsymmetricKeyParameter publicKey) + { + return Generate(publicKey, null); + } + + /// <summary> + /// Generate an X509 certificate, based on the current issuer and subject, + /// using the supplied source of randomness, if required. + /// </summary> + public IX509AttributeCertificate Generate( + AsymmetricKeyParameter publicKey, + SecureRandom random) + { + if (!extGenerator.IsEmpty) + { + acInfoGen.SetExtensions(extGenerator.Generate()); + } + + AttributeCertificateInfo acInfo = acInfoGen.GenerateAttributeCertificateInfo(); + + Asn1EncodableVector v = new Asn1EncodableVector(); + + v.Add(acInfo, sigAlgId); + + try + { + v.Add(new DerBitString(X509Utilities.GetSignatureForObject(sigOID, signatureAlgorithm, publicKey, random, acInfo))); + + return new X509V2AttributeCertificate(AttributeCertificate.GetInstance(new DerSequence(v))); + } + catch (Exception e) + { + // TODO +// throw new ExtCertificateEncodingException("constructed invalid certificate", e); + throw new CertificateEncodingException("constructed invalid certificate", e); + } + } + + /// <summary> + /// Allows enumeration of the signature names supported by the generator. + /// </summary> + public IEnumerable SignatureAlgNames + { + get { return X509Utilities.GetAlgNames(); } + } + } +} diff --git a/Crypto/src/x509/X509V2CRLGenerator.cs b/Crypto/src/x509/X509V2CRLGenerator.cs new file mode 100644
index 000000000..a2293b333 --- /dev/null +++ b/Crypto/src/x509/X509V2CRLGenerator.cs
@@ -0,0 +1,261 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.X509 +{ + /** + * class to produce an X.509 Version 2 CRL. + */ + public class X509V2CrlGenerator + { + private readonly X509ExtensionsGenerator extGenerator = new X509ExtensionsGenerator(); + + private V2TbsCertListGenerator tbsGen; + private DerObjectIdentifier sigOID; + private AlgorithmIdentifier sigAlgId; + private string signatureAlgorithm; + + public X509V2CrlGenerator() + { + tbsGen = new V2TbsCertListGenerator(); + } + + /** + * reset the generator + */ + public void Reset() + { + tbsGen = new V2TbsCertListGenerator(); + extGenerator.Reset(); + } + + /** + * Set the issuer distinguished name - the issuer is the entity whose private key is used to sign the + * certificate. + */ + public void SetIssuerDN( + X509Name issuer) + { + tbsGen.SetIssuer(issuer); + } + + public void SetThisUpdate( + DateTime date) + { + tbsGen.SetThisUpdate(new Time(date)); + } + + public void SetNextUpdate( + DateTime date) + { + tbsGen.SetNextUpdate(new Time(date)); + } + + /** + * Reason being as indicated by CrlReason, i.e. CrlReason.KeyCompromise + * or 0 if CrlReason is not to be used + **/ + public void AddCrlEntry( + BigInteger userCertificate, + DateTime revocationDate, + int reason) + { + tbsGen.AddCrlEntry(new DerInteger(userCertificate), new Time(revocationDate), reason); + } + + /** + * Add a CRL entry with an Invalidity Date extension as well as a CrlReason extension. + * Reason being as indicated by CrlReason, i.e. CrlReason.KeyCompromise + * or 0 if CrlReason is not to be used + **/ + public void AddCrlEntry( + BigInteger userCertificate, + DateTime revocationDate, + int reason, + DateTime invalidityDate) + { + tbsGen.AddCrlEntry(new DerInteger(userCertificate), new Time(revocationDate), reason, new DerGeneralizedTime(invalidityDate)); + } + + /** + * Add a CRL entry with extensions. + **/ + public void AddCrlEntry( + BigInteger userCertificate, + DateTime revocationDate, + X509Extensions extensions) + { + tbsGen.AddCrlEntry(new DerInteger(userCertificate), new Time(revocationDate), extensions); + } + + /** + * Add the CRLEntry objects contained in a previous CRL. + * + * @param other the X509Crl to source the other entries from. + */ + public void AddCrl( + X509Crl other) + { + if (other == null) + throw new ArgumentNullException("other"); + + ISet revocations = other.GetRevokedCertificates(); + + if (revocations != null) + { + foreach (X509CrlEntry entry in revocations) + { + try + { + tbsGen.AddCrlEntry( + Asn1Sequence.GetInstance( + Asn1Object.FromByteArray(entry.GetEncoded()))); + } + catch (IOException e) + { + throw new CrlException("exception processing encoding of CRL", e); + } + } + } + } + + /** + * Set the signature algorithm. This can be either a name or an oid, names + * are treated as case insensitive. + * + * @param signatureAlgorithm string representation of the algorithm name. + */ + public void SetSignatureAlgorithm( + string signatureAlgorithm) + { + this.signatureAlgorithm = signatureAlgorithm; + + try + { + sigOID = X509Utilities.GetAlgorithmOid(signatureAlgorithm); + } + catch (Exception e) + { + throw new ArgumentException("Unknown signature type requested", e); + } + + sigAlgId = X509Utilities.GetSigAlgID(sigOID, signatureAlgorithm); + + tbsGen.SetSignature(sigAlgId); + } + + /** + * add a given extension field for the standard extensions tag (tag 0) + */ + public void AddExtension( + string oid, + bool critical, + Asn1Encodable extensionValue) + { + extGenerator.AddExtension(new DerObjectIdentifier(oid), critical, extensionValue); + } + + /** + * add a given extension field for the standard extensions tag (tag 0) + */ + public void AddExtension( + DerObjectIdentifier oid, + bool critical, + Asn1Encodable extensionValue) + { + extGenerator.AddExtension(oid, critical, extensionValue); + } + + /** + * add a given extension field for the standard extensions tag (tag 0) + */ + public void AddExtension( + string oid, + bool critical, + byte[] extensionValue) + { + extGenerator.AddExtension(new DerObjectIdentifier(oid), critical, new DerOctetString(extensionValue)); + } + + /** + * add a given extension field for the standard extensions tag (tag 0) + */ + public void AddExtension( + DerObjectIdentifier oid, + bool critical, + byte[] extensionValue) + { + extGenerator.AddExtension(oid, critical, new DerOctetString(extensionValue)); + } + + /// <summary>Generate an X509 CRL, based on the current issuer and subject.</summary> + /// <param name="privateKey">The key used for signing.</param> + public X509Crl Generate( + AsymmetricKeyParameter privateKey) + { + return Generate(privateKey, null); + } + + /// <summary>Generate an X509 CRL, based on the current issuer and subject.</summary> + /// <param name="privateKey">The key used for signing.</param> + /// <param name="random">A user-defined source of randomness.</param> + public X509Crl Generate( + AsymmetricKeyParameter privateKey, + SecureRandom random) + { + TbsCertificateList tbsCrl = GenerateCertList(); + byte[] signature; + + try + { + signature = X509Utilities.GetSignatureForObject( + sigOID, signatureAlgorithm, privateKey, random, tbsCrl); + } + catch (IOException e) + { + // TODO +// throw new ExtCrlException("cannot generate CRL encoding", e); + throw new CrlException("cannot generate CRL encoding", e); + } + + return GenerateJcaObject(tbsCrl, signature); + } + + private TbsCertificateList GenerateCertList() + { + if (!extGenerator.IsEmpty) + { + tbsGen.SetExtensions(extGenerator.Generate()); + } + + return tbsGen.GenerateTbsCertList(); + } + + private X509Crl GenerateJcaObject( + TbsCertificateList tbsCrl, + byte[] signature) + { + return new X509Crl( + CertificateList.GetInstance( + new DerSequence(tbsCrl, sigAlgId, new DerBitString(signature)))); + } + + /// <summary> + /// Allows enumeration of the signature names supported by the generator. + /// </summary> + public IEnumerable SignatureAlgNames + { + get { return X509Utilities.GetAlgNames(); } + } + } +} diff --git a/Crypto/src/x509/X509V3CertificateGenerator.cs b/Crypto/src/x509/X509V3CertificateGenerator.cs new file mode 100644
index 000000000..bb0dd9cbc --- /dev/null +++ b/Crypto/src/x509/X509V3CertificateGenerator.cs
@@ -0,0 +1,346 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.X509.Extension; + +namespace Org.BouncyCastle.X509 +{ + /// <summary> + /// A class to Generate Version 3 X509Certificates. + /// </summary> + public class X509V3CertificateGenerator + { + private readonly X509ExtensionsGenerator extGenerator = new X509ExtensionsGenerator(); + + private V3TbsCertificateGenerator tbsGen; + private DerObjectIdentifier sigOid; + private AlgorithmIdentifier sigAlgId; + private string signatureAlgorithm; + + public X509V3CertificateGenerator() + { + tbsGen = new V3TbsCertificateGenerator(); + } + + /// <summary> + /// Reset the Generator. + /// </summary> + public void Reset() + { + tbsGen = new V3TbsCertificateGenerator(); + extGenerator.Reset(); + } + + /// <summary> + /// Set the certificate's serial number. + /// </summary> + /// <remarks>Make serial numbers long, if you have no serial number policy make sure the number is at least 16 bytes of secure random data. + /// You will be surprised how ugly a serial number collision can Get.</remarks> + /// <param name="serialNumber">The serial number.</param> + public void SetSerialNumber( + BigInteger serialNumber) + { + if (serialNumber.SignValue <= 0) + { + throw new ArgumentException("serial number must be a positive integer", "serialNumber"); + } + + tbsGen.SetSerialNumber(new DerInteger(serialNumber)); + } + + /// <summary> + /// Set the distinguished name of the issuer. + /// The issuer is the entity which is signing the certificate. + /// </summary> + /// <param name="issuer">The issuer's DN.</param> + public void SetIssuerDN( + X509Name issuer) + { + tbsGen.SetIssuer(issuer); + } + + /// <summary> + /// Set the date that this certificate is to be valid from. + /// </summary> + /// <param name="date"/> + public void SetNotBefore( + DateTime date) + { + tbsGen.SetStartDate(new Time(date)); + } + + /// <summary> + /// Set the date after which this certificate will no longer be valid. + /// </summary> + /// <param name="date"/> + public void SetNotAfter( + DateTime date) + { + tbsGen.SetEndDate(new Time(date)); + } + + /// <summary> + /// Set the DN of the entity that this certificate is about. + /// </summary> + /// <param name="subject"/> + public void SetSubjectDN( + X509Name subject) + { + tbsGen.SetSubject(subject); + } + + /// <summary> + /// Set the public key that this certificate identifies. + /// </summary> + /// <param name="publicKey"/> + public void SetPublicKey( + AsymmetricKeyParameter publicKey) + { + tbsGen.SetSubjectPublicKeyInfo(SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(publicKey)); + } + + /// <summary> + /// Set the signature algorithm that will be used to sign this certificate. + /// </summary> + /// <param name="signatureAlgorithm"/> + public void SetSignatureAlgorithm( + string signatureAlgorithm) + { + this.signatureAlgorithm = signatureAlgorithm; + + try + { + sigOid = X509Utilities.GetAlgorithmOid(signatureAlgorithm); + } + catch (Exception) + { + throw new ArgumentException("Unknown signature type requested: " + signatureAlgorithm); + } + + sigAlgId = X509Utilities.GetSigAlgID(sigOid, signatureAlgorithm); + + tbsGen.SetSignature(sigAlgId); + } + + /// <summary> + /// Set the subject unique ID - note: it is very rare that it is correct to do this. + /// </summary> + /// <param name="uniqueID"/> + public void SetSubjectUniqueID( + bool[] uniqueID) + { + tbsGen.SetSubjectUniqueID(booleanToBitString(uniqueID)); + } + + /// <summary> + /// Set the issuer unique ID - note: it is very rare that it is correct to do this. + /// </summary> + /// <param name="uniqueID"/> + public void SetIssuerUniqueID( + bool[] uniqueID) + { + tbsGen.SetIssuerUniqueID(booleanToBitString(uniqueID)); + } + + private DerBitString booleanToBitString( + bool[] id) + { + byte[] bytes = new byte[(id.Length + 7) / 8]; + + for (int i = 0; i != id.Length; i++) + { + if (id[i]) + { + bytes[i / 8] |= (byte)(1 << ((7 - (i % 8)))); + } + } + + int pad = id.Length % 8; + + if (pad == 0) + { + return new DerBitString(bytes); + } + + return new DerBitString(bytes, 8 - pad); + } + + /// <summary> + /// Add a given extension field for the standard extensions tag (tag 3). + /// </summary> + /// <param name="oid">string containing a dotted decimal Object Identifier.</param> + /// <param name="critical">Is it critical.</param> + /// <param name="extensionValue">The value.</param> + public void AddExtension( + string oid, + bool critical, + Asn1Encodable extensionValue) + { + extGenerator.AddExtension(new DerObjectIdentifier(oid), critical, extensionValue); + } + + /// <summary> + /// Add an extension to this certificate. + /// </summary> + /// <param name="oid">Its Object Identifier.</param> + /// <param name="critical">Is it critical.</param> + /// <param name="extensionValue">The value.</param> + public void AddExtension( + DerObjectIdentifier oid, + bool critical, + Asn1Encodable extensionValue) + { + extGenerator.AddExtension(oid, critical, extensionValue); + } + + /// <summary> + /// Add an extension using a string with a dotted decimal OID. + /// </summary> + /// <param name="oid">string containing a dotted decimal Object Identifier.</param> + /// <param name="critical">Is it critical.</param> + /// <param name="extensionValue">byte[] containing the value of this extension.</param> + public void AddExtension( + string oid, + bool critical, + byte[] extensionValue) + { + extGenerator.AddExtension(new DerObjectIdentifier(oid), critical, new DerOctetString(extensionValue)); + } + + /// <summary> + /// Add an extension to this certificate. + /// </summary> + /// <param name="oid">Its Object Identifier.</param> + /// <param name="critical">Is it critical.</param> + /// <param name="extensionValue">byte[] containing the value of this extension.</param> + public void AddExtension( + DerObjectIdentifier oid, + bool critical, + byte[] extensionValue) + { + extGenerator.AddExtension(oid, critical, new DerOctetString(extensionValue)); + } + + /// <summary> + /// Add a given extension field for the standard extensions tag (tag 3), + /// copying the extension value from another certificate. + /// </summary> + public void CopyAndAddExtension( + string oid, + bool critical, + X509Certificate cert) + { + CopyAndAddExtension(new DerObjectIdentifier(oid), critical, cert); + } + + /** + * add a given extension field for the standard extensions tag (tag 3) + * copying the extension value from another certificate. + * @throws CertificateParsingException if the extension cannot be extracted. + */ + public void CopyAndAddExtension( + DerObjectIdentifier oid, + bool critical, + X509Certificate cert) + { + Asn1OctetString extValue = cert.GetExtensionValue(oid); + + if (extValue == null) + { + throw new CertificateParsingException("extension " + oid + " not present"); + } + + try + { + Asn1Encodable value = X509ExtensionUtilities.FromExtensionValue(extValue); + + this.AddExtension(oid, critical, value); + } + catch (Exception e) + { + throw new CertificateParsingException(e.Message, e); + } + } + + /// <summary> + /// Generate an X509Certificate. + /// </summary> + /// <param name="privateKey">The private key of the issuer that is signing this certificate.</param> + /// <returns>An X509Certificate.</returns> + public X509Certificate Generate( + AsymmetricKeyParameter privateKey) + { + return Generate(privateKey, null); + } + + /// <summary> + /// Generate an X509Certificate using your own SecureRandom. + /// </summary> + /// <param name="privateKey">The private key of the issuer that is signing this certificate.</param> + /// <param name="random">You Secure Random instance.</param> + /// <returns>An X509Certificate.</returns> + public X509Certificate Generate( + AsymmetricKeyParameter privateKey, + SecureRandom random) + { + TbsCertificateStructure tbsCert = GenerateTbsCert(); + byte[] signature; + + try + { + signature = X509Utilities.GetSignatureForObject( + sigOid, signatureAlgorithm, privateKey, random, tbsCert); + } + catch (Exception e) + { + // TODO +// throw new ExtCertificateEncodingException("exception encoding TBS cert", e); + throw new CertificateEncodingException("exception encoding TBS cert", e); + } + + try + { + return GenerateJcaObject(tbsCert, signature); + } + catch (CertificateParsingException e) + { + // TODO + // throw new ExtCertificateEncodingException("exception producing certificate object", e); + throw new CertificateEncodingException("exception producing certificate object", e); + } + } + + private TbsCertificateStructure GenerateTbsCert() + { + if (!extGenerator.IsEmpty) + { + tbsGen.SetExtensions(extGenerator.Generate()); + } + + return tbsGen.GenerateTbsCertificate(); + } + + private X509Certificate GenerateJcaObject( + TbsCertificateStructure tbsCert, + byte[] signature) + { + return new X509Certificate( + new X509CertificateStructure(tbsCert, sigAlgId, new DerBitString(signature))); + } + + /// <summary> + /// Allows enumeration of the signature names supported by the generator. + /// </summary> + public IEnumerable SignatureAlgNames + { + get { return X509Utilities.GetAlgNames(); } + } + } +} diff --git a/Crypto/src/x509/extension/AuthorityKeyIdentifierStructure.cs b/Crypto/src/x509/extension/AuthorityKeyIdentifierStructure.cs new file mode 100644
index 000000000..006dc009b --- /dev/null +++ b/Crypto/src/x509/extension/AuthorityKeyIdentifierStructure.cs
@@ -0,0 +1,102 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Security.Certificates; + +namespace Org.BouncyCastle.X509.Extension +{ + /// <remarks>A high level authority key identifier.</remarks> + public class AuthorityKeyIdentifierStructure + : AuthorityKeyIdentifier + { + /** + * Constructor which will take the byte[] returned from getExtensionValue() + * + * @param encodedValue a DER octet encoded string with the extension structure in it. + * @throws IOException on parsing errors. + */ + // TODO Add a functional constructor from byte[]? + public AuthorityKeyIdentifierStructure( + Asn1OctetString encodedValue) + : base((Asn1Sequence) X509ExtensionUtilities.FromExtensionValue(encodedValue)) + { + } + + private static Asn1Sequence FromCertificate( + X509Certificate certificate) + { + try + { + GeneralName genName = new GeneralName( + PrincipalUtilities.GetIssuerX509Principal(certificate)); + + if (certificate.Version == 3) + { + Asn1OctetString ext = certificate.GetExtensionValue(X509Extensions.SubjectKeyIdentifier); + + if (ext != null) + { + Asn1OctetString str = (Asn1OctetString) X509ExtensionUtilities.FromExtensionValue(ext); + + return (Asn1Sequence) new AuthorityKeyIdentifier( + str.GetOctets(), new GeneralNames(genName), certificate.SerialNumber).ToAsn1Object(); + } + } + + SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo( + certificate.GetPublicKey()); + + return (Asn1Sequence) new AuthorityKeyIdentifier( + info, new GeneralNames(genName), certificate.SerialNumber).ToAsn1Object(); + } + catch (Exception e) + { + throw new CertificateParsingException("Exception extracting certificate details", e); + } + } + + private static Asn1Sequence FromKey( + AsymmetricKeyParameter pubKey) + { + try + { + SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pubKey); + + return (Asn1Sequence) new AuthorityKeyIdentifier(info).ToAsn1Object(); + } + catch (Exception e) + { + throw new InvalidKeyException("can't process key: " + e); + } + } + + /** + * Create an AuthorityKeyIdentifier using the passed in certificate's public + * key, issuer and serial number. + * + * @param certificate the certificate providing the information. + * @throws CertificateParsingException if there is a problem processing the certificate + */ + public AuthorityKeyIdentifierStructure( + X509Certificate certificate) + : base(FromCertificate(certificate)) + { + } + + /** + * Create an AuthorityKeyIdentifier using just the hash of the + * public key. + * + * @param pubKey the key to generate the hash from. + * @throws InvalidKeyException if there is a problem using the key. + */ + public AuthorityKeyIdentifierStructure( + AsymmetricKeyParameter pubKey) + : base(FromKey(pubKey)) + { + } + } +} diff --git a/Crypto/src/x509/extension/SubjectKeyIdentifierStructure.cs b/Crypto/src/x509/extension/SubjectKeyIdentifierStructure.cs new file mode 100644
index 000000000..4c7b79ab8 --- /dev/null +++ b/Crypto/src/x509/extension/SubjectKeyIdentifierStructure.cs
@@ -0,0 +1,49 @@ +using System; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security.Certificates; + +namespace Org.BouncyCastle.X509.Extension +{ + /** + * A high level subject key identifier. + */ + public class SubjectKeyIdentifierStructure + : SubjectKeyIdentifier + { + /** + * Constructor which will take the byte[] returned from getExtensionValue() + * + * @param encodedValue a DER octet encoded string with the extension structure in it. + * @throws IOException on parsing errors. + */ + public SubjectKeyIdentifierStructure( + Asn1OctetString encodedValue) + : base((Asn1OctetString) X509ExtensionUtilities.FromExtensionValue(encodedValue)) + { + } + + private static Asn1OctetString FromPublicKey( + AsymmetricKeyParameter pubKey) + { + try + { + SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pubKey); + + return (Asn1OctetString) new SubjectKeyIdentifier(info).ToAsn1Object(); + } + catch (Exception e) + { + throw new CertificateParsingException("Exception extracting certificate details: " + e.ToString()); + } + } + + public SubjectKeyIdentifierStructure( + AsymmetricKeyParameter pubKey) + : base(FromPublicKey(pubKey)) + { + } + } +} diff --git a/Crypto/src/x509/extension/X509ExtensionUtil.cs b/Crypto/src/x509/extension/X509ExtensionUtil.cs new file mode 100644
index 000000000..845a87bad --- /dev/null +++ b/Crypto/src/x509/extension/X509ExtensionUtil.cs
@@ -0,0 +1,89 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Security.Certificates; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.X509.Extension +{ + public class X509ExtensionUtilities + { + public static Asn1Object FromExtensionValue( + Asn1OctetString extensionValue) + { + return Asn1Object.FromByteArray(extensionValue.GetOctets()); + } + + public static ICollection GetIssuerAlternativeNames( + X509Certificate cert) + { + Asn1OctetString extVal = cert.GetExtensionValue(X509Extensions.IssuerAlternativeName); + + return GetAlternativeName(extVal); + } + + public static ICollection GetSubjectAlternativeNames( + X509Certificate cert) + { + Asn1OctetString extVal = cert.GetExtensionValue(X509Extensions.SubjectAlternativeName); + + return GetAlternativeName(extVal); + } + + private static ICollection GetAlternativeName( + Asn1OctetString extVal) + { + IList temp = Platform.CreateArrayList(); + + if (extVal != null) + { + try + { + Asn1Sequence seq = DerSequence.GetInstance(FromExtensionValue(extVal)); + + foreach (GeneralName genName in seq) + { + IList list = Platform.CreateArrayList(); + list.Add(genName.TagNo); + + switch (genName.TagNo) + { + case GeneralName.EdiPartyName: + case GeneralName.X400Address: + case GeneralName.OtherName: + list.Add(genName.Name.ToAsn1Object()); + break; + case GeneralName.DirectoryName: + list.Add(X509Name.GetInstance(genName.Name).ToString()); + break; + case GeneralName.DnsName: + case GeneralName.Rfc822Name: + case GeneralName.UniformResourceIdentifier: + list.Add(((IAsn1String)genName.Name).GetString()); + break; + case GeneralName.RegisteredID: + list.Add(DerObjectIdentifier.GetInstance(genName.Name).Id); + break; + case GeneralName.IPAddress: + list.Add(DerOctetString.GetInstance(genName.Name).GetOctets()); + break; + default: + throw new IOException("Bad tag number: " + genName.TagNo); + } + + temp.Add(list); + } + } + catch (Exception e) + { + throw new CertificateParsingException(e.Message); + } + } + + return temp; + } + } +} diff --git a/Crypto/src/x509/store/IX509Selector.cs b/Crypto/src/x509/store/IX509Selector.cs new file mode 100644
index 000000000..3f37fcd66 --- /dev/null +++ b/Crypto/src/x509/store/IX509Selector.cs
@@ -0,0 +1,15 @@ +using System; + +namespace Org.BouncyCastle.X509.Store +{ + public interface IX509Selector +#if !(SILVERLIGHT || PORTABLE) + : ICloneable +#endif + { +#if SILVERLIGHT || PORTABLE + object Clone(); +#endif + bool Match(object obj); + } +} diff --git a/Crypto/src/x509/store/IX509Store.cs b/Crypto/src/x509/store/IX509Store.cs new file mode 100644
index 000000000..e5c3a462a --- /dev/null +++ b/Crypto/src/x509/store/IX509Store.cs
@@ -0,0 +1,11 @@ +using System; +using System.Collections; + +namespace Org.BouncyCastle.X509.Store +{ + public interface IX509Store + { +// void Init(IX509StoreParameters parameters); + ICollection GetMatches(IX509Selector selector); + } +} diff --git a/Crypto/src/x509/store/IX509StoreParameters.cs b/Crypto/src/x509/store/IX509StoreParameters.cs new file mode 100644
index 000000000..aee3036c2 --- /dev/null +++ b/Crypto/src/x509/store/IX509StoreParameters.cs
@@ -0,0 +1,8 @@ +using System; + +namespace Org.BouncyCastle.X509.Store +{ + public interface IX509StoreParameters + { + } +} diff --git a/Crypto/src/x509/store/NoSuchStoreException.cs b/Crypto/src/x509/store/NoSuchStoreException.cs new file mode 100644
index 000000000..3df26de79 --- /dev/null +++ b/Crypto/src/x509/store/NoSuchStoreException.cs
@@ -0,0 +1,25 @@ +using System; + +namespace Org.BouncyCastle.X509.Store +{ + public class NoSuchStoreException + : X509StoreException + { + public NoSuchStoreException() + { + } + + public NoSuchStoreException( + string message) + : base(message) + { + } + + public NoSuchStoreException( + string message, + Exception e) + : base(message, e) + { + } + } +} diff --git a/Crypto/src/x509/store/X509AttrCertStoreSelector.cs b/Crypto/src/x509/store/X509AttrCertStoreSelector.cs new file mode 100644
index 000000000..9f1dc20d1 --- /dev/null +++ b/Crypto/src/x509/store/X509AttrCertStoreSelector.cs
@@ -0,0 +1,376 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.Utilities.Date; +using Org.BouncyCastle.X509.Extension; + +namespace Org.BouncyCastle.X509.Store +{ + /** + * This class is an <code>Selector</code> like implementation to select + * attribute certificates from a given set of criteria. + * + * @see org.bouncycastle.x509.X509AttributeCertificate + * @see org.bouncycastle.x509.X509Store + */ + public class X509AttrCertStoreSelector + : IX509Selector + { + // TODO: name constraints??? + + private IX509AttributeCertificate attributeCert; + private DateTimeObject attributeCertificateValid; + private AttributeCertificateHolder holder; + private AttributeCertificateIssuer issuer; + private BigInteger serialNumber; + private ISet targetNames = new HashSet(); + private ISet targetGroups = new HashSet(); + + public X509AttrCertStoreSelector() + { + } + + private X509AttrCertStoreSelector( + X509AttrCertStoreSelector o) + { + this.attributeCert = o.attributeCert; + this.attributeCertificateValid = o.attributeCertificateValid; + this.holder = o.holder; + this.issuer = o.issuer; + this.serialNumber = o.serialNumber; + this.targetGroups = new HashSet(o.targetGroups); + this.targetNames = new HashSet(o.targetNames); + } + + /// <summary> + /// Decides if the given attribute certificate should be selected. + /// </summary> + /// <param name="obj">The attribute certificate to be checked.</param> + /// <returns><code>true</code> if the object matches this selector.</returns> + public bool Match( + object obj) + { + if (obj == null) + throw new ArgumentNullException("obj"); + + IX509AttributeCertificate attrCert = obj as IX509AttributeCertificate; + + if (attrCert == null) + return false; + + if (this.attributeCert != null && !this.attributeCert.Equals(attrCert)) + return false; + + if (serialNumber != null && !attrCert.SerialNumber.Equals(serialNumber)) + return false; + + if (holder != null && !attrCert.Holder.Equals(holder)) + return false; + + if (issuer != null && !attrCert.Issuer.Equals(issuer)) + return false; + + if (attributeCertificateValid != null && !attrCert.IsValid(attributeCertificateValid.Value)) + return false; + + if (targetNames.Count > 0 || targetGroups.Count > 0) + { + Asn1OctetString targetInfoExt = attrCert.GetExtensionValue( + X509Extensions.TargetInformation); + + if (targetInfoExt != null) + { + TargetInformation targetinfo; + try + { + targetinfo = TargetInformation.GetInstance( + X509ExtensionUtilities.FromExtensionValue(targetInfoExt)); + } + catch (Exception) + { + return false; + } + + Targets[] targetss = targetinfo.GetTargetsObjects(); + + if (targetNames.Count > 0) + { + bool found = false; + + for (int i = 0; i < targetss.Length && !found; i++) + { + Target[] targets = targetss[i].GetTargets(); + + for (int j = 0; j < targets.Length; j++) + { + GeneralName targetName = targets[j].TargetName; + + if (targetName != null && targetNames.Contains(targetName)) + { + found = true; + break; + } + } + } + if (!found) + { + return false; + } + } + + if (targetGroups.Count > 0) + { + bool found = false; + + for (int i = 0; i < targetss.Length && !found; i++) + { + Target[] targets = targetss[i].GetTargets(); + + for (int j = 0; j < targets.Length; j++) + { + GeneralName targetGroup = targets[j].TargetGroup; + + if (targetGroup != null && targetGroups.Contains(targetGroup)) + { + found = true; + break; + } + } + } + + if (!found) + { + return false; + } + } + } + } + + return true; + } + + public object Clone() + { + return new X509AttrCertStoreSelector(this); + } + + /// <summary>The attribute certificate which must be matched.</summary> + /// <remarks>If <c>null</c> is given, any will do.</remarks> + public IX509AttributeCertificate AttributeCert + { + get { return attributeCert; } + set { this.attributeCert = value; } + } + + [Obsolete("Use AttributeCertificateValid instead")] + public DateTimeObject AttribueCertificateValid + { + get { return attributeCertificateValid; } + set { this.attributeCertificateValid = value; } + } + + /// <summary>The criteria for validity</summary> + /// <remarks>If <c>null</c> is given any will do.</remarks> + public DateTimeObject AttributeCertificateValid + { + get { return attributeCertificateValid; } + set { this.attributeCertificateValid = value; } + } + + /// <summary>The holder.</summary> + /// <remarks>If <c>null</c> is given any will do.</remarks> + public AttributeCertificateHolder Holder + { + get { return holder; } + set { this.holder = value; } + } + + /// <summary>The issuer.</summary> + /// <remarks>If <c>null</c> is given any will do.</remarks> + public AttributeCertificateIssuer Issuer + { + get { return issuer; } + set { this.issuer = value; } + } + + /// <summary>The serial number.</summary> + /// <remarks>If <c>null</c> is given any will do.</remarks> + public BigInteger SerialNumber + { + get { return serialNumber; } + set { this.serialNumber = value; } + } + + /** + * Adds a target name criterion for the attribute certificate to the target + * information extension criteria. The <code>X509AttributeCertificate</code> + * must contain at least one of the specified target names. + * <p> + * Each attribute certificate may contain a target information extension + * limiting the servers where this attribute certificate can be used. If + * this extension is not present, the attribute certificate is not targeted + * and may be accepted by any server. + * </p> + * + * @param name The name as a GeneralName (not <code>null</code>) + */ + public void AddTargetName( + GeneralName name) + { + targetNames.Add(name); + } + + /** + * Adds a target name criterion for the attribute certificate to the target + * information extension criteria. The <code>X509AttributeCertificate</code> + * must contain at least one of the specified target names. + * <p> + * Each attribute certificate may contain a target information extension + * limiting the servers where this attribute certificate can be used. If + * this extension is not present, the attribute certificate is not targeted + * and may be accepted by any server. + * </p> + * + * @param name a byte array containing the name in ASN.1 DER encoded form of a GeneralName + * @throws IOException if a parsing error occurs. + */ + public void AddTargetName( + byte[] name) + { + AddTargetName(GeneralName.GetInstance(Asn1Object.FromByteArray(name))); + } + + /** + * Adds a collection with target names criteria. If <code>null</code> is + * given any will do. + * <p> + * The collection consists of either GeneralName objects or byte[] arrays representing + * DER encoded GeneralName structures. + * </p> + * + * @param names A collection of target names. + * @throws IOException if a parsing error occurs. + * @see #AddTargetName(byte[]) + * @see #AddTargetName(GeneralName) + */ + public void SetTargetNames( + IEnumerable names) + { + targetNames = ExtractGeneralNames(names); + } + + /** + * Gets the target names. The collection consists of <code>List</code>s + * made up of an <code>Integer</code> in the first entry and a DER encoded + * byte array or a <code>String</code> in the second entry. + * <p>The returned collection is immutable.</p> + * + * @return The collection of target names + * @see #setTargetNames(Collection) + */ + public IEnumerable GetTargetNames() + { + return new EnumerableProxy(targetNames); + } + + /** + * Adds a target group criterion for the attribute certificate to the target + * information extension criteria. The <code>X509AttributeCertificate</code> + * must contain at least one of the specified target groups. + * <p> + * Each attribute certificate may contain a target information extension + * limiting the servers where this attribute certificate can be used. If + * this extension is not present, the attribute certificate is not targeted + * and may be accepted by any server. + * </p> + * + * @param group The group as GeneralName form (not <code>null</code>) + */ + public void AddTargetGroup( + GeneralName group) + { + targetGroups.Add(group); + } + + /** + * Adds a target group criterion for the attribute certificate to the target + * information extension criteria. The <code>X509AttributeCertificate</code> + * must contain at least one of the specified target groups. + * <p> + * Each attribute certificate may contain a target information extension + * limiting the servers where this attribute certificate can be used. If + * this extension is not present, the attribute certificate is not targeted + * and may be accepted by any server. + * </p> + * + * @param name a byte array containing the group in ASN.1 DER encoded form of a GeneralName + * @throws IOException if a parsing error occurs. + */ + public void AddTargetGroup( + byte[] name) + { + AddTargetGroup(GeneralName.GetInstance(Asn1Object.FromByteArray(name))); + } + + /** + * Adds a collection with target groups criteria. If <code>null</code> is + * given any will do. + * <p> + * The collection consists of <code>GeneralName</code> objects or <code>byte[]</code> + * representing DER encoded GeneralNames. + * </p> + * + * @param names A collection of target groups. + * @throws IOException if a parsing error occurs. + * @see #AddTargetGroup(byte[]) + * @see #AddTargetGroup(GeneralName) + */ + public void SetTargetGroups( + IEnumerable names) + { + targetGroups = ExtractGeneralNames(names); + } + + /** + * Gets the target groups. The collection consists of <code>List</code>s + * made up of an <code>Integer</code> in the first entry and a DER encoded + * byte array or a <code>String</code> in the second entry. + * <p>The returned collection is immutable.</p> + * + * @return The collection of target groups. + * @see #setTargetGroups(Collection) + */ + public IEnumerable GetTargetGroups() + { + return new EnumerableProxy(targetGroups); + } + + private ISet ExtractGeneralNames( + IEnumerable names) + { + ISet result = new HashSet(); + + if (names != null) + { + foreach (object o in names) + { + if (o is GeneralName) + { + result.Add(o); + } + else + { + result.Add(GeneralName.GetInstance(Asn1Object.FromByteArray((byte[]) o))); + } + } + } + + return result; + } + } +} diff --git a/Crypto/src/x509/store/X509CertPairStoreSelector.cs b/Crypto/src/x509/store/X509CertPairStoreSelector.cs new file mode 100644
index 000000000..2796971c7 --- /dev/null +++ b/Crypto/src/x509/store/X509CertPairStoreSelector.cs
@@ -0,0 +1,92 @@ +using System; + +namespace Org.BouncyCastle.X509.Store +{ + /// <remarks> + /// This class is an <code>IX509Selector</code> implementation to select + /// certificate pairs, which are e.g. used for cross certificates. The set of + /// criteria is given from two <code>X509CertStoreSelector</code> objects, + /// each of which, if present, must match the respective component of a pair. + /// </remarks> + public class X509CertPairStoreSelector + : IX509Selector + { + private static X509CertStoreSelector CloneSelector( + X509CertStoreSelector s) + { + return s == null ? null : (X509CertStoreSelector) s.Clone(); + } + + private X509CertificatePair certPair; + private X509CertStoreSelector forwardSelector; + private X509CertStoreSelector reverseSelector; + + public X509CertPairStoreSelector() + { + } + + private X509CertPairStoreSelector( + X509CertPairStoreSelector o) + { + this.certPair = o.CertPair; + this.forwardSelector = o.ForwardSelector; + this.reverseSelector = o.ReverseSelector; + } + + /// <summary>The certificate pair which is used for testing on equality.</summary> + public X509CertificatePair CertPair + { + get { return certPair; } + set { this.certPair = value; } + } + + /// <summary>The certificate selector for the forward part.</summary> + public X509CertStoreSelector ForwardSelector + { + get { return CloneSelector(forwardSelector); } + set { this.forwardSelector = CloneSelector(value); } + } + + /// <summary>The certificate selector for the reverse part.</summary> + public X509CertStoreSelector ReverseSelector + { + get { return CloneSelector(reverseSelector); } + set { this.reverseSelector = CloneSelector(value); } + } + + /// <summary> + /// Decides if the given certificate pair should be selected. If + /// <c>obj</c> is not a <code>X509CertificatePair</code>, this method + /// returns <code>false</code>. + /// </summary> + /// <param name="obj">The <code>X509CertificatePair</code> to be tested.</param> + /// <returns><code>true</code> if the object matches this selector.</returns> + public bool Match( + object obj) + { + if (obj == null) + throw new ArgumentNullException("obj"); + + X509CertificatePair pair = obj as X509CertificatePair; + + if (pair == null) + return false; + + if (certPair != null && !certPair.Equals(pair)) + return false; + + if (forwardSelector != null && !forwardSelector.Match(pair.Forward)) + return false; + + if (reverseSelector != null && !reverseSelector.Match(pair.Reverse)) + return false; + + return true; + } + + public object Clone() + { + return new X509CertPairStoreSelector(this); + } + } +} diff --git a/Crypto/src/x509/store/X509CertStoreSelector.cs b/Crypto/src/x509/store/X509CertStoreSelector.cs new file mode 100644
index 000000000..3874edf1d --- /dev/null +++ b/Crypto/src/x509/store/X509CertStoreSelector.cs
@@ -0,0 +1,337 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.Utilities.Date; +using Org.BouncyCastle.X509.Extension; + +namespace Org.BouncyCastle.X509.Store +{ + public class X509CertStoreSelector + : IX509Selector + { + // TODO Missing criteria? + + private byte[] authorityKeyIdentifier; + private int basicConstraints = -1; + private X509Certificate certificate; + private DateTimeObject certificateValid; + private ISet extendedKeyUsage; + private X509Name issuer; + private bool[] keyUsage; + private ISet policy; + private DateTimeObject privateKeyValid; + private BigInteger serialNumber; + private X509Name subject; + private byte[] subjectKeyIdentifier; + private SubjectPublicKeyInfo subjectPublicKey; + private DerObjectIdentifier subjectPublicKeyAlgID; + + public X509CertStoreSelector() + { + } + + public X509CertStoreSelector( + X509CertStoreSelector o) + { + this.authorityKeyIdentifier = o.AuthorityKeyIdentifier; + this.basicConstraints = o.BasicConstraints; + this.certificate = o.Certificate; + this.certificateValid = o.CertificateValid; + this.extendedKeyUsage = o.ExtendedKeyUsage; + this.issuer = o.Issuer; + this.keyUsage = o.KeyUsage; + this.policy = o.Policy; + this.privateKeyValid = o.PrivateKeyValid; + this.serialNumber = o.SerialNumber; + this.subject = o.Subject; + this.subjectKeyIdentifier = o.SubjectKeyIdentifier; + this.subjectPublicKey = o.SubjectPublicKey; + this.subjectPublicKeyAlgID = o.SubjectPublicKeyAlgID; + } + + public virtual object Clone() + { + return new X509CertStoreSelector(this); + } + + public byte[] AuthorityKeyIdentifier + { + get { return Arrays.Clone(authorityKeyIdentifier); } + set { authorityKeyIdentifier = Arrays.Clone(value); } + } + + public int BasicConstraints + { + get { return basicConstraints; } + set + { + if (value < -2) + throw new ArgumentException("value can't be less than -2", "value"); + + basicConstraints = value; + } + } + + public X509Certificate Certificate + { + get { return certificate; } + set { this.certificate = value; } + } + + public DateTimeObject CertificateValid + { + get { return certificateValid; } + set { certificateValid = value; } + } + + public ISet ExtendedKeyUsage + { + get { return CopySet(extendedKeyUsage); } + set { extendedKeyUsage = CopySet(value); } + } + + public X509Name Issuer + { + get { return issuer; } + set { issuer = value; } + } + + [Obsolete("Avoid working with X509Name objects in string form")] + public string IssuerAsString + { + get { return issuer != null ? issuer.ToString() : null; } + } + + public bool[] KeyUsage + { + get { return CopyBoolArray(keyUsage); } + set { keyUsage = CopyBoolArray(value); } + } + + /// <summary> + /// An <code>ISet</code> of <code>DerObjectIdentifier</code> objects. + /// </summary> + public ISet Policy + { + get { return CopySet(policy); } + set { policy = CopySet(value); } + } + + public DateTimeObject PrivateKeyValid + { + get { return privateKeyValid; } + set { privateKeyValid = value; } + } + + public BigInteger SerialNumber + { + get { return serialNumber; } + set { serialNumber = value; } + } + + public X509Name Subject + { + get { return subject; } + set { subject = value; } + } + + public string SubjectAsString + { + get { return subject != null ? subject.ToString() : null; } + } + + public byte[] SubjectKeyIdentifier + { + get { return Arrays.Clone(subjectKeyIdentifier); } + set { subjectKeyIdentifier = Arrays.Clone(value); } + } + + public SubjectPublicKeyInfo SubjectPublicKey + { + get { return subjectPublicKey; } + set { subjectPublicKey = value; } + } + + public DerObjectIdentifier SubjectPublicKeyAlgID + { + get { return subjectPublicKeyAlgID; } + set { subjectPublicKeyAlgID = value; } + } + + public virtual bool Match( + object obj) + { + X509Certificate c = obj as X509Certificate; + + if (c == null) + return false; + + if (!MatchExtension(authorityKeyIdentifier, c, X509Extensions.AuthorityKeyIdentifier)) + return false; + + if (basicConstraints != -1) + { + int bc = c.GetBasicConstraints(); + + if (basicConstraints == -2) + { + if (bc != -1) + return false; + } + else + { + if (bc < basicConstraints) + return false; + } + } + + if (certificate != null && !certificate.Equals(c)) + return false; + + if (certificateValid != null && !c.IsValid(certificateValid.Value)) + return false; + + if (extendedKeyUsage != null) + { + IList eku = c.GetExtendedKeyUsage(); + + // Note: if no extended key usage set, all key purposes are implicitly allowed + + if (eku != null) + { + foreach (DerObjectIdentifier oid in extendedKeyUsage) + { + if (!eku.Contains(oid.Id)) + return false; + } + } + } + + if (issuer != null && !issuer.Equivalent(c.IssuerDN, true)) + return false; + + if (keyUsage != null) + { + bool[] ku = c.GetKeyUsage(); + + // Note: if no key usage set, all key purposes are implicitly allowed + + if (ku != null) + { + for (int i = 0; i < 9; ++i) + { + if (keyUsage[i] && !ku[i]) + return false; + } + } + } + + if (policy != null) + { + Asn1OctetString extVal = c.GetExtensionValue(X509Extensions.CertificatePolicies); + if (extVal == null) + return false; + + Asn1Sequence certPolicies = Asn1Sequence.GetInstance( + X509ExtensionUtilities.FromExtensionValue(extVal)); + + if (policy.Count < 1 && certPolicies.Count < 1) + return false; + + bool found = false; + foreach (PolicyInformation pi in certPolicies) + { + if (policy.Contains(pi.PolicyIdentifier)) + { + found = true; + break; + } + } + + if (!found) + return false; + } + + if (privateKeyValid != null) + { + Asn1OctetString extVal = c.GetExtensionValue(X509Extensions.PrivateKeyUsagePeriod); + if (extVal == null) + return false; + + PrivateKeyUsagePeriod pkup = PrivateKeyUsagePeriod.GetInstance( + X509ExtensionUtilities.FromExtensionValue(extVal)); + + DateTime dt = privateKeyValid.Value; + DateTime notAfter = pkup.NotAfter.ToDateTime(); + DateTime notBefore = pkup.NotBefore.ToDateTime(); + + if (dt.CompareTo(notAfter) > 0 || dt.CompareTo(notBefore) < 0) + return false; + } + + if (serialNumber != null && !serialNumber.Equals(c.SerialNumber)) + return false; + + if (subject != null && !subject.Equivalent(c.SubjectDN, true)) + return false; + + if (!MatchExtension(subjectKeyIdentifier, c, X509Extensions.SubjectKeyIdentifier)) + return false; + + if (subjectPublicKey != null && !subjectPublicKey.Equals(GetSubjectPublicKey(c))) + return false; + + if (subjectPublicKeyAlgID != null + && !subjectPublicKeyAlgID.Equals(GetSubjectPublicKey(c).AlgorithmID)) + return false; + + return true; + } + + internal static bool IssuersMatch( + X509Name a, + X509Name b) + { + return a == null ? b == null : a.Equivalent(b, true); + } + + private static bool[] CopyBoolArray( + bool[] b) + { + return b == null ? null : (bool[]) b.Clone(); + } + + private static ISet CopySet( + ISet s) + { + return s == null ? null : new HashSet(s); + } + + private static SubjectPublicKeyInfo GetSubjectPublicKey( + X509Certificate c) + { + return SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(c.GetPublicKey()); + } + + private static bool MatchExtension( + byte[] b, + X509Certificate c, + DerObjectIdentifier oid) + { + if (b == null) + return true; + + Asn1OctetString extVal = c.GetExtensionValue(oid); + + if (extVal == null) + return false; + + return Arrays.AreEqual(b, extVal.GetOctets()); + } + } +} diff --git a/Crypto/src/x509/store/X509CollectionStore.cs b/Crypto/src/x509/store/X509CollectionStore.cs new file mode 100644
index 000000000..92173140b --- /dev/null +++ b/Crypto/src/x509/store/X509CollectionStore.cs
@@ -0,0 +1,51 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.X509.Store +{ + /** + * A simple collection backed store. + */ + internal class X509CollectionStore + : IX509Store + { + private ICollection _local; + + /** + * Basic constructor. + * + * @param collection - initial contents for the store, this is copied. + */ + internal X509CollectionStore( + ICollection collection) + { + _local = Platform.CreateArrayList(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 ICollection GetMatches( + IX509Selector selector) + { + if (selector == null) + { + return Platform.CreateArrayList(_local); + } + + IList result = Platform.CreateArrayList(); + foreach (object obj in _local) + { + if (selector.Match(obj)) + result.Add(obj); + } + + return result; + } + } +} diff --git a/Crypto/src/x509/store/X509CollectionStoreParameters.cs b/Crypto/src/x509/store/X509CollectionStoreParameters.cs new file mode 100644
index 000000000..7fd047a47 --- /dev/null +++ b/Crypto/src/x509/store/X509CollectionStoreParameters.cs
@@ -0,0 +1,60 @@ +using System; +using System.Collections; +using System.Text; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.X509.Store +{ + /// <remarks>This class contains a collection for collection based <code>X509Store</code>s.</remarks> + public class X509CollectionStoreParameters + : IX509StoreParameters + { + private readonly IList collection; + + /// <summary> + /// Constructor. + /// <p> + /// The collection is copied. + /// </p> + /// </summary> + /// <param name="collection">The collection containing X.509 object types.</param> + /// <exception cref="ArgumentNullException">If collection is null.</exception> + public X509CollectionStoreParameters( + ICollection collection) + { + if (collection == null) + throw new ArgumentNullException("collection"); + + this.collection = Platform.CreateArrayList(collection); + } + + // TODO Do we need to be able to Clone() these, and should it really be shallow? +// /** +// * Returns a shallow clone. The returned contents are not copied, so adding +// * or removing objects will effect this. +// * +// * @return a shallow clone. +// */ +// public object Clone() +// { +// return new X509CollectionStoreParameters(collection); +// } + + /// <summary>Returns a copy of the <code>ICollection</code>.</summary> + public ICollection GetCollection() + { + return Platform.CreateArrayList(collection); + } + + /// <summary>Returns a formatted string describing the parameters.</summary> + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("X509CollectionStoreParameters: [\n"); + sb.Append(" collection: " + collection + "\n"); + sb.Append("]"); + return sb.ToString(); + } + } +} diff --git a/Crypto/src/x509/store/X509CrlStoreSelector.cs b/Crypto/src/x509/store/X509CrlStoreSelector.cs new file mode 100644
index 000000000..c4b0062c1 --- /dev/null +++ b/Crypto/src/x509/store/X509CrlStoreSelector.cs
@@ -0,0 +1,283 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Date; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.X509.Extension; + +namespace Org.BouncyCastle.X509.Store +{ + public class X509CrlStoreSelector + : IX509Selector + { + // TODO Missing criteria? + + private X509Certificate certificateChecking; + private DateTimeObject dateAndTime; + private ICollection issuers; + private BigInteger maxCrlNumber; + private BigInteger minCrlNumber; + + private IX509AttributeCertificate attrCertChecking; + private bool completeCrlEnabled; + private bool deltaCrlIndicatorEnabled; + private byte[] issuingDistributionPoint; + private bool issuingDistributionPointEnabled; + private BigInteger maxBaseCrlNumber; + + public X509CrlStoreSelector() + { + } + + public X509CrlStoreSelector( + X509CrlStoreSelector o) + { + this.certificateChecking = o.CertificateChecking; + this.dateAndTime = o.DateAndTime; + this.issuers = o.Issuers; + this.maxCrlNumber = o.MaxCrlNumber; + this.minCrlNumber = o.MinCrlNumber; + + this.deltaCrlIndicatorEnabled = o.DeltaCrlIndicatorEnabled; + this.completeCrlEnabled = o.CompleteCrlEnabled; + this.maxBaseCrlNumber = o.MaxBaseCrlNumber; + this.attrCertChecking = o.AttrCertChecking; + this.issuingDistributionPointEnabled = o.IssuingDistributionPointEnabled; + this.issuingDistributionPoint = o.IssuingDistributionPoint; + } + + public virtual object Clone() + { + return new X509CrlStoreSelector(this); + } + + public X509Certificate CertificateChecking + { + get { return certificateChecking; } + set { certificateChecking = value; } + } + + public DateTimeObject DateAndTime + { + get { return dateAndTime; } + set { dateAndTime = value; } + } + + /// <summary> + /// An <code>ICollection</code> of <code>X509Name</code> objects + /// </summary> + public ICollection Issuers + { + get { return Platform.CreateArrayList(issuers); } + set { issuers = Platform.CreateArrayList(value); } + } + + public BigInteger MaxCrlNumber + { + get { return maxCrlNumber; } + set { maxCrlNumber = value; } + } + + public BigInteger MinCrlNumber + { + get { return minCrlNumber; } + set { minCrlNumber = value; } + } + + /** + * The attribute certificate being checked. This is not a criterion. + * Rather, it is optional information that may help a {@link X509Store} find + * CRLs that would be relevant when checking revocation for the specified + * attribute certificate. If <code>null</code> is specified, then no such + * optional information is provided. + * + * @param attrCert the <code>IX509AttributeCertificate</code> being checked (or + * <code>null</code>) + * @see #getAttrCertificateChecking() + */ + public IX509AttributeCertificate AttrCertChecking + { + get { return attrCertChecking; } + set { this.attrCertChecking = value; } + } + + /** + * If <code>true</code> only complete CRLs are returned. Defaults to + * <code>false</code>. + * + * @return <code>true</code> if only complete CRLs are returned. + */ + public bool CompleteCrlEnabled + { + get { return completeCrlEnabled; } + set { this.completeCrlEnabled = value; } + } + + /** + * Returns if this selector must match CRLs with the delta CRL indicator + * extension set. Defaults to <code>false</code>. + * + * @return Returns <code>true</code> if only CRLs with the delta CRL + * indicator extension are selected. + */ + public bool DeltaCrlIndicatorEnabled + { + get { return deltaCrlIndicatorEnabled; } + set { this.deltaCrlIndicatorEnabled = value; } + } + + /** + * The issuing distribution point. + * <p> + * The issuing distribution point extension is a CRL extension which + * identifies the scope and the distribution point of a CRL. The scope + * contains among others information about revocation reasons contained in + * the CRL. Delta CRLs and complete CRLs must have matching issuing + * distribution points.</p> + * <p> + * The byte array is cloned to protect against subsequent modifications.</p> + * <p> + * You must also enable or disable this criteria with + * {@link #setIssuingDistributionPointEnabled(bool)}.</p> + * + * @param issuingDistributionPoint The issuing distribution point to set. + * This is the DER encoded OCTET STRING extension value. + * @see #getIssuingDistributionPoint() + */ + public byte[] IssuingDistributionPoint + { + get { return Arrays.Clone(issuingDistributionPoint); } + set { this.issuingDistributionPoint = Arrays.Clone(value); } + } + + /** + * Whether the issuing distribution point criteria should be applied. + * Defaults to <code>false</code>. + * <p> + * You may also set the issuing distribution point criteria if not a missing + * issuing distribution point should be assumed.</p> + * + * @return Returns if the issuing distribution point check is enabled. + */ + public bool IssuingDistributionPointEnabled + { + get { return issuingDistributionPointEnabled; } + set { this.issuingDistributionPointEnabled = value; } + } + + /** + * The maximum base CRL number. Defaults to <code>null</code>. + * + * @return Returns the maximum base CRL number. + * @see #setMaxBaseCRLNumber(BigInteger) + */ + public BigInteger MaxBaseCrlNumber + { + get { return maxBaseCrlNumber; } + set { this.maxBaseCrlNumber = value; } + } + + public virtual bool Match( + object obj) + { + X509Crl c = obj as X509Crl; + + if (c == null) + return false; + + if (dateAndTime != null) + { + DateTime dt = dateAndTime.Value; + DateTime tu = c.ThisUpdate; + DateTimeObject nu = c.NextUpdate; + + if (dt.CompareTo(tu) < 0 || nu == null || dt.CompareTo(nu.Value) >= 0) + return false; + } + + if (issuers != null) + { + X509Name i = c.IssuerDN; + + bool found = false; + + foreach (X509Name issuer in issuers) + { + if (issuer.Equivalent(i, true)) + { + found = true; + break; + } + } + + if (!found) + return false; + } + + if (maxCrlNumber != null || minCrlNumber != null) + { + Asn1OctetString extVal = c.GetExtensionValue(X509Extensions.CrlNumber); + if (extVal == null) + return false; + + BigInteger cn = CrlNumber.GetInstance( + X509ExtensionUtilities.FromExtensionValue(extVal)).PositiveValue; + + if (maxCrlNumber != null && cn.CompareTo(maxCrlNumber) > 0) + return false; + + if (minCrlNumber != null && cn.CompareTo(minCrlNumber) < 0) + return false; + } + + DerInteger dci = null; + try + { + Asn1OctetString bytes = c.GetExtensionValue(X509Extensions.DeltaCrlIndicator); + if (bytes != null) + { + dci = DerInteger.GetInstance(X509ExtensionUtilities.FromExtensionValue(bytes)); + } + } + catch (Exception) + { + return false; + } + + if (dci == null) + { + if (DeltaCrlIndicatorEnabled) + return false; + } + else + { + if (CompleteCrlEnabled) + return false; + + if (maxBaseCrlNumber != null && dci.PositiveValue.CompareTo(maxBaseCrlNumber) > 0) + return false; + } + + if (issuingDistributionPointEnabled) + { + Asn1OctetString idp = c.GetExtensionValue(X509Extensions.IssuingDistributionPoint); + if (issuingDistributionPoint == null) + { + if (idp != null) + return false; + } + else + { + if (!Arrays.AreEqual(idp.GetOctets(), issuingDistributionPoint)) + return false; + } + } + + return true; + } + } +} diff --git a/Crypto/src/x509/store/X509StoreException.cs b/Crypto/src/x509/store/X509StoreException.cs new file mode 100644
index 000000000..f6a5e235f --- /dev/null +++ b/Crypto/src/x509/store/X509StoreException.cs
@@ -0,0 +1,25 @@ +using System; + +namespace Org.BouncyCastle.X509.Store +{ + public class X509StoreException + : Exception + { + public X509StoreException() + { + } + + public X509StoreException( + string message) + : base(message) + { + } + + public X509StoreException( + string message, + Exception e) + : base(message, e) + { + } + } +} diff --git a/Crypto/src/x509/store/X509StoreFactory.cs b/Crypto/src/x509/store/X509StoreFactory.cs new file mode 100644
index 000000000..322639991 --- /dev/null +++ b/Crypto/src/x509/store/X509StoreFactory.cs
@@ -0,0 +1,61 @@ +using System; +using System.Collections; +using System.Globalization; + +namespace Org.BouncyCastle.X509.Store +{ + public sealed class X509StoreFactory + { + private X509StoreFactory() + { + } + + public static IX509Store Create( + string type, + IX509StoreParameters parameters) + { + if (type == null) + throw new ArgumentNullException("type"); + + string[] parts = type.ToUpperInvariant().Split('/'); + + if (parts.Length < 2) + throw new ArgumentException("type"); + + if (parts[1] != "COLLECTION") + throw new NoSuchStoreException("X.509 store type '" + type + "' not available."); + + X509CollectionStoreParameters p = (X509CollectionStoreParameters) parameters; + ICollection coll = p.GetCollection(); + + switch (parts[0]) + { + case "ATTRIBUTECERTIFICATE": + checkCorrectType(coll, typeof(IX509AttributeCertificate)); + break; + case "CERTIFICATE": + checkCorrectType(coll, typeof(X509Certificate)); + break; + case "CERTIFICATEPAIR": + checkCorrectType(coll, typeof(X509CertificatePair)); + break; + case "CRL": + checkCorrectType(coll, typeof(X509Crl)); + break; + default: + throw new NoSuchStoreException("X.509 store type '" + type + "' not available."); + } + + return new X509CollectionStore(coll); + } + + private static void checkCorrectType(ICollection coll, Type t) + { + foreach (object o in coll) + { + if (!t.IsInstanceOfType(o)) + throw new InvalidCastException("Can't cast object to type: " + t.FullName); + } + } + } +}