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.cs20
-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.cs14
-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.cs5
-rw-r--r--crypto/src/asn1/Asn1InputStream.cs687
-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.cs5
-rw-r--r--crypto/src/asn1/Asn1Sequence.cs257
-rw-r--r--crypto/src/asn1/Asn1Set.cs383
-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.cs146
-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.cs12
-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.cs86
-rw-r--r--crypto/src/asn1/DerEnumerated.cs66
-rw-r--r--crypto/src/asn1/DerGeneralString.cs81
-rw-r--r--crypto/src/asn1/DerGeneralizedTime.cs347
-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.cs427
-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.cs2
-rw-r--r--crypto/src/asn1/DerSet.cs2
-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/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.cs12
-rw-r--r--crypto/src/asn1/LazyASN1InputStream.cs33
-rw-r--r--crypto/src/asn1/LazyDERSequence.cs10
-rw-r--r--crypto/src/asn1/LazyDERSet.cs10
-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.cs6
-rw-r--r--crypto/src/asn1/cms/Attributes.cs88
-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.cs16
-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.cs155
-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.cs141
-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.cs40
-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.cs50
-rw-r--r--crypto/src/asn1/cms/OtherRevocationInfoFormat.cs77
-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/SCVPReqRes.cs77
-rw-r--r--crypto/src/asn1/cms/SignedData.cs363
-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.cs136
-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.cs15
-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.cs7
-rw-r--r--crypto/src/asn1/cryptopro/ECGOST3410NamedCurves.cs79
-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.cs266
-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.cs204
-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.csbin20844 -> 11549 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.cs164
-rw-r--r--crypto/src/asn1/nist/NISTObjectIdentifiers.cs36
-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.cs56
-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.cs66
-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.cs147
-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.cs145
-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.cs126
-rw-r--r--crypto/src/asn1/pkcs/SignerInfo.cs154
-rw-r--r--crypto/src/asn1/sec/ECPrivateKeyStructure.cs226
-rw-r--r--crypto/src/asn1/sec/SECNamedCurves.cs2411
-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.cs2
-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.cs872
-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.cs289
-rw-r--r--crypto/src/asn1/util/Dump.cs4
-rw-r--r--crypto/src/asn1/util/FilterStream.cs9
-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.cs87
-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.cs6
-rw-r--r--crypto/src/asn1/x509/AuthorityInformationAccess.cs183
-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/CertificatePolicies.cs81
-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.cs87
-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.cs2
-rw-r--r--crypto/src/asn1/x509/NoticeReference.cs261
-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.cs2
-rw-r--r--crypto/src/asn1/x509/PolicyQualifierId.cs28
-rw-r--r--crypto/src/asn1/x509/PolicyQualifierInfo.cs160
-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.cs2
-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.cs76
-rw-r--r--crypto/src/asn1/x509/UserNotice.cs102
-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.cs84
-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.cs86
-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.cs2
-rw-r--r--crypto/src/asn1/x509/X509ExtensionsGenerator.cs81
-rw-r--r--crypto/src/asn1/x509/X509Name.cs1100
-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/ECNamedCurveTable.cs118
-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.cs1456
-rw-r--r--crypto/src/asn1/x9/X962Parameters.cs53
-rw-r--r--crypto/src/asn1/x9/X9Curve.cs161
-rw-r--r--crypto/src/asn1/x9/X9ECParameters.cs108
-rw-r--r--crypto/src/asn1/x9/X9ECParametersHolder.cs22
-rw-r--r--crypto/src/asn1/x9/X9ECPoint.cs12
-rw-r--r--crypto/src/asn1/x9/X9FieldElement.cs90
-rw-r--r--crypto/src/asn1/x9/X9FieldID.cs144
-rw-r--r--crypto/src/asn1/x9/X9IntegerConverter.cs56
-rw-r--r--crypto/src/asn1/x9/X9ObjectIdentifiers.cs168
-rw-r--r--crypto/src/bcpg/ArmoredInputStream.cs14
-rw-r--r--crypto/src/bcpg/ArmoredOutputStream.cs51
-rw-r--r--crypto/src/bcpg/BcpgInputStream.cs14
-rw-r--r--crypto/src/bcpg/BcpgObject.cs8
-rw-r--r--crypto/src/bcpg/BcpgOutputStream.cs14
-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.cs102
-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.cs48
-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.cs25
-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.cs42
-rw-r--r--crypto/src/bcpg/UserAttributeSubpacketTags.cs10
-rw-r--r--crypto/src/bcpg/UserAttributeSubpacketsReader.cs100
-rw-r--r--crypto/src/bcpg/UserIdPacket.cs37
-rw-r--r--crypto/src/bcpg/attr/ImageAttrib.cs70
-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.cs1
-rw-r--r--crypto/src/bcpg/sig/RevocationKeyTags.cs9
-rw-r--r--crypto/src/bcpg/sig/RevocationReason.cs1
-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.cs5
-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.cs4
-rw-r--r--crypto/src/cms/CMSAuthenticatedDataParser.cs214
-rw-r--r--crypto/src/cms/CMSAuthenticatedDataStreamGenerator.cs27
-rw-r--r--crypto/src/cms/CMSAuthenticatedGenerator.cs35
-rw-r--r--crypto/src/cms/CMSCompressedData.cs2
-rw-r--r--crypto/src/cms/CMSCompressedDataGenerator.cs2
-rw-r--r--crypto/src/cms/CMSCompressedDataParser.cs57
-rw-r--r--crypto/src/cms/CMSCompressedDataStreamGenerator.cs28
-rw-r--r--crypto/src/cms/CMSContentInfoParser.cs2
-rw-r--r--crypto/src/cms/CMSEnvelopedData.cs115
-rw-r--r--crypto/src/cms/CMSEnvelopedDataGenerator.cs2
-rw-r--r--crypto/src/cms/CMSEnvelopedDataParser.cs161
-rw-r--r--crypto/src/cms/CMSEnvelopedDataStreamGenerator.cs32
-rw-r--r--crypto/src/cms/CMSEnvelopedGenerator.cs331
-rw-r--r--crypto/src/cms/CMSEnvelopedHelper.cs311
-rw-r--r--crypto/src/cms/CMSException.cs9
-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.cs3
-rw-r--r--crypto/src/cms/CMSProcessableInputStream.cs2
-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.cs6
-rw-r--r--crypto/src/cms/CMSSignedDataParser.cs9
-rw-r--r--crypto/src/cms/CMSSignedDataStreamGenerator.cs150
-rw-r--r--crypto/src/cms/CMSSignedGenerator.cs261
-rw-r--r--crypto/src/cms/CMSSignedHelper.cs319
-rw-r--r--crypto/src/cms/CMSStreamException.cs3
-rw-r--r--crypto/src/cms/CMSTypedStream.cs4
-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.cs4
-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.cs410
-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/OriginatorId.cs51
-rw-r--r--crypto/src/cms/OriginatorInfoGenerator.cs42
-rw-r--r--crypto/src/cms/OriginatorInformation.cs96
-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.cs50
-rw-r--r--crypto/src/crypto/BufferedAsymmetricBlockCipher.cs152
-rw-r--r--crypto/src/crypto/BufferedBlockCipher.cs9
-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.cs3
-rw-r--r--crypto/src/crypto/DataLengthException.cs3
-rw-r--r--crypto/src/crypto/IAsymmetricBlockCipher.cs30
-rw-r--r--crypto/src/crypto/IAsymmetricCipherKeyPairGenerator.cs24
-rw-r--r--crypto/src/crypto/IBasicAgreement.cs5
-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.cs3
-rw-r--r--crypto/src/crypto/KeyGenerationParameters.cs55
-rw-r--r--crypto/src/crypto/MaxBytesExceededException.cs5
-rw-r--r--crypto/src/crypto/PbeParametersGenerator.cs366
-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.cs104
-rw-r--r--crypto/src/crypto/agreement/DHStandardGroups.cs206
-rw-r--r--crypto/src/crypto/agreement/ECDHBasicAgreement.cs31
-rw-r--r--crypto/src/crypto/agreement/ECDHCBasicAgreement.cs32
-rw-r--r--crypto/src/crypto/agreement/ECDHWithKdfBasicAgreement.cs7
-rw-r--r--crypto/src/crypto/agreement/ECMqvBasicAgreement.cs154
-rw-r--r--crypto/src/crypto/agreement/ECMqvWithKdfBasicAgreement.cs7
-rw-r--r--crypto/src/crypto/agreement/kdf/DHKdfParameters.cs57
-rw-r--r--crypto/src/crypto/agreement/kdf/DHKekGenerator.cs227
-rw-r--r--crypto/src/crypto/agreement/kdf/ECDHKekGenerator.cs106
-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.cs49
-rw-r--r--crypto/src/crypto/digests/GeneralDigest.cs15
-rw-r--r--crypto/src/crypto/digests/LongDigest.cs15
-rw-r--r--crypto/src/crypto/digests/MD2Digest.cs26
-rw-r--r--crypto/src/crypto/digests/MD4Digest.cs23
-rw-r--r--crypto/src/crypto/digests/MD5Digest.cs288
-rw-r--r--crypto/src/crypto/digests/NullDigest.cs49
-rw-r--r--crypto/src/crypto/digests/RipeMD128Digest.cs26
-rw-r--r--crypto/src/crypto/digests/RipeMD160Digest.cs22
-rw-r--r--crypto/src/crypto/digests/RipeMD256Digest.cs21
-rw-r--r--crypto/src/crypto/digests/RipeMD320Digest.cs21
-rw-r--r--crypto/src/crypto/digests/SHA3Digest.cs560
-rw-r--r--crypto/src/crypto/digests/SM3Digest.cs328
-rw-r--r--crypto/src/crypto/digests/Sha1Digest.cs265
-rw-r--r--crypto/src/crypto/digests/Sha224Digest.cs21
-rw-r--r--crypto/src/crypto/digests/Sha256Digest.cs21
-rw-r--r--crypto/src/crypto/digests/Sha384Digest.cs14
-rw-r--r--crypto/src/crypto/digests/Sha512Digest.cs46
-rw-r--r--crypto/src/crypto/digests/Sha512tDigest.cs200
-rw-r--r--crypto/src/crypto/digests/ShortenedDigest.cs82
-rw-r--r--crypto/src/crypto/digests/SkeinDigest.cs117
-rw-r--r--crypto/src/crypto/digests/SkeinEngine.cs804
-rw-r--r--crypto/src/crypto/digests/TigerDigest.cs41
-rw-r--r--crypto/src/crypto/digests/WhirlpoolDigest.cs44
-rw-r--r--crypto/src/crypto/ec/CustomNamedCurves.cs366
-rw-r--r--crypto/src/crypto/encodings/ISO9796d1Encoding.cs273
-rw-r--r--crypto/src/crypto/encodings/OaepEncoding.cs674
-rw-r--r--crypto/src/crypto/encodings/Pkcs1Encoding.cs592
-rw-r--r--crypto/src/crypto/engines/AesEngine.cs1041
-rw-r--r--crypto/src/crypto/engines/AesFastEngine.cs1221
-rw-r--r--crypto/src/crypto/engines/AesLightEngine.cs830
-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/ChaChaEngine.cs189
-rw-r--r--crypto/src/crypto/engines/DesEdeEngine.cs88
-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.cs7
-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.cs450
-rw-r--r--crypto/src/crypto/engines/IdeaEngine.cs46
-rw-r--r--crypto/src/crypto/engines/IesEngine.cs73
-rw-r--r--crypto/src/crypto/engines/NaccacheSternEngine.cs40
-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.cs6
-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.cs277
-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/ThreefishEngine.cs1490
-rw-r--r--crypto/src/crypto/engines/TwofishEngine.cs675
-rw-r--r--crypto/src/crypto/engines/VMPCEngine.cs265
-rw-r--r--crypto/src/crypto/engines/VMPCKSA3Engine.cs51
-rw-r--r--crypto/src/crypto/engines/XSalsa20Engine.cs71
-rw-r--r--crypto/src/crypto/engines/XTEAEngine.cs168
-rw-r--r--crypto/src/crypto/generators/BaseKdfBytesGenerator.cs257
-rw-r--r--crypto/src/crypto/generators/DHBasicKeyPairGenerator.cs38
-rw-r--r--crypto/src/crypto/generators/DHKeyGeneratorHelper.cs105
-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.cs354
-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.cs79
-rw-r--r--crypto/src/crypto/generators/DsaParametersGenerator.cs547
-rw-r--r--crypto/src/crypto/generators/ECKeyPairGenerator.cs269
-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.cs123
-rw-r--r--crypto/src/crypto/generators/GOST3410ParametersGenerator.cs530
-rw-r--r--crypto/src/crypto/generators/Kdf1BytesGenerator.cs3
-rw-r--r--crypto/src/crypto/generators/Kdf2BytesGenerator.cs3
-rw-r--r--crypto/src/crypto/generators/Mgf1BytesGenerator.cs117
-rw-r--r--crypto/src/crypto/generators/NaccacheSternKeyPairGenerator.cs56
-rw-r--r--crypto/src/crypto/generators/OpenSSLPBEParametersGenerator.cs167
-rw-r--r--crypto/src/crypto/generators/Pkcs12ParametersGenerator.cs470
-rw-r--r--crypto/src/crypto/generators/Pkcs5S1ParametersGenerator.cs162
-rw-r--r--crypto/src/crypto/generators/Pkcs5S2ParametersGenerator.cs326
-rw-r--r--crypto/src/crypto/generators/Poly1305KeyGenerator.cs123
-rw-r--r--crypto/src/crypto/generators/RSABlindingFactorGenerator.cs69
-rw-r--r--crypto/src/crypto/generators/RsaKeyPairGenerator.cs151
-rw-r--r--crypto/src/crypto/generators/SCrypt.cs140
-rw-r--r--crypto/src/crypto/io/CipherStream.cs19
-rw-r--r--crypto/src/crypto/io/DigestStream.cs11
-rw-r--r--crypto/src/crypto/io/MacStream.cs11
-rw-r--r--crypto/src/crypto/io/SignerStream.cs11
-rw-r--r--crypto/src/crypto/macs/CMac.cs481
-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/GMac.cs112
-rw-r--r--crypto/src/crypto/macs/GOST28147Mac.cs296
-rw-r--r--crypto/src/crypto/macs/HMac.cs94
-rw-r--r--crypto/src/crypto/macs/ISO9797Alg3Mac.cs275
-rw-r--r--crypto/src/crypto/macs/Poly1305.cs291
-rw-r--r--crypto/src/crypto/macs/SipHash.cs199
-rw-r--r--crypto/src/crypto/macs/SkeinMac.cs117
-rw-r--r--crypto/src/crypto/macs/VMPCMac.cs173
-rw-r--r--crypto/src/crypto/modes/CbcBlockCipher.cs12
-rw-r--r--crypto/src/crypto/modes/CcmBlockCipher.cs326
-rw-r--r--crypto/src/crypto/modes/CfbBlockCipher.cs8
-rw-r--r--crypto/src/crypto/modes/CtsBlockCipher.cs253
-rw-r--r--crypto/src/crypto/modes/EAXBlockCipher.cs174
-rw-r--r--crypto/src/crypto/modes/GCMBlockCipher.cs896
-rw-r--r--crypto/src/crypto/modes/GOFBBlockCipher.cs6
-rw-r--r--crypto/src/crypto/modes/IAeadBlockCipher.cs17
-rw-r--r--crypto/src/crypto/modes/OCBBlockCipher.cs558
-rw-r--r--crypto/src/crypto/modes/OfbBlockCipher.cs8
-rw-r--r--crypto/src/crypto/modes/OpenPgpCfbBlockCipher.cs337
-rw-r--r--crypto/src/crypto/modes/SicBlockCipher.cs185
-rw-r--r--crypto/src/crypto/modes/gcm/BasicGcmExponentiator.cs40
-rw-r--r--crypto/src/crypto/modes/gcm/BasicGcmMultiplier.cs6
-rw-r--r--crypto/src/crypto/modes/gcm/GcmUtilities.cs360
-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.cs62
-rw-r--r--crypto/src/crypto/modes/gcm/Tables64kGcmMultiplier.cs117
-rw-r--r--crypto/src/crypto/modes/gcm/Tables8kGcmMultiplier.cs163
-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.cs14
-rw-r--r--crypto/src/crypto/parameters/CcmParameters.cs1
-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/DSAParameterGenerationParameters.cs74
-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.cs69
-rw-r--r--crypto/src/crypto/parameters/ECDomainParameters.cs82
-rw-r--r--crypto/src/crypto/parameters/ECKeyGenerationParameters.cs41
-rw-r--r--crypto/src/crypto/parameters/ECKeyParameters.cs235
-rw-r--r--crypto/src/crypto/parameters/ECPrivateKeyParameters.cs132
-rw-r--r--crypto/src/crypto/parameters/ECPublicKeyParameters.cs106
-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.cs4
-rw-r--r--crypto/src/crypto/parameters/ParametersWithIV.cs3
-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/parameters/SkeinParameters.cs285
-rw-r--r--crypto/src/crypto/parameters/TweakableBlockCipherParameters.cs40
-rw-r--r--crypto/src/crypto/prng/CryptoApiRandomGenerator.cs2
-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.cs26
-rw-r--r--crypto/src/crypto/prng/VMPCRandomGenerator.cs209
-rw-r--r--crypto/src/crypto/signers/DsaDigestSigner.cs145
-rw-r--r--crypto/src/crypto/signers/DsaSigner.cs278
-rw-r--r--crypto/src/crypto/signers/ECDsaSigner.cs325
-rw-r--r--crypto/src/crypto/signers/ECGOST3410Signer.cs294
-rw-r--r--crypto/src/crypto/signers/ECNRSigner.cs350
-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/HMacDsaKCalculator.cs150
-rw-r--r--crypto/src/crypto/signers/IDsaKCalculator.cs44
-rw-r--r--crypto/src/crypto/signers/Iso9796d2PssSigner.cs1173
-rw-r--r--crypto/src/crypto/signers/Iso9796d2Signer.cs1098
-rw-r--r--crypto/src/crypto/signers/PssSigner.cs345
-rw-r--r--crypto/src/crypto/signers/RandomDsaKCalculator.cs44
-rw-r--r--crypto/src/crypto/signers/RsaDigestSigner.cs192
-rw-r--r--crypto/src/crypto/tls/AbstractTlsAgreementCredentials.cs12
-rw-r--r--crypto/src/crypto/tls/AbstractTlsCipherFactory.cs15
-rw-r--r--crypto/src/crypto/tls/AbstractTlsClient.cs232
-rw-r--r--crypto/src/crypto/tls/AbstractTlsContext.cs132
-rw-r--r--crypto/src/crypto/tls/AbstractTlsCredentials.cs10
-rw-r--r--crypto/src/crypto/tls/AbstractTlsEncryptionCredentials.cs12
-rw-r--r--crypto/src/crypto/tls/AbstractTlsKeyExchange.cs166
-rw-r--r--crypto/src/crypto/tls/AbstractTlsPeer.cs48
-rw-r--r--crypto/src/crypto/tls/AbstractTlsServer.cs318
-rw-r--r--crypto/src/crypto/tls/AbstractTlsSigner.cs50
-rw-r--r--crypto/src/crypto/tls/AbstractTlsSignerCredentials.cs20
-rw-r--r--crypto/src/crypto/tls/AlertDescription.cs258
-rw-r--r--crypto/src/crypto/tls/AlertLevel.cs16
-rw-r--r--crypto/src/crypto/tls/BulkCipherAlgorithm.cs25
-rw-r--r--crypto/src/crypto/tls/ByteQueue.cs242
-rw-r--r--crypto/src/crypto/tls/CertChainType.cs18
-rw-r--r--crypto/src/crypto/tls/Certificate.cs221
-rw-r--r--crypto/src/crypto/tls/CertificateRequest.cs174
-rw-r--r--crypto/src/crypto/tls/CertificateStatus.cs102
-rw-r--r--crypto/src/crypto/tls/CertificateStatusRequest.cs95
-rw-r--r--crypto/src/crypto/tls/CertificateStatusType.cs12
-rw-r--r--crypto/src/crypto/tls/CertificateUrl.cs124
-rw-r--r--crypto/src/crypto/tls/Chacha20Poly1305.cs153
-rw-r--r--crypto/src/crypto/tls/ChangeCipherSpec.cs9
-rw-r--r--crypto/src/crypto/tls/CipherSuite.cs482
-rw-r--r--crypto/src/crypto/tls/CipherType.cs20
-rw-r--r--crypto/src/crypto/tls/ClientAuthenticationType.cs14
-rw-r--r--crypto/src/crypto/tls/ClientCertificateType.cs37
-rw-r--r--crypto/src/crypto/tls/CombinedHash.cs203
-rw-r--r--crypto/src/crypto/tls/CompressionMethod.cs32
-rw-r--r--crypto/src/crypto/tls/ConnectionEnd.cs15
-rw-r--r--crypto/src/crypto/tls/ContentType.cs21
-rw-r--r--crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs104
-rw-r--r--crypto/src/crypto/tls/DefaultTlsCipherFactory.cs280
-rw-r--r--crypto/src/crypto/tls/DefaultTlsClient.cs656
-rw-r--r--crypto/src/crypto/tls/DefaultTlsEncryptionCredentials.cs51
-rw-r--r--crypto/src/crypto/tls/DefaultTlsServer.cs548
-rw-r--r--crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs142
-rw-r--r--crypto/src/crypto/tls/DeferredHash.cs201
-rw-r--r--crypto/src/crypto/tls/DigestInputBuffer.cs39
-rw-r--r--crypto/src/crypto/tls/DigitallySigned.cs70
-rw-r--r--crypto/src/crypto/tls/ECBasisType.cs16
-rw-r--r--crypto/src/crypto/tls/ECCurveType.cs46
-rw-r--r--crypto/src/crypto/tls/ECPointFormat.cs24
-rw-r--r--crypto/src/crypto/tls/EncryptionAlgorithm.cs91
-rw-r--r--crypto/src/crypto/tls/ExporterLabel.cs32
-rw-r--r--crypto/src/crypto/tls/ExtensionType.cs86
-rw-r--r--crypto/src/crypto/tls/HandshakeType.cs53
-rw-r--r--crypto/src/crypto/tls/HashAlgorithm.cs16
-rw-r--r--crypto/src/crypto/tls/HeartbeatExtension.cs52
-rw-r--r--crypto/src/crypto/tls/HeartbeatMessage.cs102
-rw-r--r--crypto/src/crypto/tls/HeartbeatMessageType.cs18
-rw-r--r--crypto/src/crypto/tls/HeartbeatMode.cs18
-rw-r--r--crypto/src/crypto/tls/KeyExchangeAlgorithm.cs80
-rw-r--r--crypto/src/crypto/tls/MacAlgorithm.cs25
-rw-r--r--crypto/src/crypto/tls/MaxFragmentLength.cs20
-rw-r--r--crypto/src/crypto/tls/NameType.cs12
-rw-r--r--crypto/src/crypto/tls/NamedCurve.cs123
-rw-r--r--crypto/src/crypto/tls/NewSessionTicket.cs53
-rw-r--r--crypto/src/crypto/tls/OcspStatusRequest.cs130
-rw-r--r--crypto/src/crypto/tls/PrfAlgorithm.cs24
-rw-r--r--crypto/src/crypto/tls/ProtocolVersion.cs159
-rw-r--r--crypto/src/crypto/tls/PskTlsClient.cs431
-rw-r--r--crypto/src/crypto/tls/RecordStream.cs487
-rw-r--r--crypto/src/crypto/tls/SecurityParameters.cs110
-rw-r--r--crypto/src/crypto/tls/ServerDHParams.cs60
-rw-r--r--crypto/src/crypto/tls/ServerName.cs105
-rw-r--r--crypto/src/crypto/tls/ServerNameList.cs81
-rw-r--r--crypto/src/crypto/tls/ServerOnlyTlsAuthentication.cs15
-rw-r--r--crypto/src/crypto/tls/SessionParameters.cs137
-rw-r--r--crypto/src/crypto/tls/SignatureAlgorithm.cs15
-rw-r--r--crypto/src/crypto/tls/SignatureAndHashAlgorithm.cs94
-rw-r--r--crypto/src/crypto/tls/SignerInputBuffer.cs39
-rw-r--r--crypto/src/crypto/tls/SrpTlsClient.cs289
-rw-r--r--crypto/src/crypto/tls/SrtpProtectionProfile.cs15
-rw-r--r--crypto/src/crypto/tls/Ssl3Mac.cs206
-rw-r--r--crypto/src/crypto/tls/SupplementalDataEntry.cs26
-rw-r--r--crypto/src/crypto/tls/SupplementalDataType.cs13
-rw-r--r--crypto/src/crypto/tls/TlsAeadCipher.cs189
-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.cs606
-rw-r--r--crypto/src/crypto/tls/TlsCipher.cs16
-rw-r--r--crypto/src/crypto/tls/TlsCipherFactory.cs11
-rw-r--r--crypto/src/crypto/tls/TlsClient.cs215
-rw-r--r--crypto/src/crypto/tls/TlsClientContext.cs12
-rw-r--r--crypto/src/crypto/tls/TlsClientContextImpl.cs41
-rw-r--r--crypto/src/crypto/tls/TlsClientProtocol.cs861
-rw-r--r--crypto/src/crypto/tls/TlsCompression.cs12
-rw-r--r--crypto/src/crypto/tls/TlsContext.cs45
-rw-r--r--crypto/src/crypto/tls/TlsCredentials.cs9
-rw-r--r--crypto/src/crypto/tls/TlsDHKeyExchange.cs374
-rw-r--r--crypto/src/crypto/tls/TlsDHUtilities.cs147
-rw-r--r--crypto/src/crypto/tls/TlsDeflateCompression.cs97
-rw-r--r--crypto/src/crypto/tls/TlsDheKeyExchange.cs143
-rw-r--r--crypto/src/crypto/tls/TlsDsaSigner.cs114
-rw-r--r--crypto/src/crypto/tls/TlsDssSigner.cs29
-rw-r--r--crypto/src/crypto/tls/TlsECDHKeyExchange.cs351
-rw-r--r--crypto/src/crypto/tls/TlsECDheKeyExchange.cs234
-rw-r--r--crypto/src/crypto/tls/TlsECDsaSigner.cs29
-rw-r--r--crypto/src/crypto/tls/TlsEccUtilities.cs647
-rw-r--r--crypto/src/crypto/tls/TlsEncryptionCredentials.cs12
-rw-r--r--crypto/src/crypto/tls/TlsExtensionsUtilities.cs243
-rw-r--r--crypto/src/crypto/tls/TlsFatalAlert.cs32
-rw-r--r--crypto/src/crypto/tls/TlsHandshakeHash.cs22
-rw-r--r--crypto/src/crypto/tls/TlsKeyExchange.cs80
-rw-r--r--crypto/src/crypto/tls/TlsMac.cs255
-rw-r--r--crypto/src/crypto/tls/TlsNullCipher.cs136
-rw-r--r--crypto/src/crypto/tls/TlsNullCompression.cs19
-rw-r--r--crypto/src/crypto/tls/TlsPeer.cs62
-rw-r--r--crypto/src/crypto/tls/TlsProtocol.cs1102
-rw-r--r--crypto/src/crypto/tls/TlsProtocolHandler.cs1240
-rw-r--r--crypto/src/crypto/tls/TlsPskIdentity.cs15
-rw-r--r--crypto/src/crypto/tls/TlsPskKeyExchange.cs387
-rw-r--r--crypto/src/crypto/tls/TlsRsaKeyExchange.cs265
-rw-r--r--crypto/src/crypto/tls/TlsRsaSigner.cs131
-rw-r--r--crypto/src/crypto/tls/TlsRsaUtilities.cs152
-rw-r--r--crypto/src/crypto/tls/TlsServer.cs90
-rw-r--r--crypto/src/crypto/tls/TlsServerContext.cs11
-rw-r--r--crypto/src/crypto/tls/TlsServerContextImpl.cs20
-rw-r--r--crypto/src/crypto/tls/TlsServerProtocol.cs744
-rw-r--r--crypto/src/crypto/tls/TlsSession.cs15
-rw-r--r--crypto/src/crypto/tls/TlsSessionImpl.cs54
-rw-r--r--crypto/src/crypto/tls/TlsSigner.cs33
-rw-r--r--crypto/src/crypto/tls/TlsSignerCredentials.cs13
-rw-r--r--crypto/src/crypto/tls/TlsSrpKeyExchange.cs366
-rw-r--r--crypto/src/crypto/tls/TlsSrpUtilities.cs41
-rw-r--r--crypto/src/crypto/tls/TlsSrtpUtilities.cs62
-rw-r--r--crypto/src/crypto/tls/TlsStream.cs118
-rw-r--r--crypto/src/crypto/tls/TlsStreamCipher.cs164
-rw-r--r--crypto/src/crypto/tls/TlsUtilities.cs1885
-rw-r--r--crypto/src/crypto/tls/UrlAndHash.cs94
-rw-r--r--crypto/src/crypto/tls/UseSrtpData.cs56
-rw-r--r--crypto/src/crypto/tls/UserMappingType.cs13
-rw-r--r--crypto/src/crypto/util/Pack.cs475
-rw-r--r--crypto/src/math/BigInteger.cs6316
-rw-r--r--crypto/src/math/ec/ECAlgorithms.cs540
-rw-r--r--crypto/src/math/ec/ECCurve.cs1482
-rw-r--r--crypto/src/math/ec/ECFieldElement.cs2154
-rw-r--r--crypto/src/math/ec/ECPoint.cs2634
-rw-r--r--crypto/src/math/ec/ECPointMap.cs9
-rw-r--r--crypto/src/math/ec/LongArray.cs2201
-rw-r--r--crypto/src/math/ec/Mod.cs184
-rw-r--r--crypto/src/math/ec/Nat.cs1013
-rw-r--r--crypto/src/math/ec/ScaleXPointMap.cs20
-rw-r--r--crypto/src/math/ec/ScaleYPointMap.cs20
-rw-r--r--crypto/src/math/ec/abc/SimpleBigDecimal.cs241
-rw-r--r--crypto/src/math/ec/abc/Tnaf.cs1652
-rw-r--r--crypto/src/math/ec/abc/ZTauElement.cs36
-rw-r--r--crypto/src/math/ec/custom/djb/Curve25519.cs77
-rw-r--r--crypto/src/math/ec/custom/djb/Curve25519Field.cs253
-rw-r--r--crypto/src/math/ec/custom/djb/Curve25519FieldElement.cs233
-rw-r--r--crypto/src/math/ec/custom/djb/Curve25519Point.cs313
-rw-r--r--crypto/src/math/ec/custom/sec/Nat192.cs962
-rw-r--r--crypto/src/math/ec/custom/sec/Nat224.cs1176
-rw-r--r--crypto/src/math/ec/custom/sec/Nat256.cs1300
-rw-r--r--crypto/src/math/ec/custom/sec/Nat384.cs46
-rw-r--r--crypto/src/math/ec/custom/sec/Nat512.cs46
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192K1Curve.cs75
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192K1Field.cs176
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192K1FieldElement.cs212
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192K1Point.cs265
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192R1Curve.cs78
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192R1Field.cs281
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192R1FieldElement.cs187
-rw-r--r--crypto/src/math/ec/custom/sec/SecP192R1Point.cs277
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224K1Curve.cs75
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224K1Field.cs177
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224K1FieldElement.cs241
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224K1Point.cs265
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224R1Curve.cs78
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224R1Field.cs295
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224R1FieldElement.cs268
-rw-r--r--crypto/src/math/ec/custom/sec/SecP224R1Point.cs277
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256K1Curve.cs75
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256K1Field.cs178
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256K1FieldElement.cs213
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256K1Point.cs265
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256R1Curve.cs77
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256R1Field.cs309
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256R1FieldElement.cs187
-rw-r--r--crypto/src/math/ec/custom/sec/SecP256R1Point.cs277
-rw-r--r--crypto/src/math/ec/custom/sec/SecP384R1Curve.cs77
-rw-r--r--crypto/src/math/ec/custom/sec/SecP384R1Field.cs292
-rw-r--r--crypto/src/math/ec/custom/sec/SecP384R1FieldElement.cs209
-rw-r--r--crypto/src/math/ec/custom/sec/SecP384R1Point.cs278
-rw-r--r--crypto/src/math/ec/custom/sec/SecP521R1Curve.cs77
-rw-r--r--crypto/src/math/ec/custom/sec/SecP521R1Field.cs153
-rw-r--r--crypto/src/math/ec/custom/sec/SecP521R1FieldElement.cs166
-rw-r--r--crypto/src/math/ec/custom/sec/SecP521R1Point.cs273
-rw-r--r--crypto/src/math/ec/endo/ECEndomorphism.cs11
-rw-r--r--crypto/src/math/ec/endo/GlvEndomorphism.cs10
-rw-r--r--crypto/src/math/ec/endo/GlvTypeBEndomorphism.cs55
-rw-r--r--crypto/src/math/ec/endo/GlvTypeBParameters.cs60
-rw-r--r--crypto/src/math/ec/multiplier/AbstractECMultiplier.cs24
-rw-r--r--crypto/src/math/ec/multiplier/DoubleAddMultiplier.cs24
-rw-r--r--crypto/src/math/ec/multiplier/ECMultiplier.cs30
-rw-r--r--crypto/src/math/ec/multiplier/FixedPointCombMultiplier.cs59
-rw-r--r--crypto/src/math/ec/multiplier/FixedPointPreCompInfo.cs34
-rw-r--r--crypto/src/math/ec/multiplier/FixedPointUtilities.cs72
-rw-r--r--crypto/src/math/ec/multiplier/GlvMultiplier.cs40
-rw-r--r--crypto/src/math/ec/multiplier/MixedNafR2LMultiplier.cs75
-rw-r--r--crypto/src/math/ec/multiplier/MontgomeryLadderMultiplier.cs25
-rw-r--r--crypto/src/math/ec/multiplier/NafL2RMultiplier.cs30
-rw-r--r--crypto/src/math/ec/multiplier/NafR2LMultiplier.cs31
-rw-r--r--crypto/src/math/ec/multiplier/PreCompInfo.cs2
-rw-r--r--crypto/src/math/ec/multiplier/ReferenceMultiplier.cs35
-rw-r--r--crypto/src/math/ec/multiplier/WNafL2RMultiplier.cs98
-rw-r--r--crypto/src/math/ec/multiplier/WNafPreCompInfo.cs76
-rw-r--r--crypto/src/math/ec/multiplier/WNafUtilities.cs469
-rw-r--r--crypto/src/math/ec/multiplier/WTauNafMultiplier.cs200
-rw-r--r--crypto/src/math/ec/multiplier/WTauNafPreCompInfo.cs57
-rw-r--r--crypto/src/math/ec/multiplier/ZSignedDigitL2RMultiplier.cs29
-rw-r--r--crypto/src/math/ec/multiplier/ZSignedDigitR2LMultiplier.cs30
-rw-r--r--crypto/src/math/field/FiniteFields.cs54
-rw-r--r--crypto/src/math/field/GF2Polynomial.cs46
-rw-r--r--crypto/src/math/field/GenericPolynomialExtensionField.cs63
-rw-r--r--crypto/src/math/field/IExtensionField.cs12
-rw-r--r--crypto/src/math/field/IFiniteField.cs11
-rw-r--r--crypto/src/math/field/IPolynomial.cs15
-rw-r--r--crypto/src/math/field/IPolynomialExtensionField.cs10
-rw-r--r--crypto/src/math/field/PrimeField.cs44
-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.cs5
-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.cs5
-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.cs26
-rw-r--r--crypto/src/openpgp/PgpDataValidationException.cs3
-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.cs5
-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.cs5
-rw-r--r--crypto/src/openpgp/PgpLiteralData.cs63
-rw-r--r--crypto/src/openpgp/PgpLiteralDataGenerator.cs20
-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.cs176
-rw-r--r--crypto/src/openpgp/PgpPublicKeyRingBundle.cs7
-rw-r--r--crypto/src/openpgp/PgpSecretKey.cs792
-rw-r--r--crypto/src/openpgp/PgpSecretKeyRing.cs377
-rw-r--r--crypto/src/openpgp/PgpSecretKeyRingBundle.cs8
-rw-r--r--crypto/src/openpgp/PgpSignature.cs2
-rw-r--r--crypto/src/openpgp/PgpSignatureGenerator.cs4
-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.cs15
-rw-r--r--crypto/src/openpgp/PgpV3SignatureGenerator.cs199
-rw-r--r--crypto/src/openpgp/WrappedGeneratorStream.cs35
-rw-r--r--crypto/src/openssl/EncryptionException.cs5
-rw-r--r--crypto/src/openssl/IPasswordFinder.cs9
-rw-r--r--crypto/src/openssl/MiscPemGenerator.cs475
-rw-r--r--crypto/src/openssl/PEMException.cs5
-rw-r--r--crypto/src/openssl/PEMReader.cs699
-rw-r--r--crypto/src/openssl/PEMUtilities.cs158
-rw-r--r--crypto/src/openssl/PEMWriter.cs61
-rw-r--r--crypto/src/openssl/PasswordException.cs5
-rw-r--r--crypto/src/openssl/Pkcs8Generator.cs111
-rw-r--r--crypto/src/pkcs/AsymmetricKeyEntry.cs2
-rw-r--r--crypto/src/pkcs/EncryptedPrivateKeyInfoFactory.cs63
-rw-r--r--crypto/src/pkcs/PKCS12StoreBuilder.cs41
-rw-r--r--crypto/src/pkcs/Pkcs10CertificationRequest.cs3
-rw-r--r--crypto/src/pkcs/Pkcs10CertificationRequestDelaySigned.cs2
-rw-r--r--crypto/src/pkcs/Pkcs12Entry.cs64
-rw-r--r--crypto/src/pkcs/Pkcs12Store.cs2378
-rw-r--r--crypto/src/pkcs/Pkcs12Utilities.cs77
-rw-r--r--crypto/src/pkcs/PrivateKeyInfoFactory.cs386
-rw-r--r--crypto/src/pkcs/X509CertificateEntry.cs2
-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.cs2
-rw-r--r--crypto/src/pkix/PkixCertPathBuilder.cs205
-rw-r--r--crypto/src/pkix/PkixCertPathBuilderException.cs5
-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.cs7
-rw-r--r--crypto/src/pkix/PkixCertPathValidatorResult.cs69
-rw-r--r--crypto/src/pkix/PkixCertPathValidatorUtilities.cs10
-rw-r--r--crypto/src/pkix/PkixCrlUtilities.cs114
-rw-r--r--crypto/src/pkix/PkixNameConstraintValidator.cs1937
-rw-r--r--crypto/src/pkix/PkixNameConstraintValidatorException.cs6
-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.cs8
-rw-r--r--crypto/src/pkix/Rfc3281CertPathUtilities.cs8
-rw-r--r--crypto/src/pkix/TrustAnchor.cs259
-rw-r--r--crypto/src/security/AgreementUtilities.cs5
-rw-r--r--crypto/src/security/CipherUtilities.cs1357
-rw-r--r--crypto/src/security/DigestUtilities.cs193
-rw-r--r--crypto/src/security/DotNetUtilities.cs49
-rw-r--r--crypto/src/security/GeneralSecurityException.cs5
-rw-r--r--crypto/src/security/GeneratorUtilities.cs646
-rw-r--r--crypto/src/security/InvalidKeyException.cs5
-rw-r--r--crypto/src/security/InvalidParameterException.cs5
-rw-r--r--crypto/src/security/KeyException.cs5
-rw-r--r--crypto/src/security/MacUtilities.cs407
-rw-r--r--crypto/src/security/NoSuchAlgorithmException.cs5
-rw-r--r--crypto/src/security/ParameterUtilities.cs612
-rw-r--r--crypto/src/security/PbeUtilities.cs1255
-rw-r--r--crypto/src/security/PrivateKeyFactory.cs327
-rw-r--r--crypto/src/security/PublicKeyFactory.cs398
-rw-r--r--crypto/src/security/SecureRandom.cs6
-rw-r--r--crypto/src/security/SecurityUtilityException.cs3
-rw-r--r--crypto/src/security/SignatureException.cs5
-rw-r--r--crypto/src/security/SignerUtilities.cs728
-rw-r--r--crypto/src/security/WrapperUtilities.cs272
-rw-r--r--crypto/src/security/cert/CertificateEncodingException.cs5
-rw-r--r--crypto/src/security/cert/CertificateException.cs5
-rw-r--r--crypto/src/security/cert/CertificateExpiredException.cs5
-rw-r--r--crypto/src/security/cert/CertificateNotYetValidException.cs5
-rw-r--r--crypto/src/security/cert/CertificateParsingException.cs5
-rw-r--r--crypto/src/security/cert/CrlException.cs5
-rw-r--r--crypto/src/tsp/GenTimeAccuracy.cs33
-rw-r--r--crypto/src/tsp/TSPAlgorithms.cs48
-rw-r--r--crypto/src/tsp/TSPException.cs5
-rw-r--r--crypto/src/tsp/TSPUtil.cs202
-rw-r--r--crypto/src/tsp/TSPValidationException.cs7
-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.cs700
-rw-r--r--crypto/src/util/BigIntegers.cs132
-rw-r--r--crypto/src/util/Enums.cs79
-rw-r--r--crypto/src/util/IMemoable.cs29
-rw-r--r--crypto/src/util/Integers.cs17
-rw-r--r--crypto/src/util/MemoableResetException.cs27
-rw-r--r--crypto/src/util/Platform.cs117
-rw-r--r--crypto/src/util/Strings.cs92
-rw-r--r--crypto/src/util/Times.cs14
-rw-r--r--crypto/src/util/collections/CollectionUtilities.cs65
-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.cs185
-rw-r--r--crypto/src/util/encoders/Base64Encoder.cs557
-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.cs241
-rw-r--r--crypto/src/util/encoders/HexEncoder.cs328
-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.cs11
-rw-r--r--crypto/src/util/io/BaseOutputStream.cs9
-rw-r--r--crypto/src/util/io/NullOutputStream.cs18
-rw-r--r--crypto/src/util/io/PushbackStream.cs52
-rw-r--r--crypto/src/util/io/StreamOverflowException.cs5
-rw-r--r--crypto/src/util/io/Streams.cs94
-rw-r--r--crypto/src/util/io/TeeInputStream.cs13
-rw-r--r--crypto/src/util/io/TeeOutputStream.cs13
-rw-r--r--crypto/src/util/io/pem/PemGenerationException.cs5
-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.cs24
-rw-r--r--crypto/src/util/zlib/ZInflaterInputStream.cs8
-rw-r--r--crypto/src/util/zlib/ZInputStream.cs17
-rw-r--r--crypto/src/util/zlib/ZOutputStream.cs71
-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.cs240
-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.cs7
-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.cs6
-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.cs5
-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.cs5
-rw-r--r--crypto/src/x509/store/X509StoreFactory.cs7
1290 files changed, 158880 insertions, 34219 deletions
diff --git a/crypto/src/AssemblyInfo.cs b/crypto/src/AssemblyInfo.cs

index 7064bf82e..7dd625878 100644 --- a/crypto/src/AssemblyInfo.cs +++ b/crypto/src/AssemblyInfo.cs
@@ -9,17 +9,12 @@ using System.Runtime.InteropServices; // 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: AssemblyCompany("The Legion of the Bouncy Castle Inc.")] [assembly: AssemblyProduct("Bouncy Castle for .NET")] -[assembly: AssemblyCopyright("Copyright (C) 2000-2011")] +[assembly: AssemblyCopyright("Copyright (C) 2000-2014")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -30,8 +25,11 @@ using System.Runtime.InteropServices; // Minor Version // Build Number // Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: -[assembly: AssemblyVersion(AssemblyInfo.Version)] +[assembly: AssemblyVersion("1.8.*")] // // In order to sign your assembly you must specify a key to use. Refer to the @@ -67,6 +65,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyKeyName("")] [assembly: CLSCompliant(true)] +[assembly: ComVisible(false)] // Start with no permissions //[assembly: PermissionSet(SecurityAction.RequestOptional, Unrestricted=false)] @@ -75,8 +74,3 @@ using System.Runtime.InteropServices; // 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
index 6c256db53..0c6b4413a 100644 --- a/crypto/src/asn1/ASN1StreamParser.cs +++ b/crypto/src/asn1/ASN1StreamParser.cs
@@ -8,7 +8,9 @@ namespace Org.BouncyCastle.Asn1 private readonly Stream _in; private readonly int _limit; - public Asn1StreamParser( + private readonly byte[][] tmpBuffers; + + public Asn1StreamParser( Stream inStream) : this(inStream, Asn1InputStream.FindLimit(inStream)) { @@ -23,7 +25,8 @@ namespace Org.BouncyCastle.Asn1 this._in = inStream; this._limit = limit; - } + this.tmpBuffers = new byte[16][]; + } public Asn1StreamParser( byte[] encoding) @@ -184,9 +187,8 @@ namespace Org.BouncyCastle.Asn1 case Asn1Tags.External: return new DerExternalParser(new Asn1StreamParser(defIn)); default: - // TODO Add DerUnknownTagParser class? - return new DerUnknownTag(true, tagNo, defIn.ToArray()); - } + throw new IOException("unknown tag " + tagNo + " encountered"); + } } // Some primitive encodings can be handled by parsers too... @@ -198,7 +200,7 @@ namespace Org.BouncyCastle.Asn1 try { - return Asn1InputStream.CreatePrimitiveDerObject(tagNo, defIn.ToArray()); + return Asn1InputStream.CreatePrimitiveDerObject(tagNo, defIn, tmpBuffers); } catch (ArgumentException e) { 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
index dfc1641a4..806cc95d5 100644 --- a/crypto/src/asn1/Asn1Exception.cs +++ b/crypto/src/asn1/Asn1Exception.cs
@@ -3,7 +3,10 @@ using System.IO; namespace Org.BouncyCastle.Asn1 { - public class Asn1Exception +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class Asn1Exception : IOException { public Asn1Exception() diff --git a/crypto/src/asn1/Asn1InputStream.cs b/crypto/src/asn1/Asn1InputStream.cs
index 9a9761653..18d13c32d 100644 --- a/crypto/src/asn1/Asn1InputStream.cs +++ b/crypto/src/asn1/Asn1InputStream.cs
@@ -7,333 +7,362 @@ 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); - } - } - } + /** + * 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; + + private readonly byte[][] tmpBuffers; + + 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; + this.tmpBuffers = new byte[16][]; + } + + /** + * 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: + throw new IOException("unknown tag " + tagNo + " encountered"); + } + } + + return CreatePrimitiveDerObject(tagNo, defIn, tmpBuffers); + } + + 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 byte[] GetBuffer(DefiniteLengthInputStream defIn, byte[][] tmpBuffers) + { + int len = defIn.GetRemaining(); + if (len >= tmpBuffers.Length) + { + return defIn.ToArray(); + } + + byte[] buf = tmpBuffers[len]; + if (buf == null) + { + buf = tmpBuffers[len] = new byte[len]; + } + + defIn.ReadAllIntoByteArray(buf); + + return buf; + } + + internal static Asn1Object CreatePrimitiveDerObject( + int tagNo, + DefiniteLengthInputStream defIn, + byte[][] tmpBuffers) + { + switch (tagNo) + { + case Asn1Tags.Boolean: + return DerBoolean.FromOctetString(GetBuffer(defIn, tmpBuffers)); + case Asn1Tags.Enumerated: + return DerEnumerated.FromOctetString(GetBuffer(defIn, tmpBuffers)); + case Asn1Tags.ObjectIdentifier: + return DerObjectIdentifier.FromOctetString(GetBuffer(defIn, tmpBuffers)); + } + + byte[] bytes = defIn.ToArray(); + + switch (tagNo) + { + case Asn1Tags.BitString: + return DerBitString.FromAsn1Octets(bytes); + case Asn1Tags.BmpString: + return new DerBmpString(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.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: + throw new IOException("unknown tag " + tagNo + " encountered"); + } + } + } } 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
index 8827d8329..40e5da480 100644 --- a/crypto/src/asn1/Asn1ParsingException.cs +++ b/crypto/src/asn1/Asn1ParsingException.cs
@@ -2,7 +2,10 @@ using System; namespace Org.BouncyCastle.Asn1 { - public class Asn1ParsingException +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class Asn1ParsingException : InvalidOperationException { public Asn1ParsingException() diff --git a/crypto/src/asn1/Asn1Sequence.cs b/crypto/src/asn1/Asn1Sequence.cs
index 3131ead84..5f9ea4460 100644 --- a/crypto/src/asn1/Asn1Sequence.cs +++ b/crypto/src/asn1/Asn1Sequence.cs
@@ -8,11 +8,11 @@ using Org.BouncyCastle.Utilities.Collections; namespace Org.BouncyCastle.Asn1 { public abstract class Asn1Sequence - : Asn1Object, IEnumerable + : Asn1Object, IEnumerable { private readonly IList seq; - /** + /** * return an Asn1Sequence from the given object. * * @param obj the object we want converted. @@ -25,22 +25,35 @@ namespace Org.BouncyCastle.Asn1 { 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"); - } - - /** + else if (obj is Asn1SequenceParser) + { + return Asn1Sequence.GetInstance(((Asn1SequenceParser)obj).ToAsn1Object()); + } + else if (obj is byte[]) + { + try + { + return Asn1Sequence.GetInstance(FromByteArray((byte[])obj)); + } + catch (IOException e) + { + throw new ArgumentException("failed to construct sequence from byte[]: " + e.Message); + } + } + else if (obj is Asn1Encodable) + { + Asn1Object primitive = ((Asn1Encodable)obj).ToAsn1Object(); + + if (primitive is Asn1Sequence) + { + return (Asn1Sequence)primitive; + } + } + + 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 @@ -60,17 +73,17 @@ namespace Org.BouncyCastle.Asn1 Asn1TaggedObject obj, bool explicitly) { - Asn1Object inner = obj.GetObject(); + Asn1Object inner = obj.GetObject(); - if (explicitly) + if (explicitly) { if (!obj.IsExplicit()) throw new ArgumentException("object implicit - explicit expected."); - return (Asn1Sequence) inner; + return (Asn1Sequence) inner; } - // + // // constructed object which appears to be explicitly tagged // when it should be implicit means we have to add the // surrounding sequence. @@ -82,78 +95,78 @@ namespace Org.BouncyCastle.Asn1 return new BerSequence(inner); } - return new DerSequence(inner); + return new DerSequence(inner); } - if (inner is Asn1Sequence) + if (inner is Asn1Sequence) { return (Asn1Sequence) inner; } - throw new ArgumentException("Unknown object in GetInstance: " + obj.GetType().FullName, "obj"); - } + throw new ArgumentException("Unknown object in GetInstance: " + obj.GetType().FullName, "obj"); + } - protected internal Asn1Sequence( - int capacity) - { + protected internal Asn1Sequence( + int capacity) + { seq = Platform.CreateArrayList(capacity); - } + } - public virtual IEnumerator GetEnumerator() - { - return seq.GetEnumerator(); - } + public virtual IEnumerator GetEnumerator() + { + return seq.GetEnumerator(); + } - [Obsolete("Use GetEnumerator() instead")] - public IEnumerator GetObjects() + [Obsolete("Use GetEnumerator() instead")] + public IEnumerator GetObjects() { return GetEnumerator(); } - private class Asn1SequenceParserImpl - : Asn1SequenceParser - { - private readonly Asn1Sequence outer; - private readonly int max; - private int index; + 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 Asn1SequenceParserImpl( + Asn1Sequence outer) + { + this.outer = outer; + this.max = outer.Count; + } - public IAsn1Convertible ReadObject() - { - if (index == max) - return null; + public IAsn1Convertible ReadObject() + { + if (index == max) + return null; - Asn1Encodable obj = outer[index++]; + Asn1Encodable obj = outer[index++]; - if (obj is Asn1Sequence) - return ((Asn1Sequence)obj).Parser; + if (obj is Asn1Sequence) + return ((Asn1Sequence)obj).Parser; - if (obj is Asn1Set) - return ((Asn1Set)obj).Parser; + if (obj is Asn1Set) + return ((Asn1Set)obj).Parser; - // NB: Asn1OctetString implements Asn1OctetStringParser directly + // NB: Asn1OctetString implements Asn1OctetStringParser directly // if (obj is Asn1OctetString) // return ((Asn1OctetString)obj).Parser; - return obj; - } + return obj; + } - public Asn1Object ToAsn1Object() - { - return outer; - } - } + public Asn1Object ToAsn1Object() + { + return outer; + } + } - public virtual Asn1SequenceParser Parser - { - get { return new Asn1SequenceParserImpl(this); } - } + public virtual Asn1SequenceParser Parser + { + get { return new Asn1SequenceParserImpl(this); } + } /** * return the object at the sequence position indicated by index. @@ -161,95 +174,95 @@ namespace Org.BouncyCastle.Asn1 * @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]; } - } + public virtual Asn1Encodable this[int index] + { + get { return (Asn1Encodable) seq[index]; } + } - [Obsolete("Use 'object[index]' syntax instead")] + [Obsolete("Use 'object[index]' syntax instead")] public Asn1Encodable GetObjectAt( int index) { return this[index]; } - [Obsolete("Use 'Count' property instead")] - public int Size + [Obsolete("Use 'Count' property instead")] + public int Size { - get { return Count; } + get { return Count; } } - public virtual int Count - { - get { return seq.Count; } - } + public virtual int Count + { + get { return seq.Count; } + } - protected override int Asn1GetHashCode() - { - int hc = Count; + protected override int Asn1GetHashCode() + { + int hc = Count; - foreach (object o in this) - { - hc *= 17; - if (o == null) - { - hc ^= DerNull.Instance.GetHashCode(); - } - else + foreach (object o in this) + { + hc *= 17; + if (o == null) { - hc ^= o.GetHashCode(); + hc ^= DerNull.Instance.GetHashCode(); + } + else + { + hc ^= o.GetHashCode(); } } - return hc; + return hc; } - protected override bool Asn1Equals( - Asn1Object asn1Object) - { - Asn1Sequence other = asn1Object as Asn1Sequence; + protected override bool Asn1Equals( + Asn1Object asn1Object) + { + Asn1Sequence other = asn1Object as Asn1Sequence; - if (other == null) + if (other == null) return false; - if (Count != other.Count) + if (Count != other.Count) return false; - IEnumerator s1 = GetEnumerator(); + IEnumerator s1 = GetEnumerator(); IEnumerator s2 = other.GetEnumerator(); - while (s1.MoveNext() && s2.MoveNext()) - { - Asn1Object o1 = GetCurrent(s1).ToAsn1Object(); - Asn1Object o2 = GetCurrent(s2).ToAsn1Object(); + while (s1.MoveNext() && s2.MoveNext()) + { + Asn1Object o1 = GetCurrent(s1).ToAsn1Object(); + Asn1Object o2 = GetCurrent(s2).ToAsn1Object(); - if (!o1.Equals(o2)) - return false; - } + if (!o1.Equals(o2)) + return false; + } - return true; + return true; } - private Asn1Encodable GetCurrent(IEnumerator e) - { - Asn1Encodable encObj = (Asn1Encodable)e.Current; + 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; + // unfortunately null was allowed as a substitute for DER null + if (encObj == null) + return DerNull.Instance; - return encObj; - } + return encObj; + } - protected internal void AddObject( + protected internal void AddObject( Asn1Encodable obj) { seq.Add(obj); } - public override string ToString() - { - return CollectionUtilities.ToString(seq); - } + public override string ToString() + { + return CollectionUtilities.ToString(seq); + } } } diff --git a/crypto/src/asn1/Asn1Set.cs b/crypto/src/asn1/Asn1Set.cs
index f5b66495c..2e77ca2a9 100644 --- a/crypto/src/asn1/Asn1Set.cs +++ b/crypto/src/asn1/Asn1Set.cs
@@ -1,5 +1,6 @@ using System; using System.Collections; +using System.IO; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Collections; @@ -11,7 +12,7 @@ namespace Org.BouncyCastle.Asn1 { private readonly IList _set; - /** + /** * return an ASN1Set from the given object. * * @param obj the object we want converted. @@ -24,8 +25,32 @@ namespace Org.BouncyCastle.Asn1 { return (Asn1Set)obj; } + else if (obj is Asn1SetParser) + { + return Asn1Set.GetInstance(((Asn1SetParser)obj).ToAsn1Object()); + } + else if (obj is byte[]) + { + try + { + return Asn1Set.GetInstance(FromByteArray((byte[])obj)); + } + catch (IOException e) + { + throw new ArgumentException("failed to construct set from byte[]: " + e.Message); + } + } + else if (obj is Asn1Encodable) + { + Asn1Object primitive = ((Asn1Encodable)obj).ToAsn1Object(); + + if (primitive is Asn1Set) + { + return (Asn1Set)primitive; + } + } - throw new ArgumentException("Unknown object in GetInstance: " + obj.GetType().FullName, "obj"); + throw new ArgumentException("Unknown object in GetInstance: " + obj.GetType().FullName, "obj"); } /** @@ -48,17 +73,17 @@ namespace Org.BouncyCastle.Asn1 Asn1TaggedObject obj, bool explicitly) { - Asn1Object inner = obj.GetObject(); + Asn1Object inner = obj.GetObject(); - if (explicitly) + if (explicitly) { if (!obj.IsExplicit()) throw new ArgumentException("object implicit - explicit expected."); - return (Asn1Set) inner; + return (Asn1Set) inner; } - // + // // constructed object which appears to be explicitly tagged // and it's really implicit means we have to add the // surrounding sequence. @@ -68,7 +93,7 @@ namespace Org.BouncyCastle.Asn1 return new DerSet(inner); } - if (inner is Asn1Set) + if (inner is Asn1Set) { return (Asn1Set) inner; } @@ -77,250 +102,250 @@ namespace Org.BouncyCastle.Asn1 // in this case the parser returns a sequence, convert it // into a set. // - if (inner is Asn1Sequence) + if (inner is Asn1Sequence) { - Asn1EncodableVector v = new Asn1EncodableVector(); - Asn1Sequence s = (Asn1Sequence) inner; + Asn1EncodableVector v = new Asn1EncodableVector(); + Asn1Sequence s = (Asn1Sequence) inner; - foreach (Asn1Encodable ae in s) - { + foreach (Asn1Encodable ae in s) + { v.Add(ae); } - // TODO Should be able to construct set directly from sequence? - return new DerSet(v, false); + // 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"); - } + throw new ArgumentException("Unknown object in GetInstance: " + obj.GetType().FullName, "obj"); + } - protected internal Asn1Set( - int capacity) + protected internal Asn1Set( + int capacity) { - _set = Platform.CreateArrayList(capacity); + _set = Platform.CreateArrayList(capacity); } - public virtual IEnumerator GetEnumerator() - { - return _set.GetEnumerator(); - } + public virtual IEnumerator GetEnumerator() + { + return _set.GetEnumerator(); + } - [Obsolete("Use GetEnumerator() instead")] + [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]; } - } + public virtual Asn1Encodable this[int index] + { + get { return (Asn1Encodable) _set[index]; } + } - [Obsolete("Use 'object[index]' syntax instead")] - public Asn1Encodable GetObjectAt( + [Obsolete("Use 'object[index]' syntax instead")] + public Asn1Encodable GetObjectAt( int index) { return this[index]; } - [Obsolete("Use 'Count' property instead")] - public int Size + [Obsolete("Use 'Count' property instead")] + public int Size { - get { return Count; } + 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 + 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; - } + return obj; + } - public virtual Asn1Object ToAsn1Object() - { - return outer; - } - } + public virtual Asn1Object ToAsn1Object() + { + return outer; + } + } - public Asn1SetParser Parser - { - get { return new Asn1SetParserImpl(this); } - } + public Asn1SetParser Parser + { + get { return new Asn1SetParserImpl(this); } + } - protected override int Asn1GetHashCode() - { + protected override int Asn1GetHashCode() + { int hc = Count; - foreach (object o in this) - { - hc *= 17; - if (o == null) - { - hc ^= DerNull.Instance.GetHashCode(); - } - else + foreach (object o in this) + { + hc *= 17; + if (o == null) { - hc ^= o.GetHashCode(); + hc ^= DerNull.Instance.GetHashCode(); + } + else + { + hc ^= o.GetHashCode(); } } - return hc; + return hc; } - protected override bool Asn1Equals( - Asn1Object asn1Object) + protected override bool Asn1Equals( + Asn1Object asn1Object) { - Asn1Set other = asn1Object as Asn1Set; + Asn1Set other = asn1Object as Asn1Set; - if (other == null) - return false; + if (other == null) + return false; - if (Count != other.Count) + if (Count != other.Count) { return false; } - IEnumerator s1 = GetEnumerator(); + IEnumerator s1 = GetEnumerator(); IEnumerator s2 = other.GetEnumerator(); - while (s1.MoveNext() && s2.MoveNext()) - { - Asn1Object o1 = GetCurrent(s1).ToAsn1Object(); - Asn1Object o2 = GetCurrent(s2).ToAsn1Object(); + while (s1.MoveNext() && s2.MoveNext()) + { + Asn1Object o1 = GetCurrent(s1).ToAsn1Object(); + Asn1Object o2 = GetCurrent(s2).ToAsn1Object(); - if (!o1.Equals(o2)) - return false; - } + if (!o1.Equals(o2)) + return false; + } - return true; + return true; } - private Asn1Encodable GetCurrent(IEnumerator e) - { - Asn1Encodable encObj = (Asn1Encodable)e.Current; + 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; + // unfortunately null was allowed as a substitute for DER null + if (encObj == null) + return DerNull.Instance; - return encObj; - } + 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() + 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; - } - } + 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) + protected internal void AddObject( + Asn1Encodable obj) { _set.Add(obj); } - public override string ToString() - { - return CollectionUtilities.ToString(_set); - } - } + 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
index 644060765..7468a6b0b 100644 --- a/crypto/src/asn1/BEROctetStringGenerator.cs +++ b/crypto/src/asn1/BEROctetStringGenerator.cs
@@ -42,80 +42,76 @@ namespace Org.BouncyCastle.Asn1 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); - } - } + 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; + } + } + + public override void Close() + { + if (_off != 0) + { + DerOctetString.Encode(_derOut, _buf, 0, _off); + } + + _gen.WriteBerEnd(); + base.Close(); + } + } } } 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
index cfea89f21..4ae803c0e 100644 --- a/crypto/src/asn1/DefiniteLengthInputStream.cs +++ b/crypto/src/asn1/DefiniteLengthInputStream.cs
@@ -75,7 +75,17 @@ namespace Org.BouncyCastle.Asn1 return numRead; } - internal byte[] ToArray() + internal void ReadAllIntoByteArray(byte[] buf) + { + if (_remaining != buf.Length) + throw new ArgumentException("buffer length not right for data"); + + if ((_remaining -= Streams.ReadFully(_in, buf)) != 0) + throw new EndOfStreamException("DEF length " + _originalLength + " object truncated by " + _remaining); + SetParentEofDetect(true); + } + + internal byte[] ToArray() { if (_remaining == 0) return EmptyBytes; 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
index 41ccae8a1..66791d16c 100644 --- a/crypto/src/asn1/DerBoolean.cs +++ b/crypto/src/asn1/DerBoolean.cs
@@ -7,10 +7,10 @@ namespace Org.BouncyCastle.Asn1 { private readonly byte value; - public static readonly DerBoolean False = new DerBoolean(false); + 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. @@ -23,10 +23,10 @@ namespace Org.BouncyCastle.Asn1 return (DerBoolean) obj; } - throw new ArgumentException("illegal object in GetInstance: " + obj.GetType().Name); + throw new ArgumentException("illegal object in GetInstance: " + obj.GetType().Name); } - /** + /** * return a DerBoolean from the passed in bool. */ public static DerBoolean GetInstance( @@ -35,7 +35,7 @@ namespace Org.BouncyCastle.Asn1 return value ? True : False; } - /** + /** * return a Boolean from a tagged object. * * @param obj the tagged object holding the object we want @@ -48,63 +48,75 @@ namespace Org.BouncyCastle.Asn1 Asn1TaggedObject obj, bool isExplicit) { - Asn1Object o = obj.GetObject(); + Asn1Object o = obj.GetObject(); - if (isExplicit || o is DerBoolean) - { - return GetInstance(o); - } + if (isExplicit || o is DerBoolean) + { + return GetInstance(o); + } - return new DerBoolean(((Asn1OctetString)o).GetOctets()); + return FromOctetString(((Asn1OctetString)o).GetOctets()); } - public DerBoolean( + public DerBoolean( byte[] val) { - if (val.Length != 1) - throw new ArgumentException("byte value should have 1 byte in it", "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? + // TODO Are there any constraints on the possible byte values? this.value = val[0]; } - private DerBoolean( + private DerBoolean( bool value) { this.value = value ? (byte)0xff : (byte)0; } - public bool IsTrue - { - get { return value != 0; } - } + public bool IsTrue + { + get { return value != 0; } + } - internal override void Encode( + 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 }); + // 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) + protected override bool Asn1Equals( + Asn1Object asn1Object) { - DerBoolean other = asn1Object as DerBoolean; + DerBoolean other = asn1Object as DerBoolean; - if (other == null) - return false; + if (other == null) + return false; - return IsTrue == other.IsTrue; + return IsTrue == other.IsTrue; + } + + protected override int Asn1GetHashCode() + { + return IsTrue.GetHashCode(); } - protected override int Asn1GetHashCode() - { - return IsTrue.GetHashCode(); + public override string ToString() + { + return IsTrue ? "TRUE" : "FALSE"; } - public override string ToString() - { - return IsTrue ? "TRUE" : "FALSE"; - } - } + internal static DerBoolean FromOctetString(byte[] value) + { + if (value.Length != 1) + { + throw new ArgumentException("BOOLEAN value should have 1 byte in it", "value"); + } + + byte b = value[0]; + + return b == 0 ? False : b == 0xFF ? True : new DerBoolean(value); + } + } } diff --git a/crypto/src/asn1/DerEnumerated.cs b/crypto/src/asn1/DerEnumerated.cs
index 0e67e6dbe..2638b0205 100644 --- a/crypto/src/asn1/DerEnumerated.cs +++ b/crypto/src/asn1/DerEnumerated.cs
@@ -10,7 +10,7 @@ namespace Org.BouncyCastle.Asn1 { private readonly byte[] bytes; - /** + /** * return an integer from the passed in object * * @exception ArgumentException if the object cannot be converted. @@ -39,14 +39,14 @@ namespace Org.BouncyCastle.Asn1 Asn1TaggedObject obj, bool isExplicit) { - Asn1Object o = obj.GetObject(); + Asn1Object o = obj.GetObject(); - if (isExplicit || o is DerEnumerated) - { - return GetInstance(o); - } + if (isExplicit || o is DerEnumerated) + { + return GetInstance(o); + } - return new DerEnumerated(((Asn1OctetString)o).GetOctets()); + return FromOctetString(((Asn1OctetString)o).GetOctets()); } public DerEnumerated( @@ -69,32 +69,56 @@ namespace Org.BouncyCastle.Asn1 public BigInteger Value { - get - { - return new BigInteger(bytes); - } + get { return new BigInteger(bytes); } } - internal override void Encode( + internal override void Encode( DerOutputStream derOut) { derOut.WriteEncoded(Asn1Tags.Enumerated, bytes); } - protected override bool Asn1Equals( - Asn1Object asn1Object) + protected override bool Asn1Equals( + Asn1Object asn1Object) { - DerEnumerated other = asn1Object as DerEnumerated; + DerEnumerated other = asn1Object as DerEnumerated; + + if (other == null) + return false; - if (other == null) - return false; + return Arrays.AreEqual(this.bytes, other.bytes); + } - return Arrays.AreEqual(this.bytes, other.bytes); + protected override int Asn1GetHashCode() + { + return Arrays.GetHashCode(bytes); } - protected override int Asn1GetHashCode() - { - return Arrays.GetHashCode(bytes); + private static readonly DerEnumerated[] cache = new DerEnumerated[12]; + + internal static DerEnumerated FromOctetString(byte[] enc) + { + if (enc.Length == 0) + { + throw new ArgumentException("ENUMERATED has zero length", "enc"); + } + + if (enc.Length == 1) + { + int value = enc[0]; + if (value < cache.Length) + { + DerEnumerated cached = cache[value]; + if (cached != null) + { + return cached; + } + + return cache[value] = new DerEnumerated(Arrays.Clone(enc)); + } + } + + return new DerEnumerated(Arrays.Clone(enc)); } } } 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
index 0a0e6fd7c..548a268e1 100644 --- a/crypto/src/asn1/DerGeneralizedTime.cs +++ b/crypto/src/asn1/DerGeneralizedTime.cs
@@ -14,7 +14,7 @@ namespace Org.BouncyCastle.Asn1 { private readonly string time; - /** + /** * return a generalized time from the passed in object * * @exception ArgumentException if the object cannot be converted. @@ -22,15 +22,15 @@ namespace Org.BouncyCastle.Asn1 public static DerGeneralizedTime GetInstance( object obj) { - if (obj == null || obj is DerGeneralizedTime) + if (obj == null || obj is DerGeneralizedTime) { return (DerGeneralizedTime)obj; } - throw new ArgumentException("illegal object in GetInstance: " + obj.GetType().Name, "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 @@ -43,41 +43,41 @@ namespace Org.BouncyCastle.Asn1 Asn1TaggedObject obj, bool isExplicit) { - Asn1Object o = obj.GetObject(); + Asn1Object o = obj.GetObject(); - if (isExplicit || o is DerGeneralizedTime) - { - return GetInstance(o); - } + 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; - return new DerGeneralizedTime(((Asn1OctetString)o).GetOctets()); + try + { + ToDateTime(); + } + catch (FormatException e) + { + throw new ArgumentException("invalid date string: " + e.Message); + } } - /** - * 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( @@ -86,7 +86,7 @@ namespace Org.BouncyCastle.Asn1 this.time = time.ToString(@"yyyyMMddHHmmss\Z"); } - internal DerGeneralizedTime( + internal DerGeneralizedTime( byte[] bytes) { // @@ -95,16 +95,16 @@ namespace Org.BouncyCastle.Asn1 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. + * @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> @@ -154,151 +154,162 @@ namespace Org.BouncyCastle.Asn1 return time + CalculateGmtOffset(); } - private string CalculateGmtOffset() - { - char sign = '+'; + private string CalculateGmtOffset() + { + char sign = '+'; DateTime time = ToDateTime(); -#if (SILVERLIGHT || PORTABLE) +#if SILVERLIGHT 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; + 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; + 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? + 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 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 s, string format, bool makeUniversal) + { + /* + * NOTE: DateTime.Kind and DateTimeStyles.AssumeUniversal not available in .NET 1.1 + */ + DateTimeStyles style = DateTimeStyles.None; + if (format.EndsWith("Z")) + { + try + { + style = (DateTimeStyles)Enum.Parse(typeof(DateTimeStyles), "AssumeUniversal"); + } + catch (Exception) + { + } + + style |= DateTimeStyles.AdjustToUniversal; + } + + DateTime dt = DateTime.ParseExact(s, format, DateTimeFormatInfo.InvariantInfo, style); + + return makeUniversal ? dt.ToUniversalTime() : dt; + } + + private bool HasFractionalSeconds + { + get { return time.IndexOf('.') == 14; } + } + + private byte[] GetOctets() { return Strings.ToAsciiByteArray(time); } - internal override void Encode( + internal override void Encode( DerOutputStream derOut) { derOut.WriteEncoded(Asn1Tags.GeneralizedTime, GetOctets()); } - protected override bool Asn1Equals( - Asn1Object asn1Object) + protected override bool Asn1Equals( + Asn1Object asn1Object) { - DerGeneralizedTime other = asn1Object as DerGeneralizedTime; + DerGeneralizedTime other = asn1Object as DerGeneralizedTime; - if (other == null) - return false; + if (other == null) + return false; - return this.time.Equals(other.time); + return this.time.Equals(other.time); } - protected override int Asn1GetHashCode() - { + 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
index 7dc963729..f9f6a79d6 100644 --- a/crypto/src/asn1/DerObjectIdentifier.cs +++ b/crypto/src/asn1/DerObjectIdentifier.cs
@@ -4,33 +4,32 @@ using System.Text; using System.Text.RegularExpressions; using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; 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; - private readonly string identifier; + private byte[] body = null; - /** + /** * return an Oid from the passed in object * * @exception ArgumentException if the object cannot be converted. */ - public static DerObjectIdentifier GetInstance( - object obj) + 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"); + if (obj is byte[]) + return FromOctetString((byte[])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 @@ -46,27 +45,35 @@ namespace Org.BouncyCastle.Asn1 return GetInstance(obj.GetObject()); } - public DerObjectIdentifier( + public DerObjectIdentifier( string identifier) { - if (identifier == null) - throw new ArgumentNullException("identifier"); - if (!OidRegex.IsMatch(identifier)) - throw new FormatException("string " + identifier + " not an OID"); + if (identifier == null) + throw new ArgumentNullException("identifier"); + if (!IsValidIdentifier(identifier)) + throw new FormatException("string " + identifier + " not an OID"); - this.identifier = identifier; + this.identifier = identifier; } - // TODO Change to ID? - public string Id + internal DerObjectIdentifier(DerObjectIdentifier oid, string branchID) + { + if (!IsValidBranchID(branchID, 0)) + throw new ArgumentException("string " + branchID + " not a valid OID branch", "branchID"); + + this.identifier = oid.Id + "." + branchID; + } + + // TODO Change to ID? + public string Id { get { return identifier; } } - public virtual DerObjectIdentifier Branch(string branchID) - { - return new DerObjectIdentifier(identifier + "." + branchID); - } + public virtual DerObjectIdentifier Branch(string branchID) + { + return new DerObjectIdentifier(this, branchID); + } /** * Return true if this oid is an extension of the passed in branch, stem. @@ -79,164 +86,262 @@ namespace Org.BouncyCastle.Asn1 return id.Length > stemId.Length && id[stemId.Length] == '.' && id.StartsWith(stemId); } - internal DerObjectIdentifier( - byte[] bytes) - : this(MakeOidStringFromBytes(bytes)) + internal DerObjectIdentifier(byte[] bytes) { + this.identifier = MakeOidStringFromBytes(bytes); + this.body = Arrays.Clone(bytes); } - private void WriteField( + 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); - } - } + 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); + } - internal override void Encode( - DerOutputStream derOut) + private void WriteField( + Stream outputStream, + BigInteger fieldValue) { - OidTokenizer tok = new OidTokenizer(identifier); - MemoryStream bOut = new MemoryStream(); - DerOutputStream dOut = new DerOutputStream(bOut); + 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); + } + } - string token = tok.NextToken(); - int first = int.Parse(token); + private void DoOutput(MemoryStream bOut) + { + OidTokenizer tok = new OidTokenizer(identifier); - token = tok.NextToken(); - int second = int.Parse(token); + string token = tok.NextToken(); + int first = int.Parse(token) * 40; - WriteField(bOut, first * 40 + second); + token = tok.NextToken(); + if (token.Length <= 18) + { + WriteField(bOut, first + Int64.Parse(token)); + } + else + { + WriteField(bOut, new BigInteger(token).Add(BigInteger.ValueOf(first))); + } while (tok.HasMoreTokens) { - token = tok.NextToken(); - if (token.Length < 18) - { - WriteField(bOut, Int64.Parse(token)); - } - else - { - WriteField(bOut, new BigInteger(token)); - } - } + token = tok.NextToken(); + if (token.Length <= 18) + { + WriteField(bOut, Int64.Parse(token)); + } + else + { + WriteField(bOut, new BigInteger(token)); + } + } + } - dOut.Dispose(); + internal byte[] GetBody() + { + lock (this) + { + if (body == null) + { + MemoryStream bOut = new MemoryStream(); + DoOutput(bOut); + body = bOut.ToArray(); + } + } + + return body; + } - derOut.WriteEncoded(Asn1Tags.ObjectIdentifier, bOut.ToArray()); + internal override void Encode( + DerOutputStream derOut) + { + derOut.WriteEncoded(Asn1Tags.ObjectIdentifier, GetBody()); } - protected override int Asn1GetHashCode() - { + 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(); - } - } + 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 bool IsValidBranchID( + String branchID, int start) + { + bool periodAllowed = false; + + int pos = branchID.Length; + while (--pos >= start) + { + char ch = branchID[pos]; + + // TODO Leading zeroes? + if ('0' <= ch && ch <= '9') + { + periodAllowed = true; + continue; + } + + if (ch == '.') + { + if (!periodAllowed) + return false; + + periodAllowed = false; + continue; + } + + return false; + } + + return periodAllowed; + } + + private static bool IsValidIdentifier(string identifier) + { + if (identifier.Length < 3 || identifier[1] != '.') + return false; + + char first = identifier[0]; + if (first < '0' || first > '2') + return false; + + return IsValidBranchID(identifier, 2); + } + + private const long LONG_LIMIT = (long.MaxValue >> 7) - 0x7f; + + 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 <= LONG_LIMIT) + { + value += (b & 0x7f); + if ((b & 0x80) == 0) // end of number reached + { + if (first) + { + if (value < 40) + { + objId.Append('0'); + } + else if (value < 80) + { + objId.Append('1'); + value -= 40; + } + else + { + objId.Append('2'); + value -= 80; + } + first = false; + } + + objId.Append('.'); + objId.Append(value); + value = 0; + } + else + { + value <<= 7; + } + } + else + { + if (bigValue == null) + { + bigValue = BigInteger.ValueOf(value); + } + bigValue = bigValue.Or(BigInteger.ValueOf(b & 0x7f)); + if ((b & 0x80) == 0) + { + if (first) + { + objId.Append('2'); + bigValue = bigValue.Subtract(BigInteger.ValueOf(80)); + first = false; + } + + objId.Append('.'); + objId.Append(bigValue); + bigValue = null; + value = 0; + } + else + { + bigValue = bigValue.ShiftLeft(7); + } + } + } + + return objId.ToString(); + } + + private static readonly DerObjectIdentifier[] cache = new DerObjectIdentifier[1024]; + + internal static DerObjectIdentifier FromOctetString(byte[] enc) + { + int hashCode = Arrays.GetHashCode(enc); + int first = hashCode & 1023; + + lock (cache) + { + DerObjectIdentifier entry = cache[first]; + if (entry != null && Arrays.AreEqual(enc, entry.GetBody())) + { + return entry; + } + + return cache[first] = new DerObjectIdentifier(enc); + } + } + } } 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
index dd67d2fed..b50a77962 100644 --- a/crypto/src/asn1/DerSequence.cs +++ b/crypto/src/asn1/DerSequence.cs
@@ -75,7 +75,7 @@ namespace Org.BouncyCastle.Asn1 dOut.WriteObject(obj); } - dOut.Dispose(); + dOut.Close(); byte[] bytes = bOut.ToArray(); diff --git a/crypto/src/asn1/DerSet.cs b/crypto/src/asn1/DerSet.cs
index 6d3f438bd..c66dde8c7 100644 --- a/crypto/src/asn1/DerSet.cs +++ b/crypto/src/asn1/DerSet.cs
@@ -98,7 +98,7 @@ namespace Org.BouncyCastle.Asn1 dOut.WriteObject(obj); } - dOut.Dispose(); + dOut.Close(); byte[] bytes = bOut.ToArray(); 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/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
index 56c1bdfbc..09d0e3a42 100644 --- a/crypto/src/asn1/IndefiniteLengthInputStream.cs +++ b/crypto/src/asn1/IndefiniteLengthInputStream.cs
@@ -22,12 +22,15 @@ namespace Org.BouncyCastle.Asn1 bool eofOn00) { _eofOn00 = eofOn00; - CheckForEof(); + if (_eofOn00) + { + CheckForEof(); + } } - private bool CheckForEof() + private bool CheckForEof() { - if (_lookAhead == 0x00 && _eofOn00) + if (_lookAhead == 0x00) { int extra = RequireByte(); if (extra != 0) @@ -37,6 +40,7 @@ namespace Org.BouncyCastle.Asn1 _lookAhead = -1; SetParentEofDetect(true); + return true; } return _lookAhead < 0; } @@ -69,7 +73,7 @@ namespace Org.BouncyCastle.Asn1 public override int ReadByte() { - if (CheckForEof()) + if (_eofOn00 && CheckForEof()) return -1; int result = _lookAhead; 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
index 5e3dd076e..7301bc158 100644 --- a/crypto/src/asn1/LazyDERSequence.cs +++ b/crypto/src/asn1/LazyDERSequence.cs
@@ -7,10 +7,9 @@ namespace Org.BouncyCastle.Asn1 internal class LazyDerSequence : DerSequence { - private byte[] encoded; - private bool parsed = false; + private byte[] encoded; - internal LazyDerSequence( + internal LazyDerSequence( byte[] encoded) { this.encoded = encoded; @@ -20,7 +19,7 @@ namespace Org.BouncyCastle.Asn1 { lock (this) { - if (!parsed) + if (encoded != null) { Asn1InputStream e = new LazyAsn1InputStream(encoded); @@ -31,7 +30,6 @@ namespace Org.BouncyCastle.Asn1 } encoded = null; - parsed = true; } } } @@ -68,7 +66,7 @@ namespace Org.BouncyCastle.Asn1 { lock (this) { - if (parsed) + if (encoded == null) { base.Encode(derOut); } diff --git a/crypto/src/asn1/LazyDERSet.cs b/crypto/src/asn1/LazyDERSet.cs
index 84fce4808..e6c9319dd 100644 --- a/crypto/src/asn1/LazyDERSet.cs +++ b/crypto/src/asn1/LazyDERSet.cs
@@ -7,10 +7,9 @@ namespace Org.BouncyCastle.Asn1 internal class LazyDerSet : DerSet { - private byte[] encoded; - private bool parsed = false; + private byte[] encoded; - internal LazyDerSet( + internal LazyDerSet( byte[] encoded) { this.encoded = encoded; @@ -20,7 +19,7 @@ namespace Org.BouncyCastle.Asn1 { lock (this) { - if (!parsed) + if (encoded != null) { Asn1InputStream e = new LazyAsn1InputStream(encoded); @@ -31,7 +30,6 @@ namespace Org.BouncyCastle.Asn1 } encoded = null; - parsed = true; } } } @@ -68,7 +66,7 @@ namespace Org.BouncyCastle.Asn1 { lock (this) { - if (parsed) + if (encoded == null) { base.Encode(derOut); } 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
index 3c0164ce8..8a3ee5d0e 100644 --- a/crypto/src/asn1/cms/AttributeTable.cs +++ b/crypto/src/asn1/cms/AttributeTable.cs
@@ -10,7 +10,7 @@ namespace Org.BouncyCastle.Asn1.Cms { private readonly IDictionary attributes; -#if !(SILVERLIGHT || PORTABLE) +#if !SILVERLIGHT [Obsolete] public AttributeTable( Hashtable attrs) @@ -168,7 +168,7 @@ namespace Org.BouncyCastle.Asn1.Cms return Platform.CreateHashtable(attributes); } -#if !(SILVERLIGHT || PORTABLE) +#if !SILVERLIGHT [Obsolete("Use 'ToDictionary' instead")] public Hashtable ToHashtable() { @@ -176,7 +176,7 @@ namespace Org.BouncyCastle.Asn1.Cms } #endif - public Asn1EncodableVector ToAsn1EncodableVector() + public Asn1EncodableVector ToAsn1EncodableVector() { Asn1EncodableVector v = new Asn1EncodableVector(); diff --git a/crypto/src/asn1/cms/Attributes.cs b/crypto/src/asn1/cms/Attributes.cs
index 47cb66bdb..5b6b13034 100644 --- a/crypto/src/asn1/cms/Attributes.cs +++ b/crypto/src/asn1/cms/Attributes.cs
@@ -2,42 +2,54 @@ 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; - } - } + 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; + } + + public virtual Attribute[] GetAttributes() + { + Attribute[] rv = new Attribute[attributes.Count]; + + for (int i = 0; i != rv.Length; i++) + { + rv[i] = Attribute.GetInstance(attributes[i]); + } + + return rv; + } + + /** + * <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
index 59009bda7..2ad0a3c7c 100644 --- a/crypto/src/asn1/cms/CMSObjectIdentifiers.cs +++ b/crypto/src/asn1/cms/CMSObjectIdentifiers.cs
@@ -10,9 +10,19 @@ namespace Org.BouncyCastle.Asn1.Cms 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 AuthenticatedData = PkcsObjectIdentifiers.IdCTAuthData; public static readonly DerObjectIdentifier CompressedData = PkcsObjectIdentifiers.IdCTCompressedData; - public static readonly DerObjectIdentifier AuthEnvelopedData = PkcsObjectIdentifiers.IdCTAuthEnvelopedData; - public static readonly DerObjectIdentifier timestampedData = PkcsObjectIdentifiers.IdCTTimestampedData; + public static readonly DerObjectIdentifier AuthEnvelopedData = PkcsObjectIdentifiers.IdCTAuthEnvelopedData; + public static readonly DerObjectIdentifier timestampedData = PkcsObjectIdentifiers.IdCTTimestampedData; + + /** + * The other Revocation Info arc + * id-ri OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) + * dod(6) internet(1) security(5) mechanisms(5) pkix(7) ri(16) } + */ + public static readonly DerObjectIdentifier id_ri = new DerObjectIdentifier("1.3.6.1.5.5.7.16"); + + public static readonly DerObjectIdentifier id_ri_ocsp_response = id_ri.Branch("2"); + public static readonly DerObjectIdentifier id_ri_scvp = id_ri.Branch("4"); } } 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
index 61a277535..278ceca46 100644 --- a/crypto/src/asn1/cms/ContentInfo.cs +++ b/crypto/src/asn1/cms/ContentInfo.cs
@@ -5,79 +5,84 @@ 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); - } - } + 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); + } + + public static ContentInfo GetInstance(Asn1TaggedObject obj, bool isExplicit) + { + return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit)); + } + + 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
index 47cd4a9f5..09f291a93 100644 --- a/crypto/src/asn1/cms/EnvelopedData.cs +++ b/crypto/src/asn1/cms/EnvelopedData.cs
@@ -14,63 +14,58 @@ namespace Org.BouncyCastle.Asn1.Cms private EncryptedContentInfo encryptedContentInfo; private Asn1Set unprotectedAttrs; - public EnvelopedData( + 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.version = new DerInteger(CalculateVersion(originatorInfo, recipientInfos, unprotectedAttrs)); + this.originatorInfo = originatorInfo; this.recipientInfos = recipientInfos; this.encryptedContentInfo = encryptedContentInfo; this.unprotectedAttrs = unprotectedAttrs; } - public EnvelopedData( + public EnvelopedData( + OriginatorInfo originatorInfo, + Asn1Set recipientInfos, + EncryptedContentInfo encryptedContentInfo, + Attributes unprotectedAttrs) + { + this.version = new DerInteger(CalculateVersion(originatorInfo, recipientInfos, Asn1Set.GetInstance(unprotectedAttrs))); + this.originatorInfo = originatorInfo; + this.recipientInfos = recipientInfos; + this.encryptedContentInfo = encryptedContentInfo; + this.unprotectedAttrs = Asn1Set.GetInstance(unprotectedAttrs); + } + + [Obsolete("Use 'GetInstance' instead")] + public EnvelopedData( Asn1Sequence seq) { int index = 0; - version = (DerInteger) seq[index++]; + version = (DerInteger) seq[index++]; - object tmp = seq[index++]; + object tmp = seq[index++]; - if (tmp is Asn1TaggedObject) + if (tmp is Asn1TaggedObject) { originatorInfo = OriginatorInfo.GetInstance((Asn1TaggedObject) tmp, false); tmp = seq[index++]; } - recipientInfos = Asn1Set.GetInstance(tmp); + recipientInfos = Asn1Set.GetInstance(tmp); encryptedContentInfo = EncryptedContentInfo.GetInstance(seq[index++]); - if (seq.Count > index) + if (seq.Count > index) { - unprotectedAttrs = Asn1Set.GetInstance((Asn1TaggedObject) seq[index], false); + 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. @@ -86,7 +81,7 @@ namespace Org.BouncyCastle.Asn1.Cms return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); } - /** + /** * return an EnvelopedData object from the given object. * * @param obj the object we want converted. @@ -95,45 +90,39 @@ namespace Org.BouncyCastle.Asn1.Cms public static EnvelopedData GetInstance( object obj) { - if (obj == null || obj is EnvelopedData) - { + if (obj is EnvelopedData) return (EnvelopedData)obj; - } - - if (obj is Asn1Sequence) - { - return new EnvelopedData((Asn1Sequence)obj); - } - - throw new ArgumentException("Invalid EnvelopedData: " + obj.GetType().Name); + if (obj == null) + return null; + return new EnvelopedData(Asn1Sequence.GetInstance(obj)); } - public DerInteger Version - { - get { return version; } - } + public DerInteger Version + { + get { return version; } + } - public OriginatorInfo OriginatorInfo - { - get { return originatorInfo; } - } + public OriginatorInfo OriginatorInfo + { + get { return originatorInfo; } + } - public Asn1Set RecipientInfos - { - get { return recipientInfos; } - } + public Asn1Set RecipientInfos + { + get { return recipientInfos; } + } - public EncryptedContentInfo EncryptedContentInfo - { - get { return encryptedContentInfo; } - } + public EncryptedContentInfo EncryptedContentInfo + { + get { return encryptedContentInfo; } + } - public Asn1Set UnprotectedAttrs - { - get { return unprotectedAttrs; } - } + public Asn1Set UnprotectedAttrs + { + get { return unprotectedAttrs; } + } - /** + /** * Produce an object suitable for an Asn1OutputStream. * <pre> * EnvelopedData ::= Sequence { @@ -149,19 +138,39 @@ namespace Org.BouncyCastle.Asn1.Cms { Asn1EncodableVector v = new Asn1EncodableVector(version); - if (originatorInfo != null) + if (originatorInfo != null) { v.Add(new DerTaggedObject(false, 0, originatorInfo)); } - v.Add(recipientInfos, encryptedContentInfo); + v.Add(recipientInfos, encryptedContentInfo); - if (unprotectedAttrs != null) + if (unprotectedAttrs != null) { v.Add(new DerTaggedObject(false, 1, unprotectedAttrs)); } - return new BerSequence(v); + return new BerSequence(v); + } + + public static int CalculateVersion(OriginatorInfo originatorInfo, Asn1Set recipientInfos, Asn1Set unprotectedAttrs) + { + if (originatorInfo != null || unprotectedAttrs != null) + { + return 2; + } + + foreach (object o in recipientInfos) + { + RecipientInfo ri = RecipientInfo.GetInstance(o); + + if (ri.Version.Value.IntValue != 0) + { + return 2; + } + } + + return 0; } } } 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
index ac1af961f..b509e7e19 100644 --- a/crypto/src/asn1/cms/IssuerAndSerialNumber.cs +++ b/crypto/src/asn1/cms/IssuerAndSerialNumber.cs
@@ -12,19 +12,17 @@ namespace Org.BouncyCastle.Asn1.Cms private X509Name name; private DerInteger serialNumber; - public static IssuerAndSerialNumber GetInstance( - object obj) + 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); + if (obj == null) + return null; + IssuerAndSerialNumber existing = obj as IssuerAndSerialNumber; + if (existing != null) + return existing; + return new IssuerAndSerialNumber(Asn1Sequence.GetInstance(obj)); } + [Obsolete("Use GetInstance() instead")] public IssuerAndSerialNumber( Asn1Sequence seq) { @@ -32,7 +30,7 @@ namespace Org.BouncyCastle.Asn1.Cms this.serialNumber = (DerInteger) seq[1]; } - public IssuerAndSerialNumber( + public IssuerAndSerialNumber( X509Name name, BigInteger serialNumber) { @@ -48,19 +46,19 @@ namespace Org.BouncyCastle.Asn1.Cms this.serialNumber = serialNumber; } - public X509Name Name - { - get { return name; } - } + public X509Name Name + { + get { return name; } + } - public DerInteger SerialNumber - { - get { return serialNumber; } - } + public DerInteger SerialNumber + { + get { return serialNumber; } + } - public override Asn1Object ToAsn1Object() + public override Asn1Object ToAsn1Object() { - return new DerSequence(name, serialNumber); + 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
index 8b06b1946..80dd68e7c 100644 --- a/crypto/src/asn1/cms/OtherRecipientInfo.cs +++ b/crypto/src/asn1/cms/OtherRecipientInfo.cs
@@ -1,16 +1,14 @@ using System; -using Org.BouncyCastle.Asn1; - namespace Org.BouncyCastle.Asn1.Cms { public class OtherRecipientInfo : Asn1Encodable { - private DerObjectIdentifier oriType; - private Asn1Encodable oriValue; + private readonly DerObjectIdentifier oriType; + private readonly Asn1Encodable oriValue; - public OtherRecipientInfo( + public OtherRecipientInfo( DerObjectIdentifier oriType, Asn1Encodable oriValue) { @@ -18,14 +16,15 @@ namespace Org.BouncyCastle.Asn1.Cms this.oriValue = oriValue; } - public OtherRecipientInfo( + [Obsolete("Use GetInstance() instead")] + 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. @@ -41,7 +40,7 @@ namespace Org.BouncyCastle.Asn1.Cms return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); } - /** + /** * return a OtherRecipientInfo object from the given object. * * @param obj the object we want converted. @@ -50,26 +49,25 @@ namespace Org.BouncyCastle.Asn1.Cms 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); + if (obj == null) + return null; + OtherRecipientInfo existing = obj as OtherRecipientInfo; + if (existing != null) + return existing; + return new OtherRecipientInfo(Asn1Sequence.GetInstance(obj)); } - public DerObjectIdentifier OriType - { - get { return oriType; } - } + public virtual DerObjectIdentifier OriType + { + get { return oriType; } + } - public Asn1Encodable OriValue - { - get { return oriValue; } - } + public virtual Asn1Encodable OriValue + { + get { return oriValue; } + } - /** + /** * Produce an object suitable for an Asn1OutputStream. * <pre> * OtherRecipientInfo ::= Sequence { @@ -79,7 +77,7 @@ namespace Org.BouncyCastle.Asn1.Cms */ public override Asn1Object ToAsn1Object() { - return new DerSequence(oriType, oriValue); + return new DerSequence(oriType, oriValue); } } } diff --git a/crypto/src/asn1/cms/OtherRevocationInfoFormat.cs b/crypto/src/asn1/cms/OtherRevocationInfoFormat.cs new file mode 100644
index 000000000..78354896f --- /dev/null +++ b/crypto/src/asn1/cms/OtherRevocationInfoFormat.cs
@@ -0,0 +1,77 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class OtherRevocationInfoFormat + : Asn1Encodable + { + private readonly DerObjectIdentifier otherRevInfoFormat; + private readonly Asn1Encodable otherRevInfo; + + public OtherRevocationInfoFormat( + DerObjectIdentifier otherRevInfoFormat, + Asn1Encodable otherRevInfo) + { + this.otherRevInfoFormat = otherRevInfoFormat; + this.otherRevInfo = otherRevInfo; + } + + private OtherRevocationInfoFormat(Asn1Sequence seq) + { + otherRevInfoFormat = DerObjectIdentifier.GetInstance(seq[0]); + otherRevInfo = seq[1]; + } + + /** + * return a OtherRevocationInfoFormat object from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static OtherRevocationInfoFormat GetInstance(Asn1TaggedObject obj, bool isExplicit) + { + return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit)); + } + + /** + * return a OtherRevocationInfoFormat object from the given object. + * + * @param obj the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static OtherRevocationInfoFormat GetInstance(object obj) + { + if (obj is OtherRevocationInfoFormat) + return (OtherRevocationInfoFormat)obj; + if (obj != null) + return new OtherRevocationInfoFormat(Asn1Sequence.GetInstance(obj)); + return null; + } + + public virtual DerObjectIdentifier InfoFormat + { + get { return otherRevInfoFormat; } + } + + public virtual Asn1Encodable Info + { + get { return otherRevInfo; } + } + + /** + * Produce an object suitable for an ASN1OutputStream. + * <pre> + * OtherRevocationInfoFormat ::= SEQUENCE { + * otherRevInfoFormat OBJECT IDENTIFIER, + * otherRevInfo ANY DEFINED BY otherRevInfoFormat } + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + return new DerSequence(otherRevInfoFormat, otherRevInfo); + } + } +} 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/SCVPReqRes.cs b/crypto/src/asn1/cms/SCVPReqRes.cs new file mode 100644
index 000000000..486979a29 --- /dev/null +++ b/crypto/src/asn1/cms/SCVPReqRes.cs
@@ -0,0 +1,77 @@ +using System; + +namespace Org.BouncyCastle.Asn1.Cms +{ + public class ScvpReqRes + : Asn1Encodable + { + private readonly ContentInfo request; + private readonly ContentInfo response; + + public static ScvpReqRes GetInstance(object obj) + { + if (obj is ScvpReqRes) + return (ScvpReqRes)obj; + if (obj != null) + return new ScvpReqRes(Asn1Sequence.GetInstance(obj)); + return null; + } + + private ScvpReqRes(Asn1Sequence seq) + { + if (seq[0] is Asn1TaggedObject) + { + this.request = ContentInfo.GetInstance(Asn1TaggedObject.GetInstance(seq[0]), true); + this.response = ContentInfo.GetInstance(seq[1]); + } + else + { + this.request = null; + this.response = ContentInfo.GetInstance(seq[0]); + } + } + + public ScvpReqRes(ContentInfo response) + : this(null, response) + { + } + + public ScvpReqRes(ContentInfo request, ContentInfo response) + { + this.request = request; + this.response = response; + } + + public virtual ContentInfo Request + { + get { return request; } + } + + public virtual ContentInfo Response + { + get { return response; } + } + + /** + * <pre> + * ScvpReqRes ::= SEQUENCE { + * request [0] EXPLICIT ContentInfo OPTIONAL, + * response ContentInfo } + * </pre> + * @return the ASN.1 primitive representation. + */ + public override Asn1Object ToAsn1Object() + { + Asn1EncodableVector v = new Asn1EncodableVector(); + + if (request != null) + { + v.Add(new DerTaggedObject(true, 0, request)); + } + + v.Add(response); + + return new DerSequence(v); + } + } +} diff --git a/crypto/src/asn1/cms/SignedData.cs b/crypto/src/asn1/cms/SignedData.cs
index 40ed09f32..6cea79a49 100644 --- a/crypto/src/asn1/cms/SignedData.cs +++ b/crypto/src/asn1/cms/SignedData.cs
@@ -11,6 +11,11 @@ namespace Org.BouncyCastle.Asn1.Cms public class SignedData : Asn1Encodable { + private static readonly DerInteger Version1 = new DerInteger(1); + private static readonly DerInteger Version3 = new DerInteger(3); + private static readonly DerInteger Version4 = new DerInteger(4); + private static readonly DerInteger Version5 = new DerInteger(5); + private readonly DerInteger version; private readonly Asn1Set digestAlgorithms; private readonly ContentInfo contentInfo; @@ -20,159 +25,159 @@ namespace Org.BouncyCastle.Asn1.Cms private readonly bool certsBer; private readonly bool crlsBer; - public static SignedData GetInstance( + public static SignedData GetInstance( object obj) { if (obj is SignedData) return (SignedData) obj; - if (obj is Asn1Sequence) + 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( + 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 Version5; + } + + if (crls != null) + { + foreach (object obj in crls) + { + if (obj is Asn1TaggedObject) + { + otherCrl = true; + break; + } + } + } + + if (otherCrl) + { + return Version5; + } + + if (attrCertV2Found) + { + return Version4; + } + + if (attrCertV1Found || !CmsObjectIdentifiers.Data.Equals(contentOid) || CheckForVersion3(signerInfs)) + { + return Version3; + } + + return Version1; + } + + 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(); + e.MoveNext(); version = (DerInteger)e.Current; - e.MoveNext(); + e.MoveNext(); digestAlgorithms = ((Asn1Set)e.Current); - e.MoveNext(); + e.MoveNext(); contentInfo = ContentInfo.GetInstance(e.Current); - while (e.MoveNext()) + 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. @@ -183,16 +188,16 @@ namespace Org.BouncyCastle.Asn1.Cms 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); + 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 @@ -202,37 +207,37 @@ namespace Org.BouncyCastle.Asn1.Cms } } - 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; } - } - - /** + 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 { @@ -248,9 +253,9 @@ namespace Org.BouncyCastle.Asn1.Cms public override Asn1Object ToAsn1Object() { Asn1EncodableVector v = new Asn1EncodableVector( - version, digestAlgorithms, contentInfo); + version, digestAlgorithms, contentInfo); - if (certificates != null) + if (certificates != null) { if (certsBer) { @@ -262,7 +267,7 @@ namespace Org.BouncyCastle.Asn1.Cms } } - if (crls != null) + if (crls != null) { if (crlsBer) { @@ -274,9 +279,9 @@ namespace Org.BouncyCastle.Asn1.Cms } } - v.Add(signerInfos); + v.Add(signerInfos); - return new BerSequence(v); + 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
index ef71ddfc4..a4e893d96 100644 --- a/crypto/src/asn1/cms/SignerInfo.cs +++ b/crypto/src/asn1/cms/SignerInfo.cs
@@ -23,13 +23,13 @@ namespace Org.BouncyCastle.Asn1.Cms if (obj == null || obj is SignerInfo) return (SignerInfo) obj; - if (obj is Asn1Sequence) + if (obj is Asn1Sequence) return new SignerInfo((Asn1Sequence) obj); - throw new ArgumentException("Unknown object in factory: " + obj.GetType().FullName, "obj"); - } + throw new ArgumentException("Unknown object in factory: " + obj.GetType().FullName, "obj"); + } - public SignerInfo( + public SignerInfo( SignerIdentifier sid, AlgorithmIdentifier digAlgorithm, Asn1Set authenticatedAttributes, @@ -37,16 +37,8 @@ namespace Org.BouncyCastle.Asn1.Cms Asn1OctetString encryptedDigest, Asn1Set unauthenticatedAttributes) { - if (sid.IsTagged) - { - this.version = new DerInteger(3); - } - else - { - this.version = new DerInteger(1); - } - - this.sid = sid; + this.version = new DerInteger(sid.IsTagged ? 3 : 1); + this.sid = sid; this.digAlgorithm = digAlgorithm; this.authenticatedAttributes = authenticatedAttributes; this.digEncryptionAlgorithm = digEncryptionAlgorithm; @@ -54,7 +46,25 @@ namespace Org.BouncyCastle.Asn1.Cms this.unauthenticatedAttributes = unauthenticatedAttributes; } - public SignerInfo( + public SignerInfo( + SignerIdentifier sid, + AlgorithmIdentifier digAlgorithm, + Attributes authenticatedAttributes, + AlgorithmIdentifier digEncryptionAlgorithm, + Asn1OctetString encryptedDigest, + Attributes unauthenticatedAttributes) + { + this.version = new DerInteger(sid.IsTagged ? 3 : 1); + this.sid = sid; + this.digAlgorithm = digAlgorithm; + this.authenticatedAttributes = Asn1Set.GetInstance(authenticatedAttributes); + this.digEncryptionAlgorithm = digEncryptionAlgorithm; + this.encryptedDigest = encryptedDigest; + this.unauthenticatedAttributes = Asn1Set.GetInstance(unauthenticatedAttributes); + } + + [Obsolete("Use 'GetInstance' instead")] + public SignerInfo( Asn1Sequence seq) { IEnumerator e = seq.GetEnumerator(); @@ -62,20 +72,20 @@ namespace Org.BouncyCastle.Asn1.Cms e.MoveNext(); version = (DerInteger) e.Current; - e.MoveNext(); + e.MoveNext(); sid = SignerIdentifier.GetInstance(e.Current); - e.MoveNext(); + e.MoveNext(); digAlgorithm = AlgorithmIdentifier.GetInstance(e.Current); - e.MoveNext(); + e.MoveNext(); object obj = e.Current; - if (obj is Asn1TaggedObject) + if (obj is Asn1TaggedObject) { authenticatedAttributes = Asn1Set.GetInstance((Asn1TaggedObject) obj, false); - e.MoveNext(); + e.MoveNext(); digEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(e.Current); } else @@ -84,10 +94,10 @@ namespace Org.BouncyCastle.Asn1.Cms digEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(obj); } - e.MoveNext(); + e.MoveNext(); encryptedDigest = DerOctetString.GetInstance(e.Current); - if (e.MoveNext()) + if (e.MoveNext()) { unauthenticatedAttributes = Asn1Set.GetInstance((Asn1TaggedObject) e.Current, false); } @@ -97,42 +107,42 @@ namespace Org.BouncyCastle.Asn1.Cms } } - 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; } - } - - /** + 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 { @@ -155,21 +165,21 @@ namespace Org.BouncyCastle.Asn1.Cms public override Asn1Object ToAsn1Object() { Asn1EncodableVector v = new Asn1EncodableVector( - version, sid, digAlgorithm); + version, sid, digAlgorithm); - if (authenticatedAttributes != null) + if (authenticatedAttributes != null) { v.Add(new DerTaggedObject(false, 0, authenticatedAttributes)); } - v.Add(digEncryptionAlgorithm, encryptedDigest); + v.Add(digEncryptionAlgorithm, encryptedDigest); - if (unauthenticatedAttributes != null) + if (unauthenticatedAttributes != null) { v.Add(new DerTaggedObject(false, 1, unauthenticatedAttributes)); } - return new DerSequence(v); + 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
index c0037999a..d1a0f7ffb 100644 --- a/crypto/src/asn1/crmf/OptionalValidity.cs +++ b/crypto/src/asn1/crmf/OptionalValidity.cs
@@ -27,13 +27,20 @@ namespace Org.BouncyCastle.Asn1.Crmf public static OptionalValidity GetInstance(object obj) { - if (obj is OptionalValidity) + if (obj == null || obj is OptionalValidity) return (OptionalValidity)obj; - if (obj is Asn1Sequence) - return new OptionalValidity((Asn1Sequence)obj); + return new OptionalValidity(Asn1Sequence.GetInstance(obj)); + } - throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj"); + public virtual Time NotBefore + { + get { return notBefore; } + } + + public virtual Time NotAfter + { + get { return notAfter; } } /** 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
index 6e0e023b9..e2f2c1848 100644 --- a/crypto/src/asn1/cryptopro/CryptoProObjectIdentifiers.cs +++ b/crypto/src/asn1/cryptopro/CryptoProObjectIdentifiers.cs
@@ -11,10 +11,13 @@ namespace Org.BouncyCastle.Asn1.CryptoPro public const string GostID = "1.2.643.2.2"; public static readonly DerObjectIdentifier GostR3411 = new DerObjectIdentifier(GostID + ".9"); + public static readonly DerObjectIdentifier GostR3411Hmac = new DerObjectIdentifier(GostID + ".10"); - public static readonly DerObjectIdentifier GostR28147Cbc = new DerObjectIdentifier(GostID + ".21"); + public static readonly DerObjectIdentifier GostR28147Cbc = new DerObjectIdentifier(GostID + ".21"); - public static readonly DerObjectIdentifier GostR3410x94 = new DerObjectIdentifier(GostID + ".20"); + public static readonly DerObjectIdentifier ID_Gost28147_89_CryptoPro_A_ParamSet = new DerObjectIdentifier(GostID + ".31.1"); + + 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"); diff --git a/crypto/src/asn1/cryptopro/ECGOST3410NamedCurves.cs b/crypto/src/asn1/cryptopro/ECGOST3410NamedCurves.cs
index 998e0e06f..ca57c283d 100644 --- a/crypto/src/asn1/cryptopro/ECGOST3410NamedCurves.cs +++ b/crypto/src/asn1/cryptopro/ECGOST3410NamedCurves.cs
@@ -15,9 +15,9 @@ namespace Org.BouncyCastle.Asn1.CryptoPro */ public sealed class ECGost3410NamedCurves { - private ECGost3410NamedCurves() - { - } + private ECGost3410NamedCurves() + { + } internal static readonly IDictionary objIds = Platform.CreateHashtable(); internal static readonly IDictionary parameters = Platform.CreateHashtable(); @@ -31,17 +31,18 @@ namespace Org.BouncyCastle.Asn1.CryptoPro FpCurve curve = new FpCurve( mod_p, // p new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639316"), // a - new BigInteger("166")); // b + new BigInteger("166"), // b + mod_q, + BigInteger.One); ECDomainParameters ecParams = new ECDomainParameters( curve, - curve.CreatePoint( - BigInteger.One, // x - new BigInteger("64033881142927202683649881450433473985931760268884941288852745803908878638612"), // y - false), + curve.CreatePoint( + new BigInteger("1"), // x + new BigInteger("64033881142927202683649881450433473985931760268884941288852745803908878638612")), // y mod_q); - parameters[CryptoProObjectIdentifiers.GostR3410x2001CryptoProA] = ecParams; + parameters[CryptoProObjectIdentifiers.GostR3410x2001CryptoProA] = ecParams; mod_p = new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639319"); mod_q = new BigInteger("115792089237316195423570985008687907853073762908499243225378155805079068850323"); @@ -49,14 +50,15 @@ namespace Org.BouncyCastle.Asn1.CryptoPro curve = new FpCurve( mod_p, // p new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639316"), - new BigInteger("166")); + new BigInteger("166"), + mod_q, + BigInteger.One); ecParams = new ECDomainParameters( curve, - curve.CreatePoint( - BigInteger.One, // x - new BigInteger("64033881142927202683649881450433473985931760268884941288852745803908878638612"), // y - false), + curve.CreatePoint( + new BigInteger("1"), // x + new BigInteger("64033881142927202683649881450433473985931760268884941288852745803908878638612")), // y mod_q); parameters[CryptoProObjectIdentifiers.GostR3410x2001CryptoProXchA] = ecParams; @@ -67,14 +69,15 @@ namespace Org.BouncyCastle.Asn1.CryptoPro curve = new FpCurve( mod_p, // p new BigInteger("57896044618658097711785492504343953926634992332820282019728792003956564823190"), // a - new BigInteger("28091019353058090096996979000309560759124368558014865957655842872397301267595")); // b + new BigInteger("28091019353058090096996979000309560759124368558014865957655842872397301267595"), // b + mod_q, + BigInteger.One); ecParams = new ECDomainParameters( curve, curve.CreatePoint( - BigInteger.One, // x - new BigInteger("28792665814854611296992347458380284135028636778229113005756334730996303888124"), // y - false), + new BigInteger("1"), // x + new BigInteger("28792665814854611296992347458380284135028636778229113005756334730996303888124")), // y mod_q); // q parameters[CryptoProObjectIdentifiers.GostR3410x2001CryptoProB] = ecParams; @@ -85,14 +88,15 @@ namespace Org.BouncyCastle.Asn1.CryptoPro curve = new FpCurve( mod_p, // p new BigInteger("70390085352083305199547718019018437841079516630045180471284346843705633502616"), - new BigInteger("32858")); + new BigInteger("32858"), + mod_q, + BigInteger.One); ecParams = new ECDomainParameters( curve, curve.CreatePoint( - BigInteger.Zero, // x - new BigInteger("29818893917731240733471273240314769927240550812383695689146495261604565990247"), // y - false), + new BigInteger("0"), + new BigInteger("29818893917731240733471273240314769927240550812383695689146495261604565990247")), mod_q); parameters[CryptoProObjectIdentifiers.GostR3410x2001CryptoProXchB] = ecParams; @@ -102,17 +106,18 @@ namespace Org.BouncyCastle.Asn1.CryptoPro curve = new FpCurve( mod_p, // p new BigInteger("70390085352083305199547718019018437841079516630045180471284346843705633502616"), // a - new BigInteger("32858")); // b + new BigInteger("32858"), // b + mod_q, + BigInteger.One); ecParams = new ECDomainParameters( curve, curve.CreatePoint( - BigInteger.Zero, // x - new BigInteger("29818893917731240733471273240314769927240550812383695689146495261604565990247"), // y - false), + new BigInteger("0"), // x + new BigInteger("29818893917731240733471273240314769927240550812383695689146495261604565990247")), // y mod_q); // q - parameters[CryptoProObjectIdentifiers.GostR3410x2001CryptoProC] = ecParams; + parameters[CryptoProObjectIdentifiers.GostR3410x2001CryptoProC] = ecParams; objIds["GostR3410-2001-CryptoPro-A"] = CryptoProObjectIdentifiers.GostR3410x2001CryptoProA; objIds["GostR3410-2001-CryptoPro-B"] = CryptoProObjectIdentifiers.GostR3410x2001CryptoProB; @@ -139,14 +144,14 @@ namespace Org.BouncyCastle.Asn1.CryptoPro 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); } - } + /** + * 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) @@ -170,8 +175,8 @@ namespace Org.BouncyCastle.Asn1.CryptoPro return (string) names[oid]; } - public static DerObjectIdentifier GetOid( - string name) + 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
index 1d154cd27..35ce699e8 100644 --- a/crypto/src/asn1/ess/ESSCertIDv2.cs +++ b/crypto/src/asn1/ess/ESSCertIDv2.cs
@@ -6,133 +6,141 @@ 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); - } - - } + 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 obj) + { + if (obj == null) + return null; + EssCertIDv2 existing = obj as EssCertIDv2; + if (existing != null) + return existing; + return new EssCertIDv2(Asn1Sequence.GetInstance(obj)); + } + + 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(byte[] certHash) + : this(null, certHash, null) + { + } + + public EssCertIDv2( + AlgorithmIdentifier algId, + byte[] certHash) + : this(algId, certHash, null) + { + } + + public EssCertIDv2( + byte[] certHash, + IssuerSerial issuerSerial) + : this(null, certHash, issuerSerial) + { + } + + public EssCertIDv2( + AlgorithmIdentifier algId, + byte[] certHash, + IssuerSerial issuerSerial) + { + if (algId == null) + { + // Default value + this.hashAlgorithm = 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
index a2aff4866..cabecc1ba 100644 --- a/crypto/src/asn1/ess/SigningCertificateV2.cs +++ b/crypto/src/asn1/ess/SigningCertificateV2.cs
@@ -4,103 +4,109 @@ 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); - } - } + 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 cert) + { + this.certs = new DerSequence(cert); + } + + 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
index f322ef88f..64b5dbec8 100644 --- a/crypto/src/asn1/isismtt/x509/AdmissionSyntax.cs +++ 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
index 3b4c59df2..0e82dda7a 100644 --- a/crypto/src/asn1/nist/NISTNamedCurves.cs +++ b/crypto/src/asn1/nist/NISTNamedCurves.cs
@@ -1,6 +1,5 @@ using System; using System.Collections; -using System.Globalization; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.Sec; @@ -10,93 +9,100 @@ 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() - { - } + /** + * Utility class for fetching curves using their NIST names as published in FIPS-PUB 186-3 + */ + public sealed class NistNamedCurves + { + private NistNamedCurves() + { + } - private static readonly IDictionary objIds = Platform.CreateHashtable(); + 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); - } + 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); - } + 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("K-571", SecObjectIdentifiers.SecT571k1); + DefineCurve("K-409", SecObjectIdentifiers.SecT409k1); + DefineCurve("K-283", SecObjectIdentifiers.SecT283k1); + DefineCurve("K-233", SecObjectIdentifiers.SecT233k1); + DefineCurve("K-163", SecObjectIdentifiers.SecT163k1); + DefineCurve("P-521", SecObjectIdentifiers.SecP521r1); + DefineCurve("P-384", SecObjectIdentifiers.SecP384r1); + DefineCurve("P-256", SecObjectIdentifiers.SecP256r1); + DefineCurve("P-224", SecObjectIdentifiers.SecP224r1); + DefineCurve("P-192", SecObjectIdentifiers.SecP192r1); + } - public static X9ECParameters GetByName( - string name) - { - DerObjectIdentifier oid = (DerObjectIdentifier) objIds[name.ToUpperInvariant()]; + public static X9ECParameters GetByName( + string name) + { + DerObjectIdentifier oid = (DerObjectIdentifier) objIds[ + Platform.ToUpperInvariant(name)]; - if (oid != null) - { - return GetByOid(oid); - } + if (oid != null) + { + return GetByOid(oid); + } - return null; - } + 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 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 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[ + Platform.ToUpperInvariant(name)]; + } - /** - * return the named curve name represented by the given object identifier. - */ - public static string GetName( - DerObjectIdentifier oid) - { - return (string) names[oid]; - } + /** + * 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); } - } - } + /** + * 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
index f3957062f..8eb5ed437 100644 --- a/crypto/src/asn1/nist/NISTObjectIdentifiers.cs +++ b/crypto/src/asn1/nist/NISTObjectIdentifiers.cs
@@ -4,9 +4,9 @@ namespace Org.BouncyCastle.Asn1.Nist { public sealed class NistObjectIdentifiers { - private NistObjectIdentifiers() - { - } + private NistObjectIdentifiers() + { + } // // NIST @@ -17,10 +17,14 @@ namespace Org.BouncyCastle.Asn1.Nist // 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 HashAlgs = NistAlgorithm.Branch("2"); + + public static readonly DerObjectIdentifier IdSha256 = HashAlgs.Branch("1"); + public static readonly DerObjectIdentifier IdSha384 = HashAlgs.Branch("2"); + public static readonly DerObjectIdentifier IdSha512 = HashAlgs.Branch("3"); + public static readonly DerObjectIdentifier IdSha224 = HashAlgs.Branch("4"); + public static readonly DerObjectIdentifier IdSha512_224 = HashAlgs.Branch("5"); + public static readonly DerObjectIdentifier IdSha512_256 = HashAlgs.Branch("6"); public static readonly DerObjectIdentifier Aes = new DerObjectIdentifier(NistAlgorithm + ".1"); @@ -48,14 +52,14 @@ namespace Org.BouncyCastle.Asn1.Nist 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"); + // + // 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"); - } + 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
index 78213c138..526a3c48e 100644 --- a/crypto/src/asn1/pkcs/ContentInfo.cs +++ b/crypto/src/asn1/pkcs/ContentInfo.cs
@@ -11,34 +11,28 @@ namespace Org.BouncyCastle.Asn1.Pkcs private readonly DerObjectIdentifier contentType; private readonly Asn1Encodable content; - public static ContentInfo GetInstance( - object obj) + 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"); + if (obj == null) + return null; + ContentInfo existing = obj as ContentInfo; + if (existing != null) + return existing; + return new ContentInfo(Asn1Sequence.GetInstance(obj)); } - private ContentInfo( + private ContentInfo( Asn1Sequence seq) { - contentType = (DerObjectIdentifier) seq[0]; + contentType = (DerObjectIdentifier) seq[0]; - if (seq.Count > 1) - { - content = ((Asn1TaggedObject) seq[1]).GetObject(); - } + if (seq.Count > 1) + { + content = ((Asn1TaggedObject) seq[1]).GetObject(); + } } - public ContentInfo( + public ContentInfo( DerObjectIdentifier contentType, Asn1Encodable content) { @@ -46,17 +40,17 @@ namespace Org.BouncyCastle.Asn1.Pkcs this.content = content; } - public DerObjectIdentifier ContentType - { - get { return contentType; } - } + public DerObjectIdentifier ContentType + { + get { return contentType; } + } - public Asn1Encodable Content - { - get { return content; } - } + public Asn1Encodable Content + { + get { return content; } + } - /** + /** * Produce an object suitable for an Asn1OutputStream. * <pre> * ContentInfo ::= Sequence { @@ -69,12 +63,12 @@ namespace Org.BouncyCastle.Asn1.Pkcs { Asn1EncodableVector v = new Asn1EncodableVector(contentType); - if (content != null) + if (content != null) { v.Add(new BerTaggedObject(0, content)); } - return new BerSequence(v); + 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
index 645bb867c..fc6904eed 100644 --- a/crypto/src/asn1/pkcs/PBES2Parameters.cs +++ b/crypto/src/asn1/pkcs/PBES2Parameters.cs
@@ -1,5 +1,4 @@ using System; -using System.Collections; namespace Org.BouncyCastle.Asn1.Pkcs { @@ -9,53 +8,58 @@ namespace Org.BouncyCastle.Asn1.Pkcs 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); + public static PbeS2Parameters GetInstance(object obj) + { + if (obj == null) + return null; + PbeS2Parameters existing = obj as PbeS2Parameters; + if (existing != null) + return existing; + return new PbeS2Parameters(Asn1Sequence.GetInstance(obj)); + } - throw new ArgumentException("Unknown object in factory: " + obj.GetType().FullName, "obj"); - } + public PbeS2Parameters(KeyDerivationFunc keyDevFunc, EncryptionScheme encScheme) + { + this.func = keyDevFunc; + this.scheme = encScheme; + } - public PbeS2Parameters( + [Obsolete("Use GetInstance() instead")] + public PbeS2Parameters( Asn1Sequence seq) { - if (seq.Count != 2) - throw new ArgumentException("Wrong number of elements in sequence", "seq"); + if (seq.Count != 2) + throw new ArgumentException("Wrong number of elements in sequence", "seq"); - Asn1Sequence funcSeq = (Asn1Sequence)seq[0].ToAsn1Object(); + Asn1Sequence funcSeq = (Asn1Sequence)seq[0].ToAsn1Object(); - // TODO Not sure if this special case is really necessary/appropriate - if (funcSeq[0].Equals(PkcsObjectIdentifiers.IdPbkdf2)) + // 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])); - } + func = new KeyDerivationFunc(PkcsObjectIdentifiers.IdPbkdf2, + Pbkdf2Params.GetInstance(funcSeq[1])); + } else { func = new KeyDerivationFunc(funcSeq); } - scheme = EncryptionScheme.GetInstance(seq[1].ToAsn1Object()); + scheme = EncryptionScheme.GetInstance(seq[1].ToAsn1Object()); } - public KeyDerivationFunc KeyDerivationFunc - { - get { return func; } - } + public KeyDerivationFunc KeyDerivationFunc + { + get { return func; } + } - public EncryptionScheme EncryptionScheme - { - get { return scheme; } - } + public EncryptionScheme EncryptionScheme + { + get { return scheme; } + } - public override Asn1Object ToAsn1Object() + public override Asn1Object ToAsn1Object() { - return new DerSequence(func, scheme); + 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
index bd0ef7d1e..404277ba6 100644 --- a/crypto/src/asn1/pkcs/PrivateKeyInfo.cs +++ b/crypto/src/asn1/pkcs/PrivateKeyInfo.cs
@@ -10,88 +10,100 @@ namespace Org.BouncyCastle.Asn1.Pkcs public class PrivateKeyInfo : Asn1Encodable { - private readonly Asn1Object privKey; + private readonly Asn1OctetString privKey; private readonly AlgorithmIdentifier algID; - private readonly Asn1Set attributes; + private readonly Asn1Set attributes; - public static PrivateKeyInfo GetInstance( - object obj) - { - if (obj is PrivateKeyInfo) - return (PrivateKeyInfo) obj; + public static PrivateKeyInfo GetInstance(Asn1TaggedObject obj, bool explicitly) + { + return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); + } - if (obj != null) - return new PrivateKeyInfo(Asn1Sequence.GetInstance(obj)); + public static PrivateKeyInfo GetInstance( + object obj) + { + if (obj == null) + return null; + if (obj is PrivateKeyInfo) + return (PrivateKeyInfo) obj; + return new PrivateKeyInfo(Asn1Sequence.GetInstance(obj)); + } - return null; - } + public PrivateKeyInfo(AlgorithmIdentifier algID, Asn1Object privateKey) + : this(algID, privateKey, null) + { + } - public PrivateKeyInfo( + 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) + Asn1Object privateKey, + Asn1Set attributes) + { + this.algID = algID; + this.privKey = new DerOctetString(privateKey.GetEncoded(Asn1Encodable.Der)); + this.attributes = attributes; + } + + private PrivateKeyInfo(Asn1Sequence seq) { IEnumerator e = seq.GetEnumerator(); - e.MoveNext(); - BigInteger version = ((DerInteger) e.Current).Value; + e.MoveNext(); + BigInteger version = ((DerInteger)e.Current).Value; if (version.IntValue != 0) { - throw new ArgumentException("wrong version for private key info"); + throw new ArgumentException("wrong version for private key info: " + version.IntValue); } - e.MoveNext(); + e.MoveNext(); algID = AlgorithmIdentifier.GetInstance(e.Current); + e.MoveNext(); + privKey = Asn1OctetString.GetInstance(e.Current); - try + if (e.MoveNext()) { - e.MoveNext(); - Asn1OctetString data = (Asn1OctetString) e.Current; - - privKey = Asn1Object.FromByteArray(data.GetOctets()); - } - catch (IOException) - { - throw new ArgumentException("Error recoverying private key from sequence"); + attributes = Asn1Set.GetInstance((Asn1TaggedObject)e.Current, false); } + } + + public virtual AlgorithmIdentifier PrivateKeyAlgorithm + { + get { return algID; } + } - if (e.MoveNext()) - { - attributes = Asn1Set.GetInstance((Asn1TaggedObject) e.Current, false); - } - } + [Obsolete("Use 'PrivateKeyAlgorithm' property instead")] + public virtual AlgorithmIdentifier AlgorithmID + { + get { return algID; } + } - public AlgorithmIdentifier AlgorithmID - { - get { return algID; } - } + public virtual Asn1Object ParsePrivateKey() + { + return Asn1Object.FromByteArray(privKey.GetOctets()); + } - public Asn1Object PrivateKey - { - get { return privKey; } - } + [Obsolete("Use 'ParsePrivateKey' instead")] + public virtual Asn1Object PrivateKey + { + get + { + try + { + return ParsePrivateKey(); + } + catch (IOException) + { + throw new InvalidOperationException("unable to parse private key"); + } + } + } - public Asn1Set Attributes - { - get { return attributes; } - } + public virtual Asn1Set Attributes + { + get { return attributes; } + } - /** + /** * write out an RSA private key with its associated information * as described in Pkcs8. * <pre> @@ -110,17 +122,14 @@ namespace Org.BouncyCastle.Asn1.Pkcs */ public override Asn1Object ToAsn1Object() { - Asn1EncodableVector v = new Asn1EncodableVector( - new DerInteger(0), - algID, - new DerOctetString(privKey)); + Asn1EncodableVector v = new Asn1EncodableVector(new DerInteger(0), algID, privKey); - if (attributes != null) - { - v.Add(new DerTaggedObject(false, 0, attributes)); - } + if (attributes != null) + { + v.Add(new DerTaggedObject(false, 0, attributes)); + } - return new DerSequence(v); + 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
index dbb07c744..721299105 100644 --- a/crypto/src/asn1/pkcs/RSAPrivateKeyStructure.cs +++ b/crypto/src/asn1/pkcs/RSAPrivateKeyStructure.cs
@@ -18,7 +18,21 @@ namespace Org.BouncyCastle.Asn1.Pkcs private readonly BigInteger exponent2; private readonly BigInteger coefficient; - public RsaPrivateKeyStructure( + public static RsaPrivateKeyStructure GetInstance(Asn1TaggedObject obj, bool isExplicit) + { + return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit)); + } + + public static RsaPrivateKeyStructure GetInstance(object obj) + { + if (obj == null) + return null; + if (obj is RsaPrivateKeyStructure) + return (RsaPrivateKeyStructure)obj; + return new RsaPrivateKeyStructure(Asn1Sequence.GetInstance(obj)); + } + + public RsaPrivateKeyStructure( BigInteger modulus, BigInteger publicExponent, BigInteger privateExponent, @@ -38,64 +52,65 @@ namespace Org.BouncyCastle.Asn1.Pkcs this.coefficient = coefficient; } - public RsaPrivateKeyStructure( + [Obsolete("Use 'GetInstance' method(s) instead")] + public RsaPrivateKeyStructure( Asn1Sequence seq) { - BigInteger version = ((DerInteger) seq[0]).Value; - if (version.IntValue != 0) + 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; } - } - - /** + 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 { @@ -116,16 +131,16 @@ namespace Org.BouncyCastle.Asn1.Pkcs */ 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)); + 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
index 10951d37a..6e72bd0a9 100644 --- a/crypto/src/asn1/pkcs/SignedData.cs +++ b/crypto/src/asn1/pkcs/SignedData.cs
@@ -18,23 +18,17 @@ namespace Org.BouncyCastle.Asn1.Pkcs private readonly Asn1Set crls; private readonly Asn1Set signerInfos; - public static SignedData GetInstance( - object obj) + 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"); - } + if (obj == null) + return null; + SignedData existing = obj as SignedData; + if (existing != null) + return existing; + return new SignedData(Asn1Sequence.GetInstance(obj)); + } - public SignedData( + public SignedData( DerInteger _version, Asn1Set _digestAlgorithms, ContentInfo _contentInfo, @@ -50,25 +44,25 @@ namespace Org.BouncyCastle.Asn1.Pkcs signerInfos = _signerInfos; } - private SignedData( + private SignedData( Asn1Sequence seq) { IEnumerator e = seq.GetEnumerator(); - e.MoveNext(); + e.MoveNext(); version = (DerInteger) e.Current; - e.MoveNext(); + e.MoveNext(); digestAlgorithms = (Asn1Set) e.Current; - e.MoveNext(); + e.MoveNext(); contentInfo = ContentInfo.GetInstance(e.Current); - while (e.MoveNext()) + 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. // @@ -76,16 +70,16 @@ namespace Org.BouncyCastle.Asn1.Pkcs { DerTaggedObject tagged = (DerTaggedObject) o; - switch (tagged.TagNo) + 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); + 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 @@ -95,37 +89,37 @@ namespace Org.BouncyCastle.Asn1.Pkcs } } - 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; } - } - - /** + 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 { @@ -143,21 +137,21 @@ namespace Org.BouncyCastle.Asn1.Pkcs public override Asn1Object ToAsn1Object() { Asn1EncodableVector v = new Asn1EncodableVector( - version, digestAlgorithms, contentInfo); + version, digestAlgorithms, contentInfo); - if (certificates != null) + if (certificates != null) { v.Add(new DerTaggedObject(false, 0, certificates)); } - if (crls != null) + if (crls != null) { v.Add(new DerTaggedObject(false, 1, crls)); } - v.Add(signerInfos); + v.Add(signerInfos); - return new BerSequence(v); + 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
index 2e9c27fd2..8d805fa30 100644 --- a/crypto/src/asn1/sec/ECPrivateKeyStructure.cs +++ b/crypto/src/asn1/sec/ECPrivateKeyStructure.cs
@@ -6,113 +6,121 @@ 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; - } - } + /** + * the elliptic curve private key object from SEC 1 + */ + public class ECPrivateKeyStructure + : Asn1Encodable + { + private readonly Asn1Sequence seq; + + public static ECPrivateKeyStructure GetInstance(object obj) + { + if (obj == null) + return null; + if (obj is ECPrivateKeyStructure) + return (ECPrivateKeyStructure)obj; + return new ECPrivateKeyStructure(Asn1Sequence.GetInstance(obj)); + } + + 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 virtual BigInteger GetKey() + { + Asn1OctetString octs = (Asn1OctetString) seq[1]; + + return new BigInteger(1, octs.GetOctets()); + } + + public virtual DerBitString GetPublicKey() + { + return (DerBitString) GetObjectInTag(1); + } + + public virtual 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
index c7503144f..60d456ef0 100644 --- a/crypto/src/asn1/sec/SECNamedCurves.cs +++ b/crypto/src/asn1/sec/SECNamedCurves.cs
@@ -1,1192 +1,1253 @@ 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.Math.EC.Endo; 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(); + public sealed class SecNamedCurves + { + private SecNamedCurves() + { + } + + private static ECCurve ConfigureCurve(ECCurve curve) + { + return curve; + } + + private static ECCurve ConfigureCurveGlv(ECCurve c, GlvTypeBParameters p) + { + return c.Configure().SetEndomorphism(new GlvTypeBEndomorphism(c, p)).Create(); + } + + 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.One; + + ECCurve curve = ConfigureCurve(new FpCurve(p, a, b, n, h)); + //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 = ConfigureCurve(new FpCurve(p, a, b, n, h)); + //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.One; + + ECCurve curve = ConfigureCurve(new FpCurve(p, a, b, n, h)); + //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 = ConfigureCurve(new FpCurve(p, a, b, n, h)); + //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.One; + + GlvTypeBParameters glv = new GlvTypeBParameters( + new BigInteger("9ba48cba5ebcb9b6bd33b92830b2a2e0e192f10a", 16), + new BigInteger("c39c6c3b3a36d7701b9c71a1f5804ae5d0003f4", 16), + new BigInteger[]{ + new BigInteger("9162fbe73984472a0a9e", 16), + new BigInteger("-96341f1138933bc2f505", 16) }, + new BigInteger[]{ + new BigInteger("127971af8721782ecffa3", 16), + new BigInteger("9162fbe73984472a0a9e", 16) }, + new BigInteger("9162fbe73984472a0a9d0590", 16), + new BigInteger("96341f1138933bc2f503fd44", 16), + 176); + + ECCurve curve = ConfigureCurveGlv(new FpCurve(p, a, b, n, h), glv); + //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.One; + + ECCurve curve = ConfigureCurve(new FpCurve(p, a, b, n, h)); + //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.One; + + ECCurve curve = ConfigureCurve(new FpCurve(p, a, b, n, h)); + //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.One; + + GlvTypeBParameters glv = new GlvTypeBParameters( + new BigInteger("bb85691939b869c1d087f601554b96b80cb4f55b35f433c2", 16), + new BigInteger("3d84f26c12238d7b4f3d516613c1759033b1a5800175d0b1", 16), + new BigInteger[]{ + new BigInteger("71169be7330b3038edb025f1", 16), + new BigInteger("-b3fb3400dec5c4adceb8655c", 16) }, + new BigInteger[]{ + new BigInteger("12511cfe811d0f4e6bc688b4d", 16), + new BigInteger("71169be7330b3038edb025f1", 16) }, + new BigInteger("71169be7330b3038edb025f1d0f9", 16), + new BigInteger("b3fb3400dec5c4adceb8655d4c94", 16), + 208); + + ECCurve curve = ConfigureCurveGlv(new FpCurve(p, a, b, n, h), glv); + //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.One; + + ECCurve curve = ConfigureCurve(new FpCurve(p, a, b, n, h)); + //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.One; + + GlvTypeBParameters glv = new GlvTypeBParameters( + new BigInteger("fe0e87005b4e83761908c5131d552a850b3f58b749c37cf5b84d6768", 16), + new BigInteger("60dcd2104c4cbc0be6eeefc2bdd610739ec34e317f9b33046c9e4788", 16), + new BigInteger[]{ + new BigInteger("6b8cf07d4ca75c88957d9d670591", 16), + new BigInteger("-b8adf1378a6eb73409fa6c9c637d", 16) }, + new BigInteger[]{ + new BigInteger("1243ae1b4d71613bc9f780a03690e", 16), + new BigInteger("6b8cf07d4ca75c88957d9d670591", 16) }, + new BigInteger("6b8cf07d4ca75c88957d9d67059037a4", 16), + new BigInteger("b8adf1378a6eb73409fa6c9c637ba7f5", 16), + 240); + + ECCurve curve = ConfigureCurveGlv(new FpCurve(p, a, b, n, h), glv); + //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.One; + + ECCurve curve = ConfigureCurve(new FpCurve(p, a, b, n, h)); + //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.One; + + GlvTypeBParameters glv = new GlvTypeBParameters( + new BigInteger("7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee", 16), + new BigInteger("5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72", 16), + new BigInteger[]{ + new BigInteger("3086d221a7d46bcde86c90e49284eb15", 16), + new BigInteger("-e4437ed6010e88286f547fa90abfe4c3", 16) }, + new BigInteger[]{ + new BigInteger("114ca50f7a8e2f3f657c1108d9d44cfd8", 16), + new BigInteger("3086d221a7d46bcde86c90e49284eb15", 16) }, + new BigInteger("3086d221a7d46bcde86c90e49284eb153dab", 16), + new BigInteger("e4437ed6010e88286f547fa90abfe4c42212", 16), + 272); + + ECCurve curve = ConfigureCurveGlv(new FpCurve(p, a, b, n, h), glv); + //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.One; + + ECCurve curve = ConfigureCurve(new FpCurve(p, a, b, n, h)); + //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.One; + + ECCurve curve = ConfigureCurve(new FpCurve(p, a, b, n, h)); + //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.One; + + ECCurve curve = ConfigureCurve(new FpCurve(p, a, b, n, h)); + //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.One; + BigInteger b = BigInteger.One; + 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.One; + 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.One; + 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.One; + 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.One; + 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.One; + 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.One; + 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.One; + 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.One; + 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.One; + 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.One; + 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); } - } - } + 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[Platform.ToLowerInvariant(name)]; + + 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[Platform.ToLowerInvariant(name)]; + } + + /** + * 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
index adf254e56..ca3c3af7d 100644 --- a/crypto/src/asn1/smime/SMIMECapabilities.cs +++ b/crypto/src/asn1/smime/SMIMECapabilities.cs
@@ -65,7 +65,7 @@ namespace Org.BouncyCastle.Asn1.Smime capabilities = seq; } -#if !(SILVERLIGHT || PORTABLE) +#if !SILVERLIGHT [Obsolete("Use 'GetCapabilitiesForOid' instead")] public ArrayList GetCapabilities( DerObjectIdentifier capability) 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
index 0cfdb8483..05060c109 100644 --- a/crypto/src/asn1/teletrust/TeleTrusTNamedCurves.cs +++ b/crypto/src/asn1/teletrust/TeleTrusTNamedCurves.cs
@@ -1,5 +1,4 @@ using System.Collections; -using System.Globalization; using Org.BouncyCastle.Asn1.X9; using Org.BouncyCastle.Math; @@ -10,419 +9,466 @@ 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(); + /** + * 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 + { + private static ECCurve ConfigureCurve(ECCurve curve) + { + return curve; + } + + internal class BrainpoolP160r1Holder + : X9ECParametersHolder + { + private BrainpoolP160r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new BrainpoolP160r1Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("E95E4A5F737059DC60DF5991D45029409E60FC09", 16); + BigInteger h = new BigInteger("01", 16); + + ECCurve curve = ConfigureCurve(new FpCurve( + new BigInteger("E95E4A5F737059DC60DFC7AD95B3D8139515620F", 16), // q + new BigInteger("340E7BE2A280EB74E2BE61BADA745D97E8F7C300", 16), // a + new BigInteger("1E589A8595423412134FAA2DBDEC95C8D8675E58", 16), // b + n, h)); + + return new X9ECParameters( + curve, + curve.DecodePoint(Hex.Decode("04BED5AF16EA3F6A4F62938C4631EB5AF7BDBCDBC31667CB477A1A8EC338F94741669C976316DA6321")), // G + n, h); + } + } + + internal class BrainpoolP160t1Holder + : X9ECParametersHolder + { + private BrainpoolP160t1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new BrainpoolP160t1Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("E95E4A5F737059DC60DF5991D45029409E60FC09", 16); + BigInteger h = new BigInteger("01", 16); + + ECCurve curve = ConfigureCurve(new FpCurve( + // new BigInteger("24DBFF5DEC9B986BBFE5295A29BFBAE45E0F5D0B", 16), // Z + new BigInteger("E95E4A5F737059DC60DFC7AD95B3D8139515620F", 16), // q + new BigInteger("E95E4A5F737059DC60DFC7AD95B3D8139515620C", 16), // a' + new BigInteger("7A556B6DAE535B7B51ED2C4D7DAA7A0B5C55F380", 16), // b' + n, h)); + + return new X9ECParameters( + curve, + curve.DecodePoint(Hex.Decode("04B199B13B9B34EFC1397E64BAEB05ACC265FF2378ADD6718B7C7C1961F0991B842443772152C9E0AD")), // G + n, h); + } + } + + internal class BrainpoolP192r1Holder + : X9ECParametersHolder + { + private BrainpoolP192r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new BrainpoolP192r1Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("C302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1", 16); + BigInteger h = new BigInteger("01", 16); + + ECCurve curve = ConfigureCurve(new FpCurve( + new BigInteger("C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297", 16), // q + new BigInteger("6A91174076B1E0E19C39C031FE8685C1CAE040E5C69A28EF", 16), // a + new BigInteger("469A28EF7C28CCA3DC721D044F4496BCCA7EF4146FBF25C9", 16), // b + n, h)); + + return new X9ECParameters( + curve, + curve.DecodePoint(Hex.Decode("04C0A0647EAAB6A48753B033C56CB0F0900A2F5C4853375FD614B690866ABD5BB88B5F4828C1490002E6773FA2FA299B8F")), // G + n, h); + } + } + + internal class BrainpoolP192t1Holder + : X9ECParametersHolder + { + private BrainpoolP192t1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new BrainpoolP192t1Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("C302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1", 16); + BigInteger h = new BigInteger("01", 16); + + ECCurve curve = ConfigureCurve(new FpCurve( + //new BigInteger("1B6F5CC8DB4DC7AF19458A9CB80DC2295E5EB9C3732104CB") //Z + new BigInteger("C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297", 16), // q + new BigInteger("C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86294", 16), // a' + new BigInteger("13D56FFAEC78681E68F9DEB43B35BEC2FB68542E27897B79", 16), // b' + n, h)); + + return new X9ECParameters( + curve, + curve.DecodePoint(Hex.Decode("043AE9E58C82F63C30282E1FE7BBF43FA72C446AF6F4618129097E2C5667C2223A902AB5CA449D0084B7E5B3DE7CCC01C9")), // G' + n, h); + } + } + + internal class BrainpoolP224r1Holder + : X9ECParametersHolder + { + private BrainpoolP224r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new BrainpoolP224r1Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("D7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F", 16); + BigInteger h = new BigInteger("01", 16); + + ECCurve curve = ConfigureCurve(new FpCurve( + new BigInteger("D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF", 16), // q + new BigInteger("68A5E62CA9CE6C1C299803A6C1530B514E182AD8B0042A59CAD29F43", 16), // a + new BigInteger("2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B", 16), // b + n, h)); + + return new X9ECParameters( + curve, + curve.DecodePoint(Hex.Decode("040D9029AD2C7E5CF4340823B2A87DC68C9E4CE3174C1E6EFDEE12C07D58AA56F772C0726F24C6B89E4ECDAC24354B9E99CAA3F6D3761402CD")), // G + n, h); + } + } + + internal class BrainpoolP224t1Holder + : X9ECParametersHolder + { + private BrainpoolP224t1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new BrainpoolP224t1Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("D7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F", 16); + BigInteger h = new BigInteger("01", 16); + + ECCurve curve = ConfigureCurve(new FpCurve( + //new BigInteger("2DF271E14427A346910CF7A2E6CFA7B3F484E5C2CCE1C8B730E28B3F") //Z + new BigInteger("D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF", 16), // q + new BigInteger("D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FC", 16), // a' + new BigInteger("4B337D934104CD7BEF271BF60CED1ED20DA14C08B3BB64F18A60888D", 16), // b' + n, h)); + + return new X9ECParameters( + curve, + curve.DecodePoint(Hex.Decode("046AB1E344CE25FF3896424E7FFE14762ECB49F8928AC0C76029B4D5800374E9F5143E568CD23F3F4D7C0D4B1E41C8CC0D1C6ABD5F1A46DB4C")), // G' + n, h); + } + } + + internal class BrainpoolP256r1Holder + : X9ECParametersHolder + { + private BrainpoolP256r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new BrainpoolP256r1Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7", 16); + BigInteger h = new BigInteger("01", 16); + + ECCurve curve = ConfigureCurve(new FpCurve( + new BigInteger("A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377", 16), // q + new BigInteger("7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9", 16), // a + new BigInteger("26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6", 16), // b + n, h)); + + return new X9ECParameters( + curve, + curve.DecodePoint(Hex.Decode("048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997")), // G + n, h); + } + } + + internal class BrainpoolP256t1Holder + : X9ECParametersHolder + { + private BrainpoolP256t1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new BrainpoolP256t1Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7", 16); + BigInteger h = new BigInteger("01", 16); + + ECCurve curve = ConfigureCurve(new FpCurve( + //new BigInteger("3E2D4BD9597B58639AE7AA669CAB9837CF5CF20A2C852D10F655668DFC150EF0") //Z + new BigInteger("A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377", 16), // q + new BigInteger("A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5374", 16), // a' + new BigInteger("662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04", 16), // b' + n, h)); + + return new X9ECParameters( + curve, + curve.DecodePoint(Hex.Decode("04A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F42D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE")), // G' + n, h); + } + } + + internal class BrainpoolP320r1Holder + : X9ECParametersHolder + { + private BrainpoolP320r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new BrainpoolP320r1Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("D35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D482EC7EE8658E98691555B44C59311", 16); + BigInteger h = new BigInteger("01", 16); + + ECCurve curve = ConfigureCurve(new FpCurve( + new BigInteger("D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC28FCD412B1F1B32E27", 16), // q + new BigInteger("3EE30B568FBAB0F883CCEBD46D3F3BB8A2A73513F5EB79DA66190EB085FFA9F492F375A97D860EB4", 16), // a + new BigInteger("520883949DFDBC42D3AD198640688A6FE13F41349554B49ACC31DCCD884539816F5EB4AC8FB1F1A6", 16), // b + n, h)); + + return new X9ECParameters( + curve, + curve.DecodePoint(Hex.Decode("0443BD7E9AFB53D8B85289BCC48EE5BFE6F20137D10A087EB6E7871E2A10A599C710AF8D0D39E2061114FDD05545EC1CC8AB4093247F77275E0743FFED117182EAA9C77877AAAC6AC7D35245D1692E8EE1")), // G + n, h); + } + } + + internal class BrainpoolP320t1Holder + : X9ECParametersHolder + { + private BrainpoolP320t1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new BrainpoolP320t1Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("D35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D482EC7EE8658E98691555B44C59311", 16); + BigInteger h = new BigInteger("01", 16); + + ECCurve curve = ConfigureCurve(new FpCurve( + //new BigInteger("15F75CAF668077F7E85B42EB01F0A81FF56ECD6191D55CB82B7D861458A18FEFC3E5AB7496F3C7B1") //Z + new BigInteger("D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC28FCD412B1F1B32E27", 16), // q + new BigInteger("D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC28FCD412B1F1B32E24", 16), // a' + new BigInteger("A7F561E038EB1ED560B3D147DB782013064C19F27ED27C6780AAF77FB8A547CEB5B4FEF422340353", 16), // b' + n, h)); + + return new X9ECParameters( + curve, + curve.DecodePoint(Hex.Decode("04925BE9FB01AFC6FB4D3E7D4990010F813408AB106C4F09CB7EE07868CC136FFF3357F624A21BED5263BA3A7A27483EBF6671DBEF7ABB30EBEE084E58A0B077AD42A5A0989D1EE71B1B9BC0455FB0D2C3")), // G' + n, h); + } + } + + internal class BrainpoolP384r1Holder + : X9ECParametersHolder + { + private BrainpoolP384r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new BrainpoolP384r1Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC3103B883202E9046565", 16); + BigInteger h = new BigInteger("01", 16); + + ECCurve curve = ConfigureCurve(new FpCurve( + new BigInteger("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53", 16), // q + new BigInteger("7BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F90F8AA5814A503AD4EB04A8C7DD22CE2826", 16), // a + new BigInteger("4A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62D57CB4390295DBC9943AB78696FA504C11", 16), // b + n, h)); + + return new X9ECParameters( + curve, + curve.DecodePoint(Hex.Decode("041D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10E8E826E03436D646AAEF87B2E247D4AF1E8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E4646217791811142820341263C5315")), // G + n, h); + } + } + + internal class BrainpoolP384t1Holder + : X9ECParametersHolder + { + private BrainpoolP384t1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new BrainpoolP384t1Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC3103B883202E9046565", 16); + BigInteger h = new BigInteger("01", 16); + + ECCurve curve = ConfigureCurve(new FpCurve( + //new BigInteger("41DFE8DD399331F7166A66076734A89CD0D2BCDB7D068E44E1F378F41ECBAE97D2D63DBC87BCCDDCCC5DA39E8589291C") //Z + new BigInteger("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53", 16), // q + new BigInteger("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC50", 16), // a' + new BigInteger("7F519EADA7BDA81BD826DBA647910F8C4B9346ED8CCDC64E4B1ABD11756DCE1D2074AA263B88805CED70355A33B471EE", 16), // b' + n, h)); + + return new X9ECParameters( + curve, + curve.DecodePoint(Hex.Decode("0418DE98B02DB9A306F2AFCD7235F72A819B80AB12EBD653172476FECD462AABFFC4FF191B946A5F54D8D0AA2F418808CC25AB056962D30651A114AFD2755AD336747F93475B7A1FCA3B88F2B6A208CCFE469408584DC2B2912675BF5B9E582928")), // G' + n, h); + } + } + + internal class BrainpoolP512r1Holder + : X9ECParametersHolder + { + private BrainpoolP512r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new BrainpoolP512r1Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069", 16); + BigInteger h = new BigInteger("01", 16); + + ECCurve curve = ConfigureCurve(new FpCurve( + new BigInteger("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3", 16), // q + new BigInteger("7830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA", 16), // a + new BigInteger("3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F723", 16), // b + n, h)); + + return new X9ECParameters( + curve, + curve.DecodePoint(Hex.Decode("0481AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D0098EFF3B1F78E2D0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F8227DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892")), // G + n, h); + } + } + + internal class BrainpoolP512t1Holder + : X9ECParametersHolder + { + private BrainpoolP512t1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new BrainpoolP512t1Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069", 16); + BigInteger h = new BigInteger("01", 16); + + ECCurve curve = ConfigureCurve(new FpCurve( + //new BigInteger("12EE58E6764838B69782136F0F2D3BA06E27695716054092E60A80BEDB212B64E585D90BCE13761F85C3F1D2A64E3BE8FEA2220F01EBA5EEB0F35DBD29D922AB") //Z + new BigInteger("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3", 16), // q + new BigInteger("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F0", 16), // a' + new BigInteger("7CBBBCF9441CFAB76E1890E46884EAE321F70C0BCB4981527897504BEC3E36A62BCDFA2304976540F6450085F2DAE145C22553B465763689180EA2571867423E", 16), // b' + n, h)); + + return new X9ECParameters( + curve, + curve.DecodePoint(Hex.Decode("04640ECE5C12788717B9C1BA06CBC2A6FEBA85842458C56DDE9DB1758D39C0313D82BA51735CDB3EA499AA77A7D6943A64F7A3F25FE26F06B51BAA2696FA9035DA5B534BD595F5AF0FA2C892376C84ACE1BB4E3019B71634C01131159CAE03CEE9D9932184BEEF216BD71DF2DADF86A627306ECFF96DBB8BACE198B61E00F8B332")), // G' + n, 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"); - } - } + 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[Platform.ToLowerInvariant(name)]; + + 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[Platform.ToLowerInvariant(name)]; + } + + /** + * 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
index 43d485500..36b17c8fd 100644 --- a/crypto/src/asn1/util/Asn1Dump.cs +++ b/crypto/src/asn1/util/Asn1Dump.cs
@@ -10,9 +10,9 @@ namespace Org.BouncyCastle.Asn1.Utilities { public sealed class Asn1Dump { - private static readonly string NewLine = Platform.NewLine; + private static readonly string NewLine = Platform.NewLine; - private Asn1Dump() + private Asn1Dump() { } @@ -28,12 +28,12 @@ namespace Org.BouncyCastle.Asn1.Utilities string indent, bool verbose, Asn1Object obj, - StringBuilder buf) + StringBuilder buf) { if (obj is Asn1Sequence) { - string tab = indent + Tab; - buf.Append(indent); + string tab = indent + Tab; + buf.Append(indent); if (obj is BerSequence) { buf.Append("BER Sequence"); @@ -49,8 +49,8 @@ namespace Org.BouncyCastle.Asn1.Utilities buf.Append(NewLine); - foreach (Asn1Encodable o in ((Asn1Sequence)obj)) - { + foreach (Asn1Encodable o in ((Asn1Sequence)obj)) + { if (o == null || o is Asn1Null) { buf.Append(tab); @@ -66,7 +66,7 @@ namespace Org.BouncyCastle.Asn1.Utilities else if (obj is DerTaggedObject) { string tab = indent + Tab; - buf.Append(indent); + buf.Append(indent); if (obj is BerTaggedObject) { buf.Append("BER Tagged ["); @@ -76,19 +76,19 @@ namespace Org.BouncyCastle.Asn1.Utilities buf.Append("Tagged ["); } - DerTaggedObject o = (DerTaggedObject)obj; + DerTaggedObject o = (DerTaggedObject)obj; - buf.Append(((int)o.TagNo).ToString()); + buf.Append(((int)o.TagNo).ToString()); buf.Append(']'); - if (!o.IsExplicit()) + if (!o.IsExplicit()) { buf.Append(" IMPLICIT "); } - buf.Append(NewLine); + buf.Append(NewLine); - if (o.IsEmpty()) + if (o.IsEmpty()) { buf.Append(tab); buf.Append("EMPTY"); @@ -103,12 +103,12 @@ namespace Org.BouncyCastle.Asn1.Utilities { string tab = indent + Tab; - buf.Append(indent); + buf.Append(indent); buf.Append("BER Set"); buf.Append(NewLine); - foreach (Asn1Encodable o in ((Asn1Set)obj)) - { + foreach (Asn1Encodable o in ((Asn1Set)obj)) + { if (o == null) { buf.Append(tab); @@ -125,12 +125,12 @@ namespace Org.BouncyCastle.Asn1.Utilities { string tab = indent + Tab; - buf.Append(indent); + buf.Append(indent); buf.Append("DER Set"); buf.Append(NewLine); - foreach (Asn1Encodable o in ((Asn1Set)obj)) - { + foreach (Asn1Encodable o in ((Asn1Set)obj)) + { if (o == null) { buf.Append(tab); @@ -155,33 +155,33 @@ namespace Org.BouncyCastle.Asn1.Utilities { 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 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); - } + 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 DerUtf8String) + { + buf.Append(indent + "UTF8String(" + ((DerUtf8String)obj).GetString() + ") " + NewLine); + } else if (obj is DerPrintableString) { buf.Append(indent + "PrintableString(" + ((DerPrintableString)obj).GetString() + ") " + NewLine); @@ -202,14 +202,9 @@ namespace Org.BouncyCastle.Asn1.Utilities { 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) + else if (obj is DerGeneralizedTime) { - string hex = Hex.ToHexString(((DerUnknownTag)obj).GetData()); - buf.Append(indent + "Unknown " + ((int)((DerUnknownTag)obj).Tag).ToString("X") + " " + hex + NewLine); + buf.Append(indent + "GeneralizedTime(" + ((DerGeneralizedTime)obj).GetTime() + ") " + NewLine); } else if (obj is BerApplicationSpecific) { @@ -219,32 +214,32 @@ namespace Org.BouncyCastle.Asn1.Utilities { 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); + 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); - } + 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); @@ -267,7 +262,7 @@ namespace Org.BouncyCastle.Asn1.Utilities buf.Append(indent + type + " ApplicationSpecific[" + app.ApplicationTag + "]" + NewLine); foreach (Asn1Encodable ae in s) { - AsString(indent + Tab, verbose, ae.ToAsn1Object(), buf); + AsString(indent + Tab, verbose, ae.ToAsn1Object(), buf); } } catch (IOException e) @@ -281,98 +276,98 @@ namespace Org.BouncyCastle.Asn1.Utilities + Hex.ToHexString(app.GetContents()) + ")" + NewLine; } - [Obsolete("Use version accepting Asn1Encodable")] - public static string DumpAsString( + [Obsolete("Use version accepting Asn1Encodable")] + public static string DumpAsString( object obj) { if (obj is Asn1Encodable) { - StringBuilder buf = new StringBuilder(); + StringBuilder buf = new StringBuilder(); AsString("", false, ((Asn1Encodable)obj).ToAsn1Object(), buf); - return buf.ToString(); + 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 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(); - } + /** + * 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; + private static string dumpBinaryDataAsString(string indent, byte[] bytes) + { + indent += Tab; - StringBuilder buf = new StringBuilder(NewLine); + 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); - } - } + 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(); - } + return buf.ToString(); + } - private static string calculateAscString( - byte[] bytes, - int off, - int len) - { - StringBuilder buf = new StringBuilder(); + 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); - } - } + for (int i = off; i != off + len; i++) + { + char c = (char)bytes[i]; + if (c >= ' ' && c <= '~') + { + buf.Append(c); + } + } - return buf.ToString(); - } + return buf.ToString(); + } } } diff --git a/crypto/src/asn1/util/Dump.cs b/crypto/src/asn1/util/Dump.cs
index 2712ca1b3..27c87f127 100644 --- a/crypto/src/asn1/util/Dump.cs +++ b/crypto/src/asn1/util/Dump.cs
@@ -1,4 +1,3 @@ -#if !PORTABLE using Org.BouncyCastle.Asn1; using System; @@ -23,8 +22,7 @@ namespace Org.BouncyCastle.Asn1.Utilities Console.WriteLine(Asn1Dump.DumpAsString(obj)); } - bIn.Dispose(); + bIn.Close(); } } } -#endif \ No newline at end of file diff --git a/crypto/src/asn1/util/FilterStream.cs b/crypto/src/asn1/util/FilterStream.cs
index f277e33e6..2b0494b78 100644 --- a/crypto/src/asn1/util/FilterStream.cs +++ b/crypto/src/asn1/util/FilterStream.cs
@@ -30,15 +30,10 @@ namespace Org.BouncyCastle.Asn1.Utilities get { return s.Position; } set { s.Position = value; } } - - protected override void Dispose(bool disposing) + public override void Close() { - if (disposing) - { - s.Dispose(); - } + s.Close(); } - public override void Flush() { s.Flush(); 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
index caf9e77c9..4ed3a400d 100644 --- a/crypto/src/asn1/x509/AlgorithmIdentifier.cs +++ b/crypto/src/asn1/x509/AlgorithmIdentifier.cs
@@ -7,80 +7,79 @@ namespace Org.BouncyCastle.Asn1.X509 { private readonly DerObjectIdentifier objectID; private readonly Asn1Encodable parameters; - private readonly bool parametersDefined; + private readonly bool parametersDefined; - public static AlgorithmIdentifier GetInstance( + public static AlgorithmIdentifier GetInstance( Asn1TaggedObject obj, bool explicitly) { return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); } - public static AlgorithmIdentifier GetInstance( + public static AlgorithmIdentifier GetInstance( object obj) { if (obj == null || obj is AlgorithmIdentifier) return (AlgorithmIdentifier) obj; - if (obj is DerObjectIdentifier) + // TODO: delete + if (obj is DerObjectIdentifier) return new AlgorithmIdentifier((DerObjectIdentifier) obj); - if (obj is string) + // TODO: delete + 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"); - } + return new AlgorithmIdentifier(Asn1Sequence.GetInstance(obj)); + } - public AlgorithmIdentifier( + public AlgorithmIdentifier( DerObjectIdentifier objectID) { this.objectID = objectID; } - public AlgorithmIdentifier( + public AlgorithmIdentifier( string objectID) { this.objectID = new DerObjectIdentifier(objectID); } - public AlgorithmIdentifier( + public AlgorithmIdentifier( DerObjectIdentifier objectID, Asn1Encodable parameters) { this.objectID = objectID; this.parameters = parameters; - this.parametersDefined = true; + this.parametersDefined = true; } - internal AlgorithmIdentifier( + internal AlgorithmIdentifier( Asn1Sequence seq) { - if (seq.Count < 1 || seq.Count > 2) - throw new ArgumentException("Bad sequence size: " + seq.Count); + 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); + this.objectID = DerObjectIdentifier.GetInstance(seq[0]); + this.parametersDefined = (seq.Count == 2); - if (parametersDefined) + if (parametersDefined) { this.parameters = seq[1]; } } - public virtual DerObjectIdentifier ObjectID - { - get { return objectID; } - } + public virtual DerObjectIdentifier ObjectID + { + get { return objectID; } + } - public Asn1Encodable Parameters - { - get { return parameters; } - } + public Asn1Encodable Parameters + { + get { return parameters; } + } - /** + /** * Produce an object suitable for an Asn1OutputStream. * <pre> * AlgorithmIdentifier ::= Sequence { @@ -90,21 +89,21 @@ namespace Org.BouncyCastle.Asn1.X509 */ 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); + 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
index 87b112f01..ffe0ea935 100644 --- a/crypto/src/asn1/x509/AttributeTable.cs +++ b/crypto/src/asn1/x509/AttributeTable.cs
@@ -16,7 +16,7 @@ namespace Org.BouncyCastle.Asn1.X509 this.attributes = Platform.CreateHashtable(attrs); } -#if !(SILVERLIGHT || PORTABLE) +#if !SILVERLIGHT [Obsolete] public AttributeTable( Hashtable attrs) @@ -25,7 +25,7 @@ namespace Org.BouncyCastle.Asn1.X509 } #endif - public AttributeTable( + public AttributeTable( Asn1EncodableVector v) { this.attributes = Platform.CreateHashtable(v.Count); @@ -57,7 +57,7 @@ namespace Org.BouncyCastle.Asn1.X509 return (AttributeX509) attributes[oid]; } -#if !(SILVERLIGHT || PORTABLE) +#if !SILVERLIGHT [Obsolete("Use 'ToDictionary' instead")] public Hashtable ToHashtable() { diff --git a/crypto/src/asn1/x509/AuthorityInformationAccess.cs b/crypto/src/asn1/x509/AuthorityInformationAccess.cs
index 3eeba8cd2..9329e2b98 100644 --- a/crypto/src/asn1/x509/AuthorityInformationAccess.cs +++ b/crypto/src/asn1/x509/AuthorityInformationAccess.cs
@@ -7,99 +7,92 @@ 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(); - } - } + /** + * 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 == null) + return null; + return new AuthorityInformationAccess(Asn1Sequence.GetInstance(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]); + } + } + + public AuthorityInformationAccess( + AccessDescription description) + { + this.descriptions = new AccessDescription[]{ description }; + } + + /** + * create an AuthorityInformationAccess with the oid and location provided. + */ + public AuthorityInformationAccess(DerObjectIdentifier oid, GeneralName location) + : this(new AccessDescription(oid, location)) + { + } + + public AccessDescription[] GetAccessDescriptions() + { + return (AccessDescription[])descriptions.Clone(); + } + + public override Asn1Object ToAsn1Object() + { + return new DerSequence(descriptions); + } + + public override string ToString() + { + //return "AuthorityInformationAccess: Oid(" + this.descriptions[0].AccessMethod.Id + ")"; + + 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/CertificatePolicies.cs b/crypto/src/asn1/x509/CertificatePolicies.cs new file mode 100644
index 000000000..a83565bb2 --- /dev/null +++ b/crypto/src/asn1/x509/CertificatePolicies.cs
@@ -0,0 +1,81 @@ +using System; +using System.Text; + +namespace Org.BouncyCastle.Asn1.X509 +{ + public class CertificatePolicies + : Asn1Encodable + { + private readonly PolicyInformation[] policyInformation; + + public static CertificatePolicies GetInstance(object obj) + { + if (obj == null || obj is CertificatePolicies) + return (CertificatePolicies)obj; + + return new CertificatePolicies(Asn1Sequence.GetInstance(obj)); + } + + public static CertificatePolicies GetInstance(Asn1TaggedObject obj, bool isExplicit) + { + return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit)); + } + + /** + * Construct a CertificatePolicies object containing one PolicyInformation. + * + * @param name the name to be contained. + */ + public CertificatePolicies(PolicyInformation name) + { + this.policyInformation = new PolicyInformation[] { name }; + } + + public CertificatePolicies(PolicyInformation[] policyInformation) + { + this.policyInformation = policyInformation; + } + + private CertificatePolicies(Asn1Sequence seq) + { + this.policyInformation = new PolicyInformation[seq.Count]; + + for (int i = 0; i < seq.Count; ++i) + { + policyInformation[i] = PolicyInformation.GetInstance(seq[i]); + } + } + + public virtual PolicyInformation[] GetPolicyInformation() + { + return (PolicyInformation[])policyInformation.Clone(); + } + + /** + * Produce an object suitable for an ASN1OutputStream. + * <pre> + * CertificatePolicies ::= SEQUENCE SIZE {1..MAX} OF PolicyInformation + * </pre> + */ + public override Asn1Object ToAsn1Object() + { + return new DerSequence(policyInformation); + } + + public override string ToString() + { + StringBuilder sb = new StringBuilder("CertificatePolicies:"); + if (policyInformation != null && policyInformation.Length > 0) + { + sb.Append(' '); + sb.Append(policyInformation[0]); + for (int i = 1; i < policyInformation.Length; ++i) + { + sb.Append(", "); + sb.Append(policyInformation[i]); + } + } + return sb.ToString(); + } + } +} 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
index 3bf79f392..a5b11f210 100644 --- a/crypto/src/asn1/x509/ExtendedKeyUsage.cs +++ b/crypto/src/asn1/x509/ExtendedKeyUsage.cs
@@ -17,14 +17,14 @@ namespace Org.BouncyCastle.Asn1.X509 internal readonly IDictionary usageTable = Platform.CreateHashtable(); internal readonly Asn1Sequence seq; - public static ExtendedKeyUsage GetInstance( + public static ExtendedKeyUsage GetInstance( Asn1TaggedObject obj, bool explicitly) { return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); } - public static ExtendedKeyUsage GetInstance( + public static ExtendedKeyUsage GetInstance( object obj) { if (obj is ExtendedKeyUsage) @@ -32,45 +32,45 @@ namespace Org.BouncyCastle.Asn1.X509 return (ExtendedKeyUsage) obj; } - if (obj is Asn1Sequence) + if (obj is Asn1Sequence) { return new ExtendedKeyUsage((Asn1Sequence) obj); } - if (obj is X509Extension) - { - return GetInstance(X509Extension.ConvertValueToObject((X509Extension) obj)); - } + if (obj is X509Extension) + { + return GetInstance(X509Extension.ConvertValueToObject((X509Extension) obj)); + } - throw new ArgumentException("Invalid ExtendedKeyUsage: " + obj.GetType().Name); + throw new ArgumentException("Invalid ExtendedKeyUsage: " + obj.GetType().Name); } - private ExtendedKeyUsage( + 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."); + foreach (object o in seq) + { + if (!(o is DerObjectIdentifier)) + throw new ArgumentException("Only DerObjectIdentifier instances allowed in ExtendedKeyUsage."); - this.usageTable.Add(o, o); + this.usageTable[o] = o; } } - public ExtendedKeyUsage( - params KeyPurposeID[] usages) - { - this.seq = new DerSequence(usages); + public ExtendedKeyUsage( + params KeyPurposeID[] usages) + { + this.seq = new DerSequence(usages); - foreach (KeyPurposeID usage in usages) - { - this.usageTable.Add(usage, usage); - } - } + foreach (KeyPurposeID usage in usages) + { + this.usageTable[usage] = usage; + } + } -#if !(SILVERLIGHT || PORTABLE) +#if !SILVERLIGHT [Obsolete] public ExtendedKeyUsage( ArrayList usages) @@ -84,23 +84,24 @@ namespace Org.BouncyCastle.Asn1.X509 { Asn1EncodableVector v = new Asn1EncodableVector(); - foreach (Asn1Object o in usages) + foreach (object usage in usages) { - v.Add(o); + Asn1Encodable o = KeyPurposeID.GetInstance(usage); - this.usageTable.Add(o, o); + v.Add(o); + this.usageTable[o] = o; } - this.seq = new DerSequence(v); + this.seq = new DerSequence(v); } - public bool HasKeyPurposeId( + public bool HasKeyPurposeId( KeyPurposeID keyPurposeId) { - return usageTable[keyPurposeId] != null; + return usageTable.Contains(keyPurposeId); } -#if !(SILVERLIGHT || PORTABLE) +#if !SILVERLIGHT [Obsolete("Use 'GetAllUsages'")] public ArrayList GetUsages() { @@ -109,21 +110,21 @@ namespace Org.BouncyCastle.Asn1.X509 #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); - } + * 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; } - } + { + get { return usageTable.Count; } + } - public override Asn1Object ToAsn1Object() + 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
index c178f5b45..8374ff60a 100644 --- a/crypto/src/asn1/x509/NameConstraints.cs +++ b/crypto/src/asn1/x509/NameConstraints.cs
@@ -41,7 +41,7 @@ namespace Org.BouncyCastle.Asn1.X509 } } -#if !(SILVERLIGHT || PORTABLE) +#if !SILVERLIGHT public NameConstraints( ArrayList permitted, ArrayList excluded) diff --git a/crypto/src/asn1/x509/NoticeReference.cs b/crypto/src/asn1/x509/NoticeReference.cs
index 718fe92cf..f0d3a7b7f 100644 --- a/crypto/src/asn1/x509/NoticeReference.cs +++ b/crypto/src/asn1/x509/NoticeReference.cs
@@ -1,138 +1,143 @@ using System; using System.Collections; +using Org.BouncyCastle.Math; + 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) + /** + * <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 + { + private readonly DisplayText organization; + private readonly Asn1Sequence noticeNumbers; + + private static Asn1EncodableVector ConvertVector(IList numbers) + { + Asn1EncodableVector av = new Asn1EncodableVector(); + + foreach (object o in numbers) + { + DerInteger di; + + if (o is BigInteger) + { + di = new DerInteger((BigInteger)o); + } + else if (o is int) + { + di = new DerInteger((int)o); + } + else + { + throw new ArgumentException(); + } + + av.Add(di); + } + return av; + } + + /** + * Creates a new <code>NoticeReference</code> instance. + * + * @param organization a <code>String</code> value + * @param numbers a <code>Vector</code> value + */ + public NoticeReference(string organization, IList numbers) + : this(organization, ConvertVector(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); - } - } + * Creates a new <code>NoticeReference</code> instance. + * + * @param organization a <code>String</code> value + * @param noticeNumbers an <code>ASN1EncodableVector</code> value + */ + public NoticeReference(string organization, Asn1EncodableVector noticeNumbers) + : this(new DisplayText(organization), noticeNumbers) + { + } + + /** + * Creates a new <code>NoticeReference</code> instance. + * + * @param organization displayText + * @param noticeNumbers an <code>ASN1EncodableVector</code> value + */ + public NoticeReference(DisplayText organization, Asn1EncodableVector noticeNumbers) + { + this.organization = organization; + this.noticeNumbers = new DerSequence(noticeNumbers); + } + + /** + * Creates a new <code>NoticeReference</code> instance. + * <p>Useful for reconstructing a <code>NoticeReference</code> + * instance from its encodable/encoded form.</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 == null) + return null; + return new NoticeReference(Asn1Sequence.GetInstance(obj)); + } + + public virtual DisplayText Organization + { + get { return organization; } + } + + public virtual DerInteger[] GetNoticeNumbers() + { + DerInteger[] tmp = new DerInteger[noticeNumbers.Count]; + + for (int i = 0; i != noticeNumbers.Count; ++i) + { + tmp[i] = DerInteger.GetInstance(noticeNumbers[i]); + } + + return tmp; + } + + /** + * 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
index 928ad134d..3ad351107 100644 --- a/crypto/src/asn1/x509/PolicyMappings.cs +++ b/crypto/src/asn1/x509/PolicyMappings.cs
@@ -29,7 +29,7 @@ namespace Org.BouncyCastle.Asn1.X509 this.seq = seq; } -#if !(SILVERLIGHT || PORTABLE) +#if !SILVERLIGHT public PolicyMappings( Hashtable mappings) : this((IDictionary)mappings) 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
index 187f8d3bb..3cf6d7e10 100644 --- a/crypto/src/asn1/x509/PolicyQualifierInfo.cs +++ b/crypto/src/asn1/x509/PolicyQualifierInfo.cs
@@ -2,90 +2,94 @@ 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; + /** + * Policy qualifiers, used in the X509V3 CertificatePolicies + * extension. + * + * <pre> + * PolicyQualifierInfo ::= Sequence { + * policyQualifierId PolicyQualifierId, + * qualifier ANY DEFINED BY policyQualifierId } + * </pre> + */ + public class PolicyQualifierInfo + : Asn1Encodable + { + private readonly DerObjectIdentifier policyQualifierId; + private 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> 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> 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"); + /** + * 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]; - } + policyQualifierId = DerObjectIdentifier.GetInstance(seq[0]); + qualifier = seq[1]; + } - public static PolicyQualifierInfo GetInstance( - object obj) - { - if (obj is PolicyQualifierInfo) - { - return (PolicyQualifierInfo) obj; - } + public static PolicyQualifierInfo GetInstance( + object obj) + { + if (obj is PolicyQualifierInfo) + return (PolicyQualifierInfo)obj; + if (obj == null) + return null; + return new PolicyQualifierInfo(Asn1Sequence.GetInstance(obj)); + } - if (obj is Asn1Sequence) - { - return new PolicyQualifierInfo((Asn1Sequence) obj); - } + public virtual DerObjectIdentifier PolicyQualifierId + { + get { return policyQualifierId; } + } - throw new ArgumentException("unknown object in GetInstance: " + obj.GetType().FullName, "obj"); - } + public virtual Asn1Encodable Qualifier + { + get { return qualifier; } + } - /** - * Returns a Der-encodable representation of this instance. - * - * @return a <code>Asn1Object</code> value - */ - public override Asn1Object ToAsn1Object() - { - return new DerSequence(policyQualifierId, qualifier); - } - } + /** + * 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
index c76d94d78..fcb30290d 100644 --- a/crypto/src/asn1/x509/SubjectDirectoryAttributes.cs +++ b/crypto/src/asn1/x509/SubjectDirectoryAttributes.cs
@@ -78,7 +78,7 @@ namespace Org.BouncyCastle.Asn1.X509 } } -#if !(SILVERLIGHT || PORTABLE) +#if !SILVERLIGHT [Obsolete] public SubjectDirectoryAttributes( ArrayList 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
index ca4e99ac7..0f2511e6d 100644 --- a/crypto/src/asn1/x509/Time.cs +++ b/crypto/src/asn1/x509/Time.cs
@@ -7,28 +7,28 @@ namespace Org.BouncyCastle.Asn1.X509 { internal Asn1Object time; - public static Time GetInstance( + public static Time GetInstance( Asn1TaggedObject obj, bool explicitly) { return GetInstance(obj.GetObject()); } - public Time( + public Time( Asn1Object time) { - if (time == null) - throw new ArgumentNullException("time"); + if (time == null) + throw new ArgumentNullException("time"); - if (!(time is DerUtcTime) && !(time is DerGeneralizedTime)) + if (!(time is DerUtcTime) && !(time is DerGeneralizedTime)) { throw new ArgumentException("unknown object passed to Time"); } - this.time = 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. @@ -38,9 +38,9 @@ namespace Org.BouncyCastle.Asn1.X509 { string d = date.ToString("yyyyMMddHHmmss") + "Z"; - int year = Int32.Parse(d.Substring(0, 4)); + int year = Int32.Parse(d.Substring(0, 4)); - if (year < 1950 || year > 2049) + if (year < 1950 || year > 2049) { time = new DerGeneralizedTime(d); } @@ -50,56 +50,56 @@ namespace Org.BouncyCastle.Asn1.X509 } } - public static Time GetInstance( + public static Time GetInstance( object obj) { if (obj == null || obj is Time) return (Time) obj; - if (obj is DerUtcTime) + if (obj is DerUtcTime) return new Time((DerUtcTime) obj); - if (obj is DerGeneralizedTime) + if (obj is DerGeneralizedTime) return new Time((DerGeneralizedTime) obj); - throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); } - public string GetTime() + public string GetTime() { if (time is DerUtcTime) { return ((DerUtcTime) time).AdjustedTimeString; } - return ((DerGeneralizedTime) time).GetTime(); + return ((DerGeneralizedTime) time).GetTime(); } - /// <summary> + /// <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); - } + 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 { @@ -112,9 +112,9 @@ namespace Org.BouncyCastle.Asn1.X509 return time; } - public override string ToString() - { - return GetTime(); - } - } + public override string ToString() + { + return GetTime(); + } + } } diff --git a/crypto/src/asn1/x509/UserNotice.cs b/crypto/src/asn1/x509/UserNotice.cs
index 2878a180f..5938f7c49 100644 --- a/crypto/src/asn1/x509/UserNotice.cs +++ b/crypto/src/asn1/x509/UserNotice.cs
@@ -19,10 +19,10 @@ namespace Org.BouncyCastle.Asn1.X509 public class UserNotice : Asn1Encodable { - internal NoticeReference noticeRef; - internal DisplayText explicitText; + private readonly NoticeReference noticeRef; + private readonly DisplayText explicitText; - /** + /** * Creates a new <code>UserNotice</code> instance. * * @param noticeRef a <code>NoticeReference</code> value @@ -36,7 +36,7 @@ namespace Org.BouncyCastle.Asn1.X509 this.explicitText = explicitText; } - /** + /** * Creates a new <code>UserNotice</code> instance. * * @param noticeRef a <code>NoticeReference</code> value @@ -45,60 +45,78 @@ namespace Org.BouncyCastle.Asn1.X509 public UserNotice( NoticeReference noticeRef, string str) + : this(noticeRef, new DisplayText(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); - } + /** + * 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 static UserNotice GetInstance(object obj) + { + if (obj is UserNotice) + return (UserNotice)obj; + if (obj == null) + return null; + return new UserNotice(Asn1Sequence.GetInstance(obj)); + } + + public virtual NoticeReference NoticeRef + { + get { return noticeRef; } + } + + public virtual DisplayText ExplicitText + { + get { return explicitText; } } - public override Asn1Object ToAsn1Object() + public override Asn1Object ToAsn1Object() { Asn1EncodableVector av = new Asn1EncodableVector(); - if (noticeRef != null) + if (noticeRef != null) { av.Add(noticeRef); } - if (explicitText != null) + if (explicitText != null) { av.Add(explicitText); } - return new DerSequence(av); + 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
index a9c43357c..2c6e54a77 100644 --- a/crypto/src/asn1/x509/V2Form.cs +++ b/crypto/src/asn1/x509/V2Form.cs
@@ -9,55 +9,67 @@ namespace Org.BouncyCastle.Asn1.X509 internal IssuerSerial baseCertificateID; internal ObjectDigestInfo objectDigestInfo; - public static V2Form GetInstance( + public static V2Form GetInstance( Asn1TaggedObject obj, bool explicitly) { return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); } - public static V2Form GetInstance( - object obj) + public static V2Form GetInstance(object obj) { if (obj is V2Form) - { - return (V2Form) obj; - } + return (V2Form)obj; + if (obj != null) + return new V2Form(Asn1Sequence.GetInstance(obj)); + return null; + } - if (obj is Asn1Sequence) - { - return new V2Form((Asn1Sequence) obj); - } + public V2Form(GeneralNames issuerName) + : this(issuerName, null, null) + { + } + + public V2Form(GeneralNames issuerName, IssuerSerial baseCertificateID) + : this(issuerName, baseCertificateID, null) + { + } - throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj"); + public V2Form(GeneralNames issuerName, ObjectDigestInfo objectDigestInfo) + : this(issuerName, null, objectDigestInfo) + { } - public V2Form( - GeneralNames issuerName) + public V2Form( + GeneralNames issuerName, + IssuerSerial baseCertificateID, + ObjectDigestInfo objectDigestInfo) { this.issuerName = issuerName; + this.baseCertificateID = baseCertificateID; + this.objectDigestInfo = objectDigestInfo; } - private V2Form( + private V2Form( Asn1Sequence seq) { - if (seq.Count > 3) - { - throw new ArgumentException("Bad sequence size: " + seq.Count); - } + if (seq.Count > 3) + { + throw new ArgumentException("Bad sequence size: " + seq.Count); + } - int index = 0; + int index = 0; - if (!(seq[0] is Asn1TaggedObject)) + if (!(seq[0] is Asn1TaggedObject)) { index++; this.issuerName = GeneralNames.GetInstance(seq[0]); } - for (int i = index; i != seq.Count; i++) + for (int i = index; i != seq.Count; i++) { - Asn1TaggedObject o = Asn1TaggedObject.GetInstance(seq[i]); - if (o.TagNo == 0) + Asn1TaggedObject o = Asn1TaggedObject.GetInstance(seq[i]); + if (o.TagNo == 0) { baseCertificateID = IssuerSerial.GetInstance(o, false); } @@ -65,29 +77,29 @@ namespace Org.BouncyCastle.Asn1.X509 { objectDigestInfo = ObjectDigestInfo.GetInstance(o, false); } - else - { - throw new ArgumentException("Bad tag number: " + o.TagNo); - } - } + else + { + throw new ArgumentException("Bad tag number: " + o.TagNo); + } + } } - public GeneralNames IssuerName + public GeneralNames IssuerName { get { return issuerName; } } - public IssuerSerial BaseCertificateID + public IssuerSerial BaseCertificateID { get { return baseCertificateID; } } - public ObjectDigestInfo ObjectDigestInfo + public ObjectDigestInfo ObjectDigestInfo { get { return objectDigestInfo; } } - /** + /** * Produce an object suitable for an Asn1OutputStream. * <pre> * V2Form ::= Sequence { @@ -104,22 +116,22 @@ namespace Org.BouncyCastle.Asn1.X509 { Asn1EncodableVector v = new Asn1EncodableVector(); - if (issuerName != null) + if (issuerName != null) { v.Add(issuerName); } - if (baseCertificateID != null) + if (baseCertificateID != null) { v.Add(new DerTaggedObject(false, 0, baseCertificateID)); } - if (objectDigestInfo != null) + if (objectDigestInfo != null) { v.Add(new DerTaggedObject(false, 1, objectDigestInfo)); } - return new DerSequence(v); + 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
index e50d3563b..c8558ae61 100644 --- a/crypto/src/asn1/x509/X509CertificateStructure.cs +++ b/crypto/src/asn1/x509/X509CertificateStructure.cs
@@ -21,109 +21,107 @@ namespace Org.BouncyCastle.Asn1.X509 private readonly AlgorithmIdentifier sigAlgID; private readonly DerBitString sig; - public static X509CertificateStructure GetInstance( + public static X509CertificateStructure GetInstance( Asn1TaggedObject obj, bool explicitly) { return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); } - public static X509CertificateStructure GetInstance( + public static X509CertificateStructure GetInstance( object obj) { if (obj is X509CertificateStructure) return (X509CertificateStructure)obj; + if (obj == null) + return null; + return new X509CertificateStructure(Asn1Sequence.GetInstance(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; } - 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( + private X509CertificateStructure( Asn1Sequence seq) { - if (seq.Count != 3) - throw new ArgumentException("sequence wrong size for a certificate", "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]); + tbsCert = TbsCertificateStructure.GetInstance(seq[0]); + sigAlgID = AlgorithmIdentifier.GetInstance(seq[1]); + sig = DerBitString.GetInstance(seq[2]); } - public TbsCertificateStructure TbsCertificate + public TbsCertificateStructure TbsCertificate { - get { return tbsCert; } + get { return tbsCert; } } - public int Version + public int Version { get { return tbsCert.Version; } } - public DerInteger SerialNumber + public DerInteger SerialNumber { get { return tbsCert.SerialNumber; } } - public X509Name Issuer + public X509Name Issuer { get { return tbsCert.Issuer; } } - public Time StartDate + public Time StartDate { get { return tbsCert.StartDate; } } - public Time EndDate + public Time EndDate { get { return tbsCert.EndDate; } } - public X509Name Subject + public X509Name Subject { get { return tbsCert.Subject; } } - public SubjectPublicKeyInfo SubjectPublicKeyInfo + public SubjectPublicKeyInfo SubjectPublicKeyInfo { get { return tbsCert.SubjectPublicKeyInfo; } } - public AlgorithmIdentifier SignatureAlgorithm + public AlgorithmIdentifier SignatureAlgorithm { get { return sigAlgID; } } - public DerBitString Signature + public DerBitString Signature { get { return sig; } } - public override Asn1Object ToAsn1Object() + public override Asn1Object ToAsn1Object() { - return new DerSequence(tbsCert, sigAlgID, sig); + 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
index 1896450f5..5dce5622d 100644 --- a/crypto/src/asn1/x509/X509Extensions.cs +++ b/crypto/src/asn1/x509/X509Extensions.cs
@@ -278,7 +278,7 @@ namespace Org.BouncyCastle.Asn1.X509 } } -#if !(SILVERLIGHT || PORTABLE) +#if !SILVERLIGHT /** * constructor from a table of extensions. * <p> 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
index b459cbe1b..c183e5798 100644 --- a/crypto/src/asn1/x509/X509Name.cs +++ b/crypto/src/asn1/x509/X509Name.cs
@@ -1,10 +1,9 @@ using System; using System.Collections; -using System.Globalization; using System.IO; using System.Text; -#if (SILVERLIGHT || PORTABLE) +#if SILVERLIGHT using System.Collections.Generic; #endif @@ -53,17 +52,17 @@ namespace Org.BouncyCastle.Asn1.X509 */ 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"); + /** + * 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"); + /** + * 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"); @@ -82,89 +81,89 @@ namespace Org.BouncyCastle.Asn1.X509 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; - - /** + /** + * 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> */ @@ -196,31 +195,31 @@ namespace Org.BouncyCastle.Asn1.X509 * from back to front. */ // public static bool DefaultReverse = false; - public static bool DefaultReverse - { - get { return defaultReverse[0]; } - set { defaultReverse[0] = value; } - } + public static bool DefaultReverse + { + get { return defaultReverse[0]; } + set { defaultReverse[0] = value; } + } + + private static readonly bool[] defaultReverse = { false }; - private static readonly bool[] defaultReverse = { false }; +#if SILVERLIGHT + /** + * 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(); -#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 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. @@ -228,21 +227,21 @@ namespace Org.BouncyCastle.Asn1.X509 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(); + * 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. @@ -263,26 +262,26 @@ namespace Org.BouncyCastle.Asn1.X509 DefaultSymbols.Add(EmailAddress, "E"); DefaultSymbols.Add(DC, "DC"); DefaultSymbols.Add(UID, "UID"); - DefaultSymbols.Add(Street, "STREET"); + 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"); + 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"); @@ -290,28 +289,28 @@ namespace Org.BouncyCastle.Asn1.X509 RFC2253Symbols.Add(CN, "CN"); RFC2253Symbols.Add(L, "L"); RFC2253Symbols.Add(ST, "ST"); - RFC2253Symbols.Add(Street, "STREET"); - RFC2253Symbols.Add(DC, "DC"); + 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"); + 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("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("serialnumber", SerialNumber); + DefaultLookup.Add("street", Street); + DefaultLookup.Add("emailaddress", E); DefaultLookup.Add("dc", DC); DefaultLookup.Add("e", E); DefaultLookup.Add("uid", UID); @@ -321,29 +320,29 @@ namespace Org.BouncyCastle.Asn1.X509 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); - } + 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 readonly X509NameEntryConverter converter; - private IList values = Platform.CreateArrayList(); + 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. @@ -357,23 +356,23 @@ namespace Org.BouncyCastle.Asn1.X509 return GetInstance(Asn1Sequence.GetInstance(obj, explicitly)); } - public static X509Name GetInstance( + public static X509Name GetInstance( object obj) { if (obj == null || obj is X509Name) return (X509Name)obj; - if (obj != null) - return new X509Name(Asn1Sequence.GetInstance(obj)); + if (obj != null) + return new X509Name(Asn1Sequence.GetInstance(obj)); - throw new ArgumentException("null object in factory", "obj"); + throw new ArgumentException("null object in factory", "obj"); } - protected X509Name() - { - } + protected X509Name() + { + } - /** + /** * Constructor from Asn1Sequence * * the principal will be a list of constructed sets, each containing an (OID, string) pair. @@ -383,50 +382,40 @@ namespace Org.BouncyCastle.Asn1.X509 { this.seq = seq; - foreach (Asn1Encodable asn1Obj in seq) - { - Asn1Set asn1Set = Asn1Set.GetInstance(asn1Obj.ToAsn1Object()); + foreach (Asn1Encodable asn1Obj in seq) + { + Asn1Set asn1Set = Asn1Set.GetInstance(asn1Obj.ToAsn1Object()); - for (int i = 0; i < asn1Set.Count; i++) + for (int i = 0; i < asn1Set.Count; i++) { - Asn1Sequence s = Asn1Sequence.GetInstance(asn1Set[i].ToAsn1Object()); + Asn1Sequence s = Asn1Sequence.GetInstance(asn1Set[i].ToAsn1Object()); - if (s.Count != 2) - throw new ArgumentException("badly sized pair"); + if (s.Count != 2) + throw new ArgumentException("badly sized pair"); - ordering.Add(DerObjectIdentifier.GetInstance(s[0].ToAsn1Object())); + 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; - } + 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); - } + values.Add(v); + } else { - values.Add("#" + Hex.ToHexString(derValue.GetEncoded())); + values.Add("#" + Hex.ToHexString(derValue.GetEncoded())); } - added.Add(i != 0); + 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> @@ -442,17 +431,6 @@ namespace Org.BouncyCastle.Asn1.X509 { } -#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> @@ -469,31 +447,21 @@ namespace Org.BouncyCastle.Asn1.X509 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 - } - } + this.converter = converter; -#if !(SILVERLIGHT || PORTABLE) - [Obsolete] - public X509Name( - ArrayList oids, - ArrayList values) - : this(oids, values, new X509DefaultEntryConverter()) - { + 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 + } } -#endif /** * Takes two vectors one of the oids and the other of the values. @@ -505,17 +473,6 @@ namespace Org.BouncyCastle.Asn1.X509 { } -#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> @@ -529,12 +486,12 @@ namespace Org.BouncyCastle.Asn1.X509 { this.converter = converter; - if (oids.Count != values.Count) + if (oids.Count != values.Count) { throw new ArgumentException("'oids' must be same length as 'values'."); } - for (int i = 0; i < oids.Count; i++) + for (int i = 0; i < oids.Count; i++) { this.ordering.Add(oids[i]); this.values.Add(values[i]); @@ -548,7 +505,7 @@ namespace Org.BouncyCastle.Asn1.X509 // 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. */ @@ -558,7 +515,7 @@ namespace Org.BouncyCastle.Asn1.X509 { } - /** + /** * 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 @@ -571,7 +528,7 @@ namespace Org.BouncyCastle.Asn1.X509 { } - /** + /** * 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 @@ -584,7 +541,7 @@ namespace Org.BouncyCastle.Asn1.X509 { } - /** + /** * 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 @@ -599,17 +556,6 @@ namespace Org.BouncyCastle.Asn1.X509 { } -#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 @@ -631,11 +577,11 @@ namespace Org.BouncyCastle.Asn1.X509 { } - private DerObjectIdentifier DecodeOid( + private DerObjectIdentifier DecodeOid( string name, IDictionary lookUp) { - if (name.ToUpperInvariant().StartsWith("OID.")) + if (Platform.ToUpperInvariant(name).StartsWith("OID.")) { return new DerObjectIdentifier(name.Substring(4)); } @@ -644,16 +590,16 @@ namespace Org.BouncyCastle.Asn1.X509 return new DerObjectIdentifier(name); } - DerObjectIdentifier oid = (DerObjectIdentifier)lookUp[name.ToLowerInvariant()]; + DerObjectIdentifier oid = (DerObjectIdentifier)lookUp[Platform.ToLowerInvariant(name)]; if (oid == null) { throw new ArgumentException("Unknown object id - " + name + " - passed to distinguished name"); } - return oid; + 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 @@ -675,30 +621,30 @@ namespace Org.BouncyCastle.Asn1.X509 this.converter = converter; X509NameTokenizer nTok = new X509NameTokenizer(dirName); - while (nTok.HasMoreTokens()) + while (nTok.HasMoreTokens()) { string token = nTok.NextToken(); int index = token.IndexOf('='); - if (index == -1) + if (index == -1) { throw new ArgumentException("badly formated directory string"); } - string name = token.Substring(0, index); + string name = token.Substring(0, index); string value = token.Substring(index + 1); DerObjectIdentifier oid = DecodeOid(name, lookUp); - if (value.IndexOf('+') > 0) + if (value.IndexOf('+') > 0) { X509NameTokenizer vTok = new X509NameTokenizer(value, '+'); - string v = vTok.NextToken(); + string v = vTok.NextToken(); - this.ordering.Add(oid); + this.ordering.Add(oid); this.values.Add(v); this.added.Add(false); - while (vTok.HasMoreTokens()) + while (vTok.HasMoreTokens()) { string sv = vTok.NextToken(); int ndx = sv.IndexOf('='); @@ -718,46 +664,35 @@ namespace Org.BouncyCastle.Asn1.X509 } } - if (reverse) + if (reverse) { // this.ordering.Reverse(); // this.values.Reverse(); // this.added.Reverse(); - IList o = Platform.CreateArrayList(); + 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; - } - } + int count = 1; -#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); + 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; + } } -#endif /** * return an IList of the oids in the name, in the order they were found. @@ -767,57 +702,25 @@ namespace Org.BouncyCastle.Asn1.X509 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); + return GetValueList(null); } -#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. - */ + * 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)) + if (null == oid || oid.Equals(ordering[i])) { string val = (string)values[i]; @@ -829,9 +732,10 @@ namespace Org.BouncyCastle.Asn1.X509 v.Add(val); } } + return v; } - public override Asn1Object ToAsn1Object() + public override Asn1Object ToAsn1Object() { if (seq == null) { @@ -839,12 +743,12 @@ namespace Org.BouncyCastle.Asn1.X509 Asn1EncodableVector sVec = new Asn1EncodableVector(); DerObjectIdentifier lstOid = null; - for (int i = 0; i != ordering.Count; i++) + for (int i = 0; i != ordering.Count; i++) { DerObjectIdentifier oid = (DerObjectIdentifier)ordering[i]; - string str = (string)values[i]; + string str = (string)values[i]; - if (lstOid == null + if (lstOid == null || ((bool)this.added[i])) { } @@ -854,211 +758,211 @@ namespace Org.BouncyCastle.Asn1.X509 sVec = new Asn1EncodableVector(); } - sVec.Add( - new DerSequence( - oid, - converter.GetConvertedValue(oid, str))); + sVec.Add( + new DerSequence( + oid, + converter.GetConvertedValue(oid, str))); - lstOid = oid; + lstOid = oid; } - vec.Add(new DerSet(sVec)); + vec.Add(new DerSet(sVec)); - seq = new DerSequence(vec); + 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); + /// <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 == null) + return false; - if (other == this) - return true; + if (other == this) + return true; - int orderingSize = ordering.Count; + int orderingSize = ordering.Count; - if (orderingSize != other.ordering.Count) - return false; + 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]; + for (int i = 0; i < orderingSize; i++) + { + DerObjectIdentifier oid = (DerObjectIdentifier) ordering[i]; + DerObjectIdentifier oOid = (DerObjectIdentifier) other.ordering[i]; - if (!oid.Equals(oOid)) - return false; + if (!oid.Equals(oOid)) + return false; - string val = (string) values[i]; - string oVal = (string) other.values[i]; + string val = (string) values[i]; + string oVal = (string) other.values[i]; - if (!equivalentStrings(val, oVal)) - return false; - } + if (!equivalentStrings(val, oVal)) + return false; + } - return true; - } + 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( + * 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 = Platform.ToLowerInvariant(s).Trim(); + + if (v.StartsWith("#")) + { + Asn1Object obj = decodeObject(v); + + if (obj is IAsn1String) + { + v = Platform.ToLowerInvariant(((IAsn1String)obj).GetString()).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, @@ -1083,18 +987,18 @@ namespace Org.BouncyCastle.Asn1.X509 int end = buf.Length; - if (val.StartsWith("\\#")) - { - index += 2; - } + if (val.StartsWith("\\#")) + { + index += 2; + } - while (index != end) + while (index != end) { if ((buf[index] == ',') || (buf[index] == '"') || (buf[index] == '\\') || (buf[index] == '+') - || (buf[index] == '=') + || (buf[index] == '=') || (buf[index] == '<') || (buf[index] == '>') || (buf[index] == ';')) @@ -1107,16 +1011,6 @@ namespace Org.BouncyCastle.Asn1.X509 } } -#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 @@ -1133,55 +1027,55 @@ namespace Org.BouncyCastle.Asn1.X509 bool reverse, IDictionary oidSymbols) { -#if (SILVERLIGHT || PORTABLE) +#if SILVERLIGHT List<object> components = new List<object>(); #else - ArrayList components = new ArrayList(); + 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() + 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/ECNamedCurveTable.cs b/crypto/src/asn1/x9/ECNamedCurveTable.cs new file mode 100644
index 000000000..0030d376b --- /dev/null +++ b/crypto/src/asn1/x9/ECNamedCurveTable.cs
@@ -0,0 +1,118 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Sec; +using Org.BouncyCastle.Asn1.TeleTrust; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; + +namespace Org.BouncyCastle.Asn1.X9 +{ + /** + * A general class that reads all X9.62 style EC curve tables. + */ + public class ECNamedCurveTable + { + /** + * return a X9ECParameters object representing the passed in named + * curve. The routine returns null if the curve is not present. + * + * @param name the name of the curve requested + * @return an X9ECParameters object or null if the curve is not available. + */ + public static X9ECParameters GetByName(string name) + { + X9ECParameters ecP = X962NamedCurves.GetByName(name); + + if (ecP == null) + { + ecP = SecNamedCurves.GetByName(name); + } + + if (ecP == null) + { + ecP = TeleTrusTNamedCurves.GetByName(name); + } + + if (ecP == null) + { + ecP = NistNamedCurves.GetByName(name); + } + + return ecP; + } + + /** + * return 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) + { + DerObjectIdentifier oid = X962NamedCurves.GetOid(name); + + if (oid == null) + { + oid = SecNamedCurves.GetOid(name); + } + + if (oid == null) + { + oid = TeleTrusTNamedCurves.GetOid(name); + } + + if (oid == null) + { + oid = NistNamedCurves.GetOid(name); + } + + return oid; + } + + /** + * return a X9ECParameters object representing the passed in named + * curve. + * + * @param oid the object id of the curve requested + * @return an X9ECParameters object or null if the curve is not available. + */ + public static X9ECParameters GetByOid(DerObjectIdentifier oid) + { + X9ECParameters ecP = X962NamedCurves.GetByOid(oid); + + if (ecP == null) + { + ecP = SecNamedCurves.GetByOid(oid); + } + + if (ecP == null) + { + ecP = TeleTrusTNamedCurves.GetByOid(oid); + } + + // NOTE: All the NIST curves are currently from SEC, so no point in redundant OID lookup + + return ecP; + } + + /** + * return an enumeration of the names of the available curves. + * + * @return an enumeration of the names of the available curves. + */ + public static IEnumerable Names + { + get + { + IList v = Platform.CreateArrayList(); + CollectionUtilities.AddRange(v, X962NamedCurves.Names); + CollectionUtilities.AddRange(v, SecNamedCurves.Names); + CollectionUtilities.AddRange(v, NistNamedCurves.Names); + CollectionUtilities.AddRange(v, TeleTrusTNamedCurves.Names); + return v; + } + } + } +} 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
index de80ca280..6b76c4eb4 100644 --- a/crypto/src/asn1/x9/X962NamedCurves.cs +++ b/crypto/src/asn1/x9/X962NamedCurves.cs
@@ -1,6 +1,5 @@ using System; using System.Collections; -using System.Globalization; using Org.BouncyCastle.Math; using Org.BouncyCastle.Math.EC; @@ -10,724 +9,745 @@ 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(); + /** + * 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() + { + BigInteger n = new BigInteger("ffffffffffffffffffffffff99def836146bc9b1b4d22831", 16); + BigInteger h = BigInteger.One; + + ECCurve cFp192v1 = new FpCurve( + new BigInteger("6277101735386680763835789423207666416083908700390324961279"), + new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16), + new BigInteger("64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", 16), + n, h); + + return new X9ECParameters( + cFp192v1, + cFp192v1.DecodePoint( + Hex.Decode("03188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012")), + n, h, + Hex.Decode("3045AE6FC8422f64ED579528D38120EAE12196D5")); + } + } + + internal class Prime192v2Holder + : X9ECParametersHolder + { + private Prime192v2Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Prime192v2Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("fffffffffffffffffffffffe5fb1a724dc80418648d8dd31", 16); + BigInteger h = BigInteger.One; + + ECCurve cFp192v2 = new FpCurve( + new BigInteger("6277101735386680763835789423207666416083908700390324961279"), + new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16), + new BigInteger("cc22d6dfb95c6b25e49c0d6364a4e5980c393aa21668d953", 16), + n, h); + + return new X9ECParameters( + cFp192v2, + cFp192v2.DecodePoint( + Hex.Decode("03eea2bae7e1497842f2de7769cfe9c989c072ad696f48034a")), + n, h, + Hex.Decode("31a92ee2029fd10d901b113e990710f0d21ac6b6")); + } + } + + internal class Prime192v3Holder + : X9ECParametersHolder + { + private Prime192v3Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Prime192v3Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("ffffffffffffffffffffffff7a62d031c83f4294f640ec13", 16); + BigInteger h = BigInteger.One; + + ECCurve cFp192v3 = new FpCurve( + new BigInteger("6277101735386680763835789423207666416083908700390324961279"), + new BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16), + new BigInteger("22123dc2395a05caa7423daeccc94760a7d462256bd56916", 16), + n, h); + + return new X9ECParameters( + cFp192v3, + cFp192v3.DecodePoint( + Hex.Decode("027d29778100c65a1da1783716588dce2b8b4aee8e228f1896")), + n, h, + Hex.Decode("c469684435deb378c4b65ca9591e2a5763059a2e")); + } + } + + internal class Prime239v1Holder + : X9ECParametersHolder + { + private Prime239v1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Prime239v1Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("7fffffffffffffffffffffff7fffff9e5e9a9f5d9071fbd1522688909d0b", 16); + BigInteger h = BigInteger.One; + + ECCurve cFp239v1 = new FpCurve( + new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), + new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), + new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16), + n, h); + + return new X9ECParameters( + cFp239v1, + cFp239v1.DecodePoint( + Hex.Decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), + n, h, + Hex.Decode("e43bb460f0b80cc0c0b075798e948060f8321b7d")); + } + } + + internal class Prime239v2Holder + : X9ECParametersHolder + { + private Prime239v2Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Prime239v2Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("7fffffffffffffffffffffff800000cfa7e8594377d414c03821bc582063", 16); + BigInteger h = BigInteger.One; + + ECCurve cFp239v2 = new FpCurve( + new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), + new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), + new BigInteger("617fab6832576cbbfed50d99f0249c3fee58b94ba0038c7ae84c8c832f2c", 16), + n, h); + + return new X9ECParameters( + cFp239v2, + cFp239v2.DecodePoint( + Hex.Decode("0238af09d98727705120c921bb5e9e26296a3cdcf2f35757a0eafd87b830e7")), + n, h, + Hex.Decode("e8b4011604095303ca3b8099982be09fcb9ae616")); + } + } + + internal class Prime239v3Holder + : X9ECParametersHolder + { + private Prime239v3Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Prime239v3Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("7fffffffffffffffffffffff7fffff975deb41b3a6057c3c432146526551", 16); + BigInteger h = BigInteger.One; + + ECCurve cFp239v3 = new FpCurve( + new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), + new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), + new BigInteger("255705fa2a306654b1f4cb03d6a750a30c250102d4988717d9ba15ab6d3e", 16), + n, h); + + return new X9ECParameters( + cFp239v3, + cFp239v3.DecodePoint( + Hex.Decode("036768ae8e18bb92cfcf005c949aa2c6d94853d0e660bbf854b1c9505fe95a")), + n, h, + Hex.Decode("7d7374168ffe3471b60a857686a19475d3bfa2ff")); + } + } + + internal class Prime256v1Holder + : X9ECParametersHolder + { + private Prime256v1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Prime256v1Holder(); + + protected override X9ECParameters CreateParameters() + { + BigInteger n = new BigInteger("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", 16); + BigInteger h = BigInteger.One; + + ECCurve cFp256v1 = new FpCurve( + new BigInteger("115792089210356248762697446949407573530086143415290314195533631308867097853951"), + new BigInteger("ffffffff00000001000000000000000000000000fffffffffffffffffffffffc", 16), + new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16), + n, h); + + return new X9ECParameters( + cFp256v1, + cFp256v1.DecodePoint( + Hex.Decode("036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296")), + n, h, + 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.Two; + + 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("D2C0FB15760860DEF1EEF4D696E6768756151754")); + } + } + + 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.Two; + + 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.Two; + + 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.Two; + + 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); } - } - } + 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[Platform.ToLowerInvariant(name)]; + + 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[Platform.ToLowerInvariant(name)]; + } + + /** + * 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
index b92e7b3b5..f05a946c2 100644 --- a/crypto/src/asn1/x9/X9Curve.cs +++ b/crypto/src/asn1/x9/X9Curve.cs
@@ -15,51 +15,50 @@ namespace Org.BouncyCastle.Asn1.X9 { private readonly ECCurve curve; private readonly byte[] seed; - private readonly DerObjectIdentifier fieldIdentifier; + private readonly DerObjectIdentifier fieldIdentifier; - public X9Curve( + public X9Curve( ECCurve curve) - : this(curve, null) + : this(curve, null) { - this.curve = curve; } - public X9Curve( + public X9Curve( ECCurve curve, byte[] seed) { - if (curve == null) - throw new ArgumentNullException("curve"); + if (curve == null) + throw new ArgumentNullException("curve"); - this.curve = 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( + if (ECAlgorithms.IsFpCurve(curve)) + { + this.fieldIdentifier = X9ObjectIdentifiers.PrimeField; + } + else if (ECAlgorithms.IsF2mCurve(curve)) + { + 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"); + if (fieldID == null) + throw new ArgumentNullException("fieldID"); + if (seq == null) + throw new ArgumentNullException("seq"); - this.fieldIdentifier = fieldID.Identifier; + this.fieldIdentifier = fieldID.Identifier; - if (fieldIdentifier.Equals(X9ObjectIdentifiers.PrimeField)) + if (fieldIdentifier.Equals(X9ObjectIdentifiers.PrimeField)) { BigInteger q = ((DerInteger) fieldID.Parameters).Value; X9FieldElement x9A = new X9FieldElement(q, (Asn1OctetString) seq[0]); @@ -68,54 +67,54 @@ namespace Org.BouncyCastle.Asn1.X9 } 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) + 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 + public ECCurve Curve { - get { return curve; } + get { return curve; } } - public byte[] GetSeed() + public byte[] GetSeed() { return Arrays.Clone(seed); } - /** + /** * Produce an object suitable for an Asn1OutputStream. * <pre> * Curve ::= Sequence { @@ -127,21 +126,21 @@ namespace Org.BouncyCastle.Asn1.X9 */ 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); - } + 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
index d025b36ce..a192e4c52 100644 --- a/crypto/src/asn1/x9/X9ECParameters.cs +++ b/crypto/src/asn1/x9/X9ECParameters.cs
@@ -2,6 +2,7 @@ using System; using Org.BouncyCastle.Math; using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Math.Field; namespace Org.BouncyCastle.Asn1.X9 { @@ -19,7 +20,7 @@ namespace Org.BouncyCastle.Asn1.X9 private BigInteger h; private byte[] seed; - public X9ECParameters( + public X9ECParameters( Asn1Sequence seq) { if (!(seq[0] is DerInteger) @@ -28,7 +29,7 @@ namespace Org.BouncyCastle.Asn1.X9 throw new ArgumentException("bad version in X9ECParameters"); } - X9Curve x9c = null; + X9Curve x9c = null; if (seq[2] is X9Curve) { x9c = (X9Curve) seq[2]; @@ -36,14 +37,14 @@ namespace Org.BouncyCastle.Asn1.X9 else { x9c = new X9Curve( - new X9FieldID( - (Asn1Sequence) seq[1]), - (Asn1Sequence) seq[2]); + new X9FieldID( + (Asn1Sequence) seq[1]), + (Asn1Sequence) seq[2]); } - this.curve = x9c.Curve; + this.curve = x9c.Curve; - if (seq[3] is X9ECPoint) + if (seq[3] is X9ECPoint) { this.g = ((X9ECPoint) seq[3]).Point; } @@ -52,16 +53,16 @@ namespace Org.BouncyCastle.Asn1.X9 this.g = new X9ECPoint(curve, (Asn1OctetString) seq[3]).Point; } - this.n = ((DerInteger) seq[4]).Value; + this.n = ((DerInteger) seq[4]).Value; this.seed = x9c.GetSeed(); - if (seq.Count == 6) + if (seq.Count == 6) { this.h = ((DerInteger) seq[5]).Value; } } - public X9ECParameters( + public X9ECParameters( ECCurve curve, ECPoint g, BigInteger n) @@ -69,7 +70,7 @@ namespace Org.BouncyCastle.Asn1.X9 { } - public X9ECParameters( + public X9ECParameters( ECCurve curve, ECPoint g, BigInteger n, @@ -78,7 +79,7 @@ namespace Org.BouncyCastle.Asn1.X9 { } - public X9ECParameters( + public X9ECParameters( ECCurve curve, ECPoint g, BigInteger n, @@ -86,58 +87,73 @@ namespace Org.BouncyCastle.Asn1.X9 byte[] seed) { this.curve = curve; - this.g = g; + this.g = g.Normalize(); 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); - } + if (ECAlgorithms.IsFpCurve(curve)) + { + this.fieldID = new X9FieldID(curve.Field.Characteristic); + } + else if (ECAlgorithms.IsF2mCurve(curve)) + { + IPolynomialExtensionField field = (IPolynomialExtensionField)curve.Field; + int[] exponents = field.MinimalPolynomial.GetExponentsPresent(); + if (exponents.Length == 3) + { + this.fieldID = new X9FieldID(exponents[2], exponents[1]); + } + else if (exponents.Length == 5) + { + this.fieldID = new X9FieldID(exponents[4], exponents[1], exponents[2], exponents[3]); + } + else + { + throw new ArgumentException("Only trinomial and pentomial curves are supported"); + } + } + else + { + throw new ArgumentException("'curve' is of an unsupported type"); + } } - public ECCurve Curve + public ECCurve Curve { - get { return curve; } + get { return curve; } } - public ECPoint G + public ECPoint G { get { return g; } } - public BigInteger N + public BigInteger N { get { return n; } } - public BigInteger H + public BigInteger H { get - { - if (h == null) - { - // TODO - this should be calculated, it will cause issues with custom curves. - return BigInteger.One; - } - - return h; - } + { + if (h == null) + { + // TODO - this should be calculated, it will cause issues with custom curves. + return BigInteger.One; + } + + return h; + } } - public byte[] GetSeed() + public byte[] GetSeed() { return seed; } - /** + /** * Produce an object suitable for an Asn1OutputStream. * <pre> * ECParameters ::= Sequence { @@ -153,18 +169,18 @@ namespace Org.BouncyCastle.Asn1.X9 public override Asn1Object ToAsn1Object() { Asn1EncodableVector v = new Asn1EncodableVector( - new DerInteger(1), - fieldID, - new X9Curve(curve, seed), - new X9ECPoint(g), - new DerInteger(n)); + new DerInteger(1), + fieldID, + new X9Curve(curve, seed), + new X9ECPoint(g), + new DerInteger(n)); - if (h != null) + if (h != null) { v.Add(new DerInteger(h)); } - return new DerSequence(v); + 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
index ba2b2bcbf..75d58cd38 100644 --- a/crypto/src/asn1/x9/X9ECPoint.cs +++ b/crypto/src/asn1/x9/X9ECPoint.cs
@@ -10,25 +10,25 @@ namespace Org.BouncyCastle.Asn1.X9 { private readonly ECPoint p; - public X9ECPoint( + public X9ECPoint( ECPoint p) { - this.p = p; + this.p = p.Normalize(); } - public X9ECPoint( + public X9ECPoint( ECCurve c, Asn1OctetString s) { this.p = c.DecodePoint(s.GetOctets()); } - public ECPoint Point + public ECPoint Point { - get { return p; } + get { return p; } } - /** + /** * Produce an object suitable for an Asn1OutputStream. * <pre> * ECPoint ::= OCTET STRING diff --git a/crypto/src/asn1/x9/X9FieldElement.cs b/crypto/src/asn1/x9/X9FieldElement.cs
index 06fa0e3dc..94bd96b24 100644 --- a/crypto/src/asn1/x9/X9FieldElement.cs +++ b/crypto/src/asn1/x9/X9FieldElement.cs
@@ -11,59 +11,59 @@ namespace Org.BouncyCastle.Asn1.X9 public class X9FieldElement : Asn1Encodable { - private ECFieldElement f; + private ECFieldElement f; - public X9FieldElement( - ECFieldElement f) - { - this.f = 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( + 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 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 + 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); + /** + * 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); - } + return new DerOctetString(paddedBigInteger); + } } } diff --git a/crypto/src/asn1/x9/X9FieldID.cs b/crypto/src/asn1/x9/X9FieldID.cs
index c51cc4df2..58823a285 100644 --- a/crypto/src/asn1/x9/X9FieldID.cs +++ b/crypto/src/asn1/x9/X9FieldID.cs
@@ -1,3 +1,5 @@ +using System; + using Org.BouncyCastle.Math; namespace Org.BouncyCastle.Asn1.X9 @@ -12,80 +14,100 @@ namespace Org.BouncyCastle.Asn1.X9 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 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; + /** + * 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>k1</sup> + 1</code> + * represents the reduction polynomial <code>f(z)</code>. + */ + public X9FieldID(int m, int k1) + : this(m, k1, 0, 0) + { + } - Asn1EncodableVector fieldIdParams = new Asn1EncodableVector(new DerInteger(m)); + /** + * 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))); - } + if (k2 == 0) + { + if (k3 != 0) + throw new ArgumentException("inconsistent k values"); - this.parameters = new DerSequence(fieldIdParams); - } + fieldIdParams.Add( + X9ObjectIdentifiers.TPBasis, + new DerInteger(k1)); + } + else + { + if (k2 <= k1 || k3 <= k2) + throw new ArgumentException("inconsistent k values"); - internal X9FieldID( - Asn1Sequence seq) - { - this.id = (DerObjectIdentifier) seq[0]; - this.parameters = (Asn1Object) seq[1]; - } + 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 + public DerObjectIdentifier Identifier { get { return id; } } - public Asn1Object Parameters + public Asn1Object Parameters { get { return parameters; } } - /** + /** * Produce a Der encoding of the following structure. * <pre> * FieldID ::= Sequence { @@ -96,7 +118,7 @@ namespace Org.BouncyCastle.Asn1.X9 */ public override Asn1Object ToAsn1Object() { - return new DerSequence(id, parameters); + return new DerSequence(id, parameters); } } } diff --git a/crypto/src/asn1/x9/X9IntegerConverter.cs b/crypto/src/asn1/x9/X9IntegerConverter.cs
index 2bce20488..e8f457114 100644 --- a/crypto/src/asn1/x9/X9IntegerConverter.cs +++ b/crypto/src/asn1/x9/X9IntegerConverter.cs
@@ -5,44 +5,36 @@ using Org.BouncyCastle.Math.EC; namespace Org.BouncyCastle.Asn1.X9 { - public sealed class X9IntegerConverter + public abstract class X9IntegerConverter { - private X9IntegerConverter() - { - } - - public static int GetByteLength( - ECFieldElement fe) + public static int GetByteLength(ECFieldElement fe) { - return (fe.FieldSize + 7) / 8; + return (fe.FieldSize + 7) / 8; } - public static int GetByteLength( - ECCurve c) - { - return (c.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(); + 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; - } + 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; - } + return bytes; + } } } diff --git a/crypto/src/asn1/x9/X9ObjectIdentifiers.cs b/crypto/src/asn1/x9/X9ObjectIdentifiers.cs
index 5b2fdbd2d..9d7ecae6e 100644 --- a/crypto/src/asn1/x9/X9ObjectIdentifiers.cs +++ b/crypto/src/asn1/x9/X9ObjectIdentifiers.cs
@@ -1,3 +1,5 @@ +using System; + namespace Org.BouncyCastle.Asn1.X9 { public abstract class X9ObjectIdentifiers @@ -8,128 +10,128 @@ namespace Org.BouncyCastle.Asn1.X9 // 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 ansi_X9_62 = new DerObjectIdentifier(AnsiX962); - public static readonly DerObjectIdentifier CharacteristicTwoField - = new DerObjectIdentifier(IdFieldType + ".2"); + public static readonly DerObjectIdentifier IdFieldType = ansi_X9_62.Branch("1"); - public static readonly DerObjectIdentifier GNBasis - = new DerObjectIdentifier(IdFieldType + ".2.3.1"); + public static readonly DerObjectIdentifier PrimeField = IdFieldType.Branch("1"); + public static readonly DerObjectIdentifier CharacteristicTwoField = IdFieldType.Branch("2"); - public static readonly DerObjectIdentifier TPBasis - = new DerObjectIdentifier(IdFieldType + ".2.3.2"); + public static readonly DerObjectIdentifier GNBasis = CharacteristicTwoField.Branch("3.1"); + public static readonly DerObjectIdentifier TPBasis = CharacteristicTwoField.Branch("3.2"); + public static readonly DerObjectIdentifier PPBasis = CharacteristicTwoField.Branch("3.3"); - public static readonly DerObjectIdentifier PPBasis - = new DerObjectIdentifier(IdFieldType + ".2.3.3"); + [Obsolete("Use 'id_ecSigType' instead")] + public const string IdECSigType = AnsiX962 + ".4"; + public static readonly DerObjectIdentifier id_ecSigType = ansi_X9_62.Branch("4"); - public const string IdECSigType = AnsiX962 + ".4"; + public static readonly DerObjectIdentifier ECDsaWithSha1 = id_ecSigType.Branch("1"); - public static readonly DerObjectIdentifier ECDsaWithSha1 - = new DerObjectIdentifier(IdECSigType + ".1"); + [Obsolete("Use 'id_publicKeyType' instead")] + public const string IdPublicKeyType = AnsiX962 + ".2"; + public static readonly DerObjectIdentifier id_publicKeyType = ansi_X9_62.Branch("2"); - public const string IdPublicKeyType = AnsiX962 + ".2"; + public static readonly DerObjectIdentifier IdECPublicKey = id_publicKeyType.Branch("1"); - public static readonly DerObjectIdentifier IdECPublicKey - = new DerObjectIdentifier(IdPublicKeyType + ".1"); + public static readonly DerObjectIdentifier ECDsaWithSha2 = id_ecSigType.Branch("3"); - 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"); + public static readonly DerObjectIdentifier ECDsaWithSha224 = ECDsaWithSha2.Branch("1"); + public static readonly DerObjectIdentifier ECDsaWithSha256 = ECDsaWithSha2.Branch("2"); + public static readonly DerObjectIdentifier ECDsaWithSha384 = ECDsaWithSha2.Branch("3"); + public static readonly DerObjectIdentifier ECDsaWithSha512 = ECDsaWithSha2.Branch("4"); - // + // // named curves // - public static readonly DerObjectIdentifier EllipticCurve = new DerObjectIdentifier(AnsiX962 + ".3"); + public static readonly DerObjectIdentifier EllipticCurve = ansi_X9_62.Branch("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"); - - // + public static readonly DerObjectIdentifier CTwoCurve = EllipticCurve.Branch("0"); + + public static readonly DerObjectIdentifier C2Pnb163v1 = CTwoCurve.Branch("1"); + public static readonly DerObjectIdentifier C2Pnb163v2 = CTwoCurve.Branch("2"); + public static readonly DerObjectIdentifier C2Pnb163v3 = CTwoCurve.Branch("3"); + public static readonly DerObjectIdentifier C2Pnb176w1 = CTwoCurve.Branch("4"); + public static readonly DerObjectIdentifier C2Tnb191v1 = CTwoCurve.Branch("5"); + public static readonly DerObjectIdentifier C2Tnb191v2 = CTwoCurve.Branch("6"); + public static readonly DerObjectIdentifier C2Tnb191v3 = CTwoCurve.Branch("7"); + public static readonly DerObjectIdentifier C2Onb191v4 = CTwoCurve.Branch("8"); + public static readonly DerObjectIdentifier C2Onb191v5 = CTwoCurve.Branch("9"); + public static readonly DerObjectIdentifier C2Pnb208w1 = CTwoCurve.Branch("10"); + public static readonly DerObjectIdentifier C2Tnb239v1 = CTwoCurve.Branch("11"); + public static readonly DerObjectIdentifier C2Tnb239v2 = CTwoCurve.Branch("12"); + public static readonly DerObjectIdentifier C2Tnb239v3 = CTwoCurve.Branch("13"); + public static readonly DerObjectIdentifier C2Onb239v4 = CTwoCurve.Branch("14"); + public static readonly DerObjectIdentifier C2Onb239v5 = CTwoCurve.Branch("15"); + public static readonly DerObjectIdentifier C2Pnb272w1 = CTwoCurve.Branch("16"); + public static readonly DerObjectIdentifier C2Pnb304w1 = CTwoCurve.Branch("17"); + public static readonly DerObjectIdentifier C2Tnb359v1 = CTwoCurve.Branch("18"); + public static readonly DerObjectIdentifier C2Pnb368w1 = CTwoCurve.Branch("19"); + public static readonly DerObjectIdentifier C2Tnb431r1 = CTwoCurve.Branch("20"); + + // // Prime // - public static readonly DerObjectIdentifier PrimeCurve = new DerObjectIdentifier(EllipticCurve + ".1"); + public static readonly DerObjectIdentifier PrimeCurve = EllipticCurve.Branch("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"); + public static readonly DerObjectIdentifier Prime192v1 = PrimeCurve.Branch("1"); + public static readonly DerObjectIdentifier Prime192v2 = PrimeCurve.Branch("2"); + public static readonly DerObjectIdentifier Prime192v3 = PrimeCurve.Branch("3"); + public static readonly DerObjectIdentifier Prime239v1 = PrimeCurve.Branch("4"); + public static readonly DerObjectIdentifier Prime239v2 = PrimeCurve.Branch("5"); + public static readonly DerObjectIdentifier Prime239v3 = PrimeCurve.Branch("6"); + public static readonly DerObjectIdentifier Prime256v1 = PrimeCurve.Branch("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.63 + */ + public static readonly DerObjectIdentifier X9x63Scheme = new DerObjectIdentifier("1.3.133.16.840.63.0"); + public static readonly DerObjectIdentifier DHSinglePassStdDHSha1KdfScheme = X9x63Scheme.Branch("2"); + public static readonly DerObjectIdentifier DHSinglePassCofactorDHSha1KdfScheme = X9x63Scheme.Branch("3"); + public static readonly DerObjectIdentifier MqvSinglePassSha1KdfScheme = X9x63Scheme.Branch("16"); - /** - * X9.42 - */ + /** + * X9.42 + */ - internal const string AnsiX942 = "1.2.840.10046"; + public static readonly DerObjectIdentifier ansi_x9_42 = new DerObjectIdentifier("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"); - } + public static readonly DerObjectIdentifier DHPublicNumber = ansi_x9_42.Branch("2.1"); + + public static readonly DerObjectIdentifier X9x42Schemes = ansi_x9_42.Branch("2.3"); + + public static readonly DerObjectIdentifier DHStatic = X9x42Schemes.Branch("1"); + public static readonly DerObjectIdentifier DHEphem = X9x42Schemes.Branch("2"); + public static readonly DerObjectIdentifier DHOneFlow = X9x42Schemes.Branch("3"); + public static readonly DerObjectIdentifier DHHybrid1 = X9x42Schemes.Branch("4"); + public static readonly DerObjectIdentifier DHHybrid2 = X9x42Schemes.Branch("5"); + public static readonly DerObjectIdentifier DHHybridOneFlow = X9x42Schemes.Branch("6"); + public static readonly DerObjectIdentifier Mqv2 = X9x42Schemes.Branch("7"); + public static readonly DerObjectIdentifier Mqv1 = X9x42Schemes.Branch("8"); + } } diff --git a/crypto/src/bcpg/ArmoredInputStream.cs b/crypto/src/bcpg/ArmoredInputStream.cs
index 0dcc948d3..3109dd4fd 100644 --- a/crypto/src/bcpg/ArmoredInputStream.cs +++ b/crypto/src/bcpg/ArmoredInputStream.cs
@@ -504,14 +504,10 @@ namespace Org.BouncyCastle.Bcpg return pos - offset; } - protected override void Dispose(bool disposing) - { - if (disposing) - { - input.Dispose(); - } - - base.Dispose(disposing); - } + public override void Close() + { + input.Close(); + base.Close(); + } } } diff --git a/crypto/src/bcpg/ArmoredOutputStream.cs b/crypto/src/bcpg/ArmoredOutputStream.cs
index 7ee26886a..b3a32c6f5 100644 --- a/crypto/src/bcpg/ArmoredOutputStream.cs +++ b/crypto/src/bcpg/ArmoredOutputStream.cs
@@ -97,7 +97,8 @@ namespace Org.BouncyCastle.Bcpg private static readonly string footerStart = "-----END PGP "; private static readonly string footerTail = "-----"; - private static readonly string version = "BCPG C# v" + AssemblyInfo.Version; + private static readonly string version = "BCPG C# v" + + Assembly.GetExecutingAssembly().GetName().Version; private readonly IDictionary headers; @@ -276,43 +277,39 @@ namespace Org.BouncyCastle.Bcpg } /** - * <b>Note</b>: Dispose does nor Dispose the underlying stream. So it is possible to write + * <b>Note</b>: close does nor close the underlying stream. So it is possible to write * multiple objects using armoring to a single stream. */ - protected override void Dispose(bool disposing) + public override void Close() { - if (disposing) + if (type != null) { - if (type != null) - { - if (bufPtr > 0) - { - Encode(outStream, buf, bufPtr); - } + if (bufPtr > 0) + { + Encode(outStream, buf, bufPtr); + } - DoWrite(nl + '='); + DoWrite(nl + '='); - int crcV = crc.Value; + int crcV = crc.Value; - buf[0] = ((crcV >> 16) & 0xff); - buf[1] = ((crcV >> 8) & 0xff); - buf[2] = (crcV & 0xff); + buf[0] = ((crcV >> 16) & 0xff); + buf[1] = ((crcV >> 8) & 0xff); + buf[2] = (crcV & 0xff); - Encode(outStream, buf, 3); + Encode(outStream, buf, 3); - DoWrite(nl); - DoWrite(footerStart); - DoWrite(type); - DoWrite(footerTail); - DoWrite(nl); - - outStream.Flush(); + DoWrite(nl); + DoWrite(footerStart); + DoWrite(type); + DoWrite(footerTail); + DoWrite(nl); - type = null; - start = true; - } + outStream.Flush(); - base.Dispose(disposing); + type = null; + start = true; + base.Close(); } } diff --git a/crypto/src/bcpg/BcpgInputStream.cs b/crypto/src/bcpg/BcpgInputStream.cs
index b4afab901..3c69fbdf5 100644 --- a/crypto/src/bcpg/BcpgInputStream.cs +++ b/crypto/src/bcpg/BcpgInputStream.cs
@@ -250,15 +250,11 @@ namespace Org.BouncyCastle.Bcpg } } - protected override void Dispose(bool disposing) - { - if (disposing) - { - m_in.Dispose(); - } - - base.Dispose(disposing); - } + public override void Close() + { + m_in.Close(); + base.Close(); + } /// <summary> /// A stream that overlays our input stream, allowing the user to only read a segment of it. diff --git a/crypto/src/bcpg/BcpgObject.cs b/crypto/src/bcpg/BcpgObject.cs
index 92811c3d3..4807ad46e 100644 --- a/crypto/src/bcpg/BcpgObject.cs +++ b/crypto/src/bcpg/BcpgObject.cs
@@ -3,7 +3,7 @@ using System.IO; namespace Org.BouncyCastle.Bcpg { - /// <remarks>Base class for a PGP object.</remarks> + /// <remarks>Base class for a PGP object.</remarks> public abstract class BcpgObject { public virtual byte[] GetEncoded() @@ -11,12 +11,12 @@ namespace Org.BouncyCastle.Bcpg MemoryStream bOut = new MemoryStream(); BcpgOutputStream pOut = new BcpgOutputStream(bOut); - pOut.WriteObject(this); + pOut.WriteObject(this); - return bOut.ToArray(); + return bOut.ToArray(); } - public abstract void Encode(BcpgOutputStream bcpgOut); + public abstract void Encode(BcpgOutputStream bcpgOut); } } diff --git a/crypto/src/bcpg/BcpgOutputStream.cs b/crypto/src/bcpg/BcpgOutputStream.cs
index 365053e11..204f65b50 100644 --- a/crypto/src/bcpg/BcpgOutputStream.cs +++ b/crypto/src/bcpg/BcpgOutputStream.cs
@@ -379,16 +379,12 @@ namespace Org.BouncyCastle.Bcpg } } - protected override void Dispose(bool disposing) + public override void Close() { - if (disposing) - { - this.Finish(); - outStr.Flush(); - outStr.Dispose(); - } - - base.Dispose(disposing); + this.Finish(); + outStr.Flush(); + outStr.Close(); + base.Close(); } } } 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
index 61159567c..11294cc22 100644 --- a/crypto/src/bcpg/DsaPublicBcpgKey.cs +++ b/crypto/src/bcpg/DsaPublicBcpgKey.cs
@@ -4,46 +4,46 @@ using Org.BouncyCastle.Math; namespace Org.BouncyCastle.Bcpg { - /// <remarks>Base class for a DSA public key.</remarks> - public class DsaPublicBcpgKey - : BcpgObject, IBcpgKey + /// <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); - } + /// <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); - } + 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>The format, as a string, always "PGP".</summary> + public string Format + { + get { return "PGP"; } + } - /// <summary>Return the standard PGP encoding of the key.</summary> + /// <summary>Return the standard PGP encoding of the key.</summary> public override byte[] GetEncoded() { try { - return base.GetEncoded(); + return base.GetEncoded(); } catch (Exception) { @@ -51,30 +51,30 @@ namespace Org.BouncyCastle.Bcpg } } - public override void Encode( - BcpgOutputStream bcpgOut) - { - bcpgOut.WriteObjects(p, q, g, y); - } + public override void Encode( + BcpgOutputStream bcpgOut) + { + bcpgOut.WriteObjects(p, q, g, y); + } - public BigInteger G - { - get { return g.Value; } - } + public BigInteger G + { + get { return g.Value; } + } - public BigInteger P - { - get { return p.Value; } - } + public BigInteger P + { + get { return p.Value; } + } - public BigInteger Q - { - get { return q.Value; } - } + public BigInteger Q + { + get { return q.Value; } + } - public BigInteger Y - { - get { return y.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
index 32d43149b..a45aeb469 100644 --- a/crypto/src/bcpg/PublicKeyPacket.cs +++ b/crypto/src/bcpg/PublicKeyPacket.cs
@@ -5,11 +5,11 @@ using Org.BouncyCastle.Utilities.Date; namespace Org.BouncyCastle.Bcpg { - /// <remarks>Basic packet for a PGP public key.</remarks> + /// <remarks>Basic packet for a PGP public key.</remarks> public class PublicKeyPacket : ContainedPacket //, PublicKeyAlgorithmTag { - private int version; + private int version; private long time; private int validDays; private PublicKeyAlgorithmTag algorithm; @@ -49,44 +49,44 @@ namespace Org.BouncyCastle.Bcpg } } - /// <summary>Construct a version 4 public key packet.</summary> + /// <summary>Construct a version 4 public key packet.</summary> public PublicKeyPacket( PublicKeyAlgorithmTag algorithm, DateTime time, IBcpgKey key) { - this.version = 4; + this.version = 4; this.time = DateTimeUtilities.DateTimeToUnixMs(time) / 1000L; this.algorithm = algorithm; this.key = key; } - public int Version + public virtual int Version { - get { return version; } + get { return version; } } - public PublicKeyAlgorithmTag Algorithm + public virtual PublicKeyAlgorithmTag Algorithm { - get { return algorithm; } + get { return algorithm; } } - public int ValidDays + public virtual int ValidDays { - get { return validDays; } + get { return validDays; } } - public DateTime GetTime() + public virtual DateTime GetTime() { return DateTimeUtilities.UnixMsToDateTime(time * 1000L); } - public IBcpgKey Key + public virtual IBcpgKey Key { - get { return key; } + get { return key; } } - public byte[] GetEncodedContents() + public virtual byte[] GetEncodedContents() { MemoryStream bOut = new MemoryStream(); BcpgOutputStream pOut = new BcpgOutputStream(bOut); @@ -94,22 +94,22 @@ namespace Org.BouncyCastle.Bcpg pOut.WriteByte((byte) version); pOut.WriteInt((int) time); - if (version <= 3) + if (version <= 3) { pOut.WriteShort((short) validDays); } - pOut.WriteByte((byte) algorithm); + pOut.WriteByte((byte) algorithm); - pOut.WriteObject((BcpgObject)key); + pOut.WriteObject((BcpgObject)key); - return bOut.ToArray(); + return bOut.ToArray(); } - public override void Encode( - BcpgOutputStream bcpgOut) - { - bcpgOut.WritePacket(PacketTag.PublicKey, GetEncodedContents(), true); - } - } + 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
index 7633b1dba..e05a48616 100644 --- a/crypto/src/bcpg/SymmetricKeyAlgorithmTags.cs +++ b/crypto/src/bcpg/SymmetricKeyAlgorithmTags.cs
@@ -5,16 +5,19 @@ namespace Org.BouncyCastle.Bcpg */ 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 + 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 + Camellia128 = 11, // Reserved for AES with 128-bit key + Camellia192 = 12, // Reserved for AES with 192-bit key + Camellia256 = 13 // Reserved for AES with 256-bit key } } 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
index bd49d2150..05f60ac17 100644 --- a/crypto/src/bcpg/UserAttributeSubpacket.cs +++ b/crypto/src/bcpg/UserAttributeSubpacket.cs
@@ -10,40 +10,44 @@ namespace Org.BouncyCastle.Bcpg */ public class UserAttributeSubpacket { - private readonly UserAttributeSubpacketTag type; - private readonly byte[] data; + internal readonly UserAttributeSubpacketTag type; + private readonly bool longLength; // we preserve this as not everyone encodes length properly. + protected readonly byte[] data; - internal UserAttributeSubpacket( - UserAttributeSubpacketTag type, - byte[] data) + protected internal UserAttributeSubpacket(UserAttributeSubpacketTag type, byte[] data) + : this(type, false, data) + { + } + + protected internal UserAttributeSubpacket(UserAttributeSubpacketTag type, bool forceLongLength, byte[] data) { this.type = type; + this.longLength = forceLongLength; this.data = data; } - public UserAttributeSubpacketTag SubpacketType + public virtual UserAttributeSubpacketTag SubpacketType { get { return type; } } - /** + /** * return the generic data making up the packet. */ - public byte[] GetData() + public virtual byte[] GetData() { return data; } - public void Encode( - Stream os) + public virtual void Encode(Stream os) { int bodyLen = data.Length + 1; - if (bodyLen < 192) + if (bodyLen < 192 && !longLength) { os.WriteByte((byte)bodyLen); } - else if (bodyLen <= 8383) + else if (bodyLen <= 8383 && !longLength) { bodyLen -= 192; @@ -69,18 +73,18 @@ namespace Org.BouncyCastle.Bcpg if (obj == this) return true; - UserAttributeSubpacket other = obj as UserAttributeSubpacket; + UserAttributeSubpacket other = obj as UserAttributeSubpacket; - if (other == null) - return false; + if (other == null) + return false; - return type == other.type - && Arrays.AreEqual(data, other.data); + return type == other.type + && Arrays.AreEqual(data, other.data); } - public override int GetHashCode() + public override int GetHashCode() { - return type.GetHashCode() ^ Arrays.GetHashCode(data); + 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
index 2e5ea0f3e..f0cc1b8e4 100644 --- a/crypto/src/bcpg/UserAttributeSubpacketsReader.cs +++ b/crypto/src/bcpg/UserAttributeSubpacketsReader.cs
@@ -5,59 +5,61 @@ using Org.BouncyCastle.Utilities.IO; namespace Org.BouncyCastle.Bcpg { - /** - * reader for user attribute sub-packets - */ - public class UserAttributeSubpacketsParser - { - private readonly Stream input; + /** + * reader for user attribute sub-packets + */ + public class UserAttributeSubpacketsParser + { + private readonly Stream input; - public UserAttributeSubpacketsParser( - Stream input) - { - this.input = input; - } + public UserAttributeSubpacketsParser( + Stream input) + { + this.input = input; + } - public UserAttributeSubpacket ReadPacket() - { - int l = input.ReadByte(); - if (l < 0) - return null; + public virtual 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 bodyLen = 0; + bool longLength = false; + 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(); + longLength = true; + } + else + { + throw new IOException("unrecognised length reading user attribute sub packet"); + } - int tag = input.ReadByte(); - if (tag < 0) - throw new EndOfStreamException("unexpected EOF reading user attribute sub packet"); + 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(); + 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); - } - } + UserAttributeSubpacketTag type = (UserAttributeSubpacketTag) tag; + switch (type) + { + case UserAttributeSubpacketTag.ImageAttribute: + return new ImageAttrib(longLength, data); + } + return new UserAttributeSubpacket(type, longLength, 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
index 73490791c..2d0fef8b8 100644 --- a/crypto/src/bcpg/attr/ImageAttrib.cs +++ b/crypto/src/bcpg/attr/ImageAttrib.cs
@@ -3,25 +3,29 @@ using System.IO; namespace Org.BouncyCastle.Bcpg.Attr { - /// <remarks>Basic type for a image attribute packet.</remarks> + /// <remarks>Basic type for a image attribute packet.</remarks> public class ImageAttrib - : UserAttributeSubpacket + : UserAttributeSubpacket { - public enum Format : byte - { - Jpeg = 1 - } + public enum Format : byte + { + Jpeg = 1 + } - private static readonly byte[] Zeroes = new byte[12]; + private static readonly byte[] Zeroes = new byte[12]; - private int hdrLength; + private int hdrLength; private int _version; private int _encoding; private byte[] imageData; - public ImageAttrib( - byte[] data) - : base(UserAttributeSubpacketTag.ImageAttribute, data) + public ImageAttrib(byte[] data) + : this(false, data) + { + } + + public ImageAttrib(bool forceLongLength, byte[] data) + : base(UserAttributeSubpacketTag.ImageAttribute, forceLongLength, data) { hdrLength = ((data[1] & 0xff) << 8) | (data[0] & 0xff); _version = data[2] & 0xff; @@ -31,36 +35,36 @@ namespace Org.BouncyCastle.Bcpg.Attr Array.Copy(data, hdrLength, imageData, 0, imageData.Length); } - public ImageAttrib( - Format imageType, - byte[] imageData) - : this(ToByteArray(imageType, imageData)) - { - } + 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(); - } + 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 + public virtual int Version { - get { return _version; } + get { return _version; } } - public int Encoding + public virtual int Encoding { - get { return _encoding; } + get { return _encoding; } } - public byte[] GetImageData() + public virtual 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
index 5e388246b..66982cb5a 100644 --- a/crypto/src/bcpg/sig/RevocationKey.cs +++ b/crypto/src/bcpg/sig/RevocationKey.cs
@@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Text; namespace Org.BouncyCastle.Bcpg 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
index f32c1f64a..98e9b0a3d 100644 --- a/crypto/src/bcpg/sig/RevocationReason.cs +++ b/crypto/src/bcpg/sig/RevocationReason.cs
@@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Text; using Org.BouncyCastle.Utilities; 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
index 2bc8ce5d1..31b06d6dd 100644 --- a/crypto/src/cms/CMSAttributeTableGenerationException.cs +++ b/crypto/src/cms/CMSAttributeTableGenerationException.cs
@@ -2,7 +2,10 @@ using System; namespace Org.BouncyCastle.Cms { - public class CmsAttributeTableGenerationException +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class CmsAttributeTableGenerationException : CmsException { public CmsAttributeTableGenerationException() 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
index 0a37ca4f5..846c19a24 100644 --- a/crypto/src/cms/CMSAuthenticatedDataGenerator.cs +++ b/crypto/src/cms/CMSAuthenticatedDataGenerator.cs
@@ -83,8 +83,8 @@ namespace Org.BouncyCastle.Cms content.Write(mOut); - mOut.Dispose(); - bOut.Dispose(); + mOut.Close(); + bOut.Close(); encContent = new BerOctetString(bOut.ToArray()); 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
index c133ae240..2603cb380 100644 --- a/crypto/src/cms/CMSAuthenticatedDataStreamGenerator.cs +++ b/crypto/src/cms/CMSAuthenticatedDataStreamGenerator.cs
@@ -251,25 +251,22 @@ namespace Org.BouncyCastle.Cms macStream.Write(bytes, off, len); } - protected override void Dispose(bool disposing) - { - if (disposing) - { - macStream.Dispose(); + public override void Close() + { + macStream.Close(); - // TODO Parent context(s) should really be be closed explicitly + // TODO Parent context(s) should really be be closed explicitly - eiGen.Close(); + eiGen.Close(); - // [TODO] auth attributes go here - byte[] macOctets = MacUtilities.DoFinal(mac); - authGen.AddObject(new DerOctetString(macOctets)); - // [TODO] unauth attributes go here + // [TODO] auth attributes go here + byte[] macOctets = MacUtilities.DoFinal(mac); + authGen.AddObject(new DerOctetString(macOctets)); + // [TODO] unauth attributes go here - authGen.Close(); - cGen.Close(); - } - } + 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
index 55d06b5b8..2d0107e88 100644 --- a/crypto/src/cms/CMSCompressedData.cs +++ b/crypto/src/cms/CMSCompressedData.cs
@@ -56,7 +56,7 @@ namespace Org.BouncyCastle.Cms } finally { - zIn.Dispose(); + zIn.Close(); } } diff --git a/crypto/src/cms/CMSCompressedDataGenerator.cs b/crypto/src/cms/CMSCompressedDataGenerator.cs
index a6381ced6..00e2a3df4 100644 --- a/crypto/src/cms/CMSCompressedDataGenerator.cs +++ b/crypto/src/cms/CMSCompressedDataGenerator.cs
@@ -45,7 +45,7 @@ namespace Org.BouncyCastle.Cms content.Write(zOut); - zOut.Dispose(); + zOut.Close(); comAlgId = new AlgorithmIdentifier(new DerObjectIdentifier(compressionOid)); comOcts = new BerOctetString(bOut.ToArray()); 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
index 13006d4b0..db0d19845 100644 --- a/crypto/src/cms/CMSCompressedDataStreamGenerator.cs +++ b/crypto/src/cms/CMSCompressedDataStreamGenerator.cs
@@ -124,21 +124,17 @@ namespace Org.BouncyCastle.Cms _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); - } - } + public override void Close() + { + _out.Close(); + + // TODO Parent context(s) should really be be closed explicitly + + _eiGen.Close(); + _cGen.Close(); + _sGen.Close(); + base.Close(); + } + } } } diff --git a/crypto/src/cms/CMSContentInfoParser.cs b/crypto/src/cms/CMSContentInfoParser.cs
index 5b1606394..fde06cf4c 100644 --- a/crypto/src/cms/CMSContentInfoParser.cs +++ b/crypto/src/cms/CMSContentInfoParser.cs
@@ -41,7 +41,7 @@ namespace Org.BouncyCastle.Cms */ public void Close() { - this.data.Dispose(); + this.data.Close(); } } } 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
index 5071af4ad..3b861cde5 100644 --- a/crypto/src/cms/CMSEnvelopedDataGenerator.cs +++ b/crypto/src/cms/CMSEnvelopedDataGenerator.cs
@@ -80,7 +80,7 @@ namespace Org.BouncyCastle.Cms content.Write(cOut); - cOut.Dispose(); + cOut.Close(); encContent = new BerOctetString(bOut.ToArray()); } 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
index 820ddfe16..a63ea7b7f 100644 --- a/crypto/src/cms/CMSEnvelopedDataStreamGenerator.cs +++ b/crypto/src/cms/CMSEnvelopedDataStreamGenerator.cs
@@ -255,31 +255,27 @@ namespace Org.BouncyCastle.Cms _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 + public override void Close() + { + _out.Close(); - _eiGen.Close(); + // TODO Parent context(s) should really be be closed explicitly - if (_outer.unprotectedAttributeGenerator != null) - { - Asn1.Cms.AttributeTable attrTable = _outer.unprotectedAttributeGenerator.GetAttributes(Platform.CreateHashtable()); + _eiGen.Close(); - Asn1Set unprotectedAttrs = new BerSet(attrTable.ToAsn1EncodableVector()); + if (_outer.unprotectedAttributeGenerator != null) + { + Asn1.Cms.AttributeTable attrTable = _outer.unprotectedAttributeGenerator.GetAttributes(Platform.CreateHashtable()); - _envGen.AddObject(new DerTaggedObject(false, 1, unprotectedAttrs)); - } + Asn1Set unprotectedAttrs = new BerSet(attrTable.ToAsn1EncodableVector()); - _envGen.Close(); - _cGen.Close(); + _envGen.AddObject(new DerTaggedObject(false, 1, unprotectedAttrs)); } - base.Dispose(disposing); - } + _envGen.Close(); + _cGen.Close(); + base.Close(); + } } } } 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
index 1d1cd82e3..5a6e33502 100644 --- a/crypto/src/cms/CMSException.cs +++ b/crypto/src/cms/CMSException.cs
@@ -2,9 +2,12 @@ using System; namespace Org.BouncyCastle.Cms { - public class CmsException - : Exception - { +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class CmsException + : Exception + { public CmsException() { } 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
index 548dc8efb..cbc74f44b 100644 --- a/crypto/src/cms/CMSProcessableFile.cs +++ b/crypto/src/cms/CMSProcessableFile.cs
@@ -1,5 +1,3 @@ -#if !PORTABLE - using System; using System.IO; @@ -54,4 +52,3 @@ namespace Org.BouncyCastle.Cms } } } -#endif \ No newline at end of file diff --git a/crypto/src/cms/CMSProcessableInputStream.cs b/crypto/src/cms/CMSProcessableInputStream.cs
index d10d059c6..7fdd1dfef 100644 --- a/crypto/src/cms/CMSProcessableInputStream.cs +++ b/crypto/src/cms/CMSProcessableInputStream.cs
@@ -29,7 +29,7 @@ namespace Org.BouncyCastle.Cms CheckSingleUsage(); Streams.PipeAll(input, output); - input.Dispose(); + input.Close(); } [Obsolete] 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
index 5b68c785d..f31105c41 100644 --- a/crypto/src/cms/CMSSignedDataGenerator.cs +++ b/crypto/src/cms/CMSSignedDataGenerator.cs
@@ -108,8 +108,8 @@ namespace Org.BouncyCastle.Cms 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); +#if NETCF_1_0 || NETCF_2_0 || SILVERLIGHT + Stream sigStr = new SigOutputStream(sig); #else Stream sigStr = new BufferedStream(new SigOutputStream(sig)); #endif @@ -145,7 +145,7 @@ namespace Org.BouncyCastle.Cms content.Write(sigStr); } - sigStr.Dispose(); + sigStr.Close(); byte[] sigBytes = sig.GenerateSignature(); Asn1Set unsignedAttr = null; diff --git a/crypto/src/cms/CMSSignedDataParser.cs b/crypto/src/cms/CMSSignedDataParser.cs
index dd794ee77..e5674a4cf 100644 --- a/crypto/src/cms/CMSSignedDataParser.cs +++ b/crypto/src/cms/CMSSignedDataParser.cs
@@ -170,11 +170,6 @@ namespace Org.BouncyCastle.Cms { throw new CmsException("io exception: " + e.Message, e); } - - if (_digests.Count < 1) - { - throw new CmsException("no digests could be created for message."); - } } /** @@ -389,7 +384,7 @@ namespace Org.BouncyCastle.Cms // gen.AddSigners(parser.GetSignerInfos()); - contentOut.Dispose(); + contentOut.Close(); return outStr; } @@ -439,7 +434,7 @@ namespace Org.BouncyCastle.Cms gen.AddSigners(parser.GetSignerInfos()); - contentOut.Dispose(); + contentOut.Close(); return outStr; } diff --git a/crypto/src/cms/CMSSignedDataStreamGenerator.cs b/crypto/src/cms/CMSSignedDataStreamGenerator.cs
index 0bed9fcad..743e9c6c1 100644 --- a/crypto/src/cms/CMSSignedDataStreamGenerator.cs +++ b/crypto/src/cms/CMSSignedDataStreamGenerator.cs
@@ -636,7 +636,7 @@ namespace Org.BouncyCastle.Cms { content.Write(signedOut); } - signedOut.Dispose(); + signedOut.Close(); } // RFC3852, section 5.1: @@ -809,100 +809,96 @@ namespace Org.BouncyCastle.Cms _out.Write(bytes, off, len); } - protected override void Dispose(bool disposing) + public override void Close() { - if (disposing) - { - _out.Dispose(); + _out.Close(); - // TODO Parent context(s) should really be be closed explicitly + // TODO Parent context(s) should really be be closed explicitly - _eiGen.Close(); + _eiGen.Close(); - outer._digests.Clear(); // clear the current preserved digest state + outer._digests.Clear(); // clear the current preserved digest state - if (outer._certs.Count > 0) - { - Asn1Set certs = CmsUtilities.CreateBerSetFromList(outer._certs); + if (outer._certs.Count > 0) + { + Asn1Set certs = CmsUtilities.CreateBerSetFromList(outer._certs); - WriteToGenerator(_sigGen, new BerTaggedObject(false, 0, certs)); - } + WriteToGenerator(_sigGen, new BerTaggedObject(false, 0, certs)); + } - if (outer._crls.Count > 0) - { - Asn1Set crls = CmsUtilities.CreateBerSetFromList(outer._crls); + if (outer._crls.Count > 0) + { + Asn1Set crls = CmsUtilities.CreateBerSetFromList(outer._crls); - WriteToGenerator(_sigGen, new BerTaggedObject(false, 1, 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)); - } + // + // 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 + // 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(); + // + // collect all the SignerInfo objects + // + Asn1EncodableVector signerInfos = new Asn1EncodableVector(); - // - // add the generated SignerInfo objects - // - { - foreach (DigestAndSignerInfoGeneratorHolder holder in outer._signerInfs) - { - AlgorithmIdentifier digestAlgorithm = holder.DigestAlgorithm; + // + // 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(); + byte[] calculatedDigest = (byte[])outer._messageHashes[ + Helper.GetDigestAlgName(holder.digestOID)]; + outer._digests[holder.digestOID] = calculatedDigest.Clone(); - signerInfos.Add(holder.signerInf.Generate(_contentOID, digestAlgorithm, calculatedDigest)); - } - } + 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()); - } - } + // + // 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? +// } +// } - WriteToGenerator(_sigGen, new DerSet(signerInfos)); + signerInfos.Add(signer.ToSignerInfo()); + } + } - _sigGen.Close(); - _sGen.Close(); - } + WriteToGenerator(_sigGen, new DerSet(signerInfos)); - base.Dispose(disposing); + _sigGen.Close(); + _sGen.Close(); + base.Close(); } private static void WriteToGenerator( 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
index 1626fb8b4..bf0a6adf4 100644 --- a/crypto/src/cms/CMSStreamException.cs +++ b/crypto/src/cms/CMSStreamException.cs
@@ -3,6 +3,9 @@ using System.IO; namespace Org.BouncyCastle.Cms { +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif public class CmsStreamException : IOException { diff --git a/crypto/src/cms/CMSTypedStream.cs b/crypto/src/cms/CMSTypedStream.cs
index 576edf234..9cb314211 100644 --- a/crypto/src/cms/CMSTypedStream.cs +++ b/crypto/src/cms/CMSTypedStream.cs
@@ -33,7 +33,7 @@ namespace Org.BouncyCastle.Cms int bufSize) { _oid = oid; -#if NETCF_1_0 || NETCF_2_0 || SILVERLIGHT || PORTABLE +#if NETCF_1_0 || NETCF_2_0 || SILVERLIGHT _in = new FullReaderStream(inStream); #else _in = new FullReaderStream(new BufferedStream(inStream, bufSize)); @@ -53,7 +53,7 @@ namespace Org.BouncyCastle.Cms public void Drain() { Streams.Drain(_in); - _in.Dispose(); + _in.Close(); } private class FullReaderStream : FilterStream 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
index 0e93392d9..055de8957 100644 --- a/crypto/src/cms/DefaultSignedAttributeTableGenerator.cs +++ b/crypto/src/cms/DefaultSignedAttributeTableGenerator.cs
@@ -41,8 +41,8 @@ namespace Org.BouncyCastle.Cms } } -#if (SILVERLIGHT || PORTABLE) - /** +#if SILVERLIGHT + /** * 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 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
index 38a94b0a4..8e006e545 100644 --- a/crypto/src/cms/KeyAgreeRecipientInformation.cs +++ b/crypto/src/cms/KeyAgreeRecipientInformation.cs
@@ -18,209 +18,209 @@ 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); - } - } + /** + * 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.PrivateKeyAlgorithm, + 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/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/OriginatorInfoGenerator.cs b/crypto/src/cms/OriginatorInfoGenerator.cs new file mode 100644
index 000000000..6bf108799 --- /dev/null +++ b/crypto/src/cms/OriginatorInfoGenerator.cs
@@ -0,0 +1,42 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.Cms +{ + public class OriginatorInfoGenerator + { + private readonly IList origCerts; + private readonly IList origCrls; + + public OriginatorInfoGenerator(X509Certificate origCert) + { + this.origCerts = Platform.CreateArrayList(1); + this.origCrls = null; + origCerts.Add(origCert.CertificateStructure); + } + + public OriginatorInfoGenerator(IX509Store origCerts) + : this(origCerts, null) + { + } + + public OriginatorInfoGenerator(IX509Store origCerts, IX509Store origCrls) + { + this.origCerts = CmsUtilities.GetCertificatesFromStore(origCerts); + this.origCrls = origCrls == null ? null : CmsUtilities.GetCrlsFromStore(origCrls); + } + + public virtual OriginatorInfo Generate() + { + Asn1Set certSet = CmsUtilities.CreateDerSetFromList(origCerts); + Asn1Set crlSet = origCrls == null ? null : CmsUtilities.CreateDerSetFromList(origCrls); + return new OriginatorInfo(certSet, crlSet); + } + } +} diff --git a/crypto/src/cms/OriginatorInformation.cs b/crypto/src/cms/OriginatorInformation.cs new file mode 100644
index 000000000..618add6e0 --- /dev/null +++ b/crypto/src/cms/OriginatorInformation.cs
@@ -0,0 +1,96 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.X509.Store; + +namespace Org.BouncyCastle.Cms +{ + public class OriginatorInformation + { + private readonly OriginatorInfo originatorInfo; + + internal OriginatorInformation(OriginatorInfo originatorInfo) + { + this.originatorInfo = originatorInfo; + } + + /** + * Return the certificates stored in the underlying OriginatorInfo object. + * + * @return a Store of X509CertificateHolder objects. + */ + public virtual IX509Store GetCertificates() + { + Asn1Set certSet = originatorInfo.Certificates; + + if (certSet != null) + { + IList certList = Platform.CreateArrayList(certSet.Count); + + foreach (Asn1Encodable enc in certSet) + { + Asn1Object obj = enc.ToAsn1Object(); + if (obj is Asn1Sequence) + { + certList.Add(new X509Certificate(X509CertificateStructure.GetInstance(obj))); + } + } + + return X509StoreFactory.Create( + "Certificate/Collection", + new X509CollectionStoreParameters(certList)); + } + + return X509StoreFactory.Create( + "Certificate/Collection", + new X509CollectionStoreParameters(Platform.CreateArrayList())); + } + + /** + * Return the CRLs stored in the underlying OriginatorInfo object. + * + * @return a Store of X509CRLHolder objects. + */ + public virtual IX509Store GetCrls() + { + Asn1Set crlSet = originatorInfo.Certificates; + + if (crlSet != null) + { + IList crlList = Platform.CreateArrayList(crlSet.Count); + + foreach (Asn1Encodable enc in crlSet) + { + Asn1Object obj = enc.ToAsn1Object(); + if (obj is Asn1Sequence) + { + crlList.Add(new X509Crl(CertificateList.GetInstance(obj))); + } + } + + return X509StoreFactory.Create( + "CRL/Collection", + new X509CollectionStoreParameters(crlList)); + } + + return X509StoreFactory.Create( + "CRL/Collection", + new X509CollectionStoreParameters(Platform.CreateArrayList())); + } + + /** + * Return the underlying ASN.1 object defining this SignerInformation object. + * + * @return a OriginatorInfo. + */ + public virtual OriginatorInfo ToAsn1Structure() + { + return originatorInfo; + } + } +} 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
index 87413b69d..04bcb88ed 100644 --- a/crypto/src/crypto/BufferedAeadBlockCipher.cs +++ b/crypto/src/crypto/BufferedAeadBlockCipher.cs
@@ -174,23 +174,18 @@ namespace Org.BouncyCastle.Crypto public override byte[] DoFinal() { - byte[] outBytes = EmptyBuffer; + byte[] outBytes = new byte[GetOutputSize(0)]; - int length = GetOutputSize(0); - if (length > 0) + int pos = DoFinal(outBytes, 0); + + if (pos < outBytes.Length) { - 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; - } + byte[] tmp = new byte[pos]; + Array.Copy(outBytes, 0, tmp, 0, pos); + outBytes = tmp; } - return outBytes; + return outBytes; } public override byte[] DoFinal( @@ -201,29 +196,22 @@ namespace Org.BouncyCastle.Crypto if (input == null) throw new ArgumentNullException("input"); - int length = GetOutputSize(inLen); - - byte[] outBytes = EmptyBuffer; + byte[] outBytes = new byte[GetOutputSize(inLen)]; - if (length > 0) - { - outBytes = new byte[length]; + int pos = (inLen > 0) + ? ProcessBytes(input, inOff, inLen, outBytes, 0) + : 0; - int pos = (inLen > 0) - ? ProcessBytes(input, inOff, inLen, outBytes, 0) - : 0; + pos += DoFinal(outBytes, pos); - pos += DoFinal(outBytes, pos); - - if (pos < outBytes.Length) - { - byte[] tmp = new byte[pos]; - Array.Copy(outBytes, 0, tmp, 0, pos); - outBytes = tmp; - } + if (pos < outBytes.Length) + { + byte[] tmp = new byte[pos]; + Array.Copy(outBytes, 0, tmp, 0, pos); + outBytes = tmp; } - return outBytes; + return outBytes; } /** 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
index 72bdfed67..3a98798a2 100644 --- a/crypto/src/crypto/BufferedBlockCipher.cs +++ b/crypto/src/crypto/BufferedBlockCipher.cs
@@ -67,12 +67,11 @@ namespace Org.BouncyCastle.Crypto { this.forEncryption = forEncryption; - if (parameters is ParametersWithRandom) - { - parameters = ((ParametersWithRandom) parameters).Parameters; - } + ParametersWithRandom pwr = parameters as ParametersWithRandom; + if (pwr != null) + parameters = pwr.Parameters; - Reset(); + Reset(); cipher.Init(forEncryption, parameters); } 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
index c012063bb..67f0d86f2 100644 --- a/crypto/src/crypto/CryptoException.cs +++ b/crypto/src/crypto/CryptoException.cs
@@ -2,6 +2,9 @@ using System; namespace Org.BouncyCastle.Crypto { +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif public class CryptoException : Exception { diff --git a/crypto/src/crypto/DataLengthException.cs b/crypto/src/crypto/DataLengthException.cs
index 8bd695bbc..e9efc0bd3 100644 --- a/crypto/src/crypto/DataLengthException.cs +++ b/crypto/src/crypto/DataLengthException.cs
@@ -8,6 +8,9 @@ namespace Org.BouncyCastle.Crypto * insufficient input. In general this exception will Get thrown rather * than an ArrayOutOfBounds exception. */ +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif public class DataLengthException : CryptoException { 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
index 8bd363d4e..7dfc618d6 100644 --- a/crypto/src/crypto/IBasicAgreement.cs +++ b/crypto/src/crypto/IBasicAgreement.cs
@@ -15,6 +15,11 @@ namespace Org.BouncyCastle.Crypto void Init(ICipherParameters parameters); /** + * return the field size for the agreement algorithm in bytes. + */ + int GetFieldSize(); + + /** * given a public key from a given party calculate the next * message in the agreement sequence. */ 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
index 598ea278d..4d2252654 100644 --- a/crypto/src/crypto/InvalidCipherTextException.cs +++ b/crypto/src/crypto/InvalidCipherTextException.cs
@@ -6,6 +6,9 @@ namespace Org.BouncyCastle.Crypto * this exception is thrown whenever we find something we don't expect in a * message. */ +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif public class InvalidCipherTextException : CryptoException { 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
index 9fa28abb0..be078cb13 100644 --- a/crypto/src/crypto/MaxBytesExceededException.cs +++ b/crypto/src/crypto/MaxBytesExceededException.cs
@@ -6,7 +6,10 @@ namespace Org.BouncyCastle.Crypto /// 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 +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class MaxBytesExceededException : CryptoException { public MaxBytesExceededException() diff --git a/crypto/src/crypto/PbeParametersGenerator.cs b/crypto/src/crypto/PbeParametersGenerator.cs
index 0e96abd0f..21679d45e 100644 --- a/crypto/src/crypto/PbeParametersGenerator.cs +++ b/crypto/src/crypto/PbeParametersGenerator.cs
@@ -5,186 +5,198 @@ 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) - { + /** + * 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) + { + if (password == null) + return new byte[0]; + return Strings.ToAsciiByteArray(password); - } + } + + [Obsolete("Use version taking 'char[]' instead")] + public static byte[] Pkcs5PasswordToBytes( + string password) + { + if (password == null) + return new byte[0]; - [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; - } - } + /** + * converts a password to a byte array according to the scheme in + * PKCS5 (UTF-8, no padding) + * + * @param password a character array representing the password. + * @return a byte array representing the password. + */ + public static byte[] Pkcs5PasswordToUtf8Bytes( + char[] password) + { + if (password == null) + return new byte[0]; + + return Encoding.UTF8.GetBytes(password); + } + + [Obsolete("Use version taking 'char[]' instead")] + public static byte[] Pkcs5PasswordToUtf8Bytes( + string password) + { + if (password == null) + return new byte[0]; + + 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 == null || 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
index 5a5277049..75b5e9db5 100644 --- a/crypto/src/crypto/agreement/DHBasicAgreement.cs +++ b/crypto/src/crypto/agreement/DHBasicAgreement.cs
@@ -6,55 +6,59 @@ 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); - } - } + /** + * 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 virtual 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; + } + + public virtual int GetFieldSize() + { + return (key.Parameters.P.BitLength + 7) / 8; + } + + /** + * given a short term public key from a given party calculate the next + * message in the agreement sequence. + */ + public virtual 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/DHStandardGroups.cs b/crypto/src/crypto/agreement/DHStandardGroups.cs new file mode 100644
index 000000000..6c46b60de --- /dev/null +++ b/crypto/src/crypto/agreement/DHStandardGroups.cs
@@ -0,0 +1,206 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Crypto.Agreement +{ + /// <summary>Standard Diffie-Hellman groups from various IETF specifications.</summary> + public class DHStandardGroups + { + private static DHParameters FromPG(string hexP, string hexG) + { + BigInteger p = new BigInteger(1, Hex.Decode(hexP)); + BigInteger g = new BigInteger(1, Hex.Decode(hexG)); + return new DHParameters(p, g); + } + + private static DHParameters FromPGQ(string hexP, string hexG, string hexQ) + { + BigInteger p = new BigInteger(1, Hex.Decode(hexP)); + BigInteger g = new BigInteger(1, Hex.Decode(hexG)); + BigInteger q = new BigInteger(1, Hex.Decode(hexQ)); + return new DHParameters(p, g, q); + } + + /* + * RFC 2409 + */ + private static readonly string rfc2409_768_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF"; + private static readonly string rfc2409_768_g = "02"; + public static readonly DHParameters rfc2409_768 = FromPG(rfc2409_768_p, rfc2409_768_g); + + private static readonly string rfc2409_1024_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" + + "FFFFFFFFFFFFFFFF"; + private static readonly string rfc2409_1024_g = "02"; + public static readonly DHParameters rfc2409_1024 = FromPG(rfc2409_1024_p, rfc2409_1024_g); + + /* + * RFC 3526 + */ + private static readonly string rfc3526_1536_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF"; + private static readonly string rfc3526_1536_g = "02"; + public static readonly DHParameters rfc3526_1536 = FromPG(rfc3526_1536_p, rfc3526_1536_g); + + private static readonly string rfc3526_2048_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AACAA68FFFFFFFFFFFFFFFF"; + private static readonly string rfc3526_2048_g = "02"; + public static readonly DHParameters rfc3526_2048 = FromPG(rfc3526_2048_p, rfc3526_2048_g); + + private static readonly string rfc3526_3072_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" + + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" + + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" + + "43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF"; + private static readonly string rfc3526_3072_g = "02"; + public static readonly DHParameters rfc3526_3072 = FromPG(rfc3526_3072_p, rfc3526_3072_g); + + private static readonly string rfc3526_4096_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" + + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" + + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" + + "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" + "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" + + "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" + "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" + + "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" + "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" + + "FFFFFFFFFFFFFFFF"; + private static readonly string rfc3526_4096_g = "02"; + public static readonly DHParameters rfc3526_4096 = FromPG(rfc3526_4096_p, rfc3526_4096_g); + + private static readonly string rfc3526_6144_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" + + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" + + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" + + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" + + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8" + + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C" + + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718" + + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D" + + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D" + + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226" + + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" + + "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26" + + "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB" + + "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2" + + "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127" + + "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" + + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406" + + "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918" + + "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151" + + "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03" + + "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F" + + "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" + + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B" + + "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632" + + "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E" + + "6DCC4024FFFFFFFFFFFFFFFF"; + private static readonly string rfc3526_6144_g = "02"; + public static readonly DHParameters rfc3526_6144 = FromPG(rfc3526_6144_p, rfc3526_6144_g); + + private static readonly string rfc3526_8192_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" + + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" + + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" + + "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" + "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" + + "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" + "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" + + "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" + "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" + + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" + "F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" + + "179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" + "DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" + + "5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" + "D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" + + "23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" + + "06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" + "DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" + + "12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4" + "38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300" + + "741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568" + "3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9" + + "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B" + "4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A" + + "062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36" + "4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1" + + "B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92" + "4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47" + + "9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" + "60C980DD98EDD3DFFFFFFFFFFFFFFFFF"; + private static readonly string rfc3526_8192_g = "02"; + public static readonly DHParameters rfc3526_8192 = FromPG(rfc3526_8192_p, rfc3526_8192_g); + + /* + * RFC 4306 + */ + public static readonly DHParameters rfc4306_768 = rfc2409_768; + public static readonly DHParameters rfc4306_1024 = rfc2409_1024; + + /* + * RFC 5114 + */ + private static readonly string rfc5114_1024_160_p = "B10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C6" + + "9A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C0" + "13ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD70" + + "98488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0" + "A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708" + + "DF1FB2BC2E4A4371"; + private static readonly string rfc5114_1024_160_g = "A4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507F" + + "D6406CFF14266D31266FEA1E5C41564B777E690F5504F213" + "160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1" + + "909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28A" + "D662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24" + + "855E6EEB22B3B2E5"; + private static readonly string rfc5114_1024_160_q = "F518AA8781A8DF278ABA4E7D64B7CB9D49462353"; + public static readonly DHParameters rfc5114_1024_160 = FromPGQ(rfc5114_1024_160_p, rfc5114_1024_160_g, + rfc5114_1024_160_q); + + private static readonly string rfc5114_2048_224_p = "AD107E1E9123A9D0D660FAA79559C51FA20D64E5683B9FD1" + + "B54B1597B61D0A75E6FA141DF95A56DBAF9A3C407BA1DF15" + "EB3D688A309C180E1DE6B85A1274A0A66D3F8152AD6AC212" + + "9037C9EDEFDA4DF8D91E8FEF55B7394B7AD5B7D0B6C12207" + "C9F98D11ED34DBF6C6BA0B2C8BBC27BE6A00E0A0B9C49708" + + "B3BF8A317091883681286130BC8985DB1602E714415D9330" + "278273C7DE31EFDC7310F7121FD5A07415987D9ADC0A486D" + + "CDF93ACC44328387315D75E198C641A480CD86A1B9E587E8" + "BE60E69CC928B2B9C52172E413042E9B23F10B0E16E79763" + + "C9B53DCF4BA80A29E3FB73C16B8E75B97EF363E2FFA31F71" + "CF9DE5384E71B81C0AC4DFFE0C10E64F"; + private static readonly string rfc5114_2048_224_g = "AC4032EF4F2D9AE39DF30B5C8FFDAC506CDEBE7B89998CAF" + + "74866A08CFE4FFE3A6824A4E10B9A6F0DD921F01A70C4AFA" + "AB739D7700C29F52C57DB17C620A8652BE5E9001A8D66AD7" + + "C17669101999024AF4D027275AC1348BB8A762D0521BC98A" + "E247150422EA1ED409939D54DA7460CDB5F6C6B250717CBE" + + "F180EB34118E98D119529A45D6F834566E3025E316A330EF" + "BB77A86F0C1AB15B051AE3D428C8F8ACB70A8137150B8EEB" + + "10E183EDD19963DDD9E263E4770589EF6AA21E7F5F2FF381" + "B539CCE3409D13CD566AFBB48D6C019181E1BCFE94B30269" + + "EDFE72FE9B6AA4BD7B5A0F1C71CFFF4C19C418E1F6EC0179" + "81BC087F2A7065B384B890D3191F2BFA"; + private static readonly string rfc5114_2048_224_q = "801C0D34C58D93FE997177101F80535A4738CEBCBF389A99B36371EB"; + public static readonly DHParameters rfc5114_2048_224 = FromPGQ(rfc5114_2048_224_p, rfc5114_2048_224_g, + rfc5114_2048_224_q); + + private static readonly string rfc5114_2048_256_p = "87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F2" + + "5D2CEED4435E3B00E00DF8F1D61957D4FAF7DF4561B2AA30" + "16C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD" + + "5A8A9D306BCF67ED91F9E6725B4758C022E0B1EF4275BF7B" + "6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C" + + "4FDB70C581B23F76B63ACAE1CAA6B7902D52526735488A0E" + "F13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D9" + + "67E144E5140564251CCACB83E6B486F6B3CA3F7971506026" + "C0B857F689962856DED4010ABD0BE621C3A3960A54E710C3" + + "75F26375D7014103A4B54330C198AF126116D2276E11715F" + "693877FAD7EF09CADB094AE91E1A1597"; + private static readonly string rfc5114_2048_256_g = "3FB32C9B73134D0B2E77506660EDBD484CA7B18F21EF2054" + + "07F4793A1A0BA12510DBC15077BE463FFF4FED4AAC0BB555" + "BE3A6C1B0C6B47B1BC3773BF7E8C6F62901228F8C28CBB18" + + "A55AE31341000A650196F931C77A57F2DDF463E5E9EC144B" + "777DE62AAAB8A8628AC376D282D6ED3864E67982428EBC83" + + "1D14348F6F2F9193B5045AF2767164E1DFC967C1FB3F2E55" + "A4BD1BFFE83B9C80D052B985D182EA0ADB2A3B7313D3FE14" + + "C8484B1E052588B9B7D2BBD2DF016199ECD06E1557CD0915" + "B3353BBB64E0EC377FD028370DF92B52C7891428CDC67EB6" + + "184B523D1DB246C32F63078490F00EF8D647D148D4795451" + "5E2327CFEF98C582664B4C0F6CC41659"; + private static readonly string rfc5114_2048_256_q = "8CF83642A709A097B447997640129DA299B1A47D1EB3750B" + + "A308B0FE64F5FBD3"; + public static readonly DHParameters rfc5114_2048_256 = FromPGQ(rfc5114_2048_256_p, rfc5114_2048_256_g, + rfc5114_2048_256_q); + + /* + * RFC 5996 + */ + public static readonly DHParameters rfc5996_768 = rfc4306_768; + public static readonly DHParameters rfc5996_1024 = rfc4306_1024; + } +} diff --git a/crypto/src/crypto/agreement/ECDHBasicAgreement.cs b/crypto/src/crypto/agreement/ECDHBasicAgreement.cs
index f272e4969..c33f16f78 100644 --- a/crypto/src/crypto/agreement/ECDHBasicAgreement.cs +++ b/crypto/src/crypto/agreement/ECDHBasicAgreement.cs
@@ -1,3 +1,5 @@ +using System; + using Org.BouncyCastle.Math; using Org.BouncyCastle.Math.EC; using Org.BouncyCastle.Crypto; @@ -20,31 +22,36 @@ namespace Org.BouncyCastle.Crypto.Agreement * Section 7.2.2). */ public class ECDHBasicAgreement - : IBasicAgreement + : IBasicAgreement { protected internal ECPrivateKeyParameters privKey; - public void Init( - ICipherParameters parameters) + public virtual void Init( + ICipherParameters parameters) { - if (parameters is ParametersWithRandom) - { - parameters = ((ParametersWithRandom)parameters).Parameters; - } + if (parameters is ParametersWithRandom) + { + parameters = ((ParametersWithRandom)parameters).Parameters; + } - this.privKey = (ECPrivateKeyParameters)parameters; + this.privKey = (ECPrivateKeyParameters)parameters; + } + + public virtual int GetFieldSize() + { + return (privKey.Parameters.Curve.FieldSize + 7) / 8; } public virtual BigInteger CalculateAgreement( ICipherParameters pubKey) { ECPublicKeyParameters pub = (ECPublicKeyParameters) pubKey; - ECPoint P = pub.Q.Multiply(privKey.D); + ECPoint P = pub.Q.Multiply(privKey.D).Normalize(); - // if ( p.IsInfinity ) throw new Exception("d*Q == infinity"); + if (P.IsInfinity) + throw new InvalidOperationException("Infinity is not a valid agreement value for ECDH"); - return P.X.ToBigInteger(); + return P.AffineXCoord.ToBigInteger(); } } - } diff --git a/crypto/src/crypto/agreement/ECDHCBasicAgreement.cs b/crypto/src/crypto/agreement/ECDHCBasicAgreement.cs
index 905d241fc..89be7061e 100644 --- a/crypto/src/crypto/agreement/ECDHCBasicAgreement.cs +++ b/crypto/src/crypto/agreement/ECDHCBasicAgreement.cs
@@ -27,32 +27,40 @@ namespace Org.BouncyCastle.Crypto.Agreement * BasicAgreement!).</p> */ public class ECDHCBasicAgreement - : IBasicAgreement + : IBasicAgreement { private ECPrivateKeyParameters key; - public void Init( + public virtual void Init( ICipherParameters parameters) { - if (parameters is ParametersWithRandom) - { - parameters = ((ParametersWithRandom) parameters).Parameters; - } + if (parameters is ParametersWithRandom) + { + parameters = ((ParametersWithRandom) parameters).Parameters; + } - this.key = (ECPrivateKeyParameters)parameters; + this.key = (ECPrivateKeyParameters)parameters; } - public BigInteger CalculateAgreement( + public virtual int GetFieldSize() + { + return (key.Parameters.Curve.FieldSize + 7) / 8; + } + + public virtual 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"); + BigInteger hd = parameters.H.Multiply(key.D).Mod(parameters.N); - return P.X.ToBigInteger(); + ECPoint P = pub.Q.Multiply(hd).Normalize(); + + if (P.IsInfinity) + throw new InvalidOperationException("Infinity is not a valid agreement value for ECDHC"); + + return P.AffineXCoord.ToBigInteger(); } } - } diff --git a/crypto/src/crypto/agreement/ECDHWithKdfBasicAgreement.cs b/crypto/src/crypto/agreement/ECDHWithKdfBasicAgreement.cs
index 28437a268..1de80d1e5 100644 --- a/crypto/src/crypto/agreement/ECDHWithKdfBasicAgreement.cs +++ b/crypto/src/crypto/agreement/ECDHWithKdfBasicAgreement.cs
@@ -44,7 +44,7 @@ namespace Org.BouncyCastle.Crypto.Agreement DHKdfParameters dhKdfParams = new DHKdfParameters( new DerObjectIdentifier(algorithm), keySize, - bigIntToBytes(result)); + BigIntToBytes(result)); kdf.Init(dhKdfParams); @@ -54,10 +54,9 @@ namespace Org.BouncyCastle.Crypto.Agreement return new BigInteger(1, keyBytes); } - private byte[] bigIntToBytes( - BigInteger r) + private byte[] BigIntToBytes(BigInteger r) { - int byteLength = X9IntegerConverter.GetByteLength(privKey.Parameters.G.X); + int byteLength = X9IntegerConverter.GetByteLength(privKey.Parameters.Curve); return X9IntegerConverter.IntegerToBytes(r, byteLength); } } diff --git a/crypto/src/crypto/agreement/ECMqvBasicAgreement.cs b/crypto/src/crypto/agreement/ECMqvBasicAgreement.cs
index 51faa8e49..f55ae46af 100644 --- a/crypto/src/crypto/agreement/ECMqvBasicAgreement.cs +++ b/crypto/src/crypto/agreement/ECMqvBasicAgreement.cs
@@ -7,79 +7,83 @@ 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; - } - } + public class ECMqvBasicAgreement + : IBasicAgreement + { + protected internal MqvPrivateParameters privParams; + + public virtual void Init( + ICipherParameters parameters) + { + if (parameters is ParametersWithRandom) + { + parameters = ((ParametersWithRandom)parameters).Parameters; + } + + this.privParams = (MqvPrivateParameters)parameters; + } + + public virtual int GetFieldSize() + { + return (privParams.StaticPrivateKey.Parameters.Curve.FieldSize + 7) / 8; + } + + 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).Normalize(); + + if (agreement.IsInfinity) + throw new InvalidOperationException("Infinity is not a valid agreement value for MQV"); + + return agreement.AffineXCoord.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); + + ECCurve curve = parameters.Curve; + + ECPoint[] points = new ECPoint[]{ + // The Q2U public key is optional + ECAlgorithms.ImportPoint(curve, Q2U == null ? parameters.G.Multiply(d2U.D) : Q2U.Q), + ECAlgorithms.ImportPoint(curve, Q1V.Q), + ECAlgorithms.ImportPoint(curve, Q2V.Q) + }; + + curve.NormalizeAll(points); + + ECPoint q2u = points[0], q1v = points[1], q2v = points[2]; + + BigInteger x = q2u.AffineXCoord.ToBigInteger(); + BigInteger xBar = x.Mod(powE); + BigInteger Q2UBar = xBar.SetBit(e); + BigInteger s = d1U.D.Multiply(Q2UBar).Add(d2U.D).Mod(n); + + BigInteger xPrime = q2v.AffineXCoord.ToBigInteger(); + BigInteger xPrimeBar = xPrime.Mod(powE); + BigInteger Q2VBar = xPrimeBar.SetBit(e); + + BigInteger hs = parameters.H.Multiply(s).Mod(n); + + return ECAlgorithms.SumOfTwoMultiplies( + q1v, Q2VBar.Multiply(hs).Mod(n), q2v, hs); + } + } } diff --git a/crypto/src/crypto/agreement/ECMqvWithKdfBasicAgreement.cs b/crypto/src/crypto/agreement/ECMqvWithKdfBasicAgreement.cs
index 093ce4056..7d79fc468 100644 --- a/crypto/src/crypto/agreement/ECMqvWithKdfBasicAgreement.cs +++ b/crypto/src/crypto/agreement/ECMqvWithKdfBasicAgreement.cs
@@ -44,7 +44,7 @@ namespace Org.BouncyCastle.Crypto.Agreement DHKdfParameters dhKdfParams = new DHKdfParameters( new DerObjectIdentifier(algorithm), keySize, - bigIntToBytes(result)); + BigIntToBytes(result)); kdf.Init(dhKdfParams); @@ -54,10 +54,9 @@ namespace Org.BouncyCastle.Crypto.Agreement return new BigInteger(1, keyBytes); } - private byte[] bigIntToBytes( - BigInteger r) + private byte[] BigIntToBytes(BigInteger r) { - int byteLength = X9IntegerConverter.GetByteLength(privParams.StaticPrivateKey.Parameters.G.X); + int byteLength = X9IntegerConverter.GetByteLength(privParams.StaticPrivateKey.Parameters.Curve); 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
index fa2921539..259e21e69 100644 --- a/crypto/src/crypto/agreement/kdf/DHKekGenerator.cs +++ b/crypto/src/crypto/agreement/kdf/DHKekGenerator.cs
@@ -1,129 +1,112 @@ using System; using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Crypto.Utilities; 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; - } - } + /** + * 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 virtual 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 virtual IDigest Digest + { + get { return digest; } + } + + public virtual 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()]; + + uint counter = 1; + + for (int i = 0; i < cThreshold; i++) + { + digest.BlockUpdate(z, 0, z.Length); + + // KeySpecificInfo + DerSequence keyInfo = new DerSequence( + algorithm, + new DerOctetString(Pack.UInt32_To_BE(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(Pack.UInt32_To_BE((uint)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 (int)oBytes; + } + } } diff --git a/crypto/src/crypto/agreement/kdf/ECDHKekGenerator.cs b/crypto/src/crypto/agreement/kdf/ECDHKekGenerator.cs
index 7d55aa485..74464574c 100644 --- a/crypto/src/crypto/agreement/kdf/ECDHKekGenerator.cs +++ b/crypto/src/crypto/agreement/kdf/ECDHKekGenerator.cs
@@ -4,68 +4,52 @@ using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; 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; - } - } + /** + * 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 virtual void Init(IDerivationParameters param) + { + DHKdfParameters parameters = (DHKdfParameters)param; + + this.algorithm = parameters.Algorithm; + this.keySize = parameters.KeySize; + this.z = parameters.GetZ(); // TODO Clone? + } + + public virtual IDigest Digest + { + get { return kdf.Digest; } + } + + public virtual 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(Pack.UInt32_To_BE((uint)keySize)))); + + kdf.Init(new KdfParameters(z, s.GetDerEncoded())); + + return kdf.GenerateBytes(outBytes, outOff, len); + } + } } 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
index 9f0bec9e6..218adf68c 100644 --- a/crypto/src/crypto/digests/GOST3411Digest.cs +++ b/crypto/src/crypto/digests/GOST3411Digest.cs
@@ -11,7 +11,7 @@ namespace Org.BouncyCastle.Crypto.Digests * implementation of GOST R 34.11-94 */ public class Gost3411Digest - : IDigest + : IDigest, IMemoable { private const int DIGEST_LENGTH = 32; @@ -24,7 +24,7 @@ namespace Org.BouncyCastle.Crypto.Digests private ulong byteCount; private readonly IBlockCipher cipher = new Gost28147Engine(); - private readonly byte[] sBox; + private byte[] sBox; private static byte[][] MakeC() { @@ -65,22 +65,7 @@ namespace Org.BouncyCastle.Crypto.Digests */ 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; + Reset(t); } public string AlgorithmName @@ -339,5 +324,33 @@ namespace Org.BouncyCastle.Crypto.Digests { return 32; } + + public IMemoable Copy() + { + return new Gost3411Digest(this); + } + + public void Reset(IMemoable other) + { + Gost3411Digest t = (Gost3411Digest)other; + + 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; + } } + } diff --git a/crypto/src/crypto/digests/GeneralDigest.cs b/crypto/src/crypto/digests/GeneralDigest.cs
index 77c17ed58..54a09ae05 100644 --- a/crypto/src/crypto/digests/GeneralDigest.cs +++ b/crypto/src/crypto/digests/GeneralDigest.cs
@@ -1,5 +1,7 @@ using System; +using Org.BouncyCastle.Utilities; + namespace Org.BouncyCastle.Crypto.Digests { /** @@ -7,7 +9,7 @@ namespace Org.BouncyCastle.Crypto.Digests * "Handbook of Applied Cryptography", pages 344 - 347. */ public abstract class GeneralDigest - : IDigest + : IDigest, IMemoable { private const int BYTE_LENGTH = 64; @@ -22,8 +24,13 @@ namespace Org.BouncyCastle.Crypto.Digests } internal GeneralDigest(GeneralDigest t) - { - xBuf = new byte[t.xBuf.Length]; + { + xBuf = new byte[t.xBuf.Length]; + CopyIn(t); + } + + protected void CopyIn(GeneralDigest t) + { Array.Copy(t.xBuf, 0, xBuf, 0, t.xBuf.Length); xBufOff = t.xBufOff; @@ -114,5 +121,7 @@ namespace Org.BouncyCastle.Crypto.Digests public abstract string AlgorithmName { get; } public abstract int GetDigestSize(); public abstract int DoFinal(byte[] output, int outOff); + public abstract IMemoable Copy(); + public abstract void Reset(IMemoable t); } } diff --git a/crypto/src/crypto/digests/LongDigest.cs b/crypto/src/crypto/digests/LongDigest.cs
index 702753b2b..9ee9bcd57 100644 --- a/crypto/src/crypto/digests/LongDigest.cs +++ b/crypto/src/crypto/digests/LongDigest.cs
@@ -2,6 +2,7 @@ using System; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests { @@ -9,7 +10,7 @@ namespace Org.BouncyCastle.Crypto.Digests * Base class for SHA-384 and SHA-512. */ public abstract class LongDigest - : IDigest + : IDigest, IMemoable { private int MyByteLength = 128; @@ -41,8 +42,14 @@ namespace Org.BouncyCastle.Crypto.Digests */ internal LongDigest( LongDigest t) - { - xBuf = new byte[t.xBuf.Length]; + { + xBuf = new byte[t.xBuf.Length]; + + CopyIn(t); + } + + protected void CopyIn(LongDigest t) + { Array.Copy(t.xBuf, 0, xBuf, 0, t.xBuf.Length); xBufOff = t.xBufOff; @@ -342,5 +349,7 @@ namespace Org.BouncyCastle.Crypto.Digests public abstract string AlgorithmName { get; } public abstract int GetDigestSize(); public abstract int DoFinal(byte[] output, int outOff); + public abstract IMemoable Copy(); + public abstract void Reset(IMemoable t); } } diff --git a/crypto/src/crypto/digests/MD2Digest.cs b/crypto/src/crypto/digests/MD2Digest.cs
index 78c696f33..6d90f3f9d 100644 --- a/crypto/src/crypto/digests/MD2Digest.cs +++ b/crypto/src/crypto/digests/MD2Digest.cs
@@ -1,5 +1,7 @@ using System; + using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests { @@ -9,7 +11,7 @@ namespace Org.BouncyCastle.Crypto.Digests * as outlined in RFC1319 by B.Kaliski from RSA Laboratories April 1992 */ public class MD2Digest - : IDigest + : IDigest, IMemoable { private const int DigestLength = 16; private const int BYTE_LENGTH = 16; @@ -32,8 +34,14 @@ namespace Org.BouncyCastle.Crypto.Digests { Reset(); } + public MD2Digest(MD2Digest t) - { + { + CopyIn(t); + } + + private void CopyIn(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); @@ -41,6 +49,7 @@ namespace Org.BouncyCastle.Crypto.Digests Array.Copy(t.C, 0, C, 0, t.C.Length); COff = t.COff; } + /** * return the algorithm name * @@ -242,6 +251,19 @@ namespace Org.BouncyCastle.Crypto.Digests (byte)237,(byte)31,(byte)26,(byte)219,(byte)153,(byte)141,(byte)51, (byte)159,(byte)17,(byte)131,(byte)20 }; + + public IMemoable Copy() + { + return new MD2Digest(this); + } + + public void Reset(IMemoable other) + { + MD2Digest d = (MD2Digest)other; + + CopyIn(d); + } + } } diff --git a/crypto/src/crypto/digests/MD4Digest.cs b/crypto/src/crypto/digests/MD4Digest.cs
index bc4eae0fd..8743f7dad 100644 --- a/crypto/src/crypto/digests/MD4Digest.cs +++ b/crypto/src/crypto/digests/MD4Digest.cs
@@ -1,5 +1,7 @@ using System; +using Org.BouncyCastle.Utilities; + namespace Org.BouncyCastle.Crypto.Digests { /** @@ -32,7 +34,13 @@ namespace Org.BouncyCastle.Crypto.Digests * message digest. */ public MD4Digest(MD4Digest t) : base(t) - { + { + CopyIn(t); + } + + private void CopyIn(MD4Digest t) + { + base.CopyIn(t); H1 = t.H1; H2 = t.H2; H3 = t.H3; @@ -266,6 +274,19 @@ namespace Org.BouncyCastle.Crypto.Digests X[i] = 0; } } + + public override IMemoable Copy() + { + return new MD4Digest(this); + } + + public override void Reset(IMemoable other) + { + MD4Digest d = (MD4Digest)other; + + CopyIn(d); + } + } } diff --git a/crypto/src/crypto/digests/MD5Digest.cs b/crypto/src/crypto/digests/MD5Digest.cs
index 50d93e4f8..c60ac92a3 100644 --- a/crypto/src/crypto/digests/MD5Digest.cs +++ b/crypto/src/crypto/digests/MD5Digest.cs
@@ -1,19 +1,22 @@ using System; +using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; + namespace Org.BouncyCastle.Crypto.Digests { /** * implementation of MD5 as outlined in "Handbook of Applied Cryptography", pages 346 - 347. */ public class MD5Digest - : GeneralDigest + : GeneralDigest { private const int DigestLength = 16; - private int H1, H2, H3, H4; // IV's + private uint H1, H2, H3, H4; // IV's - private int[] X = new int[16]; - private int xOff; + private uint[] X = new uint[16]; + private int xOff; public MD5Digest() { @@ -25,8 +28,14 @@ namespace Org.BouncyCastle.Crypto.Digests * message digest. */ public MD5Digest(MD5Digest t) - : base(t) - { + : base(t) + { + CopyIn(t); + } + + private void CopyIn(MD5Digest t) + { + base.CopyIn(t); H1 = t.H1; H2 = t.H2; H3 = t.H3; @@ -36,62 +45,58 @@ namespace Org.BouncyCastle.Crypto.Digests xOff = t.xOff; } - public override string AlgorithmName - { - get { return "MD5"; } - } + public override string AlgorithmName + { + get { return "MD5"; } + } - public override int GetDigestSize() - { - return DigestLength; - } + public override int GetDigestSize() + { + return DigestLength; + } - internal override void ProcessWord( - byte[] input, - int inOff) + 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); + X[xOff] = Pack.LE_To_UInt32(input, inOff); - if (xOff == 16) + if (++xOff == 16) { ProcessBlock(); } } internal override void ProcessLength( - long bitLength) + long bitLength) { if (xOff > 14) { + if (xOff == 15) + X[15] = 0; + ProcessBlock(); } - X[14] = (int)(bitLength & 0xffffffff); - X[15] = (int)((ulong) bitLength >> 32); - } + for (int i = xOff; i < 14; ++i) + { + X[i] = 0; + } - 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); + X[14] = (uint)((ulong)bitLength); + X[15] = (uint)((ulong)bitLength >> 32); } public override int DoFinal( - byte[] output, - int outOff) + byte[] output, + int outOff) { Finish(); - UnpackWord(H1, output, outOff); - UnpackWord(H2, output, outOff + 4); - UnpackWord(H3, output, outOff + 8); - UnpackWord(H4, output, outOff + 12); + Pack.UInt32_To_LE(H1, output, outOff); + Pack.UInt32_To_LE(H2, output, outOff + 4); + Pack.UInt32_To_LE(H3, output, outOff + 8); + Pack.UInt32_To_LE(H4, output, outOff + 12); Reset(); @@ -105,10 +110,10 @@ namespace Org.BouncyCastle.Crypto.Digests { base.Reset(); - H1 = unchecked((int) 0x67452301); - H2 = unchecked((int) 0xefcdab89); - H3 = unchecked((int) 0x98badcfe); - H4 = unchecked((int) 0x10325476); + H1 = 0x67452301; + H2 = 0xefcdab89; + H3 = 0x98badcfe; + H4 = 0x10325476; xOff = 0; @@ -153,149 +158,156 @@ namespace Org.BouncyCastle.Crypto.Digests /* * rotate int x left n bits. */ - private int RotateLeft( - int x, + private static uint RotateLeft( + uint x, int n) { - return (x << n) | (int) ((uint) x >> (32 - n)); + return (x << n) | (x >> (32 - n)); } /* * F, G, H and I are the basic MD5 functions. */ - private int F( - int u, - int v, - int w) + private static uint F( + uint u, + uint v, + uint w) { return (u & v) | (~u & w); } - private int G( - int u, - int v, - int w) + private static uint G( + uint u, + uint v, + uint w) { return (u & w) | (v & ~w); } - private int H( - int u, - int v, - int w) + private static uint H( + uint u, + uint v, + uint w) { return u ^ v ^ w; } - private int K( - int u, - int v, - int w) + private static uint K( + uint u, + uint v, + uint w) { return v ^ (u | ~w); } internal override void ProcessBlock() { - int a = H1; - int b = H2; - int c = H3; - int d = H4; + uint a = H1; + uint b = H2; + uint c = H3; + uint 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; + a = RotateLeft((a + F(b, c, d) + X[0] + 0xd76aa478), S11) + b; + d = RotateLeft((d + F(a, b, c) + X[1] + 0xe8c7b756), S12) + a; + c = RotateLeft((c + F(d, a, b) + X[2] + 0x242070db), S13) + d; + b = RotateLeft((b + F(c, d, a) + X[3] + 0xc1bdceee), S14) + c; + a = RotateLeft((a + F(b, c, d) + X[4] + 0xf57c0faf), S11) + b; + d = RotateLeft((d + F(a, b, c) + X[5] + 0x4787c62a), S12) + a; + c = RotateLeft((c + F(d, a, b) + X[6] + 0xa8304613), S13) + d; + b = RotateLeft((b + F(c, d, a) + X[7] + 0xfd469501), S14) + c; + a = RotateLeft((a + F(b, c, d) + X[8] + 0x698098d8), S11) + b; + d = RotateLeft((d + F(a, b, c) + X[9] + 0x8b44f7af), S12) + a; + c = RotateLeft((c + F(d, a, b) + X[10] + 0xffff5bb1), S13) + d; + b = RotateLeft((b + F(c, d, a) + X[11] + 0x895cd7be), S14) + c; + a = RotateLeft((a + F(b, c, d) + X[12] + 0x6b901122), S11) + b; + d = RotateLeft((d + F(a, b, c) + X[13] + 0xfd987193), S12) + a; + c = RotateLeft((c + F(d, a, b) + X[14] + 0xa679438e), S13) + d; + b = RotateLeft((b + F(c, d, a) + X[15] + 0x49b40821), S14) + c; // // Round 2 - G cycle, 16 times. // - a = RotateLeft((a + G(b, c, d) + X[ 1] + 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; + a = RotateLeft((a + G(b, c, d) + X[1] + 0xf61e2562), S21) + b; + d = RotateLeft((d + G(a, b, c) + X[6] + 0xc040b340), S22) + a; + c = RotateLeft((c + G(d, a, b) + X[11] + 0x265e5a51), S23) + d; + b = RotateLeft((b + G(c, d, a) + X[0] + 0xe9b6c7aa), S24) + c; + a = RotateLeft((a + G(b, c, d) + X[5] + 0xd62f105d), S21) + b; + d = RotateLeft((d + G(a, b, c) + X[10] + 0x02441453), S22) + a; + c = RotateLeft((c + G(d, a, b) + X[15] + 0xd8a1e681), S23) + d; + b = RotateLeft((b + G(c, d, a) + X[4] + 0xe7d3fbc8), S24) + c; + a = RotateLeft((a + G(b, c, d) + X[9] + 0x21e1cde6), S21) + b; + d = RotateLeft((d + G(a, b, c) + X[14] + 0xc33707d6), S22) + a; + c = RotateLeft((c + G(d, a, b) + X[3] + 0xf4d50d87), S23) + d; + b = RotateLeft((b + G(c, d, a) + X[8] + 0x455a14ed), S24) + c; + a = RotateLeft((a + G(b, c, d) + X[13] + 0xa9e3e905), S21) + b; + d = RotateLeft((d + G(a, b, c) + X[2] + 0xfcefa3f8), S22) + a; + c = RotateLeft((c + G(d, a, b) + X[7] + 0x676f02d9), S23) + d; + b = RotateLeft((b + G(c, d, a) + X[12] + 0x8d2a4c8a), S24) + c; // // Round 3 - H cycle, 16 times. // - a = RotateLeft((a + H(b, c, d) + X[ 5] + 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; + a = RotateLeft((a + H(b, c, d) + X[5] + 0xfffa3942), S31) + b; + d = RotateLeft((d + H(a, b, c) + X[8] + 0x8771f681), S32) + a; + c = RotateLeft((c + H(d, a, b) + X[11] + 0x6d9d6122), S33) + d; + b = RotateLeft((b + H(c, d, a) + X[14] + 0xfde5380c), S34) + c; + a = RotateLeft((a + H(b, c, d) + X[1] + 0xa4beea44), S31) + b; + d = RotateLeft((d + H(a, b, c) + X[4] + 0x4bdecfa9), S32) + a; + c = RotateLeft((c + H(d, a, b) + X[7] + 0xf6bb4b60), S33) + d; + b = RotateLeft((b + H(c, d, a) + X[10] + 0xbebfbc70), S34) + c; + a = RotateLeft((a + H(b, c, d) + X[13] + 0x289b7ec6), S31) + b; + d = RotateLeft((d + H(a, b, c) + X[0] + 0xeaa127fa), S32) + a; + c = RotateLeft((c + H(d, a, b) + X[3] + 0xd4ef3085), S33) + d; + b = RotateLeft((b + H(c, d, a) + X[6] + 0x04881d05), S34) + c; + a = RotateLeft((a + H(b, c, d) + X[9] + 0xd9d4d039), S31) + b; + d = RotateLeft((d + H(a, b, c) + X[12] + 0xe6db99e5), S32) + a; + c = RotateLeft((c + H(d, a, b) + X[15] + 0x1fa27cf8), S33) + d; + b = RotateLeft((b + H(c, d, a) + X[2] + 0xc4ac5665), S34) + c; // // Round 4 - K cycle, 16 times. // - a = RotateLeft((a + K(b, c, d) + X[ 0] + 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; + a = RotateLeft((a + K(b, c, d) + X[0] + 0xf4292244), S41) + b; + d = RotateLeft((d + K(a, b, c) + X[7] + 0x432aff97), S42) + a; + c = RotateLeft((c + K(d, a, b) + X[14] + 0xab9423a7), S43) + d; + b = RotateLeft((b + K(c, d, a) + X[5] + 0xfc93a039), S44) + c; + a = RotateLeft((a + K(b, c, d) + X[12] + 0x655b59c3), S41) + b; + d = RotateLeft((d + K(a, b, c) + X[3] + 0x8f0ccc92), S42) + a; + c = RotateLeft((c + K(d, a, b) + X[10] + 0xffeff47d), S43) + d; + b = RotateLeft((b + K(c, d, a) + X[1] + 0x85845dd1), S44) + c; + a = RotateLeft((a + K(b, c, d) + X[8] + 0x6fa87e4f), S41) + b; + d = RotateLeft((d + K(a, b, c) + X[15] + 0xfe2ce6e0), S42) + a; + c = RotateLeft((c + K(d, a, b) + X[6] + 0xa3014314), S43) + d; + b = RotateLeft((b + K(c, d, a) + X[13] + 0x4e0811a1), S44) + c; + a = RotateLeft((a + K(b, c, d) + X[4] + 0xf7537e82), S41) + b; + d = RotateLeft((d + K(a, b, c) + X[11] + 0xbd3af235), S42) + a; + c = RotateLeft((c + K(d, a, b) + X[2] + 0x2ad7d2bb), S43) + d; + b = RotateLeft((b + K(c, d, a) + X[9] + 0xeb86d391), S44) + c; H1 += a; H2 += b; H3 += c; H4 += d; - // - // reset the offset and clean out the word buffer. - // xOff = 0; - for (int i = 0; i != X.Length; i++) - { - X[i] = 0; - } } - } + + public override IMemoable Copy() + { + return new MD5Digest(this); + } + + public override void Reset(IMemoable other) + { + MD5Digest d = (MD5Digest)other; + + CopyIn(d); + } + + } } + 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
index 8977583a4..e8a0331ca 100644 --- a/crypto/src/crypto/digests/RipeMD128Digest.cs +++ b/crypto/src/crypto/digests/RipeMD128Digest.cs
@@ -1,5 +1,7 @@ using System; +using Org.BouncyCastle.Utilities; + namespace Org.BouncyCastle.Crypto.Digests { /** @@ -28,8 +30,15 @@ namespace Org.BouncyCastle.Crypto.Digests * message digest. */ public RipeMD128Digest(RipeMD128Digest t) : base(t) - { - H0 = t.H0; + { + CopyIn(t); + } + + private void CopyIn(RipeMD128Digest t) + { + base.CopyIn(t); + + H0 = t.H0; H1 = t.H1; H2 = t.H2; H3 = t.H3; @@ -457,6 +466,19 @@ namespace Org.BouncyCastle.Crypto.Digests X[i] = 0; } } + + public override IMemoable Copy() + { + return new RipeMD128Digest(this); + } + + public override void Reset(IMemoable other) + { + RipeMD128Digest d = (RipeMD128Digest)other; + + CopyIn(d); + } + } } diff --git a/crypto/src/crypto/digests/RipeMD160Digest.cs b/crypto/src/crypto/digests/RipeMD160Digest.cs
index 8ce52ae58..af4aa44bb 100644 --- a/crypto/src/crypto/digests/RipeMD160Digest.cs +++ b/crypto/src/crypto/digests/RipeMD160Digest.cs
@@ -1,5 +1,7 @@ using System; +using Org.BouncyCastle.Utilities; + namespace Org.BouncyCastle.Crypto.Digests { /** @@ -30,6 +32,13 @@ namespace Org.BouncyCastle.Crypto.Digests */ public RipeMD160Digest(RipeMD160Digest t) : base(t) { + CopyIn(t); + } + + private void CopyIn(RipeMD160Digest t) + { + base.CopyIn(t); + H0 = t.H0; H1 = t.H1; H2 = t.H2; @@ -418,6 +427,19 @@ namespace Org.BouncyCastle.Crypto.Digests X[i] = 0; } } + + public override IMemoable Copy() + { + return new RipeMD160Digest(this); + } + + public override void Reset(IMemoable other) + { + RipeMD160Digest d = (RipeMD160Digest)other; + + CopyIn(d); + } + } } diff --git a/crypto/src/crypto/digests/RipeMD256Digest.cs b/crypto/src/crypto/digests/RipeMD256Digest.cs
index 950e94f80..306275767 100644 --- a/crypto/src/crypto/digests/RipeMD256Digest.cs +++ b/crypto/src/crypto/digests/RipeMD256Digest.cs
@@ -1,5 +1,7 @@ using System; +using Org.BouncyCastle.Utilities; + namespace Org.BouncyCastle.Crypto.Digests { /// <remarks> @@ -37,6 +39,12 @@ namespace Org.BouncyCastle.Crypto.Digests /// </summary> public RipeMD256Digest(RipeMD256Digest t):base(t) { + CopyIn(t); + } + + private void CopyIn(RipeMD256Digest t) + { + base.CopyIn(t); H0 = t.H0; H1 = t.H1; @@ -405,5 +413,18 @@ namespace Org.BouncyCastle.Crypto.Digests X[i] = 0; } } + + public override IMemoable Copy() + { + return new RipeMD256Digest(this); + } + + public override void Reset(IMemoable other) + { + RipeMD256Digest d = (RipeMD256Digest)other; + + CopyIn(d); + } + } } diff --git a/crypto/src/crypto/digests/RipeMD320Digest.cs b/crypto/src/crypto/digests/RipeMD320Digest.cs
index 25c74baef..767d74dba 100644 --- a/crypto/src/crypto/digests/RipeMD320Digest.cs +++ b/crypto/src/crypto/digests/RipeMD320Digest.cs
@@ -1,5 +1,7 @@ using System; +using Org.BouncyCastle.Utilities; + namespace Org.BouncyCastle.Crypto.Digests { /// <remarks> @@ -38,6 +40,12 @@ namespace Org.BouncyCastle.Crypto.Digests public RipeMD320Digest(RipeMD320Digest t) : base(t) { + CopyIn(t); + } + + private void CopyIn(RipeMD320Digest t) + { + base.CopyIn(t); H0 = t.H0; H1 = t.H1; @@ -434,5 +442,18 @@ namespace Org.BouncyCastle.Crypto.Digests X[i] = 0; } } + + public override IMemoable Copy() + { + return new RipeMD320Digest(this); + } + + public override void Reset(IMemoable other) + { + RipeMD320Digest d = (RipeMD320Digest)other; + + CopyIn(d); + } + } } diff --git a/crypto/src/crypto/digests/SHA3Digest.cs b/crypto/src/crypto/digests/SHA3Digest.cs new file mode 100644
index 000000000..2c6837b3c --- /dev/null +++ b/crypto/src/crypto/digests/SHA3Digest.cs
@@ -0,0 +1,560 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /// <summary> + /// Implementation of SHA-3 based on following KeccakNISTInterface.c from http://keccak.noekeon.org/ + /// </summary> + /// <remarks> + /// Following the naming conventions used in the C source code to enable easy review of the implementation. + /// </remarks> + public class Sha3Digest + : IDigest, IMemoable + { + private static readonly ulong[] KeccakRoundConstants = KeccakInitializeRoundConstants(); + + private static readonly int[] KeccakRhoOffsets = KeccakInitializeRhoOffsets(); + + private static ulong[] KeccakInitializeRoundConstants() + { + ulong[] keccakRoundConstants = new ulong[24]; + byte LFSRState = 0x01; + + for (int i = 0; i < 24; i++) + { + keccakRoundConstants[i] = 0; + for (int j = 0; j < 7; j++) + { + int bitPosition = (1 << j) - 1; + + // LFSR86540 + + bool loBit = (LFSRState & 0x01) != 0; + if (loBit) + { + keccakRoundConstants[i] ^= 1UL << bitPosition; + } + + bool hiBit = (LFSRState & 0x80) != 0; + LFSRState <<= 1; + if (hiBit) + { + LFSRState ^= 0x71; + } + + } + } + + return keccakRoundConstants; + } + + private static int[] KeccakInitializeRhoOffsets() + { + int[] keccakRhoOffsets = new int[25]; + int x, y, t, newX, newY; + + int rhoOffset = 0; + keccakRhoOffsets[(((0) % 5) + 5 * ((0) % 5))] = rhoOffset; + x = 1; + y = 0; + for (t = 1; t < 25; t++) + { + //rhoOffset = ((t + 1) * (t + 2) / 2) % 64; + rhoOffset = (rhoOffset + t) & 63; + keccakRhoOffsets[(((x) % 5) + 5 * ((y) % 5))] = rhoOffset; + newX = (0 * x + 1 * y) % 5; + newY = (2 * x + 3 * y) % 5; + x = newX; + y = newY; + } + + return keccakRhoOffsets; + } + + private byte[] state = new byte[(1600 / 8)]; + private byte[] dataQueue = new byte[(1536 / 8)]; + private int rate; + private int bitsInQueue; + private int fixedOutputLength; + private bool squeezing; + private int bitsAvailableForSqueezing; + private byte[] chunk; + private byte[] oneByte; + + private void ClearDataQueueSection(int off, int len) + { + for (int i = off; i != off + len; i++) + { + dataQueue[i] = 0; + } + } + + public Sha3Digest() + { + Init(0); + } + + public Sha3Digest(int bitLength) + { + Init(bitLength); + } + + public Sha3Digest(Sha3Digest source) + { + CopyIn(source); + } + + private void CopyIn(Sha3Digest source) + { + Array.Copy(source.state, 0, this.state, 0, source.state.Length); + Array.Copy(source.dataQueue, 0, this.dataQueue, 0, source.dataQueue.Length); + this.rate = source.rate; + this.bitsInQueue = source.bitsInQueue; + this.fixedOutputLength = source.fixedOutputLength; + this.squeezing = source.squeezing; + this.bitsAvailableForSqueezing = source.bitsAvailableForSqueezing; + this.chunk = Arrays.Clone(source.chunk); + this.oneByte = Arrays.Clone(source.oneByte); + } + + public virtual string AlgorithmName + { + get { return "SHA3-" + fixedOutputLength; } + } + + public virtual int GetDigestSize() + { + return fixedOutputLength / 8; + } + + public virtual void Update(byte input) + { + oneByte[0] = input; + + DoUpdate(oneByte, 0, 8L); + } + + public virtual void BlockUpdate(byte[] input, int inOff, int len) + { + DoUpdate(input, inOff, len * 8L); + } + + public virtual int DoFinal(byte[] output, int outOff) + { + Squeeze(output, outOff, fixedOutputLength); + + Reset(); + + return GetDigestSize(); + } + + public virtual void Reset() + { + Init(fixedOutputLength); + } + + /** + * Return the size of block that the compression function is applied to in bytes. + * + * @return internal byte length of a block. + */ + public virtual int GetByteLength() + { + return rate / 8; + } + + private void Init(int bitLength) + { + switch (bitLength) + { + case 0: + case 288: + InitSponge(1024, 576); + break; + case 224: + InitSponge(1152, 448); + break; + case 256: + InitSponge(1088, 512); + break; + case 384: + InitSponge(832, 768); + break; + case 512: + InitSponge(576, 1024); + break; + default: + throw new ArgumentException("must be one of 224, 256, 384, or 512.", "bitLength"); + } + } + + private void DoUpdate(byte[] data, int off, long databitlen) + { + if ((databitlen % 8) == 0) + { + Absorb(data, off, databitlen); + } + else + { + Absorb(data, off, databitlen - (databitlen % 8)); + + byte[] lastByte = new byte[1]; + + lastByte[0] = (byte)(data[off + (int)(databitlen / 8)] >> (int)(8 - (databitlen % 8))); + Absorb(lastByte, off, databitlen % 8); + } + } + + private void InitSponge(int rate, int capacity) + { + if (rate + capacity != 1600) + { + throw new InvalidOperationException("rate + capacity != 1600"); + } + if ((rate <= 0) || (rate >= 1600) || ((rate % 64) != 0)) + { + throw new InvalidOperationException("invalid rate value"); + } + + this.rate = rate; + // this is never read, need to check to see why we want to save it + // this.capacity = capacity; + this.fixedOutputLength = 0; + Arrays.Fill(this.state, (byte)0); + Arrays.Fill(this.dataQueue, (byte)0); + this.bitsInQueue = 0; + this.squeezing = false; + this.bitsAvailableForSqueezing = 0; + this.fixedOutputLength = capacity / 2; + this.chunk = new byte[rate / 8]; + this.oneByte = new byte[1]; + } + + private void AbsorbQueue() + { + KeccakAbsorb(state, dataQueue, rate / 8); + + bitsInQueue = 0; + } + + private void Absorb(byte[] data, int off, long databitlen) + { + long i, j, wholeBlocks; + + if ((bitsInQueue % 8) != 0) + { + throw new InvalidOperationException("attempt to absorb with odd length queue."); + } + if (squeezing) + { + throw new InvalidOperationException("attempt to absorb while squeezing."); + } + + i = 0; + while (i < databitlen) + { + if ((bitsInQueue == 0) && (databitlen >= rate) && (i <= (databitlen - rate))) + { + wholeBlocks = (databitlen - i) / rate; + + for (j = 0; j < wholeBlocks; j++) + { + Array.Copy(data, (int)(off + (i / 8) + (j * chunk.Length)), chunk, 0, chunk.Length); + + //displayIntermediateValues.displayBytes(1, "Block to be absorbed", curData, rate / 8); + + KeccakAbsorb(state, chunk, chunk.Length); + } + + i += wholeBlocks * rate; + } + else + { + int partialBlock = (int)(databitlen - i); + if (partialBlock + bitsInQueue > rate) + { + partialBlock = rate - bitsInQueue; + } + int partialByte = partialBlock % 8; + partialBlock -= partialByte; + Array.Copy(data, off + (int)(i / 8), dataQueue, bitsInQueue / 8, partialBlock / 8); + + bitsInQueue += partialBlock; + i += partialBlock; + if (bitsInQueue == rate) + { + AbsorbQueue(); + } + if (partialByte > 0) + { + int mask = (1 << partialByte) - 1; + dataQueue[bitsInQueue / 8] = (byte)(data[off + ((int)(i / 8))] & mask); + bitsInQueue += partialByte; + i += partialByte; + } + } + } + } + + private void PadAndSwitchToSqueezingPhase() + { + if (bitsInQueue + 1 == rate) + { + dataQueue[bitsInQueue / 8] |= (byte)(1U << (bitsInQueue % 8)); + AbsorbQueue(); + ClearDataQueueSection(0, rate / 8); + } + else + { + ClearDataQueueSection((bitsInQueue + 7) / 8, rate / 8 - (bitsInQueue + 7) / 8); + dataQueue[bitsInQueue / 8] |= (byte)(1U << (bitsInQueue % 8)); + } + dataQueue[(rate - 1) / 8] |= (byte)(1U << ((rate - 1) % 8)); + AbsorbQueue(); + + //displayIntermediateValues.displayText(1, "--- Switching to squeezing phase ---"); + + if (rate == 1024) + { + KeccakExtract1024bits(state, dataQueue); + bitsAvailableForSqueezing = 1024; + } + else + { + KeccakExtract(state, dataQueue, rate / 64); + bitsAvailableForSqueezing = rate; + } + + //displayIntermediateValues.displayBytes(1, "Block available for squeezing", dataQueue, bitsAvailableForSqueezing / 8); + + squeezing = true; + } + + private void Squeeze(byte[] output, int offset, long outputLength) + { + long i; + int partialBlock; + + if (!squeezing) + { + PadAndSwitchToSqueezingPhase(); + } + if ((outputLength % 8) != 0) + { + throw new InvalidOperationException("outputLength not a multiple of 8"); + } + + i = 0; + while (i < outputLength) + { + if (bitsAvailableForSqueezing == 0) + { + KeccakPermutation(state); + + if (rate == 1024) + { + KeccakExtract1024bits(state, dataQueue); + bitsAvailableForSqueezing = 1024; + } + else + + { + KeccakExtract(state, dataQueue, rate / 64); + bitsAvailableForSqueezing = rate; + } + + //displayIntermediateValues.displayBytes(1, "Block available for squeezing", dataQueue, bitsAvailableForSqueezing / 8); + + } + partialBlock = bitsAvailableForSqueezing; + if ((long)partialBlock > outputLength - i) + { + partialBlock = (int)(outputLength - i); + } + + Array.Copy(dataQueue, (rate - bitsAvailableForSqueezing) / 8, output, offset + (int)(i / 8), partialBlock / 8); + bitsAvailableForSqueezing -= partialBlock; + i += partialBlock; + } + } + + private static void FromBytesToWords(ulong[] stateAsWords, byte[] state) + { + for (int i = 0; i < (1600 / 64); i++) + { + stateAsWords[i] = 0; + int index = i * (64 / 8); + for (int j = 0; j < (64 / 8); j++) + { + stateAsWords[i] |= ((ulong)state[index + j] & 0xff) << ((8 * j)); + } + } + } + + private static void FromWordsToBytes(byte[] state, ulong[] stateAsWords) + { + for (int i = 0; i < (1600 / 64); i++) + { + int index = i * (64 / 8); + for (int j = 0; j < (64 / 8); j++) + { + state[index + j] = (byte)(stateAsWords[i] >> (8 * j)); + } + } + } + + private void KeccakPermutation(byte[] state) + { + ulong[] longState = new ulong[state.Length / 8]; + + FromBytesToWords(longState, state); + + //displayIntermediateValues.displayStateAsBytes(1, "Input of permutation", longState); + + KeccakPermutationOnWords(longState); + + //displayIntermediateValues.displayStateAsBytes(1, "State after permutation", longState); + + FromWordsToBytes(state, longState); + } + + private void KeccakPermutationAfterXor(byte[] state, byte[] data, int dataLengthInBytes) + { + for (int i = 0; i < dataLengthInBytes; i++) + { + state[i] ^= data[i]; + } + + KeccakPermutation(state); + } + + private void KeccakPermutationOnWords(ulong[] state) + { + int i; + + //displayIntermediateValues.displayStateAs64bitWords(3, "Same, with lanes as 64-bit words", state); + + for (i = 0; i < 24; i++) + { + //displayIntermediateValues.displayRoundNumber(3, i); + + Theta(state); + //displayIntermediateValues.displayStateAs64bitWords(3, "After theta", state); + + Rho(state); + //displayIntermediateValues.displayStateAs64bitWords(3, "After rho", state); + + Pi(state); + //displayIntermediateValues.displayStateAs64bitWords(3, "After pi", state); + + Chi(state); + //displayIntermediateValues.displayStateAs64bitWords(3, "After chi", state); + + Iota(state, i); + //displayIntermediateValues.displayStateAs64bitWords(3, "After iota", state); + } + } + + ulong[] C = new ulong[5]; + + private void Theta(ulong[] A) + { + for (int x = 0; x < 5; x++) + { + C[x] = 0; + for (int y = 0; y < 5; y++) + { + C[x] ^= A[x + 5 * y]; + } + } + for (int x = 0; x < 5; x++) + { + ulong dX = ((((C[(x + 1) % 5]) << 1) ^ ((C[(x + 1) % 5]) >> (64 - 1)))) ^ C[(x + 4) % 5]; + for (int y = 0; y < 5; y++) + { + A[x + 5 * y] ^= dX; + } + } + } + + private void Rho(ulong[] A) + { + for (int x = 0; x < 5; x++) + { + for (int y = 0; y < 5; y++) + { + int index = x + 5 * y; + A[index] = ((KeccakRhoOffsets[index] != 0) ? (((A[index]) << KeccakRhoOffsets[index]) ^ ((A[index]) >> (64 - KeccakRhoOffsets[index]))) : A[index]); + } + } + } + + ulong[] tempA = new ulong[25]; + + private void Pi(ulong[] A) + { + Array.Copy(A, 0, tempA, 0, tempA.Length); + + for (int x = 0; x < 5; x++) + { + for (int y = 0; y < 5; y++) + { + A[y + 5 * ((2 * x + 3 * y) % 5)] = tempA[x + 5 * y]; + } + } + } + + ulong[] chiC = new ulong[5]; + + private void Chi(ulong[] A) + { + for (int y = 0; y < 5; y++) + { + for (int x = 0; x < 5; x++) + { + chiC[x] = A[x + 5 * y] ^ ((~A[(((x + 1) % 5) + 5 * y)]) & A[(((x + 2) % 5) + 5 * y)]); + } + for (int x = 0; x < 5; x++) + { + A[x + 5 * y] = chiC[x]; + } + } + } + + private static void Iota(ulong[] A, int indexRound) + { + A[(((0) % 5) + 5 * ((0) % 5))] ^= KeccakRoundConstants[indexRound]; + } + + private void KeccakAbsorb(byte[] byteState, byte[] data, int dataInBytes) + { + KeccakPermutationAfterXor(byteState, data, dataInBytes); + } + + private void KeccakExtract1024bits(byte[] byteState, byte[] data) + { + Array.Copy(byteState, 0, data, 0, 128); + } + + private void KeccakExtract(byte[] byteState, byte[] data, int laneCount) + { + Array.Copy(byteState, 0, data, 0, laneCount * 8); + } + + public IMemoable Copy() + { + return new Sha3Digest(this); + } + + public void Reset(IMemoable other) + { + Sha3Digest d = (Sha3Digest)other; + + CopyIn(d); + } + + + } +} diff --git a/crypto/src/crypto/digests/SM3Digest.cs b/crypto/src/crypto/digests/SM3Digest.cs new file mode 100644
index 000000000..d81b2ddbf --- /dev/null +++ b/crypto/src/crypto/digests/SM3Digest.cs
@@ -0,0 +1,328 @@ +using System; + +using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Digests +{ + + /// <summary> + /// Implementation of Chinese SM3 digest as described at + /// http://tools.ietf.org/html/draft-shen-sm3-hash-00 + /// and at .... ( Chinese PDF ) + /// </summary> + /// <remarks> + /// The specification says "process a bit stream", + /// but this is written to process bytes in blocks of 4, + /// meaning this will process 32-bit word groups. + /// But so do also most other digest specifications, + /// including the SHA-256 which was a origin for + /// this specification. + /// </remarks> + public class SM3Digest + : GeneralDigest + { + private const int DIGEST_LENGTH = 32; // bytes + private const int BLOCK_SIZE = 64 / 4; // of 32 bit ints (16 ints) + + private uint[] V = new uint[DIGEST_LENGTH / 4]; // in 32 bit ints (8 ints) + private uint[] inwords = new uint[BLOCK_SIZE]; + private int xOff; + + // Work-bufs used within processBlock() + private uint[] W = new uint[68]; + private uint[] W1 = new uint[64]; + + // Round constant T for processBlock() which is 32 bit integer rolled left up to (63 MOD 32) bit positions. + private static readonly uint[] T = new uint[64]; + + static SM3Digest() + { + for (int i = 0; i < 16; ++i) + { + uint t = 0x79CC4519; + T[i] = (t << i) | (t >> (32 - i)); + } + for (int i = 16; i < 64; ++i) + { + int n = i % 32; + uint t = 0x7A879D8A; + T[i] = (t << n) | (t >> (32 - n)); + } + } + + + /// <summary> + /// Standard constructor + /// </summary> + public SM3Digest() + { + Reset(); + } + + /// <summary> + /// Copy constructor. This will copy the state of the provided + /// message digest. + /// </summary> + public SM3Digest(SM3Digest t) + : base(t) + { + CopyIn(t); + } + + private void CopyIn(SM3Digest t) + { + Array.Copy(t.V, 0, this.V, 0, this.V.Length); + Array.Copy(t.inwords, 0, this.inwords, 0, this.inwords.Length); + xOff = t.xOff; + } + + public override string AlgorithmName + { + get { return "SM3"; } + } + + public override int GetDigestSize() + { + return DIGEST_LENGTH; + } + + public override IMemoable Copy() + { + return new SM3Digest(this); + } + + public override void Reset(IMemoable other) + { + SM3Digest d = (SM3Digest)other; + + base.CopyIn(d); + CopyIn(d); + } + + /// <summary> + /// reset the chaining variables + /// </summary> + public override void Reset() + { + base.Reset(); + + this.V[0] = 0x7380166F; + this.V[1] = 0x4914B2B9; + this.V[2] = 0x172442D7; + this.V[3] = 0xDA8A0600; + this.V[4] = 0xA96F30BC; + this.V[5] = 0x163138AA; + this.V[6] = 0xE38DEE4D; + this.V[7] = 0xB0FB0E4E; + + this.xOff = 0; + } + + + public override int DoFinal(byte[] output, int outOff) + { + Finish(); + + Pack.UInt32_To_BE(this.V[0], output, outOff + 0); + Pack.UInt32_To_BE(this.V[1], output, outOff + 4); + Pack.UInt32_To_BE(this.V[2], output, outOff + 8); + Pack.UInt32_To_BE(this.V[3], output, outOff + 12); + Pack.UInt32_To_BE(this.V[4], output, outOff + 16); + Pack.UInt32_To_BE(this.V[5], output, outOff + 20); + Pack.UInt32_To_BE(this.V[6], output, outOff + 24); + Pack.UInt32_To_BE(this.V[7], output, outOff + 28); + + Reset(); + + return DIGEST_LENGTH; + } + + + internal override void ProcessWord(byte[] input, + int inOff) + { + uint n = Pack.BE_To_UInt32(input, inOff); + this.inwords[this.xOff] = n; + ++this.xOff; + + if (this.xOff >= 16) + { + ProcessBlock(); + } + } + + internal override void ProcessLength(long bitLength) + { + if (this.xOff > (BLOCK_SIZE - 2)) + { + // xOff == 15 --> can't fit the 64 bit length field at tail.. + this.inwords[this.xOff] = 0; // fill with zero + ++this.xOff; + + ProcessBlock(); + } + // Fill with zero words, until reach 2nd to last slot + while (this.xOff < (BLOCK_SIZE - 2)) + { + this.inwords[this.xOff] = 0; + ++this.xOff; + } + + // Store input data length in BITS + this.inwords[this.xOff++] = (uint)(bitLength >> 32); + this.inwords[this.xOff++] = (uint)(bitLength); + } + + /* + + 3.4.2. Constants + + + Tj = 79cc4519 when 0 < = j < = 15 + Tj = 7a879d8a when 16 < = j < = 63 + + 3.4.3. Boolean function + + + FFj(X;Y;Z) = X XOR Y XOR Z when 0 < = j < = 15 + = (X AND Y) OR (X AND Z) OR (Y AND Z) when 16 < = j < = 63 + + GGj(X;Y;Z) = X XOR Y XOR Z when 0 < = j < = 15 + = (X AND Y) OR (NOT X AND Z) when 16 < = j < = 63 + + The X, Y, Z in the fomular are words!GBP + + 3.4.4. Permutation function + + + P0(X) = X XOR (X <<< 9) XOR (X <<< 17) ## ROLL, not SHIFT + P1(X) = X XOR (X <<< 15) XOR (X <<< 23) ## ROLL, not SHIFT + + The X in the fomular are a word. + + ---------- + + Each ROLL converted to Java expression: + + ROLL 9 : ((x << 9) | (x >> (32-9)))) + ROLL 17 : ((x << 17) | (x >> (32-17))) + ROLL 15 : ((x << 15) | (x >> (32-15))) + ROLL 23 : ((x << 23) | (x >> (32-23))) + + */ + + private uint P0(uint x) + { + uint r9 = ((x << 9) | (x >> (32 - 9))); + uint r17 = ((x << 17) | (x >> (32 - 17))); + return (x ^ r9 ^ r17); + } + + private uint P1(uint x) + { + uint r15 = ((x << 15) | (x >> (32 - 15))); + uint r23 = ((x << 23) | (x >> (32 - 23))); + return (x ^ r15 ^ r23); + } + + private uint FF0(uint x, uint y, uint z) + { + return (x ^ y ^ z); + } + + private uint FF1(uint x, uint y, uint z) + { + return ((x & y) | (x & z) | (y & z)); + } + + private uint GG0(uint x, uint y, uint z) + { + return (x ^ y ^ z); + } + + private uint GG1(uint x, uint y, uint z) + { + return ((x & y) | ((~x) & z)); + } + + + internal override void ProcessBlock() + { + for (int j = 0; j < 16; ++j) + { + this.W[j] = this.inwords[j]; + } + for (int j = 16; j < 68; ++j) + { + uint wj3 = this.W[j - 3]; + uint r15 = ((wj3 << 15) | (wj3 >> (32 - 15))); + uint wj13 = this.W[j - 13]; + uint r7 = ((wj13 << 7) | (wj13 >> (32 - 7))); + this.W[j] = P1(this.W[j - 16] ^ this.W[j - 9] ^ r15) ^ r7 ^ this.W[j - 6]; + } + for (int j = 0; j < 64; ++j) + { + this.W1[j] = this.W[j] ^ this.W[j + 4]; + } + + uint A = this.V[0]; + uint B = this.V[1]; + uint C = this.V[2]; + uint D = this.V[3]; + uint E = this.V[4]; + uint F = this.V[5]; + uint G = this.V[6]; + uint H = this.V[7]; + + + for (int j = 0; j < 16; ++j) + { + uint a12 = ((A << 12) | (A >> (32 - 12))); + uint s1_ = a12 + E + T[j]; + uint SS1 = ((s1_ << 7) | (s1_ >> (32 - 7))); + uint SS2 = SS1 ^ a12; + uint TT1 = FF0(A, B, C) + D + SS2 + this.W1[j]; + uint TT2 = GG0(E, F, G) + H + SS1 + this.W[j]; + D = C; + C = ((B << 9) | (B >> (32 - 9))); + B = A; + A = TT1; + H = G; + G = ((F << 19) | (F >> (32 - 19))); + F = E; + E = P0(TT2); + } + + // Different FF,GG functions on rounds 16..63 + for (int j = 16; j < 64; ++j) + { + uint a12 = ((A << 12) | (A >> (32 - 12))); + uint s1_ = a12 + E + T[j]; + uint SS1 = ((s1_ << 7) | (s1_ >> (32 - 7))); + uint SS2 = SS1 ^ a12; + uint TT1 = FF1(A, B, C) + D + SS2 + this.W1[j]; + uint TT2 = GG1(E, F, G) + H + SS1 + this.W[j]; + D = C; + C = ((B << 9) | (B >> (32 - 9))); + B = A; + A = TT1; + H = G; + G = ((F << 19) | (F >> (32 - 19))); + F = E; + E = P0(TT2); + } + + this.V[0] ^= A; + this.V[1] ^= B; + this.V[2] ^= C; + this.V[3] ^= D; + this.V[4] ^= E; + this.V[5] ^= F; + this.V[6] ^= G; + this.V[7] ^= H; + + this.xOff = 0; + } + } +} diff --git a/crypto/src/crypto/digests/Sha1Digest.cs b/crypto/src/crypto/digests/Sha1Digest.cs
index 9d8c1a4cf..60ec651d5 100644 --- a/crypto/src/crypto/digests/Sha1Digest.cs +++ b/crypto/src/crypto/digests/Sha1Digest.cs
@@ -1,6 +1,7 @@ using System; using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests { @@ -9,10 +10,10 @@ 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! + * is the "endianness" of the word processing! */ public class Sha1Digest - : GeneralDigest + : GeneralDigest { private const int DigestLength = 20; @@ -21,7 +22,7 @@ namespace Org.BouncyCastle.Crypto.Digests private uint[] X = new uint[80]; private int xOff; - public Sha1Digest() + public Sha1Digest() { Reset(); } @@ -31,8 +32,15 @@ namespace Org.BouncyCastle.Crypto.Digests * message digest. */ public Sha1Digest(Sha1Digest t) - : base(t) + : base(t) { + CopyIn(t); + } + + private void CopyIn(Sha1Digest t) + { + base.CopyIn(t); + H1 = t.H1; H2 = t.H2; H3 = t.H3; @@ -43,34 +51,34 @@ namespace Org.BouncyCastle.Crypto.Digests xOff = t.xOff; } - public override string AlgorithmName - { - get { return "SHA-1"; } - } + public override string AlgorithmName + { + get { return "SHA-1"; } + } - public override int GetDigestSize() - { - return DigestLength; - } + public override int GetDigestSize() + { + return DigestLength; + } - internal override void ProcessWord( + internal override void ProcessWord( byte[] input, int inOff) { - X[xOff] = Pack.BE_To_UInt32(input, inOff); + X[xOff] = Pack.BE_To_UInt32(input, inOff); - if (++xOff == 16) - { - ProcessBlock(); - } + if (++xOff == 16) + { + ProcessBlock(); + } } - internal override void ProcessLength(long bitLength) + internal override void ProcessLength(long bitLength) { - if (xOff > 14) - { - ProcessBlock(); - } + if (xOff > 14) + { + ProcessBlock(); + } X[14] = (uint)((ulong)bitLength >> 32); X[15] = (uint)((ulong)bitLength); @@ -107,7 +115,7 @@ namespace Org.BouncyCastle.Crypto.Digests H5 = 0xc3d2e1f0; xOff = 0; - Array.Clear(X, 0, X.Length); + Array.Clear(X, 0, X.Length); } // @@ -118,31 +126,31 @@ namespace Org.BouncyCastle.Crypto.Digests 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 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 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); - } + private static uint G(uint u, uint v, uint w) + { + return (u & v) | (u & w) | (v & w); + } - internal override void ProcessBlock() + 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; - } + 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. @@ -156,108 +164,121 @@ namespace Org.BouncyCastle.Crypto.Digests // // round 1 // - int idx = 0; + 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); + 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); + 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); + 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); + 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); - } + 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); + 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); + 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); + 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); + 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); - } + 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); + 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); + 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); + 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); + 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); - } + 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); + 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); + } + + public override IMemoable Copy() + { + return new Sha1Digest(this); } + + public override void Reset(IMemoable other) + { + Sha1Digest d = (Sha1Digest)other; + + CopyIn(d); + } + } } diff --git a/crypto/src/crypto/digests/Sha224Digest.cs b/crypto/src/crypto/digests/Sha224Digest.cs
index 66ecd4ecd..b4e853745 100644 --- a/crypto/src/crypto/digests/Sha224Digest.cs +++ b/crypto/src/crypto/digests/Sha224Digest.cs
@@ -1,6 +1,7 @@ using System; using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests { @@ -41,6 +42,13 @@ namespace Org.BouncyCastle.Crypto.Digests Sha224Digest t) : base(t) { + CopyIn(t); + } + + private void CopyIn(Sha224Digest t) + { + base.CopyIn(t); + H1 = t.H1; H2 = t.H2; H3 = t.H3; @@ -264,5 +272,18 @@ namespace Org.BouncyCastle.Crypto.Digests 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; + + public override IMemoable Copy() + { + return new Sha224Digest(this); + } + + public override void Reset(IMemoable other) + { + Sha224Digest d = (Sha224Digest)other; + + CopyIn(d); + } + } } diff --git a/crypto/src/crypto/digests/Sha256Digest.cs b/crypto/src/crypto/digests/Sha256Digest.cs
index 1c00ab71f..98e10a34d 100644 --- a/crypto/src/crypto/digests/Sha256Digest.cs +++ b/crypto/src/crypto/digests/Sha256Digest.cs
@@ -1,6 +1,7 @@ using System; using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests { @@ -36,6 +37,13 @@ namespace Org.BouncyCastle.Crypto.Digests */ public Sha256Digest(Sha256Digest t) : base(t) { + CopyIn(t); + } + + private void CopyIn(Sha256Digest t) + { + base.CopyIn(t); + H1 = t.H1; H2 = t.H2; H3 = t.H3; @@ -305,5 +313,18 @@ namespace Org.BouncyCastle.Crypto.Digests 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; + + public override IMemoable Copy() + { + return new Sha256Digest(this); + } + + public override void Reset(IMemoable other) + { + Sha256Digest d = (Sha256Digest)other; + + CopyIn(d); + } + } } diff --git a/crypto/src/crypto/digests/Sha384Digest.cs b/crypto/src/crypto/digests/Sha384Digest.cs
index f1372d0a9..e6c9a9aa9 100644 --- a/crypto/src/crypto/digests/Sha384Digest.cs +++ b/crypto/src/crypto/digests/Sha384Digest.cs
@@ -1,6 +1,7 @@ using System; using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests { @@ -83,5 +84,18 @@ namespace Org.BouncyCastle.Crypto.Digests H7 = 0xdb0c2e0d64f98fa7; H8 = 0x47b5481dbefa4fa4; } + + public override IMemoable Copy() + { + return new Sha384Digest(this); + } + + public override void Reset(IMemoable other) + { + Sha384Digest d = (Sha384Digest)other; + + CopyIn(d); + } + } } diff --git a/crypto/src/crypto/digests/Sha512Digest.cs b/crypto/src/crypto/digests/Sha512Digest.cs
index ed1a50819..2a0964fd3 100644 --- a/crypto/src/crypto/digests/Sha512Digest.cs +++ b/crypto/src/crypto/digests/Sha512Digest.cs
@@ -1,6 +1,7 @@ using System; using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests { @@ -17,35 +18,35 @@ namespace Org.BouncyCastle.Crypto.Digests * </pre> */ public class Sha512Digest - : LongDigest + : LongDigest { private const int DigestLength = 64; - public Sha512Digest() + public Sha512Digest() { } - /** + /** * Copy constructor. This will copy the state of the provided * message digest. */ public Sha512Digest( - Sha512Digest t) - : base(t) - { - } + Sha512Digest t) + : base(t) + { + } - public override string AlgorithmName - { - get { return "SHA-512"; } - } + public override string AlgorithmName + { + get { return "SHA-512"; } + } - public override int GetDigestSize() - { - return DigestLength; - } + public override int GetDigestSize() + { + return DigestLength; + } - public override int DoFinal( + public override int DoFinal( byte[] output, int outOff) { @@ -86,5 +87,18 @@ namespace Org.BouncyCastle.Crypto.Digests H7 = 0x1f83d9abfb41bd6b; H8 = 0x5be0cd19137e2179; } + + public override IMemoable Copy() + { + return new Sha512Digest(this); + } + + public override void Reset(IMemoable other) + { + Sha512Digest d = (Sha512Digest)other; + + CopyIn(d); + } + } } diff --git a/crypto/src/crypto/digests/Sha512tDigest.cs b/crypto/src/crypto/digests/Sha512tDigest.cs new file mode 100644
index 000000000..2caefa763 --- /dev/null +++ b/crypto/src/crypto/digests/Sha512tDigest.cs
@@ -0,0 +1,200 @@ +using System; + +using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Digests +{ + /** + * FIPS 180-4 implementation of SHA-512/t + */ + public class Sha512tDigest + : LongDigest + { + private const ulong A5 = 0xa5a5a5a5a5a5a5a5UL; + + private readonly int digestLength; + + private ulong H1t, H2t, H3t, H4t, H5t, H6t, H7t, H8t; + + /** + * Standard constructor + */ + public Sha512tDigest(int bitLength) + { + if (bitLength >= 512) + throw new ArgumentException("cannot be >= 512", "bitLength"); + if (bitLength % 8 != 0) + throw new ArgumentException("needs to be a multiple of 8", "bitLength"); + if (bitLength == 384) + throw new ArgumentException("cannot be 384 use SHA384 instead", "bitLength"); + + this.digestLength = bitLength / 8; + + tIvGenerate(digestLength * 8); + + Reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public Sha512tDigest(Sha512tDigest t) + : base(t) + { + this.digestLength = t.digestLength; + + Reset(t); + } + + public override string AlgorithmName + { + get { return "SHA-512/" + (digestLength * 8); } + } + + public override int GetDigestSize() + { + return digestLength; + } + + public override int DoFinal(byte[] output, int outOff) + { + Finish(); + + UInt64_To_BE(H1, output, outOff, digestLength); + UInt64_To_BE(H2, output, outOff + 8, digestLength - 8); + UInt64_To_BE(H3, output, outOff + 16, digestLength - 16); + UInt64_To_BE(H4, output, outOff + 24, digestLength - 24); + UInt64_To_BE(H5, output, outOff + 32, digestLength - 32); + UInt64_To_BE(H6, output, outOff + 40, digestLength - 40); + UInt64_To_BE(H7, output, outOff + 48, digestLength - 48); + UInt64_To_BE(H8, output, outOff + 56, digestLength - 56); + + Reset(); + + return digestLength; + } + + /** + * reset the chaining variables + */ + public override void Reset() + { + base.Reset(); + + /* + * initial hash values use the iv generation algorithm for t. + */ + H1 = H1t; + H2 = H2t; + H3 = H3t; + H4 = H4t; + H5 = H5t; + H6 = H6t; + H7 = H7t; + H8 = H8t; + } + + private void tIvGenerate(int bitLength) + { + H1 = 0x6a09e667f3bcc908UL ^ A5; + H2 = 0xbb67ae8584caa73bUL ^ A5; + H3 = 0x3c6ef372fe94f82bUL ^ A5; + H4 = 0xa54ff53a5f1d36f1UL ^ A5; + H5 = 0x510e527fade682d1UL ^ A5; + H6 = 0x9b05688c2b3e6c1fUL ^ A5; + H7 = 0x1f83d9abfb41bd6bUL ^ A5; + H8 = 0x5be0cd19137e2179UL ^ A5; + + Update(0x53); + Update(0x48); + Update(0x41); + Update(0x2D); + Update(0x35); + Update(0x31); + Update(0x32); + Update(0x2F); + + if (bitLength > 100) + { + Update((byte)(bitLength / 100 + 0x30)); + bitLength = bitLength % 100; + Update((byte)(bitLength / 10 + 0x30)); + bitLength = bitLength % 10; + Update((byte)(bitLength + 0x30)); + } + else if (bitLength > 10) + { + Update((byte)(bitLength / 10 + 0x30)); + bitLength = bitLength % 10; + Update((byte)(bitLength + 0x30)); + } + else + { + Update((byte)(bitLength + 0x30)); + } + + Finish(); + + H1t = H1; + H2t = H2; + H3t = H3; + H4t = H4; + H5t = H5; + H6t = H6; + H7t = H7; + H8t = H8; + } + + private static void UInt64_To_BE(ulong n, byte[] bs, int off, int max) + { + if (max > 0) + { + UInt32_To_BE((uint)(n >> 32), bs, off, max); + + if (max > 4) + { + UInt32_To_BE((uint)n, bs, off + 4, max - 4); + } + } + } + + private static void UInt32_To_BE(uint n, byte[] bs, int off, int max) + { + int num = System.Math.Min(4, max); + while (--num >= 0) + { + int shift = 8 * (3 - num); + bs[off + num] = (byte)(n >> shift); + } + } + + public override IMemoable Copy() + { + return new Sha512tDigest(this); + } + + public override void Reset(IMemoable other) + { + Sha512tDigest t = (Sha512tDigest)other; + + if (this.digestLength != t.digestLength) + { + throw new MemoableResetException("digestLength inappropriate in other"); + } + + base.CopyIn(t); + + this.H1t = t.H1t; + this.H2t = t.H2t; + this.H3t = t.H3t; + this.H4t = t.H4t; + this.H5t = t.H5t; + this.H6t = t.H6t; + this.H7t = t.H7t; + this.H8t = t.H8t; + } + + } +} diff --git a/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/SkeinDigest.cs b/crypto/src/crypto/digests/SkeinDigest.cs new file mode 100644
index 000000000..f826ce503 --- /dev/null +++ b/crypto/src/crypto/digests/SkeinDigest.cs
@@ -0,0 +1,117 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Digests +{ + + /// <summary> + /// Implementation of the Skein parameterised hash function in 256, 512 and 1024 bit block sizes, + /// based on the <see cref="Org.BouncyCastle.Crypto.Engines.ThreefishEngine">Threefish</see> tweakable block cipher. + /// </summary> + /// <remarks> + /// This is the 1.3 version of Skein defined in the Skein hash function submission to the NIST SHA-3 + /// competition in October 2010. + /// <p/> + /// Skein was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir + /// Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker. + /// </remarks> + /// <seealso cref="Org.BouncyCastle.Crypto.Digests.SkeinEngine"/> + /// <seealso cref="Org.BouncyCastle.Crypto.Parameters.SkeinParameters"/> + public class SkeinDigest + : IDigest, IMemoable + { + /// <summary> + /// 256 bit block size - Skein-256 + /// </summary> + public const int SKEIN_256 = SkeinEngine.SKEIN_256; + /// <summary> + /// 512 bit block size - Skein-512 + /// </summary> + public const int SKEIN_512 = SkeinEngine.SKEIN_512; + /// <summary> + /// 1024 bit block size - Skein-1024 + /// </summary> + public const int SKEIN_1024 = SkeinEngine.SKEIN_1024; + + private readonly SkeinEngine engine; + + /// <summary> + /// Constructs a Skein digest with an internal state size and output size. + /// </summary> + /// <param name="stateSizeBits">the internal state size in bits - one of <see cref="SKEIN_256"/> <see cref="SKEIN_512"/> or + /// <see cref="SKEIN_1024"/>.</param> + /// <param name="digestSizeBits">the output/digest size to produce in bits, which must be an integral number of + /// bytes.</param> + public SkeinDigest(int stateSizeBits, int digestSizeBits) + { + this.engine = new SkeinEngine(stateSizeBits, digestSizeBits); + Init(null); + } + + public SkeinDigest(SkeinDigest digest) + { + this.engine = new SkeinEngine(digest.engine); + } + + public void Reset(IMemoable other) + { + SkeinDigest d = (SkeinDigest)other; + engine.Reset(d.engine); + } + + public IMemoable Copy() + { + return new SkeinDigest(this); + } + + public String AlgorithmName + { + get { return "Skein-" + (engine.BlockSize * 8) + "-" + (engine.OutputSize * 8); } + } + + public int GetDigestSize() + { + return engine.OutputSize; + } + + public int GetByteLength() + { + return engine.BlockSize; + } + + /// <summary> + /// Optionally initialises the Skein digest with the provided parameters. + /// </summary> + /// See <see cref="Org.BouncyCastle.Crypto.Parameters.SkeinParameters"></see> for details on the parameterisation of the Skein hash function. + /// <param name="parameters">the parameters to apply to this engine, or <code>null</code> to use no parameters.</param> + public void Init(SkeinParameters parameters) + { + engine.Init(parameters); + } + + public void Reset() + { + engine.Reset(); + } + + public void Update(byte inByte) + { + engine.Update(inByte); + } + + public void BlockUpdate(byte[] inBytes, int inOff, int len) + { + engine.Update(inBytes, inOff, len); + } + + public int DoFinal(byte[] outBytes, int outOff) + { + return engine.DoFinal(outBytes, outOff); + } + + } +} \ No newline at end of file diff --git a/crypto/src/crypto/digests/SkeinEngine.cs b/crypto/src/crypto/digests/SkeinEngine.cs new file mode 100644
index 000000000..7e93138ac --- /dev/null +++ b/crypto/src/crypto/digests/SkeinEngine.cs
@@ -0,0 +1,804 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Digests +{ + + /// <summary> + /// Implementation of the Skein family of parameterised hash functions in 256, 512 and 1024 bit block + /// sizes, based on the <see cref="Org.BouncyCastle.Crypto.Engines.ThreefishEngine">Threefish</see> tweakable block cipher. + /// </summary> + /// <remarks> + /// This is the 1.3 version of Skein defined in the Skein hash function submission to the NIST SHA-3 + /// competition in October 2010. + /// <p/> + /// Skein was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir + /// Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker. + /// <p/> + /// This implementation is the basis for <see cref="Org.BouncyCastle.Crypto.Digests.SkeinDigest"/> and <see cref="Org.BouncyCastle.Crypto.Macs.SkeinMac"/>, implementing the + /// parameter based configuration system that allows Skein to be adapted to multiple applications. <br/> + /// Initialising the engine with <see cref="Org.BouncyCastle.Crypto.Parameters.SkeinParameters"/> allows standard and arbitrary parameters to + /// be applied during the Skein hash function. + /// <p/> + /// Implemented: + /// <ul> + /// <li>256, 512 and 1024 bit internal states.</li> + /// <li>Full 96 bit input length.</li> + /// <li>Parameters defined in the Skein specification, and arbitrary other pre and post message + /// parameters.</li> + /// <li>Arbitrary output size in 1 byte intervals.</li> + /// </ul> + /// <p/> + /// Not implemented: + /// <ul> + /// <li>Sub-byte length input (bit padding).</li> + /// <li>Tree hashing.</li> + /// </ul> + /// </remarks> + /// <seealso cref="Org.BouncyCastle.Crypto.Parameters.SkeinParameters"/> + public class SkeinEngine + : IMemoable + { + /// <summary> + /// 256 bit block size - Skein-256 + /// </summary> + public const int SKEIN_256 = ThreefishEngine.BLOCKSIZE_256; + /// <summary> + /// 512 bit block size - Skein-512 + /// </summary> + public const int SKEIN_512 = ThreefishEngine.BLOCKSIZE_512; + /// <summary> + /// 1024 bit block size - Skein-1024 + /// </summary> + public const int SKEIN_1024 = ThreefishEngine.BLOCKSIZE_1024; + + // Minimal at present, but more complex when tree hashing is implemented + private class Configuration + { + private byte[] bytes = new byte[32]; + + public Configuration(long outputSizeBits) + { + // 0..3 = ASCII SHA3 + bytes[0] = (byte)'S'; + bytes[1] = (byte)'H'; + bytes[2] = (byte)'A'; + bytes[3] = (byte)'3'; + + // 4..5 = version number in LSB order + bytes[4] = 1; + bytes[5] = 0; + + // 8..15 = output length + ThreefishEngine.WordToBytes((ulong)outputSizeBits, bytes, 8); + } + + public byte[] Bytes + { + get { return bytes; } + } + + } + + public class Parameter + { + private int type; + private byte[] value; + + public Parameter(int type, byte[] value) + { + this.type = type; + this.value = value; + } + + public int Type + { + get { return type; } + } + + public byte[] Value + { + get { return value; } + } + + } + + /** + * The parameter type for the Skein key. + */ + private const int PARAM_TYPE_KEY = 0; + + /** + * The parameter type for the Skein configuration block. + */ + private const int PARAM_TYPE_CONFIG = 4; + + /** + * The parameter type for the message. + */ + private const int PARAM_TYPE_MESSAGE = 48; + + /** + * The parameter type for the output transformation. + */ + private const int PARAM_TYPE_OUTPUT = 63; + + /** + * Precalculated UBI(CFG) states for common state/output combinations without key or other + * pre-message params. + */ + private static readonly IDictionary INITIAL_STATES = Platform.CreateHashtable(); + + static SkeinEngine() + { + // From Appendix C of the Skein 1.3 NIST submission + InitialState(SKEIN_256, 128, new ulong[]{ + 0xe1111906964d7260UL, + 0x883daaa77c8d811cUL, + 0x10080df491960f7aUL, + 0xccf7dde5b45bc1c2UL}); + + InitialState(SKEIN_256, 160, new ulong[]{ + 0x1420231472825e98UL, + 0x2ac4e9a25a77e590UL, + 0xd47a58568838d63eUL, + 0x2dd2e4968586ab7dUL}); + + InitialState(SKEIN_256, 224, new ulong[]{ + 0xc6098a8c9ae5ea0bUL, + 0x876d568608c5191cUL, + 0x99cb88d7d7f53884UL, + 0x384bddb1aeddb5deUL}); + + InitialState(SKEIN_256, 256, new ulong[]{ + 0xfc9da860d048b449UL, + 0x2fca66479fa7d833UL, + 0xb33bc3896656840fUL, + 0x6a54e920fde8da69UL}); + + InitialState(SKEIN_512, 128, new ulong[]{ + 0xa8bc7bf36fbf9f52UL, + 0x1e9872cebd1af0aaUL, + 0x309b1790b32190d3UL, + 0xbcfbb8543f94805cUL, + 0x0da61bcd6e31b11bUL, + 0x1a18ebead46a32e3UL, + 0xa2cc5b18ce84aa82UL, + 0x6982ab289d46982dUL}); + + InitialState(SKEIN_512, 160, new ulong[]{ + 0x28b81a2ae013bd91UL, + 0xc2f11668b5bdf78fUL, + 0x1760d8f3f6a56f12UL, + 0x4fb747588239904fUL, + 0x21ede07f7eaf5056UL, + 0xd908922e63ed70b8UL, + 0xb8ec76ffeccb52faUL, + 0x01a47bb8a3f27a6eUL}); + + InitialState(SKEIN_512, 224, new ulong[]{ + 0xccd0616248677224UL, + 0xcba65cf3a92339efUL, + 0x8ccd69d652ff4b64UL, + 0x398aed7b3ab890b4UL, + 0x0f59d1b1457d2bd0UL, + 0x6776fe6575d4eb3dUL, + 0x99fbc70e997413e9UL, + 0x9e2cfccfe1c41ef7UL}); + + InitialState(SKEIN_512, 384, new ulong[]{ + 0xa3f6c6bf3a75ef5fUL, + 0xb0fef9ccfd84faa4UL, + 0x9d77dd663d770cfeUL, + 0xd798cbf3b468fddaUL, + 0x1bc4a6668a0e4465UL, + 0x7ed7d434e5807407UL, + 0x548fc1acd4ec44d6UL, + 0x266e17546aa18ff8UL}); + + InitialState(SKEIN_512, 512, new ulong[]{ + 0x4903adff749c51ceUL, + 0x0d95de399746df03UL, + 0x8fd1934127c79bceUL, + 0x9a255629ff352cb1UL, + 0x5db62599df6ca7b0UL, + 0xeabe394ca9d5c3f4UL, + 0x991112c71a75b523UL, + 0xae18a40b660fcc33UL}); + } + + private static void InitialState(int blockSize, int outputSize, ulong[] state) + { + INITIAL_STATES.Add(VariantIdentifier(blockSize / 8, outputSize / 8), state); + } + + private static int VariantIdentifier(int blockSizeBytes, int outputSizeBytes) + { + return (outputSizeBytes << 16) | blockSizeBytes; + } + + private class UbiTweak + { + /** + * Point at which position might overflow long, so switch to add with carry logic + */ + private const ulong LOW_RANGE = UInt64.MaxValue - UInt32.MaxValue; + + /** + * Bit 127 = final + */ + private const ulong T1_FINAL = 1UL << 63; + + /** + * Bit 126 = first + */ + private const ulong T1_FIRST = 1UL << 62; + + /** + * UBI uses a 128 bit tweak + */ + private ulong[] tweak = new ulong[2]; + + /** + * Whether 64 bit position exceeded + */ + private bool extendedPosition; + + public UbiTweak() + { + Reset(); + } + + public void Reset(UbiTweak tweak) + { + this.tweak = Arrays.Clone(tweak.tweak, this.tweak); + this.extendedPosition = tweak.extendedPosition; + } + + public void Reset() + { + tweak[0] = 0; + tweak[1] = 0; + extendedPosition = false; + First = true; + } + + public uint Type + { + get + { + return (uint)((tweak[1] >> 56) & 0x3FUL); + } + + set + { + // Bits 120..125 = type + tweak[1] = (tweak[1] & 0xFFFFFFC000000000UL) | ((value & 0x3FUL) << 56); + } + } + + public bool First + { + get + { + return ((tweak[1] & T1_FIRST) != 0); + } + set + { + if (value) + { + tweak[1] |= T1_FIRST; + } + else + { + tweak[1] &= ~T1_FIRST; + } + } + } + + public bool Final + { + get + { + return ((tweak[1] & T1_FINAL) != 0); + } + set + { + if (value) + { + tweak[1] |= T1_FINAL; + } + else + { + tweak[1] &= ~T1_FINAL; + } + } + } + + /** + * Advances the position in the tweak by the specified value. + */ + public void AdvancePosition(int advance) + { + // Bits 0..95 = position + if (extendedPosition) + { + ulong[] parts = new ulong[3]; + parts[0] = tweak[0] & 0xFFFFFFFFUL; + parts[1] = (tweak[0] >> 32) & 0xFFFFFFFFUL; + parts[2] = tweak[1] & 0xFFFFFFFFUL; + + ulong carry = (ulong)advance; + for (int i = 0; i < parts.Length; i++) + { + carry += parts[i]; + parts[i] = carry; + carry >>= 32; + } + tweak[0] = ((parts[1] & 0xFFFFFFFFUL) << 32) | (parts[0] & 0xFFFFFFFFUL); + tweak[1] = (tweak[1] & 0xFFFFFFFF00000000UL) | (parts[2] & 0xFFFFFFFFUL); + } + else + { + ulong position = tweak[0]; + position += (uint)advance; + tweak[0] = position; + if (position > LOW_RANGE) + { + extendedPosition = true; + } + } + } + + public ulong[] GetWords() + { + return tweak; + } + + public override string ToString() + { + return Type + " first: " + First + ", final: " + Final; + } + + } + + /** + * The Unique Block Iteration chaining mode. + */ + // TODO: This might be better as methods... + private class UBI + { + private readonly UbiTweak tweak = new UbiTweak(); + + private readonly SkeinEngine engine; + + /** + * Buffer for the current block of message data + */ + private byte[] currentBlock; + + /** + * Offset into the current message block + */ + private int currentOffset; + + /** + * Buffer for message words for feedback into encrypted block + */ + private ulong[] message; + + public UBI(SkeinEngine engine, int blockSize) + { + this.engine = engine; + currentBlock = new byte[blockSize]; + message = new ulong[currentBlock.Length / 8]; + } + + public void Reset(UBI ubi) + { + currentBlock = Arrays.Clone(ubi.currentBlock, currentBlock); + currentOffset = ubi.currentOffset; + message = Arrays.Clone(ubi.message, this.message); + tweak.Reset(ubi.tweak); + } + + public void Reset(int type) + { + tweak.Reset(); + tweak.Type = (uint)type; + currentOffset = 0; + } + + public void Update(byte[] value, int offset, int len, ulong[] output) + { + /* + * Buffer complete blocks for the underlying Threefish cipher, only flushing when there + * are subsequent bytes (last block must be processed in doFinal() with final=true set). + */ + int copied = 0; + while (len > copied) + { + if (currentOffset == currentBlock.Length) + { + ProcessBlock(output); + tweak.First = false; + currentOffset = 0; + } + + int toCopy = System.Math.Min((len - copied), currentBlock.Length - currentOffset); + Array.Copy(value, offset + copied, currentBlock, currentOffset, toCopy); + copied += toCopy; + currentOffset += toCopy; + tweak.AdvancePosition(toCopy); + } + } + + private void ProcessBlock(ulong[] output) + { + engine.threefish.Init(true, engine.chain, tweak.GetWords()); + for (int i = 0; i < message.Length; i++) + { + message[i] = ThreefishEngine.BytesToWord(currentBlock, i * 8); + } + + engine.threefish.ProcessBlock(message, output); + + for (int i = 0; i < output.Length; i++) + { + output[i] ^= message[i]; + } + } + + public void DoFinal(ulong[] output) + { + // Pad remainder of current block with zeroes + for (int i = currentOffset; i < currentBlock.Length; i++) + { + currentBlock[i] = 0; + } + + tweak.Final = true; + ProcessBlock(output); + } + + } + + /** + * Underlying Threefish tweakable block cipher + */ + private readonly ThreefishEngine threefish; + + /** + * Size of the digest output, in bytes + */ + private readonly int outputSizeBytes; + + /** + * The current chaining/state value + */ + private ulong[] chain; + + /** + * The initial state value + */ + private ulong[] initialState; + + /** + * The (optional) key parameter + */ + private byte[] key; + + /** + * Parameters to apply prior to the message + */ + private Parameter[] preMessageParameters; + + /** + * Parameters to apply after the message, but prior to output + */ + private Parameter[] postMessageParameters; + + /** + * The current UBI operation + */ + private readonly UBI ubi; + + /** + * Buffer for single byte update method + */ + private readonly byte[] singleByte = new byte[1]; + + /// <summary> + /// Constructs a Skein digest with an internal state size and output size. + /// </summary> + /// <param name="blockSizeBits">the internal state size in bits - one of <see cref="SKEIN_256"/> <see cref="SKEIN_512"/> or + /// <see cref="SKEIN_1024"/>.</param> + /// <param name="outputSizeBits">the output/digest size to produce in bits, which must be an integral number of + /// bytes.</param> + public SkeinEngine(int blockSizeBits, int outputSizeBits) + { + if (outputSizeBits % 8 != 0) + { + throw new ArgumentException("Output size must be a multiple of 8 bits. :" + outputSizeBits); + } + // TODO: Prevent digest sizes > block size? + this.outputSizeBytes = outputSizeBits / 8; + + this.threefish = new ThreefishEngine(blockSizeBits); + this.ubi = new UBI(this,threefish.GetBlockSize()); + } + + /// <summary> + /// Creates a SkeinEngine as an exact copy of an existing instance. + /// </summary> + public SkeinEngine(SkeinEngine engine) + : this(engine.BlockSize * 8, engine.OutputSize * 8) + { + CopyIn(engine); + } + + private void CopyIn(SkeinEngine engine) + { + this.ubi.Reset(engine.ubi); + this.chain = Arrays.Clone(engine.chain, this.chain); + this.initialState = Arrays.Clone(engine.initialState, this.initialState); + this.key = Arrays.Clone(engine.key, this.key); + this.preMessageParameters = Clone(engine.preMessageParameters, this.preMessageParameters); + this.postMessageParameters = Clone(engine.postMessageParameters, this.postMessageParameters); + } + + private static Parameter[] Clone(Parameter[] data, Parameter[] existing) + { + if (data == null) + { + return null; + } + if ((existing == null) || (existing.Length != data.Length)) + { + existing = new Parameter[data.Length]; + } + Array.Copy(data, 0, existing, 0, existing.Length); + return existing; + } + + public IMemoable Copy() + { + return new SkeinEngine(this); + } + + public void Reset(IMemoable other) + { + SkeinEngine s = (SkeinEngine)other; + if ((BlockSize != s.BlockSize) || (outputSizeBytes != s.outputSizeBytes)) + { + throw new MemoableResetException("Incompatible parameters in provided SkeinEngine."); + } + CopyIn(s); + } + + public int OutputSize + { + get { return outputSizeBytes; } + } + + public int BlockSize + { + get { return threefish.GetBlockSize (); } + } + + /// <summary> + /// Initialises the Skein engine with the provided parameters. See <see cref="Org.BouncyCastle.Crypto.Parameters.SkeinParameters"/> for + /// details on the parameterisation of the Skein hash function. + /// </summary> + /// <param name="parameters">the parameters to apply to this engine, or <code>null</code> to use no parameters.</param> + public void Init(SkeinParameters parameters) + { + this.chain = null; + this.key = null; + this.preMessageParameters = null; + this.postMessageParameters = null; + + if (parameters != null) + { + byte[] key = parameters.GetKey(); + if (key.Length < 16) + { + throw new ArgumentException("Skein key must be at least 128 bits."); + } + InitParams(parameters.GetParameters()); + } + CreateInitialState(); + + // Initialise message block + UbiInit(PARAM_TYPE_MESSAGE); + } + + private void InitParams(IDictionary parameters) + { + IEnumerator keys = parameters.Keys.GetEnumerator(); + IList pre = Platform.CreateArrayList(); + IList post = Platform.CreateArrayList(); + + while (keys.MoveNext()) + { + int type = (int)keys.Current; + byte[] value = (byte[])parameters[type]; + + if (type == PARAM_TYPE_KEY) + { + this.key = value; + } + else if (type < PARAM_TYPE_MESSAGE) + { + pre.Add(new Parameter(type, value)); + } + else + { + post.Add(new Parameter(type, value)); + } + } + preMessageParameters = new Parameter[pre.Count]; + pre.CopyTo(preMessageParameters, 0); + Array.Sort(preMessageParameters); + + postMessageParameters = new Parameter[post.Count]; + post.CopyTo(postMessageParameters, 0); + Array.Sort(postMessageParameters); + } + + /** + * Calculate the initial (pre message block) chaining state. + */ + private void CreateInitialState() + { + ulong[] precalc = (ulong[])INITIAL_STATES[VariantIdentifier(BlockSize, OutputSize)]; + if ((key == null) && (precalc != null)) + { + // Precalculated UBI(CFG) + chain = Arrays.Clone(precalc); + } + else + { + // Blank initial state + chain = new ulong[BlockSize / 8]; + + // Process key block + if (key != null) + { + UbiComplete(SkeinParameters.PARAM_TYPE_KEY, key); + } + + // Process configuration block + UbiComplete(PARAM_TYPE_CONFIG, new Configuration(outputSizeBytes * 8).Bytes); + } + + // Process additional pre-message parameters + if (preMessageParameters != null) + { + for (int i = 0; i < preMessageParameters.Length; i++) + { + Parameter param = preMessageParameters[i]; + UbiComplete(param.Type, param.Value); + } + } + initialState = Arrays.Clone(chain); + } + + /// <summary> + /// Reset the engine to the initial state (with the key and any pre-message parameters , ready to + /// accept message input. + /// </summary> + public void Reset() + { + Array.Copy(initialState, 0, chain, 0, chain.Length); + + UbiInit(PARAM_TYPE_MESSAGE); + } + + private void UbiComplete(int type, byte[] value) + { + UbiInit(type); + this.ubi.Update(value, 0, value.Length, chain); + UbiFinal(); + } + + private void UbiInit(int type) + { + this.ubi.Reset(type); + } + + private void UbiFinal() + { + ubi.DoFinal(chain); + } + + private void CheckInitialised() + { + if (this.ubi == null) + { + throw new ArgumentException("Skein engine is not initialised."); + } + } + + public void Update(byte inByte) + { + singleByte[0] = inByte; + Update(singleByte, 0, 1); + } + + public void Update(byte[] inBytes, int inOff, int len) + { + CheckInitialised(); + ubi.Update(inBytes, inOff, len, chain); + } + + public int DoFinal(byte[] outBytes, int outOff) + { + CheckInitialised(); + if (outBytes.Length < (outOff + outputSizeBytes)) + { + throw new DataLengthException("Output buffer is too short to hold output of " + outputSizeBytes + " bytes"); + } + + // Finalise message block + UbiFinal(); + + // Process additional post-message parameters + if (postMessageParameters != null) + { + for (int i = 0; i < postMessageParameters.Length; i++) + { + Parameter param = postMessageParameters[i]; + UbiComplete(param.Type, param.Value); + } + } + + // Perform the output transform + int blockSize = BlockSize; + int blocksRequired = ((outputSizeBytes + blockSize - 1) / blockSize); + for (int i = 0; i < blocksRequired; i++) + { + int toWrite = System.Math.Min(blockSize, outputSizeBytes - (i * blockSize)); + Output((ulong)i, outBytes, outOff + (i * blockSize), toWrite); + } + + Reset(); + + return outputSizeBytes; + } + + private void Output(ulong outputSequence, byte[] outBytes, int outOff, int outputBytes) + { + byte[] currentBytes = new byte[8]; + ThreefishEngine.WordToBytes(outputSequence, currentBytes, 0); + + // Output is a sequence of UBI invocations all of which use and preserve the pre-output + // state + ulong[] outputWords = new ulong[chain.Length]; + UbiInit(PARAM_TYPE_OUTPUT); + this.ubi.Update(currentBytes, 0, currentBytes.Length, outputWords); + ubi.DoFinal(outputWords); + + int wordsRequired = ((outputBytes + 8 - 1) / 8); + for (int i = 0; i < wordsRequired; i++) + { + int toWrite = System.Math.Min(8, outputBytes - (i * 8)); + if (toWrite == 8) + { + ThreefishEngine.WordToBytes(outputWords[i], outBytes, outOff + (i * 8)); + } + else + { + ThreefishEngine.WordToBytes(outputWords[i], currentBytes, 0); + Array.Copy(currentBytes, 0, outBytes, outOff + (i * 8), toWrite); + } + } + } + + } +} + diff --git a/crypto/src/crypto/digests/TigerDigest.cs b/crypto/src/crypto/digests/TigerDigest.cs
index b8c9a7664..059232de0 100644 --- a/crypto/src/crypto/digests/TigerDigest.cs +++ b/crypto/src/crypto/digests/TigerDigest.cs
@@ -1,5 +1,7 @@ using System; + using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests { @@ -9,7 +11,7 @@ namespace Org.BouncyCastle.Crypto.Digests * http://www.cs.technion.ac.il/~biham/Reports/Tiger</a> */ public class TigerDigest - : IDigest + : IDigest, IMemoable { private const int MyByteLength = 64; @@ -571,17 +573,7 @@ namespace Org.BouncyCastle.Crypto.Digests */ 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; + Reset(t); } public string AlgorithmName @@ -864,5 +856,28 @@ namespace Org.BouncyCastle.Crypto.Digests byteCount = 0; } - } + + public IMemoable Copy() + { + return new TigerDigest(this); + } + + public void Reset(IMemoable other) + { + TigerDigest t = (TigerDigest)other; + + 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; + } + + } } diff --git a/crypto/src/crypto/digests/WhirlpoolDigest.cs b/crypto/src/crypto/digests/WhirlpoolDigest.cs
index df83f4508..55b71205e 100644 --- a/crypto/src/crypto/digests/WhirlpoolDigest.cs +++ b/crypto/src/crypto/digests/WhirlpoolDigest.cs
@@ -1,6 +1,7 @@ using System; using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Digests { @@ -9,7 +10,8 @@ namespace Org.BouncyCastle.Crypto.Digests * and Rijmen. * */ - public sealed class WhirlpoolDigest : IDigest + public sealed class WhirlpoolDigest + : IDigest, IMemoable { private const int BYTE_LENGTH = 64; @@ -149,19 +151,7 @@ namespace Org.BouncyCastle.Crypto.Digests */ 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); + Reset(originalDigest); } public string AlgorithmName @@ -393,5 +383,31 @@ namespace Org.BouncyCastle.Crypto.Digests { return BYTE_LENGTH; } + + public IMemoable Copy() + { + return new WhirlpoolDigest(this); + } + + public void Reset(IMemoable other) + { + WhirlpoolDigest originalDigest = (WhirlpoolDigest)other; + + 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); + } + + } } diff --git a/crypto/src/crypto/ec/CustomNamedCurves.cs b/crypto/src/crypto/ec/CustomNamedCurves.cs new file mode 100644
index 000000000..8ff1d24c7 --- /dev/null +++ b/crypto/src/crypto/ec/CustomNamedCurves.cs
@@ -0,0 +1,366 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Sec; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Math.EC.Custom.Djb; +using Org.BouncyCastle.Math.EC.Custom.Sec; +using Org.BouncyCastle.Math.EC.Endo; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Crypto.EC +{ + public sealed class CustomNamedCurves + { + private CustomNamedCurves() + { + } + + private static BigInteger FromHex(string hex) + { + return new BigInteger(1, Hex.Decode(hex)); + } + + private static ECCurve ConfigureCurve(ECCurve curve) + { + return curve; + } + + private static ECCurve ConfigureCurveGlv(ECCurve c, GlvTypeBParameters p) + { + return c.Configure().SetEndomorphism(new GlvTypeBEndomorphism(c, p)).Create(); + } + + /* + * curve25519 + */ + internal class Curve25519Holder + : X9ECParametersHolder + { + private Curve25519Holder() { } + + internal static readonly X9ECParametersHolder Instance = new Curve25519Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = null; + ECCurve curve = ConfigureCurve(new Curve25519()); + + /* + * NOTE: Curve25519 was specified in Montgomery form. Rewriting in Weierstrass form + * involves substitution of variables, so the base-point x coordinate is 9 + (486662 / 3). + * + * The Curve25519 paper doesn't say which of the two possible y values the base + * point has. The choice here is guided by language in the Ed25519 paper. + * + * (The other possible y value is 5F51E65E475F794B1FE122D388B72EB36DC2B28192839E4DD6163A5D81312C14) + */ + ECPoint G = curve.DecodePoint(Hex.Decode("04" + + "2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD245A" + + "20AE19A1B8A086B4E01EDD2C7748D14C923D4D7E6D7C61B229E9C5A27ECED3D9")); + + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + } + + /* + * secp192k1 + */ + internal class Secp192k1Holder + : X9ECParametersHolder + { + private Secp192k1Holder() { } + + internal static readonly X9ECParametersHolder Instance = new Secp192k1Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = null; + GlvTypeBParameters glv = new GlvTypeBParameters( + new BigInteger("bb85691939b869c1d087f601554b96b80cb4f55b35f433c2", 16), + new BigInteger("3d84f26c12238d7b4f3d516613c1759033b1a5800175d0b1", 16), + new BigInteger[]{ + new BigInteger("71169be7330b3038edb025f1", 16), + new BigInteger("-b3fb3400dec5c4adceb8655c", 16) }, + new BigInteger[]{ + new BigInteger("12511cfe811d0f4e6bc688b4d", 16), + new BigInteger("71169be7330b3038edb025f1", 16) }, + new BigInteger("71169be7330b3038edb025f1d0f9", 16), + new BigInteger("b3fb3400dec5c4adceb8655d4c94", 16), + 208); + ECCurve curve = ConfigureCurveGlv(new SecP192K1Curve(), glv); + ECPoint G = curve.DecodePoint(Hex.Decode("04" + + "DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D" + + "9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + } + + /* + * secp192r1 + */ + internal class Secp192r1Holder + : X9ECParametersHolder + { + private Secp192r1Holder() { } + + internal static readonly X9ECParametersHolder Instance = new Secp192r1Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = Hex.Decode("3045AE6FC8422F64ED579528D38120EAE12196D5"); + ECCurve curve = ConfigureCurve(new SecP192R1Curve()); + ECPoint G = curve.DecodePoint(Hex.Decode("04" + + "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012" + + "07192B95FFC8DA78631011ED6B24CDD573F977A11E794811")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + } + + /* + * secp224k1 + */ + internal class Secp224k1Holder + : X9ECParametersHolder + { + private Secp224k1Holder() { } + + internal static readonly X9ECParametersHolder Instance = new Secp224k1Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = null; + GlvTypeBParameters glv = new GlvTypeBParameters( + new BigInteger("fe0e87005b4e83761908c5131d552a850b3f58b749c37cf5b84d6768", 16), + new BigInteger("60dcd2104c4cbc0be6eeefc2bdd610739ec34e317f9b33046c9e4788", 16), + new BigInteger[]{ + new BigInteger("6b8cf07d4ca75c88957d9d670591", 16), + new BigInteger("-b8adf1378a6eb73409fa6c9c637d", 16) }, + new BigInteger[]{ + new BigInteger("1243ae1b4d71613bc9f780a03690e", 16), + new BigInteger("6b8cf07d4ca75c88957d9d670591", 16) }, + new BigInteger("6b8cf07d4ca75c88957d9d67059037a4", 16), + new BigInteger("b8adf1378a6eb73409fa6c9c637ba7f5", 16), + 240); + ECCurve curve = ConfigureCurveGlv(new SecP224K1Curve(), glv); + ECPoint G = curve.DecodePoint(Hex.Decode("04" + + "A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C" + + "7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + } + + /* + * secp224r1 + */ + internal class Secp224r1Holder + : X9ECParametersHolder + { + private Secp224r1Holder() { } + + internal static readonly X9ECParametersHolder Instance = new Secp224r1Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = Hex.Decode("BD71344799D5C7FCDC45B59FA3B9AB8F6A948BC5"); + ECCurve curve = ConfigureCurve(new SecP224R1Curve()); + ECPoint G = curve.DecodePoint(Hex.Decode("04" + + "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21" + + "BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + } + + /* + * secp256k1 + */ + internal class Secp256k1Holder + : X9ECParametersHolder + { + private Secp256k1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Secp256k1Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = null; + GlvTypeBParameters glv = new GlvTypeBParameters( + new BigInteger("7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee", 16), + new BigInteger("5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72", 16), + new BigInteger[]{ + new BigInteger("3086d221a7d46bcde86c90e49284eb15", 16), + new BigInteger("-e4437ed6010e88286f547fa90abfe4c3", 16) }, + new BigInteger[]{ + new BigInteger("114ca50f7a8e2f3f657c1108d9d44cfd8", 16), + new BigInteger("3086d221a7d46bcde86c90e49284eb15", 16) }, + new BigInteger("3086d221a7d46bcde86c90e49284eb153dab", 16), + new BigInteger("e4437ed6010e88286f547fa90abfe4c42212", 16), + 272); + ECCurve curve = ConfigureCurveGlv(new SecP256K1Curve(), glv); + ECPoint G = curve.DecodePoint(Hex.Decode("04" + + "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798" + + "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + } + + /* + * secp256r1 + */ + internal class Secp256r1Holder + : X9ECParametersHolder + { + private Secp256r1Holder() {} + + internal static readonly X9ECParametersHolder Instance = new Secp256r1Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = Hex.Decode("C49D360886E704936A6678E1139D26B7819F7E90"); + ECCurve curve = ConfigureCurve(new SecP256R1Curve()); + ECPoint G = curve.DecodePoint(Hex.Decode("04" + + "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296" + + "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + } + + /* + * secp384r1 + */ + internal class Secp384r1Holder + : X9ECParametersHolder + { + private Secp384r1Holder() { } + + internal static readonly X9ECParametersHolder Instance = new Secp384r1Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = Hex.Decode("A335926AA319A27A1D00896A6773A4827ACDAC73"); + ECCurve curve = ConfigureCurve(new SecP384R1Curve()); + ECPoint G = curve.DecodePoint(Hex.Decode("04" + + "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7" + + "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + } + + /* + * secp521r1 + */ + internal class Secp521r1Holder + : X9ECParametersHolder + { + private Secp521r1Holder() { } + + internal static readonly X9ECParametersHolder Instance = new Secp521r1Holder(); + + protected override X9ECParameters CreateParameters() + { + byte[] S = Hex.Decode("D09E8800291CB85396CC6717393284AAA0DA64BA"); + ECCurve curve = ConfigureCurve(new SecP521R1Curve()); + ECPoint G = curve.DecodePoint(Hex.Decode("04" + + "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66" + + "011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650")); + return new X9ECParameters(curve, G, curve.Order, curve.Cofactor, S); + } + } + + private static readonly IDictionary nameToCurve = Platform.CreateHashtable(); + private static readonly IDictionary nameToOid = Platform.CreateHashtable(); + private static readonly IDictionary oidToCurve = Platform.CreateHashtable(); + private static readonly IDictionary oidToName = Platform.CreateHashtable(); + + private static void DefineCurve(string name, X9ECParametersHolder holder) + { + nameToCurve.Add(name, holder); + } + + private static void DefineCurve(string name, DerObjectIdentifier oid, X9ECParametersHolder holder) + { + nameToCurve.Add(name, holder); + nameToOid.Add(name, oid); + oidToName.Add(oid, name); + oidToCurve.Add(oid, holder); + } + + private static void DefineCurveAlias(string alias, DerObjectIdentifier oid) + { + alias = Platform.ToLowerInvariant(alias); + nameToOid.Add(alias, oid); + nameToCurve.Add(alias, oidToCurve[oid]); + } + + static CustomNamedCurves() + { + DefineCurve("curve25519", Curve25519Holder.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); + + DefineCurveAlias("P-192", SecObjectIdentifiers.SecP192r1); + DefineCurveAlias("P-224", SecObjectIdentifiers.SecP224r1); + DefineCurveAlias("P-256", SecObjectIdentifiers.SecP256r1); + DefineCurveAlias("P-384", SecObjectIdentifiers.SecP384r1); + DefineCurveAlias("P-521", SecObjectIdentifiers.SecP521r1); + } + + public static X9ECParameters GetByName(string name) + { + X9ECParametersHolder holder = (X9ECParametersHolder)nameToCurve[Platform.ToLowerInvariant(name)]; + return holder == null ? null : holder.Parameters; + } + + /** + * 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)oidToCurve[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)nameToOid[Platform.ToLowerInvariant(name)]; + } + + /** + * return the named curve name represented by the given object identifier. + */ + public static string GetName(DerObjectIdentifier oid) + { + return (string)oidToName[oid]; + } + + /** + * returns an enumeration containing the name strings for curves + * contained in this structure. + */ + public static IEnumerable Names + { + get { return new EnumerableProxy(nameToCurve.Keys); } + } + } +} 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
index 81561e7f5..a4d2f0e36 100644 --- a/crypto/src/crypto/encodings/OaepEncoding.cs +++ b/crypto/src/crypto/encodings/OaepEncoding.cs
@@ -6,340 +6,344 @@ 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; - } - } + /** + * 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. + // long check to try to avoid this been a source of a timing attack. + // + { + int diff = 0; + for (int i = 0; i < defHash.Length; ++i) + { + diff |= (byte)(defHash[i] ^ block[defHash.Length + i]); + } + + if (diff != 0) + 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
index d2225a7d4..35fd96abe 100644 --- a/crypto/src/crypto/encodings/Pkcs1Encoding.cs +++ b/crypto/src/crypto/encodings/Pkcs1Encoding.cs
@@ -7,226 +7,376 @@ 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; - } - } + /** + * 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; + private int pLen = -1; + private byte[] fallback = null; + + /** + * Basic constructor. + * @param cipher + */ + public Pkcs1Encoding( + IAsymmetricBlockCipher cipher) + { + this.engine = cipher; + this.useStrictLength = StrictLengthEnabled; + } + + /** + * Constructor for decryption with a fixed plaintext length. + * + * @param cipher The cipher to use for cryptographic operation. + * @param pLen Length of the expected plaintext. + */ + public Pkcs1Encoding(IAsymmetricBlockCipher cipher, int pLen) + { + this.engine = cipher; + this.useStrictLength = StrictLengthEnabled; + this.pLen = pLen; + } + + /** + * Constructor for decryption with a fixed plaintext length and a fallback + * value that is returned, if the padding is incorrect. + * + * @param cipher + * The cipher to use for cryptographic operation. + * @param fallback + * The fallback value, we don't to a arraycopy here. + */ + public Pkcs1Encoding(IAsymmetricBlockCipher cipher, byte[] fallback) + { + this.engine = cipher; + this.useStrictLength = StrictLengthEnabled; + this.fallback = fallback; + this.pLen = fallback.Length; + } + + 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); + } + + /** + * Checks if the argument is a correctly PKCS#1.5 encoded Plaintext + * for encryption. + * + * @param encoded The Plaintext. + * @param pLen Expected length of the plaintext. + * @return Either 0, if the encoding is correct, or -1, if it is incorrect. + */ + private static int CheckPkcs1Encoding(byte[] encoded, int pLen) + { + int correct = 0; + /* + * Check if the first two bytes are 0 2 + */ + correct |= (encoded[0] ^ 2); + + /* + * Now the padding check, check for no 0 byte in the padding + */ + int plen = encoded.Length - ( + pLen /* Lenght of the PMS */ + + 1 /* Final 0-byte before PMS */ + ); + + for (int i = 1; i < plen; i++) + { + int tmp = encoded[i]; + tmp |= tmp >> 1; + tmp |= tmp >> 2; + tmp |= tmp >> 4; + correct |= (tmp & 1) - 1; + } + + /* + * Make sure the padding ends with a 0 byte. + */ + correct |= encoded[encoded.Length - (pLen + 1)]; + + /* + * Return 0 or 1, depending on the result. + */ + correct |= correct >> 1; + correct |= correct >> 2; + correct |= correct >> 4; + return ~((correct & 1) - 1); + } + + /** + * Decode PKCS#1.5 encoding, and return a random value if the padding is not correct. + * + * @param in The encrypted block. + * @param inOff Offset in the encrypted block. + * @param inLen Length of the encrypted block. + * @param pLen Length of the desired output. + * @return The plaintext without padding, or a random value if the padding was incorrect. + * + * @throws InvalidCipherTextException + */ + private byte[] DecodeBlockOrRandom(byte[] input, int inOff, int inLen) + { + if (!forPrivateKey) + throw new InvalidCipherTextException("sorry, this method is only for decryption, not for signing"); + + byte[] block = engine.ProcessBlock(input, inOff, inLen); + byte[] random = null; + if (this.fallback == null) + { + random = new byte[this.pLen]; + this.random.NextBytes(random); + } + else + { + random = fallback; + } + + /* + * TODO: This is a potential dangerous side channel. However, you can + * fix this by changing the RSA engine in a way, that it will always + * return blocks of the same length and prepend them with 0 bytes if + * needed. + */ + if (block.Length < GetOutputBlockSize()) + throw new InvalidCipherTextException("block truncated"); + + /* + * TODO: Potential side channel. Fix it by making the engine always + * return blocks of the correct length. + */ + if (useStrictLength && block.Length != engine.GetOutputBlockSize()) + throw new InvalidCipherTextException("block incorrect size"); + + /* + * Check the padding. + */ + int correct = Pkcs1Encoding.CheckPkcs1Encoding(block, this.pLen); + + /* + * Now, to a constant time constant memory copy of the decrypted value + * or the random value, depending on the validity of the padding. + */ + byte[] result = new byte[this.pLen]; + for (int i = 0; i < this.pLen; i++) + { + result[i] = (byte)((block[i+(block.Length-pLen)]&(~correct)) | (random[i]&correct)); + } + + return result; + } + + /** + * @exception InvalidCipherTextException if the decrypted block is not in Pkcs1 format. + */ + private byte[] DecodeBlock( + byte[] input, + int inOff, + int inLen) + { + /* + * If the length of the expected plaintext is known, we use a constant-time decryption. + * If the decryption fails, we return a random value. + */ + if (this.pLen != -1) + { + return this.DecodeBlockOrRandom(input, inOff, 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
index 4211a9559..0cdd868fa 100644 --- a/crypto/src/crypto/engines/AesEngine.cs +++ b/crypto/src/crypto/engines/AesEngine.cs
@@ -1,525 +1,532 @@ using System; +using System.Diagnostics; 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]; - } - } + /** + * 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 static 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 static 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 static 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 static 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 words in a block + for (int i = 0; i <= ROUNDS; ++i) + { + W[i] = new uint[4]; + } + + // + // 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++) + { + uint[] w = W[j]; + for (int i = 0; i < 4; i++) + { + w[i] = Inv_Mcol(w[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[] kw = KW[0]; + uint t0 = this.C0 ^ kw[0]; + uint t1 = this.C1 ^ kw[1]; + uint t2 = this.C2 ^ kw[2]; + + uint r0, r1, r2, r3 = this.C3 ^ kw[3]; + int r = 1; + while (r < ROUNDS - 1) + { + kw = KW[r++]; + r0 = T0[t0 & 255] ^ Shift(T0[(t1 >> 8) & 255], 24) ^ Shift(T0[(t2 >> 16) & 255], 16) ^ Shift(T0[(r3 >> 24) & 255], 8) ^ kw[0]; + r1 = T0[t1 & 255] ^ Shift(T0[(t2 >> 8) & 255], 24) ^ Shift(T0[(r3 >> 16) & 255], 16) ^ Shift(T0[(t0 >> 24) & 255], 8) ^ kw[1]; + r2 = T0[t2 & 255] ^ Shift(T0[(r3 >> 8) & 255], 24) ^ Shift(T0[(t0 >> 16) & 255], 16) ^ Shift(T0[(t1 >> 24) & 255], 8) ^ kw[2]; + r3 = T0[r3 & 255] ^ Shift(T0[(t0 >> 8) & 255], 24) ^ Shift(T0[(t1 >> 16) & 255], 16) ^ Shift(T0[(t2 >> 24) & 255], 8) ^ kw[3]; + kw = KW[r++]; + t0 = T0[r0 & 255] ^ Shift(T0[(r1 >> 8) & 255], 24) ^ Shift(T0[(r2 >> 16) & 255], 16) ^ Shift(T0[(r3 >> 24) & 255], 8) ^ kw[0]; + t1 = T0[r1 & 255] ^ Shift(T0[(r2 >> 8) & 255], 24) ^ Shift(T0[(r3 >> 16) & 255], 16) ^ Shift(T0[(r0 >> 24) & 255], 8) ^ kw[1]; + t2 = T0[r2 & 255] ^ Shift(T0[(r3 >> 8) & 255], 24) ^ Shift(T0[(r0 >> 16) & 255], 16) ^ Shift(T0[(r1 >> 24) & 255], 8) ^ kw[2]; + r3 = T0[r3 & 255] ^ Shift(T0[(r0 >> 8) & 255], 24) ^ Shift(T0[(r1 >> 16) & 255], 16) ^ Shift(T0[(r2 >> 24) & 255], 8) ^ kw[3]; + } + + kw = KW[r++]; + r0 = T0[t0 & 255] ^ Shift(T0[(t1 >> 8) & 255], 24) ^ Shift(T0[(t2 >> 16) & 255], 16) ^ Shift(T0[(r3 >> 24) & 255], 8) ^ kw[0]; + r1 = T0[t1 & 255] ^ Shift(T0[(t2 >> 8) & 255], 24) ^ Shift(T0[(r3 >> 16) & 255], 16) ^ Shift(T0[(t0 >> 24) & 255], 8) ^ kw[1]; + r2 = T0[t2 & 255] ^ Shift(T0[(r3 >> 8) & 255], 24) ^ Shift(T0[(t0 >> 16) & 255], 16) ^ Shift(T0[(t1 >> 24) & 255], 8) ^ kw[2]; + r3 = T0[r3 & 255] ^ Shift(T0[(t0 >> 8) & 255], 24) ^ Shift(T0[(t1 >> 16) & 255], 16) ^ Shift(T0[(t2 >> 24) & 255], 8) ^ kw[3]; + + // the final round's table is a simple function of S so we don't use a whole other four tables for it + + kw = KW[r]; + this.C0 = (uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)S[(r2 >> 16) & 255]) << 16) ^ (((uint)S[(r3 >> 24) & 255]) << 24) ^ kw[0]; + this.C1 = (uint)S[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[(r0 >> 24) & 255]) << 24) ^ kw[1]; + this.C2 = (uint)S[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[(r1 >> 24) & 255]) << 24) ^ kw[2]; + this.C3 = (uint)S[r3 & 255] ^ (((uint)S[(r0 >> 8) & 255]) << 8) ^ (((uint)S[(r1 >> 16) & 255]) << 16) ^ (((uint)S[(r2 >> 24) & 255]) << 24) ^ kw[3]; + } + + private void DecryptBlock(uint[][] KW) + { + uint[] kw = KW[ROUNDS]; + uint t0 = this.C0 ^ kw[0]; + uint t1 = this.C1 ^ kw[1]; + uint t2 = this.C2 ^ kw[2]; + + uint r0, r1, r2, r3 = this.C3 ^ kw[3]; + int r = ROUNDS - 1; + while (r > 1) + { + kw = KW[r--]; + r0 = Tinv0[t0 & 255] ^ Shift(Tinv0[(r3 >> 8) & 255], 24) ^ Shift(Tinv0[(t2 >> 16) & 255], 16) ^ Shift(Tinv0[(t1 >> 24) & 255], 8) ^ kw[0]; + r1 = Tinv0[t1 & 255] ^ Shift(Tinv0[(t0 >> 8) & 255], 24) ^ Shift(Tinv0[(r3 >> 16) & 255], 16) ^ Shift(Tinv0[(t2 >> 24) & 255], 8) ^ kw[1]; + r2 = Tinv0[t2 & 255] ^ Shift(Tinv0[(t1 >> 8) & 255], 24) ^ Shift(Tinv0[(t0 >> 16) & 255], 16) ^ Shift(Tinv0[(r3 >> 24) & 255], 8) ^ kw[2]; + r3 = Tinv0[r3 & 255] ^ Shift(Tinv0[(t2 >> 8) & 255], 24) ^ Shift(Tinv0[(t1 >> 16) & 255], 16) ^ Shift(Tinv0[(t0 >> 24) & 255], 8) ^ kw[3]; + kw = KW[r--]; + t0 = Tinv0[r0 & 255] ^ Shift(Tinv0[(r3 >> 8) & 255], 24) ^ Shift(Tinv0[(r2 >> 16) & 255], 16) ^ Shift(Tinv0[(r1 >> 24) & 255], 8) ^ kw[0]; + t1 = Tinv0[r1 & 255] ^ Shift(Tinv0[(r0 >> 8) & 255], 24) ^ Shift(Tinv0[(r3 >> 16) & 255], 16) ^ Shift(Tinv0[(r2 >> 24) & 255], 8) ^ kw[1]; + t2 = Tinv0[r2 & 255] ^ Shift(Tinv0[(r1 >> 8) & 255], 24) ^ Shift(Tinv0[(r0 >> 16) & 255], 16) ^ Shift(Tinv0[(r3 >> 24) & 255], 8) ^ kw[2]; + r3 = Tinv0[r3 & 255] ^ Shift(Tinv0[(r2 >> 8) & 255], 24) ^ Shift(Tinv0[(r1 >> 16) & 255], 16) ^ Shift(Tinv0[(r0 >> 24) & 255], 8) ^ kw[3]; + } + + kw = KW[1]; + r0 = Tinv0[t0 & 255] ^ Shift(Tinv0[(r3 >> 8) & 255], 24) ^ Shift(Tinv0[(t2 >> 16) & 255], 16) ^ Shift(Tinv0[(t1 >> 24) & 255], 8) ^ kw[0]; + r1 = Tinv0[t1 & 255] ^ Shift(Tinv0[(t0 >> 8) & 255], 24) ^ Shift(Tinv0[(r3 >> 16) & 255], 16) ^ Shift(Tinv0[(t2 >> 24) & 255], 8) ^ kw[1]; + r2 = Tinv0[t2 & 255] ^ Shift(Tinv0[(t1 >> 8) & 255], 24) ^ Shift(Tinv0[(t0 >> 16) & 255], 16) ^ Shift(Tinv0[(r3 >> 24) & 255], 8) ^ kw[2]; + r3 = Tinv0[r3 & 255] ^ Shift(Tinv0[(t2 >> 8) & 255], 24) ^ Shift(Tinv0[(t1 >> 16) & 255], 16) ^ Shift(Tinv0[(t0 >> 24) & 255], 8) ^ kw[3]; + + // the final round's table is a simple function of Si so we don't use a whole other four tables for it + + kw = KW[0]; + this.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]; + this.C1 = (uint)Si[r1 & 255] ^ (((uint)Si[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ (((uint)Si[(r2 >> 24) & 255]) << 24) ^ kw[1]; + this.C2 = (uint)Si[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ (((uint)Si[(r3 >> 24) & 255]) << 24) ^ kw[2]; + this.C3 = (uint)Si[r3 & 255] ^ (((uint)Si[(r2 >> 8) & 255]) << 8) ^ (((uint)Si[(r1 >> 16) & 255]) << 16) ^ (((uint)Si[(r0 >> 24) & 255]) << 24) ^ kw[3]; + } + } } diff --git a/crypto/src/crypto/engines/AesFastEngine.cs b/crypto/src/crypto/engines/AesFastEngine.cs
index 603b5ce4d..38d3a5841 100644 --- a/crypto/src/crypto/engines/AesFastEngine.cs +++ b/crypto/src/crypto/engines/AesFastEngine.cs
@@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Utilities; @@ -30,11 +31,11 @@ namespace Org.BouncyCastle.Crypto.Engines * </p> */ public class AesFastEngine - : IBlockCipher + : 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, @@ -71,503 +72,501 @@ namespace Org.BouncyCastle.Crypto.Engines // 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)) + { + 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)); - } + { + 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 static uint Shift(uint r, int shift) + { + return (r >> shift) | (r << (32 - shift)); + } /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */ @@ -575,10 +574,9 @@ namespace Org.BouncyCastle.Crypto.Engines private const uint m2 = 0x7f7f7f7f; private const uint m3 = 0x0000001b; - private uint FFmulX( - uint x) - { - return ((x & m2) << 1) ^ (((x & m1) >> 7) * m3); + private static uint FFmulX(uint x) + { + return ((x & m2) << 1) ^ (((x & m1) >> 7) * m3); } /* @@ -591,8 +589,8 @@ namespace Org.BouncyCastle.Crypto.Engines */ - private uint Inv_Mcol( - uint x) { + private static uint Inv_Mcol(uint x) + { uint f2 = FFmulX(x); uint f4 = FFmulX(f2); uint f8 = FFmulX(f4); @@ -601,13 +599,12 @@ namespace Org.BouncyCastle.Crypto.Engines 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); + private static 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); } /** @@ -616,7 +613,7 @@ namespace Org.BouncyCastle.Crypto.Engines * 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( + private uint[][] GenerateWorkingKey( byte[] key, bool forEncryption) { @@ -625,8 +622,13 @@ namespace Org.BouncyCastle.Crypto.Engines 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 + ROUNDS = KC + 6; // This is not always true for the generalized Rijndael that allows larger block sizes + + uint[][] W = new uint[ROUNDS + 1][]; // 4 words in a block + for (int i = 0; i <= ROUNDS; ++i) + { + W[i] = new uint[4]; + } // // copy the key into the round key array @@ -634,10 +636,10 @@ namespace Org.BouncyCastle.Crypto.Engines int t = 0; for (int i = 0; i < key.Length; t++) - { - W[t >> 2,t & 3] = Pack.LE_To_UInt32(key, i); - i+=4; - } + { + W[t >> 2][t & 3] = Pack.LE_To_UInt32(key, i); + i+=4; + } // // while not enough round key material calculated @@ -646,23 +648,24 @@ namespace Org.BouncyCastle.Crypto.Engines int k = (ROUNDS + 1) << 2; for (int i = KC; (i < k); i++) { - uint temp = W[(i-1)>>2,(i-1)&3]; + 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; + W[i>>2][i&3] = W[(i - KC)>>2][(i-KC)&3] ^ temp; } if (!forEncryption) - { + { for (int j = 1; j < ROUNDS; j++) - { + { + uint[] w = W[j]; for (int i = 0; i < 4; i++) - { - W[j,i] = Inv_Mcol(W[j,i]); + { + w[i] = Inv_Mcol(w[i]); } } } @@ -670,10 +673,10 @@ namespace Org.BouncyCastle.Crypto.Engines return W; } - private int ROUNDS; - private uint[,] WorkingKey; - private uint C0, C1, C2, C3; - private bool forEncryption; + private int ROUNDS; + private uint[][] WorkingKey; + private uint C0, C1, C2, C3; + private bool forEncryption; private const int BLOCK_SIZE = 16; @@ -696,24 +699,27 @@ namespace Org.BouncyCastle.Crypto.Engines bool forEncryption, ICipherParameters parameters) { - if (!(parameters is KeyParameter)) - throw new ArgumentException("invalid parameter passed to AES init - " + parameters.GetType().ToString()); + KeyParameter keyParameter = parameters as KeyParameter; - WorkingKey = GenerateWorkingKey(((KeyParameter)parameters).GetKey(), forEncryption); - this.forEncryption = forEncryption; + 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 + public string AlgorithmName { get { return "AES"; } } - public bool IsPartialBlockOkay - { - get { return false; } - } + public bool IsPartialBlockOkay + { + get { return false; } + } - public int GetBlockSize() + public int GetBlockSize() { return BLOCK_SIZE; } @@ -752,7 +758,7 @@ namespace Org.BouncyCastle.Crypto.Engines PackBlock(output, outOff); - return BLOCK_SIZE; + return BLOCK_SIZE; } public void Reset() @@ -763,91 +769,96 @@ namespace Org.BouncyCastle.Crypto.Engines 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); + 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( + 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); + 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) + 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]; + uint[] kw = KW[0]; + uint t0 = this.C0 ^ kw[0]; + uint t1 = this.C1 ^ kw[1]; + uint t2 = this.C2 ^ kw[2]; + + uint r0, r1, r2, r3 = this.C3 ^ kw[3]; + int r = 1; + while (r < ROUNDS - 1) + { + kw = KW[r++]; + r0 = T0[t0 & 255] ^ T1[(t1 >> 8) & 255] ^ T2[(t2 >> 16) & 255] ^ T3[r3 >> 24] ^ kw[0]; + r1 = T0[t1 & 255] ^ T1[(t2 >> 8) & 255] ^ T2[(r3 >> 16) & 255] ^ T3[t0 >> 24] ^ kw[1]; + r2 = T0[t2 & 255] ^ T1[(r3 >> 8) & 255] ^ T2[(t0 >> 16) & 255] ^ T3[t1 >> 24] ^ kw[2]; + r3 = T0[r3 & 255] ^ T1[(t0 >> 8) & 255] ^ T2[(t1 >> 16) & 255] ^ T3[t2 >> 24] ^ kw[3]; + kw = KW[r++]; + t0 = T0[r0 & 255] ^ T1[(r1 >> 8) & 255] ^ T2[(r2 >> 16) & 255] ^ T3[r3 >> 24] ^ kw[0]; + t1 = T0[r1 & 255] ^ T1[(r2 >> 8) & 255] ^ T2[(r3 >> 16) & 255] ^ T3[r0 >> 24] ^ kw[1]; + t2 = T0[r2 & 255] ^ T1[(r3 >> 8) & 255] ^ T2[(r0 >> 16) & 255] ^ T3[r1 >> 24] ^ kw[2]; + r3 = T0[r3 & 255] ^ T1[(r0 >> 8) & 255] ^ T2[(r1 >> 16) & 255] ^ T3[r2 >> 24] ^ kw[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]; + kw = KW[r++]; + r0 = T0[t0 & 255] ^ T1[(t1 >> 8) & 255] ^ T2[(t2 >> 16) & 255] ^ T3[r3 >> 24] ^ kw[0]; + r1 = T0[t1 & 255] ^ T1[(t2 >> 8) & 255] ^ T2[(r3 >> 16) & 255] ^ T3[t0 >> 24] ^ kw[1]; + r2 = T0[t2 & 255] ^ T1[(r3 >> 8) & 255] ^ T2[(t0 >> 16) & 255] ^ T3[t1 >> 24] ^ kw[2]; + r3 = T0[r3 & 255] ^ T1[(t0 >> 8) & 255] ^ T2[(t1 >> 16) & 255] ^ T3[t2 >> 24] ^ kw[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]; - } + kw = KW[r]; + this.C0 = (uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)S[(r2 >> 16) & 255]) << 16) ^ (((uint)S[r3 >> 24]) << 24) ^ kw[0]; + this.C1 = (uint)S[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[r0 >> 24]) << 24) ^ kw[1]; + this.C2 = (uint)S[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[r1 >> 24]) << 24) ^ kw[2]; + this.C3 = (uint)S[r3 & 255] ^ (((uint)S[(r0 >> 8) & 255]) << 8) ^ (((uint)S[(r1 >> 16) & 255]) << 16) ^ (((uint)S[r2 >> 24]) << 24) ^ kw[3]; + } - private void DecryptBlock( - uint[,] KW) + 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]; + uint[] kw = KW[ROUNDS]; + uint t0 = this.C0 ^ kw[0]; + uint t1 = this.C1 ^ kw[1]; + uint t2 = this.C2 ^ kw[2]; + + uint r0, r1, r2, r3 = this.C3 ^ kw[3]; + int r = ROUNDS - 1; + while (r > 1) + { + kw = KW[r--]; + r0 = Tinv0[t0 & 255] ^ Tinv1[(r3 >> 8) & 255] ^ Tinv2[(t2 >> 16) & 255] ^ Tinv3[t1 >> 24] ^ kw[0]; + r1 = Tinv0[t1 & 255] ^ Tinv1[(t0 >> 8) & 255] ^ Tinv2[(r3 >> 16) & 255] ^ Tinv3[t2 >> 24] ^ kw[1]; + r2 = Tinv0[t2 & 255] ^ Tinv1[(t1 >> 8) & 255] ^ Tinv2[(t0 >> 16) & 255] ^ Tinv3[r3 >> 24] ^ kw[2]; + r3 = Tinv0[r3 & 255] ^ Tinv1[(t2 >> 8) & 255] ^ Tinv2[(t1 >> 16) & 255] ^ Tinv3[t0 >> 24] ^ kw[3]; + kw = KW[r--]; + t0 = Tinv0[r0 & 255] ^ Tinv1[(r3 >> 8) & 255] ^ Tinv2[(r2 >> 16) & 255] ^ Tinv3[r1 >> 24] ^ kw[0]; + t1 = Tinv0[r1 & 255] ^ Tinv1[(r0 >> 8) & 255] ^ Tinv2[(r3 >> 16) & 255] ^ Tinv3[r2 >> 24] ^ kw[1]; + t2 = Tinv0[r2 & 255] ^ Tinv1[(r1 >> 8) & 255] ^ Tinv2[(r0 >> 16) & 255] ^ Tinv3[r3 >> 24] ^ kw[2]; + r3 = Tinv0[r3 & 255] ^ Tinv1[(r2 >> 8) & 255] ^ Tinv2[(r1 >> 16) & 255] ^ Tinv3[r0 >> 24] ^ kw[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]; + kw = KW[1]; + r0 = Tinv0[t0 & 255] ^ Tinv1[(r3 >> 8) & 255] ^ Tinv2[(t2 >> 16) & 255] ^ Tinv3[t1 >> 24] ^ kw[0]; + r1 = Tinv0[t1 & 255] ^ Tinv1[(t0 >> 8) & 255] ^ Tinv2[(r3 >> 16) & 255] ^ Tinv3[t2 >> 24] ^ kw[1]; + r2 = Tinv0[t2 & 255] ^ Tinv1[(t1 >> 8) & 255] ^ Tinv2[(t0 >> 16) & 255] ^ Tinv3[r3 >> 24] ^ kw[2]; + r3 = Tinv0[r3 & 255] ^ Tinv1[(t2 >> 8) & 255] ^ Tinv2[(t1 >> 16) & 255] ^ Tinv3[t0 >> 24] ^ kw[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]; - } + kw = KW[0]; + this.C0 = (uint)Si[r0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(r2 >> 16) & 255]) << 16) ^ (((uint)Si[r1 >> 24]) << 24) ^ kw[0]; + this.C1 = (uint)Si[r1 & 255] ^ (((uint)Si[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ (((uint)Si[r2 >> 24]) << 24) ^ kw[1]; + this.C2 = (uint)Si[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ (((uint)Si[r3 >> 24]) << 24) ^ kw[2]; + this.C3 = (uint)Si[r3 & 255] ^ (((uint)Si[(r2 >> 8) & 255]) << 8) ^ (((uint)Si[(r1 >> 16) & 255]) << 16) ^ (((uint)Si[r0 >> 24]) << 24) ^ kw[3]; + } } } diff --git a/crypto/src/crypto/engines/AesLightEngine.cs b/crypto/src/crypto/engines/AesLightEngine.cs
index 2c495578d..54f2d2e88 100644 --- a/crypto/src/crypto/engines/AesLightEngine.cs +++ b/crypto/src/crypto/engines/AesLightEngine.cs
@@ -1,419 +1,427 @@ using System; +using System.Diagnostics; 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]; - } - } + /** + * 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 static 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 static 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 static uint Mcol(uint x) + { + uint f2 = FFmulX(x); + return f2 ^ Shift(x ^ f2, 8) ^ Shift(x, 16) ^ Shift(x, 24); + } + + private static 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 static 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 words in a block + for (int i = 0; i <= ROUNDS; ++i) + { + W[i] = new uint[4]; + } + + // + // 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++) + { + uint[] w = W[j]; + for (int i = 0; i < 4; i++) + { + w[i] = Inv_Mcol(w[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) + { + 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[] kw = KW[0]; + uint t0 = this.C0 ^ kw[0]; + uint t1 = this.C1 ^ kw[1]; + uint t2 = this.C2 ^ kw[2]; + + uint r0, r1, r2, r3 = this.C3 ^ kw[3]; + int r = 1; + while (r < ROUNDS - 1) + { + kw = KW[r++]; + r0 = Mcol((uint)S[t0 & 255] ^ (((uint)S[(t1 >> 8) & 255]) << 8) ^ (((uint)S[(t2 >> 16) & 255]) << 16) ^ (((uint)S[(r3 >> 24) & 255]) << 24)) ^ kw[0]; + r1 = Mcol((uint)S[t1 & 255] ^ (((uint)S[(t2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[(t0 >> 24) & 255]) << 24)) ^ kw[1]; + r2 = Mcol((uint)S[t2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(t0 >> 16) & 255]) << 16) ^ (((uint)S[(t1 >> 24) & 255]) << 24)) ^ kw[2]; + r3 = Mcol((uint)S[r3 & 255] ^ (((uint)S[(t0 >> 8) & 255]) << 8) ^ (((uint)S[(t1 >> 16) & 255]) << 16) ^ (((uint)S[(t2 >> 24) & 255]) << 24)) ^ kw[3]; + kw = KW[r++]; + t0 = Mcol((uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)S[(r2 >> 16) & 255]) << 16) ^ (((uint)S[(r3 >> 24) & 255]) << 24)) ^ kw[0]; + t1 = Mcol((uint)S[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[(r0 >> 24) & 255]) << 24)) ^ kw[1]; + t2 = Mcol((uint)S[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[(r1 >> 24) & 255]) << 24)) ^ kw[2]; + r3 = Mcol((uint)S[r3 & 255] ^ (((uint)S[(r0 >> 8) & 255]) << 8) ^ (((uint)S[(r1 >> 16) & 255]) << 16) ^ (((uint)S[(r2 >> 24) & 255]) << 24)) ^ kw[3]; + } + + kw = KW[r++]; + r0 = Mcol((uint)S[t0 & 255] ^ (((uint)S[(t1 >> 8) & 255]) << 8) ^ (((uint)S[(t2 >> 16) & 255]) << 16) ^ (((uint)S[(r3 >> 24) & 255]) << 24)) ^ kw[0]; + r1 = Mcol((uint)S[t1 & 255] ^ (((uint)S[(t2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[(t0 >> 24) & 255]) << 24)) ^ kw[1]; + r2 = Mcol((uint)S[t2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(t0 >> 16) & 255]) << 16) ^ (((uint)S[(t1 >> 24) & 255]) << 24)) ^ kw[2]; + r3 = Mcol((uint)S[r3 & 255] ^ (((uint)S[(t0 >> 8) & 255]) << 8) ^ (((uint)S[(t1 >> 16) & 255]) << 16) ^ (((uint)S[(t2 >> 24) & 255]) << 24)) ^ kw[3]; + + // the final round is a simple function of S + + kw = KW[r]; + this.C0 = (uint)S[r0 & 255] ^ (((uint)S[(r1 >> 8) & 255]) << 8) ^ (((uint)S[(r2 >> 16) & 255]) << 16) ^ (((uint)S[(r3 >> 24) & 255]) << 24) ^ kw[0]; + this.C1 = (uint)S[r1 & 255] ^ (((uint)S[(r2 >> 8) & 255]) << 8) ^ (((uint)S[(r3 >> 16) & 255]) << 16) ^ (((uint)S[(r0 >> 24) & 255]) << 24) ^ kw[1]; + this.C2 = (uint)S[r2 & 255] ^ (((uint)S[(r3 >> 8) & 255]) << 8) ^ (((uint)S[(r0 >> 16) & 255]) << 16) ^ (((uint)S[(r1 >> 24) & 255]) << 24) ^ kw[2]; + this.C3 = (uint)S[r3 & 255] ^ (((uint)S[(r0 >> 8) & 255]) << 8) ^ (((uint)S[(r1 >> 16) & 255]) << 16) ^ (((uint)S[(r2 >> 24) & 255]) << 24) ^ kw[3]; + } + + private void DecryptBlock(uint[][] KW) + { + uint[] kw = KW[ROUNDS]; + uint t0 = this.C0 ^ kw[0]; + uint t1 = this.C1 ^ kw[1]; + uint t2 = this.C2 ^ kw[2]; + + uint r0, r1, r2, r3 = this.C3 ^ kw[3]; + int r = ROUNDS - 1; + while (r > 1) + { + kw = KW[r--]; + r0 = Inv_Mcol((uint)Si[t0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(t2 >> 16) & 255]) << 16) ^ ((uint)Si[(t1 >> 24) & 255] << 24)) ^ kw[0]; + r1 = Inv_Mcol((uint)Si[t1 & 255] ^ (((uint)Si[(t0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ ((uint)Si[(t2 >> 24) & 255] << 24)) ^ kw[1]; + r2 = Inv_Mcol((uint)Si[t2 & 255] ^ (((uint)Si[(t1 >> 8) & 255]) << 8) ^ (((uint)Si[(t0 >> 16) & 255]) << 16) ^ ((uint)Si[(r3 >> 24) & 255] << 24)) ^ kw[2]; + r3 = Inv_Mcol((uint)Si[r3 & 255] ^ (((uint)Si[(t2 >> 8) & 255]) << 8) ^ (((uint)Si[(t1 >> 16) & 255]) << 16) ^ ((uint)Si[(t0 >> 24) & 255] << 24)) ^ kw[3]; + kw = KW[r--]; + t0 = 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[0]; + t1 = 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[1]; + t2 = 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[2]; + r3 = 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[3]; + } + + kw = KW[1]; + r0 = Inv_Mcol((uint)Si[t0 & 255] ^ (((uint)Si[(r3 >> 8) & 255]) << 8) ^ (((uint)Si[(t2 >> 16) & 255]) << 16) ^ ((uint)Si[(t1 >> 24) & 255] << 24)) ^ kw[0]; + r1 = Inv_Mcol((uint)Si[t1 & 255] ^ (((uint)Si[(t0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ ((uint)Si[(t2 >> 24) & 255] << 24)) ^ kw[1]; + r2 = Inv_Mcol((uint)Si[t2 & 255] ^ (((uint)Si[(t1 >> 8) & 255]) << 8) ^ (((uint)Si[(t0 >> 16) & 255]) << 16) ^ ((uint)Si[(r3 >> 24) & 255] << 24)) ^ kw[2]; + r3 = Inv_Mcol((uint)Si[r3 & 255] ^ (((uint)Si[(t2 >> 8) & 255]) << 8) ^ (((uint)Si[(t1 >> 16) & 255]) << 16) ^ ((uint)Si[(t0 >> 24) & 255] << 24)) ^ kw[3]; + + // the final round's table is a simple function of Si + + kw = KW[0]; + this.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]; + this.C1 = (uint)Si[r1 & 255] ^ (((uint)Si[(r0 >> 8) & 255]) << 8) ^ (((uint)Si[(r3 >> 16) & 255]) << 16) ^ (((uint)Si[(r2 >> 24) & 255]) << 24) ^ kw[1]; + this.C2 = (uint)Si[r2 & 255] ^ (((uint)Si[(r1 >> 8) & 255]) << 8) ^ (((uint)Si[(r0 >> 16) & 255]) << 16) ^ (((uint)Si[(r3 >> 24) & 255]) << 24) ^ kw[2]; + this.C3 = (uint)Si[r3 & 255] ^ (((uint)Si[(r2 >> 8) & 255]) << 8) ^ (((uint)Si[(r1 >> 16) & 255]) << 16) ^ (((uint)Si[(r0 >> 24) & 255]) << 24) ^ kw[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/ChaChaEngine.cs b/crypto/src/crypto/engines/ChaChaEngine.cs new file mode 100644
index 000000000..f4a7b8fe1 --- /dev/null +++ b/crypto/src/crypto/engines/ChaChaEngine.cs
@@ -0,0 +1,189 @@ +using System; +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /// <summary> + /// Implementation of Daniel J. Bernstein's ChaCha stream cipher. + /// </summary> + public class ChaChaEngine + : Salsa20Engine + { + + /// <summary> + /// Creates a 20 rounds ChaCha engine. + /// </summary> + public ChaChaEngine() + { + } + + /// <summary> + /// Creates a ChaCha engine with a specific number of rounds. + /// </summary> + /// <param name="rounds">the number of rounds (must be an even number).</param> + public ChaChaEngine(int rounds) + : base(rounds) + { + } + + public override string AlgorithmName + { + get { return "ChaCha" + rounds; } + } + + protected override void AdvanceCounter() + { + if (++engineState[12] == 0) + { + ++engineState[13]; + } + } + + protected override void ResetCounter() + { + engineState[12] = engineState[13] = 0; + } + + protected override void SetKey(byte[] keyBytes, byte[] ivBytes) + { + if ((keyBytes.Length != 16) && (keyBytes.Length != 32)) + { + throw new ArgumentException(AlgorithmName + " requires 128 bit or 256 bit key"); + } + + int offset = 0; + byte[] constants; + + // Key + engineState[4] = Pack.LE_To_UInt32(keyBytes, 0); + engineState[5] = Pack.LE_To_UInt32(keyBytes, 4); + engineState[6] = Pack.LE_To_UInt32(keyBytes, 8); + engineState[7] = Pack.LE_To_UInt32(keyBytes, 12); + + if (keyBytes.Length == 32) + { + constants = sigma; + offset = 16; + } else + { + constants = tau; + } + + engineState[8] = Pack.LE_To_UInt32(keyBytes, offset); + engineState[9] = Pack.LE_To_UInt32(keyBytes, offset + 4); + engineState[10] = Pack.LE_To_UInt32(keyBytes, offset + 8); + engineState[11] = Pack.LE_To_UInt32(keyBytes, offset + 12); + + engineState[0] = Pack.LE_To_UInt32(constants, 0); + engineState[1] = Pack.LE_To_UInt32(constants, 4); + engineState[2] = Pack.LE_To_UInt32(constants, 8); + engineState[3] = Pack.LE_To_UInt32(constants, 12); + + // Counter + engineState[12] = engineState[13] = 0; + + // IV + engineState[14] = Pack.LE_To_UInt32(ivBytes, 0); + engineState[15] = Pack.LE_To_UInt32(ivBytes, 4); + } + + protected override void GenerateKeyStream(byte[] output) + { + ChachaCore(rounds, engineState, x); + Pack.UInt32_To_LE(x, output, 0); + } + + /// <summary> + /// ChacCha function. + /// </summary> + /// <param name="rounds">The number of ChaCha rounds to execute</param> + /// <param name="input">The input words.</param> + /// <param name="x">The ChaCha state to modify.</param> + internal static void ChachaCore(int rounds, uint[] input, uint[] x) + { + if (input.Length != 16) { + throw new ArgumentException(); + } + if (x.Length != 16) { + throw new ArgumentException(); + } + if (rounds % 2 != 0) { + throw new ArgumentException("Number of rounds must be even"); + } + + uint x00 = input[ 0]; + uint x01 = input[ 1]; + uint x02 = input[ 2]; + uint x03 = input[ 3]; + uint x04 = input[ 4]; + uint x05 = input[ 5]; + uint x06 = input[ 6]; + uint x07 = input[ 7]; + uint x08 = input[ 8]; + uint x09 = input[ 9]; + uint x10 = input[10]; + uint x11 = input[11]; + uint x12 = input[12]; + uint x13 = input[13]; + uint x14 = input[14]; + uint x15 = input[15]; + + for (int i = rounds; i > 0; i -= 2) + { + x00 += x04; x12 = R(x12 ^ x00, 16); + x08 += x12; x04 = R(x04 ^ x08, 12); + x00 += x04; x12 = R(x12 ^ x00, 8); + x08 += x12; x04 = R(x04 ^ x08, 7); + x01 += x05; x13 = R(x13 ^ x01, 16); + x09 += x13; x05 = R(x05 ^ x09, 12); + x01 += x05; x13 = R(x13 ^ x01, 8); + x09 += x13; x05 = R(x05 ^ x09, 7); + x02 += x06; x14 = R(x14 ^ x02, 16); + x10 += x14; x06 = R(x06 ^ x10, 12); + x02 += x06; x14 = R(x14 ^ x02, 8); + x10 += x14; x06 = R(x06 ^ x10, 7); + x03 += x07; x15 = R(x15 ^ x03, 16); + x11 += x15; x07 = R(x07 ^ x11, 12); + x03 += x07; x15 = R(x15 ^ x03, 8); + x11 += x15; x07 = R(x07 ^ x11, 7); + x00 += x05; x15 = R(x15 ^ x00, 16); + x10 += x15; x05 = R(x05 ^ x10, 12); + x00 += x05; x15 = R(x15 ^ x00, 8); + x10 += x15; x05 = R(x05 ^ x10, 7); + x01 += x06; x12 = R(x12 ^ x01, 16); + x11 += x12; x06 = R(x06 ^ x11, 12); + x01 += x06; x12 = R(x12 ^ x01, 8); + x11 += x12; x06 = R(x06 ^ x11, 7); + x02 += x07; x13 = R(x13 ^ x02, 16); + x08 += x13; x07 = R(x07 ^ x08, 12); + x02 += x07; x13 = R(x13 ^ x02, 8); + x08 += x13; x07 = R(x07 ^ x08, 7); + x03 += x04; x14 = R(x14 ^ x03, 16); + x09 += x14; x04 = R(x04 ^ x09, 12); + x03 += x04; x14 = R(x14 ^ x03, 8); + x09 += x14; x04 = R(x04 ^ x09, 7); + + } + + x[ 0] = x00 + input[ 0]; + x[ 1] = x01 + input[ 1]; + x[ 2] = x02 + input[ 2]; + x[ 3] = x03 + input[ 3]; + x[ 4] = x04 + input[ 4]; + x[ 5] = x05 + input[ 5]; + x[ 6] = x06 + input[ 6]; + x[ 7] = x07 + input[ 7]; + x[ 8] = x08 + input[ 8]; + x[ 9] = x09 + input[ 9]; + x[10] = x10 + input[10]; + x[11] = x11 + input[11]; + x[12] = x12 + input[12]; + x[13] = x13 + input[13]; + x[14] = x14 + input[14]; + x[15] = x15 + input[15]; + } + + } + +} + diff --git a/crypto/src/crypto/engines/DesEdeEngine.cs b/crypto/src/crypto/engines/DesEdeEngine.cs
index b319888e3..eec4ec59d 100644 --- a/crypto/src/crypto/engines/DesEdeEngine.cs +++ b/crypto/src/crypto/engines/DesEdeEngine.cs
@@ -4,65 +4,65 @@ using Org.BouncyCastle.Crypto.Parameters; namespace Org.BouncyCastle.Crypto.Engines { - /// <remarks>A class that provides a basic DESede (or Triple DES) engine.</remarks> + /// <remarks>A class that provides a basic DESede (or Triple DES) engine.</remarks> public class DesEdeEngine - : DesEngine + : 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()); - } + /** + * 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(); + byte[] keyMaster = ((KeyParameter)parameters).GetKey(); + if (keyMaster.Length != 24 && keyMaster.Length != 16) + throw new ArgumentException("key size must be 16 or 24 bytes."); - this.forEncryption = forEncryption; + this.forEncryption = forEncryption; - byte[] key1 = new byte[8]; - Array.Copy(keyMaster, 0, key1, 0, key1.Length); - workingKey1 = GenerateWorkingKey(forEncryption, key1); + 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); + 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; - } - } + 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 + public override string AlgorithmName { get { return "DESede"; } } - public override int GetBlockSize() + public override int GetBlockSize() { return BLOCK_SIZE; } - public override int ProcessBlock( + public override int ProcessBlock( byte[] input, int inOff, byte[] output, @@ -75,9 +75,9 @@ namespace Org.BouncyCastle.Crypto.Engines if ((outOff + BLOCK_SIZE) > output.Length) throw new DataLengthException("output buffer too short"); - byte[] temp = new byte[BLOCK_SIZE]; + byte[] temp = new byte[BLOCK_SIZE]; - if (forEncryption) + if (forEncryption) { DesFunc(workingKey1, input, inOff, temp, 0); DesFunc(workingKey2, temp, 0, temp, 0); @@ -90,10 +90,10 @@ namespace Org.BouncyCastle.Crypto.Engines DesFunc(workingKey1, temp, 0, output, outOff); } - return BLOCK_SIZE; + return BLOCK_SIZE; } - public override void Reset() + 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
index 17593d2c0..8eb6f36b5 100644 --- a/crypto/src/crypto/engines/GOST28147Engine.cs +++ b/crypto/src/crypto/engines/GOST28147Engine.cs
@@ -1,6 +1,5 @@ using System; using System.Collections; -using System.Globalization; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Utilities; @@ -134,7 +133,7 @@ namespace Org.BouncyCastle.Crypto.Engines private static void AddSBox(string sBoxName, byte[] sBox) { - sBoxes.Add(sBoxName.ToUpperInvariant(), sBox); + sBoxes.Add(Platform.ToUpperInvariant(sBoxName), sBox); } /** @@ -363,9 +362,9 @@ namespace Org.BouncyCastle.Crypto.Engines public static byte[] GetSBox( string sBoxName) { - byte[] sBox = (byte[])sBoxes[sBoxName.ToUpperInvariant()]; + byte[] sBox = (byte[])sBoxes[Platform.ToUpperInvariant(sBoxName)]; - if (sBox == null) + 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\"."); 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
index 1120a4104..9c58678a0 100644 --- a/crypto/src/crypto/engines/ISAACEngine.cs +++ b/crypto/src/crypto/engines/ISAACEngine.cs
@@ -1,252 +1,212 @@ using System; using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; 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; - } - } + /** + * 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 = Pack.UInt32_To_BE(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 = Pack.UInt32_To_BE(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] = Pack.LE_To_UInt32(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]; + } + } } diff --git a/crypto/src/crypto/engines/IdeaEngine.cs b/crypto/src/crypto/engines/IdeaEngine.cs
index f763c5939..46b5a787c 100644 --- a/crypto/src/crypto/engines/IdeaEngine.cs +++ b/crypto/src/crypto/engines/IdeaEngine.cs
@@ -1,5 +1,3 @@ -#if INCLUDE_IDEA - using System; using Org.BouncyCastle.Crypto.Parameters; @@ -12,26 +10,26 @@ namespace Org.BouncyCastle.Crypto.Engines * 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> * <p> * It can be found at ftp://ftp.funet.fi/pub/crypt/cryptography/symmetric/idea/ - * </p> + * </p> * <p> - * Note 1: This algorithm is patented in the USA, Japan, and Europe including + * 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> + * </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 + : IBlockCipher { private const int BLOCK_SIZE = 8; private int[] workingKey; @@ -54,28 +52,28 @@ namespace Org.BouncyCastle.Crypto.Engines ICipherParameters parameters) { if (!(parameters is KeyParameter)) - throw new ArgumentException("invalid parameter passed to IDEA init - " + parameters.GetType().ToString()); + throw new ArgumentException("invalid parameter passed to IDEA init - " + parameters.GetType().ToString()); - workingKey = GenerateWorkingKey(forEncryption, - ((KeyParameter)parameters).GetKey()); + workingKey = GenerateWorkingKey(forEncryption, + ((KeyParameter)parameters).GetKey()); } - public string AlgorithmName + public string AlgorithmName { get { return "IDEA"; } } - public bool IsPartialBlockOkay - { - get { return false; } - } + public bool IsPartialBlockOkay + { + get { return false; } + } - public int GetBlockSize() + public int GetBlockSize() { return BLOCK_SIZE; } - public int ProcessBlock( + public int ProcessBlock( byte[] input, int inOff, byte[] output, @@ -228,7 +226,7 @@ namespace Org.BouncyCastle.Crypto.Engines * Common Divisor algorithm. Zero and one are self inverse. * <p> * i.e. x * MulInv(x) == 1 (modulo BASE) - * </p> + * </p> */ private int MulInv( int x) @@ -261,7 +259,7 @@ namespace Org.BouncyCastle.Crypto.Engines * Return the additive inverse of x. * <p> * i.e. x + AddInv(x) == 0 - * </p> + * </p> */ int AddInv( int x) @@ -337,5 +335,3 @@ namespace Org.BouncyCastle.Crypto.Engines } } } - -#endif diff --git a/crypto/src/crypto/engines/IesEngine.cs b/crypto/src/crypto/engines/IesEngine.cs
index c49b2a9ee..70df3077c 100644 --- a/crypto/src/crypto/engines/IesEngine.cs +++ b/crypto/src/crypto/engines/IesEngine.cs
@@ -2,6 +2,7 @@ using System; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Engines { @@ -15,9 +16,9 @@ namespace Org.BouncyCastle.Crypto.Engines private readonly IDerivationFunction kdf; private readonly IMac mac; private readonly BufferedBlockCipher cipher; - private readonly byte[] macBuf; + private readonly byte[] macBuf; - private bool forEncryption; + private bool forEncryption; private ICipherParameters privParam, pubParam; private IesParameters param; @@ -100,7 +101,7 @@ namespace Org.BouncyCastle.Crypto.Engines if (cipher == null) // stream mode { - byte[] Buffer = GenerateKdfBytes(kParam, inLen + (macKeySize / 8)); + byte[] Buffer = GenerateKdfBytes(kParam, inLen + (macKeySize / 8)); M = new byte[inLen]; @@ -114,13 +115,13 @@ namespace Org.BouncyCastle.Crypto.Engines else { int cipherKeySize = ((IesWithCipherParameters)param).CipherKeySize; - byte[] Buffer = GenerateKdfBytes(kParam, (cipherKeySize / 8) + (macKeySize / 8)); + byte[] Buffer = GenerateKdfBytes(kParam, (cipherKeySize / 8) + (macKeySize / 8)); cipher.Init(false, new KeyParameter(Buffer, 0, (cipherKeySize / 8))); - M = cipher.DoFinal(in_enc, inOff, inLen); + M = cipher.DoFinal(in_enc, inOff, inLen); - macKey = new KeyParameter(Buffer, (cipherKeySize / 8), (macKeySize / 8)); + macKey = new KeyParameter(Buffer, (cipherKeySize / 8), (macKeySize / 8)); } byte[] macIV = param.GetEncodingV(); @@ -130,15 +131,12 @@ namespace Org.BouncyCastle.Crypto.Engines mac.BlockUpdate(macIV, 0, macIV.Length); mac.DoFinal(macBuf, 0); - inOff += inLen; + 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.")); - } - } + byte[] T1 = Arrays.CopyOfRange(in_enc, inOff, inOff + macBuf.Length); + + if (!Arrays.ConstantTimeAreEqual(T1, macBuf)) + throw (new InvalidCipherTextException("Invalid MAC.")); return M; } @@ -157,12 +155,12 @@ namespace Org.BouncyCastle.Crypto.Engines if (cipher == null) // stream mode { - byte[] Buffer = GenerateKdfBytes(kParam, inLen + (macKeySize / 8)); + byte[] Buffer = GenerateKdfBytes(kParam, inLen + (macKeySize / 8)); C = new byte[inLen + mac.GetMacSize()]; c_text_length = inLen; - for (int i = 0; i != inLen; i++) + for (int i = 0; i != inLen; i++) { C[i] = (byte)(input[inOff + i] ^ Buffer[i]); } @@ -172,22 +170,22 @@ namespace Org.BouncyCastle.Crypto.Engines else { int cipherKeySize = ((IesWithCipherParameters)param).CipherKeySize; - byte[] Buffer = GenerateKdfBytes(kParam, (cipherKeySize / 8) + (macKeySize / 8)); + 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]; + byte[] tmp = new byte[c_text_length]; - int len = cipher.ProcessBytes(input, inOff, inLen, tmp, 0); - len += cipher.DoFinal(tmp, len); + int len = cipher.ProcessBytes(input, inOff, inLen, tmp, 0); + len += cipher.DoFinal(tmp, len); - C = new byte[len + mac.GetMacSize()]; - c_text_length = len; + C = new byte[len + mac.GetMacSize()]; + c_text_length = len; - Array.Copy(tmp, 0, C, 0, len); + Array.Copy(tmp, 0, C, 0, len); - macKey = new KeyParameter(Buffer, (cipherKeySize / 8), (macKeySize / 8)); + macKey = new KeyParameter(Buffer, (cipherKeySize / 8), (macKeySize / 8)); } byte[] macIV = param.GetEncodingV(); @@ -202,33 +200,32 @@ namespace Org.BouncyCastle.Crypto.Engines return C; } - private byte[] GenerateKdfBytes( - KdfParameters kParam, - int length) - { - byte[] buf = new byte[length]; + private byte[] GenerateKdfBytes( + KdfParameters kParam, + int length) + { + byte[] buf = new byte[length]; - kdf.Init(kParam); + kdf.Init(kParam); - kdf.GenerateBytes(buf, 0, buf.Length); + kdf.GenerateBytes(buf, 0, buf.Length); - return buf; - } + return buf; + } - public byte[] ProcessBlock( + public byte[] ProcessBlock( byte[] input, int inOff, int inLen) { agree.Init(privParam); - BigInteger z = agree.CalculateAgreement(pubParam); + BigInteger z = agree.CalculateAgreement(pubParam); - // TODO Is a fixed length result expected? - byte[] zBytes = z.ToByteArrayUnsigned(); + byte[] zBytes = BigIntegers.AsUnsignedByteArray(agree.GetFieldSize(), z); return forEncryption - ? EncryptBlock(input, inOff, inLen, zBytes) + ? 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
index 9a0d1e0fe..9ca092351 100644 --- a/crypto/src/crypto/engines/NaccacheSternEngine.cs +++ b/crypto/src/crypto/engines/NaccacheSternEngine.cs
@@ -51,7 +51,7 @@ namespace Org.BouncyCastle.Crypto.Engines { if (debug) { - System.Diagnostics.Debug.WriteLine("Constructing lookup Array"); + Console.WriteLine("Constructing lookup Array"); } NaccacheSternPrivateKeyParameters priv = (NaccacheSternPrivateKeyParameters)key; IList primes = priv.SmallPrimesList; @@ -66,7 +66,7 @@ namespace Org.BouncyCastle.Crypto.Engines if (debug) { - System.Diagnostics.Debug.WriteLine("Constructing lookup ArrayList for " + actualPrimeValue); + Console.WriteLine("Constructing lookup ArrayList for " + actualPrimeValue); } BigInteger accJ = BigInteger.Zero; @@ -158,7 +158,7 @@ namespace Org.BouncyCastle.Crypto.Engines if (debug) { - System.Diagnostics.Debug.WriteLine("input as BigInteger: " + input); + Console.WriteLine("input as BigInteger: " + input); } byte[] output; @@ -180,7 +180,7 @@ namespace Org.BouncyCastle.Crypto.Engines { if (debug) { - System.Diagnostics.Debug.WriteLine("Prime is " + primes[i] + ", lookup table has size " + al.Count); + Console.WriteLine("Prime is " + primes[i] + ", lookup table has size " + al.Count); } throw new InvalidCipherTextException("Error in lookup Array for " + ((BigInteger)primes[i]).IntValue @@ -194,14 +194,14 @@ namespace Org.BouncyCastle.Crypto.Engines { if (debug) { - System.Diagnostics.Debug.WriteLine("Actual prime is " + primes[i]); - System.Diagnostics.Debug.WriteLine("Decrypted value is " + exp); + Console.WriteLine("Actual prime is " + primes[i]); + Console.WriteLine("Decrypted value is " + exp); - System.Diagnostics.Debug.WriteLine("LookupList for " + primes[i] + " with size " + lookup[i].Count + Console.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]); + Console.WriteLine(lookup[i][j]); } } throw new InvalidCipherTextException("Lookup failed"); @@ -258,7 +258,7 @@ namespace Org.BouncyCastle.Crypto.Engines Array.Copy(tmp, 0, output, output.Length - tmp.Length, tmp.Length); if (debug) { - System.Diagnostics.Debug.WriteLine("Encrypted value is: " + new BigInteger(output)); + Console.WriteLine("Encrypted value is: " + new BigInteger(output)); } return output; } @@ -304,9 +304,9 @@ namespace Org.BouncyCastle.Crypto.Engines 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); + Console.WriteLine("c(m1) as BigInteger:....... " + m1Crypt); + Console.WriteLine("c(m2) as BigInteger:....... " + m2Crypt); + Console.WriteLine("c(m1)*c(m2)%n = c(m1+m2)%n: " + m1m2Crypt); } //byte[] output = key.Modulus.ToByteArray(); @@ -334,7 +334,7 @@ namespace Org.BouncyCastle.Crypto.Engines { if (debug) { - System.Diagnostics.Debug.WriteLine(""); + Console.WriteLine(); } if (data.Length > GetInputBlockSize()) { @@ -342,9 +342,9 @@ namespace Org.BouncyCastle.Crypto.Engines 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"); + Console.WriteLine("Input blocksize is: " + inBlocksize + " bytes"); + Console.WriteLine("Output blocksize is: " + outBlocksize + " bytes"); + Console.WriteLine("Data has length:.... " + data.Length + " bytes"); } int datapos = 0; int retpos = 0; @@ -364,7 +364,7 @@ namespace Org.BouncyCastle.Crypto.Engines } if (debug) { - System.Diagnostics.Debug.WriteLine("new datapos is " + datapos); + Console.WriteLine("new datapos is " + datapos); } if (tmp != null) { @@ -375,7 +375,7 @@ namespace Org.BouncyCastle.Crypto.Engines { if (debug) { - System.Diagnostics.Debug.WriteLine("cipher returned null"); + Console.WriteLine("cipher returned null"); } throw new InvalidCipherTextException("cipher returned null"); } @@ -384,7 +384,7 @@ namespace Org.BouncyCastle.Crypto.Engines Array.Copy(retval, 0, ret, 0, retpos); if (debug) { - System.Diagnostics.Debug.WriteLine("returning " + ret.Length + " bytes"); + Console.WriteLine("returning " + ret.Length + " bytes"); } return ret; } @@ -392,7 +392,7 @@ namespace Org.BouncyCastle.Crypto.Engines { if (debug) { - System.Diagnostics.Debug.WriteLine("data size is less then input block size, processing directly"); + Console.WriteLine("data size is less then input block size, processing directly"); } return ProcessBlock(data, 0, data.Length); } 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
index 7596e7218..5615a63e5 100644 --- a/crypto/src/crypto/engines/RFC3394WrapEngine.cs +++ b/crypto/src/crypto/engines/RFC3394WrapEngine.cs
@@ -90,7 +90,7 @@ namespace Org.BouncyCastle.Crypto.Engines byte[] buf = new byte[8 + iv.Length]; Array.Copy(iv, 0, block, 0, iv.Length); - Array.Copy(input, 0, block, iv.Length, inLen); + Array.Copy(input, inOff, block, iv.Length, inLen); engine.Init(true, param); @@ -140,8 +140,8 @@ namespace Org.BouncyCastle.Crypto.Engines 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); + Array.Copy(input, inOff, a, 0, iv.Length); + Array.Copy(input, inOff + iv.Length, block, 0, inLen - iv.Length); engine.Init(false, param); 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
index 7d68deab1..81884d603 100644 --- a/crypto/src/crypto/engines/Salsa20Engine.cs +++ b/crypto/src/crypto/engines/Salsa20Engine.cs
@@ -7,44 +7,60 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Engines { - /** - * Implementation of Daniel J. Bernstein's Salsa20 stream cipher, Snuffle 2005 - */ + /// <summary> + /// Implementation of Daniel J. Bernstein's Salsa20 stream cipher, Snuffle 2005 + /// </summary> public class Salsa20Engine : IStreamCipher { + public static readonly int DEFAULT_ROUNDS = 20; + /** Constants */ private const int StateSize = 16; // 16, 32 bit ints = 64 bytes - private readonly static byte[] + protected readonly static byte[] sigma = Strings.ToAsciiByteArray("expand 32-byte k"), tau = Strings.ToAsciiByteArray("expand 16-byte k"); + protected int rounds; + /* * 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; + private int index = 0; + internal uint[] engineState = new uint[StateSize]; // state + internal uint[] x = new uint[StateSize]; // internal buffer + private byte[] keyStream = new byte[StateSize * 4]; // expanded state, 64 bytes + 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. - */ + /// <summary> + /// Creates a 20 round Salsa20 engine. + /// </summary> + public Salsa20Engine() + : this(DEFAULT_ROUNDS) + { + } + + /// <summary> + /// Creates a Salsa20 engine with a specific number of rounds. + /// </summary> + /// <param name="rounds">the number of rounds (must be an even number).</param> + public Salsa20Engine(int rounds) + { + if (rounds <= 0 || (rounds & 1) != 0) + { + throw new ArgumentException("'rounds' must be a positive, even number"); + } + + this.rounds = rounds; + } + public void Init( bool forEncryption, ICipherParameters parameters) @@ -58,27 +74,38 @@ namespace Org.BouncyCastle.Crypto.Engines ParametersWithIV ivParams = parameters as ParametersWithIV; if (ivParams == null) - throw new ArgumentException("Salsa20 Init requires an IV", "parameters"); + throw new ArgumentException(AlgorithmName + " 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"); + if (iv == null || iv.Length != NonceSize) + throw new ArgumentException(AlgorithmName + " requires exactly " + NonceSize + " bytes of IV"); KeyParameter key = ivParams.Parameters as KeyParameter; if (key == null) - throw new ArgumentException("Salsa20 Init requires a key", "parameters"); + throw new ArgumentException(AlgorithmName + " Init requires a key", "parameters"); - workingKey = key.GetKey(); - workingIV = iv; + SetKey(key.GetKey(), iv); + Reset(); + initialised = true; + } - SetKey(workingKey, workingIV); + protected virtual int NonceSize + { + get { return 8; } } - public string AlgorithmName + public virtual string AlgorithmName { - get { return "Salsa20"; } + get { + string name = "Salsa20"; + if (rounds != DEFAULT_ROUNDS) + { + name += "/" + rounds; + } + return name; + } } public byte ReturnByte( @@ -92,11 +119,7 @@ namespace Org.BouncyCastle.Crypto.Engines if (index == 0) { GenerateKeyStream(keyStream); - - if (++engineState[8] == 0) - { - ++engineState[9]; - } + AdvanceCounter(); } byte output = (byte)(keyStream[index] ^ input); @@ -105,6 +128,14 @@ namespace Org.BouncyCastle.Crypto.Engines return output; } + protected virtual void AdvanceCounter() + { + if (++engineState[8] == 0) + { + ++engineState[9]; + } + } + public void ProcessBytes( byte[] inBytes, int inOff, @@ -137,11 +168,7 @@ namespace Org.BouncyCastle.Crypto.Engines if (index == 0) { GenerateKeyStream(keyStream); - - if (++engineState[8] == 0) - { - ++engineState[9]; - } + AdvanceCounter(); } outBytes[i+outOff] = (byte)(keyStream[index]^inBytes[i+inOff]); index = (index + 1) & 63; @@ -150,28 +177,32 @@ namespace Org.BouncyCastle.Crypto.Engines public void Reset() { - SetKey(workingKey, workingIV); + index = 0; + ResetLimitCounter(); + ResetCounter(); } - // Private implementation + protected virtual void ResetCounter() + { + engineState[8] = engineState[9] = 0; + } - private void SetKey(byte[] keyBytes, byte[] ivBytes) + protected virtual void SetKey(byte[] keyBytes, byte[] ivBytes) { - workingKey = keyBytes; - workingIV = ivBytes; + if ((keyBytes.Length != 16) && (keyBytes.Length != 32)) { + throw new ArgumentException(AlgorithmName + " requires 128 bit or 256 bit key"); + } - 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); + engineState[1] = Pack.LE_To_UInt32(keyBytes, 0); + engineState[2] = Pack.LE_To_UInt32(keyBytes, 4); + engineState[3] = Pack.LE_To_UInt32(keyBytes, 8); + engineState[4] = Pack.LE_To_UInt32(keyBytes, 12); - if (workingKey.Length == 32) + if (keyBytes.Length == 32) { constants = sigma; offset = 16; @@ -181,83 +212,125 @@ namespace Org.BouncyCastle.Crypto.Engines 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[11] = Pack.LE_To_UInt32(keyBytes, offset); + engineState[12] = Pack.LE_To_UInt32(keyBytes, offset + 4); + engineState[13] = Pack.LE_To_UInt32(keyBytes, offset + 8); + engineState[14] = Pack.LE_To_UInt32(keyBytes, 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; + engineState[6] = Pack.LE_To_UInt32(ivBytes, 0); + engineState[7] = Pack.LE_To_UInt32(ivBytes, 4); + ResetCounter(); } - private void GenerateKeyStream(byte[] output) + protected virtual void GenerateKeyStream(byte[] output) { - SalsaCore(20, engineState, x); + SalsaCore(rounds, engineState, x); Pack.UInt32_To_LE(x, output, 0); } - internal static void SalsaCore(int rounds, uint[] state, uint[] x) + internal static void SalsaCore(int rounds, uint[] input, uint[] x) { - // TODO Exception if rounds odd? + if (input.Length != 16) { + throw new ArgumentException(); + } + if (x.Length != 16) { + throw new ArgumentException(); + } + if (rounds % 2 != 0) { + throw new ArgumentException("Number of rounds must be even"); + } - Array.Copy(state, 0, x, 0, state.Length); + uint x00 = input[ 0]; + uint x01 = input[ 1]; + uint x02 = input[ 2]; + uint x03 = input[ 3]; + uint x04 = input[ 4]; + uint x05 = input[ 5]; + uint x06 = input[ 6]; + uint x07 = input[ 7]; + uint x08 = input[ 8]; + uint x09 = input[ 9]; + uint x10 = input[10]; + uint x11 = input[11]; + uint x12 = input[12]; + uint x13 = input[13]; + uint x14 = input[14]; + uint x15 = input[15]; 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); + x04 ^= R((x00+x12), 7); + x08 ^= R((x04+x00), 9); + x12 ^= R((x08+x04),13); + x00 ^= R((x12+x08),18); + x09 ^= R((x05+x01), 7); + x13 ^= R((x09+x05), 9); + x01 ^= R((x13+x09),13); + x05 ^= R((x01+x13),18); + x14 ^= R((x10+x06), 7); + x02 ^= R((x14+x10), 9); + x06 ^= R((x02+x14),13); + x10 ^= R((x06+x02),18); + x03 ^= R((x15+x11), 7); + x07 ^= R((x03+x15), 9); + x11 ^= R((x07+x03),13); + x15 ^= R((x11+x07),18); + + x01 ^= R((x00+x03), 7); + x02 ^= R((x01+x00), 9); + x03 ^= R((x02+x01),13); + x00 ^= R((x03+x02),18); + x06 ^= R((x05+x04), 7); + x07 ^= R((x06+x05), 9); + x04 ^= R((x07+x06),13); + x05 ^= R((x04+x07),18); + x11 ^= R((x10+x09), 7); + x08 ^= R((x11+x10), 9); + x09 ^= R((x08+x11),13); + x10 ^= R((x09+x08),18); + x12 ^= R((x15+x14), 7); + x13 ^= R((x12+x15), 9); + x14 ^= R((x13+x12),13); + x15 ^= R((x14+x13),18); } - for (int i = 0; i < StateSize; ++i) - { - x[i] += state[i]; - } + x[ 0] = x00 + input[ 0]; + x[ 1] = x01 + input[ 1]; + x[ 2] = x02 + input[ 2]; + x[ 3] = x03 + input[ 3]; + x[ 4] = x04 + input[ 4]; + x[ 5] = x05 + input[ 5]; + x[ 6] = x06 + input[ 6]; + x[ 7] = x07 + input[ 7]; + x[ 8] = x08 + input[ 8]; + x[ 9] = x09 + input[ 9]; + x[10] = x10 + input[10]; + x[11] = x11 + input[11]; + x[12] = x12 + input[12]; + x[13] = x13 + input[13]; + x[14] = x14 + input[14]; + x[15] = x15 + input[15]; } - private static uint R(uint x, int y) + /** + * Rotate left + * + * @param x value to rotate + * @param y amount to rotate x + * + * @return rotated x + */ + internal static uint R(uint x, int y) { return (x << y) | (x >> (32 - y)); } - private void ResetCounter() + private void ResetLimitCounter() { cW0 = 0; cW1 = 0; 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/ThreefishEngine.cs b/crypto/src/crypto/engines/ThreefishEngine.cs new file mode 100644
index 000000000..954470345 --- /dev/null +++ b/crypto/src/crypto/engines/ThreefishEngine.cs
@@ -0,0 +1,1490 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /// <summary> + /// Implementation of the Threefish tweakable large block cipher in 256, 512 and 1024 bit block + /// sizes. + /// </summary> + /// <remarks> + /// This is the 1.3 version of Threefish defined in the Skein hash function submission to the NIST + /// SHA-3 competition in October 2010. + /// <p/> + /// Threefish was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir + /// Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker. + /// <p/> + /// This implementation inlines all round functions, unrolls 8 rounds, and uses 1.2k of static tables + /// to speed up key schedule injection. <br/> + /// 2 x block size state is retained by each cipher instance. + /// </remarks> + public class ThreefishEngine + : IBlockCipher + { + /// <summary> + /// 256 bit block size - Threefish-256 + /// </summary> + public const int BLOCKSIZE_256 = 256; + /// <summary> + /// 512 bit block size - Threefish-512 + /// </summary> + public const int BLOCKSIZE_512 = 512; + /// <summary> + /// 1024 bit block size - Threefish-1024 + /// </summary> + public const int BLOCKSIZE_1024 = 1024; + + /** + * Size of the tweak in bytes (always 128 bit/16 bytes) + */ + private const int TWEAK_SIZE_BYTES = 16; + private const int TWEAK_SIZE_WORDS = TWEAK_SIZE_BYTES / 8; + + /** + * Rounds in Threefish-256 + */ + private const int ROUNDS_256 = 72; + /** + * Rounds in Threefish-512 + */ + private const int ROUNDS_512 = 72; + /** + * Rounds in Threefish-1024 + */ + private const int ROUNDS_1024 = 80; + + /** + * Max rounds of any of the variants + */ + private const int MAX_ROUNDS = ROUNDS_1024; + + /** + * Key schedule parity constant + */ + private const ulong C_240 = 0x1BD11BDAA9FC1A22L; + + /* Pre-calculated modulo arithmetic tables for key schedule lookups */ + private static readonly int[] MOD9 = new int[MAX_ROUNDS]; + private static readonly int[] MOD17 = new int[MOD9.Length]; + private static readonly int[] MOD5 = new int[MOD9.Length]; + private static readonly int[] MOD3 = new int[MOD9.Length]; + + static ThreefishEngine() + { + for (int i = 0; i < MOD9.Length; i++) + { + MOD17[i] = i % 17; + MOD9[i] = i % 9; + MOD5[i] = i % 5; + MOD3[i] = i % 3; + } + } + + /** + * Block size in bytes + */ + private readonly int blocksizeBytes; + + /** + * Block size in 64 bit words + */ + private readonly int blocksizeWords; + + /** + * Buffer for byte oriented processBytes to call internal word API + */ + private readonly ulong[] currentBlock; + + /** + * Tweak bytes (2 byte t1,t2, calculated t3 and repeat of t1,t2 for modulo free lookup + */ + private readonly ulong[] t = new ulong[5]; + + /** + * Key schedule words + */ + private readonly ulong[] kw; + + /** + * The internal cipher implementation (varies by blocksize) + */ + private readonly ThreefishCipher cipher; + + private bool forEncryption; + + /// <summary> + /// Constructs a new Threefish cipher, with a specified block size. + /// </summary> + /// <param name="blocksizeBits">the block size in bits, one of <see cref="BLOCKSIZE_256"/>, <see cref="BLOCKSIZE_512"/>, + /// <see cref="BLOCKSIZE_1024"/> .</param> + public ThreefishEngine(int blocksizeBits) + { + this.blocksizeBytes = (blocksizeBits / 8); + this.blocksizeWords = (this.blocksizeBytes / 8); + this.currentBlock = new ulong[blocksizeWords]; + + /* + * Provide room for original key words, extended key word and repeat of key words for modulo + * free lookup of key schedule words. + */ + this.kw = new ulong[2 * blocksizeWords + 1]; + + switch (blocksizeBits) + { + case BLOCKSIZE_256: + cipher = new Threefish256Cipher(kw, t); + break; + case BLOCKSIZE_512: + cipher = new Threefish512Cipher(kw, t); + break; + case BLOCKSIZE_1024: + cipher = new Threefish1024Cipher(kw, t); + break; + default: + throw new ArgumentException( + "Invalid blocksize - Threefish is defined with block size of 256, 512, or 1024 bits"); + } + } + + /// <summary> + /// Initialise the engine. + /// </summary> + /// <param name="forEncryption">Initialise for encryption if true, for decryption if false.</param> + /// <param name="parameters">an instance of <see cref="TweakableBlockCipherParameters"/> or <see cref="KeyParameter"/> (to + /// use a 0 tweak)</param> + public void Init(bool forEncryption, ICipherParameters parameters) + { + byte[] keyBytes; + byte[] tweakBytes; + + if (parameters is TweakableBlockCipherParameters) + { + TweakableBlockCipherParameters tParams = (TweakableBlockCipherParameters)parameters; + keyBytes = tParams.Key.GetKey(); + tweakBytes = tParams.Tweak; + } + else if (parameters is KeyParameter) + { + keyBytes = ((KeyParameter)parameters).GetKey(); + tweakBytes = null; + } + else + { + throw new ArgumentException("Invalid parameter passed to Threefish init - " + + parameters.GetType().Name); + } + + ulong[] keyWords = null; + ulong[] tweakWords = null; + + if (keyBytes != null) + { + if (keyBytes.Length != this.blocksizeBytes) + { + throw new ArgumentException("Threefish key must be same size as block (" + blocksizeBytes + + " bytes)"); + } + keyWords = new ulong[blocksizeWords]; + for (int i = 0; i < keyWords.Length; i++) + { + keyWords[i] = BytesToWord(keyBytes, i * 8); + } + } + if (tweakBytes != null) + { + if (tweakBytes.Length != TWEAK_SIZE_BYTES) + { + throw new ArgumentException("Threefish tweak must be " + TWEAK_SIZE_BYTES + " bytes"); + } + tweakWords = new ulong[]{BytesToWord(tweakBytes, 0), BytesToWord(tweakBytes, 8)}; + } + Init(forEncryption, keyWords, tweakWords); + } + + /// <summary> + /// Initialise the engine, specifying the key and tweak directly. + /// </summary> + /// <param name="forEncryption">the cipher mode.</param> + /// <param name="key">the words of the key, or <code>null</code> to use the current key.</param> + /// <param name="tweak">the 2 word (128 bit) tweak, or <code>null</code> to use the current tweak.</param> + internal void Init(bool forEncryption, ulong[] key, ulong[] tweak) + { + this.forEncryption = forEncryption; + if (key != null) + { + SetKey(key); + } + if (tweak != null) + { + SetTweak(tweak); + } + } + + private void SetKey(ulong[] key) + { + if (key.Length != this.blocksizeWords) + { + throw new ArgumentException("Threefish key must be same size as block (" + blocksizeWords + + " words)"); + } + + /* + * Full subkey schedule is deferred to execution to avoid per cipher overhead (10k for 512, + * 20k for 1024). + * + * Key and tweak word sequences are repeated, and static MOD17/MOD9/MOD5/MOD3 calculations + * used, to avoid expensive mod computations during cipher operation. + */ + + ulong knw = C_240; + for (int i = 0; i < blocksizeWords; i++) + { + kw[i] = key[i]; + knw = knw ^ kw[i]; + } + kw[blocksizeWords] = knw; + Array.Copy(kw, 0, kw, blocksizeWords + 1, blocksizeWords); + } + + private void SetTweak(ulong[] tweak) + { + if (tweak.Length != TWEAK_SIZE_WORDS) + { + throw new ArgumentException("Tweak must be " + TWEAK_SIZE_WORDS + " words."); + } + + /* + * Tweak schedule partially repeated to avoid mod computations during cipher operation + */ + t[0] = tweak[0]; + t[1] = tweak[1]; + t[2] = t[0] ^ t[1]; + t[3] = t[0]; + t[4] = t[1]; + } + + public string AlgorithmName + { + get { return "Threefish-" + (blocksizeBytes * 8); } + } + + public bool IsPartialBlockOkay + { + get { return false; } + } + + public int GetBlockSize() + { + return blocksizeBytes; + } + + public void Reset() + { + } + + public int ProcessBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff) + { + if ((outOff + blocksizeBytes) > outBytes.Length) + { + throw new DataLengthException("Output buffer too short"); + } + + if ((inOff + blocksizeBytes) > inBytes.Length) + { + throw new DataLengthException("Input buffer too short"); + } + + for (int i = 0; i < blocksizeBytes; i += 8) + { + currentBlock[i >> 3] = BytesToWord(inBytes, inOff + i); + } + ProcessBlock(this.currentBlock, this.currentBlock); + for (int i = 0; i < blocksizeBytes; i += 8) + { + WordToBytes(this.currentBlock[i >> 3], outBytes, outOff + i); + } + + return blocksizeBytes; + } + + /// <summary> + /// Process a block of data represented as 64 bit words. + /// </summary> + /// <returns>the number of 8 byte words processed (which will be the same as the block size).</returns> + /// <param name="inWords">a block sized buffer of words to process.</param> + /// <param name="outWords">a block sized buffer of words to receive the output of the operation.</param> + /// <exception cref="DataLengthException">if either the input or output is not block sized</exception> + /// <exception cref="InvalidOperationException">if this engine is not initialised</exception> + internal int ProcessBlock(ulong[] inWords, ulong[] outWords) + { + if (kw[blocksizeWords] == 0) + { + throw new InvalidOperationException("Threefish engine not initialised"); + } + + if (inWords.Length != blocksizeWords) + { + throw new DataLengthException("Input buffer too short"); + } + if (outWords.Length != blocksizeWords) + { + throw new DataLengthException("Output buffer too short"); + } + + if (forEncryption) + { + cipher.EncryptBlock(inWords, outWords); + } + else + { + cipher.DecryptBlock(inWords, outWords); + } + + return blocksizeWords; + } + + /// <summary> + /// Read a single 64 bit word from input in LSB first order. + /// </summary> + internal static ulong BytesToWord(byte[] bytes, int off) + { + if ((off + 8) > bytes.Length) + { + // Help the JIT avoid index checks + throw new ArgumentException(); + } + + ulong word = 0; + int index = off; + + word = (bytes[index++] & 0xffUL); + word |= (bytes[index++] & 0xffUL) << 8; + word |= (bytes[index++] & 0xffUL) << 16; + word |= (bytes[index++] & 0xffUL) << 24; + word |= (bytes[index++] & 0xffUL) << 32; + word |= (bytes[index++] & 0xffUL) << 40; + word |= (bytes[index++] & 0xffUL) << 48; + word |= (bytes[index++] & 0xffUL) << 56; + + return word; + } + + /// <summary> + /// Write a 64 bit word to output in LSB first order. + /// </summary> + internal static void WordToBytes(ulong word, byte[] bytes, int off) + { + if ((off + 8) > bytes.Length) + { + // Help the JIT avoid index checks + throw new ArgumentException(); + } + int index = off; + + bytes[index++] = (byte)word; + bytes[index++] = (byte)(word >> 8); + bytes[index++] = (byte)(word >> 16); + bytes[index++] = (byte)(word >> 24); + bytes[index++] = (byte)(word >> 32); + bytes[index++] = (byte)(word >> 40); + bytes[index++] = (byte)(word >> 48); + bytes[index++] = (byte)(word >> 56); + } + + /** + * Rotate left + xor part of the mix operation. + */ + private static ulong RotlXor(ulong x, int n, ulong xor) + { + return ((x << n) | (x >> (64 - n))) ^ xor; + } + + /** + * Rotate xor + rotate right part of the unmix operation. + */ + private static ulong XorRotr(ulong x, int n, ulong xor) + { + ulong xored = x ^ xor; + return (xored >> n) | (xored << (64 - n)); + } + + private abstract class ThreefishCipher + { + /** + * The extended + repeated tweak words + */ + protected readonly ulong[] t; + /** + * The extended + repeated key words + */ + protected readonly ulong[] kw; + + protected ThreefishCipher(ulong[] kw, ulong[] t) + { + this.kw = kw; + this.t = t; + } + + internal abstract void EncryptBlock(ulong[] block, ulong[] outWords); + + internal abstract void DecryptBlock(ulong[] block, ulong[] outWords); + + } + + private sealed class Threefish256Cipher + : ThreefishCipher + { + /** + * Mix rotation constants defined in Skein 1.3 specification + */ + private const int ROTATION_0_0 = 14, ROTATION_0_1 = 16; + private const int ROTATION_1_0 = 52, ROTATION_1_1 = 57; + private const int ROTATION_2_0 = 23, ROTATION_2_1 = 40; + private const int ROTATION_3_0 = 5, ROTATION_3_1 = 37; + + private const int ROTATION_4_0 = 25, ROTATION_4_1 = 33; + private const int ROTATION_5_0 = 46, ROTATION_5_1 = 12; + private const int ROTATION_6_0 = 58, ROTATION_6_1 = 22; + private const int ROTATION_7_0 = 32, ROTATION_7_1 = 32; + + public Threefish256Cipher(ulong[] kw, ulong[] t) + : base(kw, t) + { + } + + internal override void EncryptBlock(ulong[] block, ulong[] outWords) + { + ulong[] kw = this.kw; + ulong[] t = this.t; + int[] mod5 = MOD5; + int[] mod3 = MOD3; + + /* Help the JIT avoid index bounds checks */ + if (kw.Length != 9) + { + throw new ArgumentException(); + } + if (t.Length != 5) + { + throw new ArgumentException(); + } + + /* + * Read 4 words of plaintext data, not using arrays for cipher state + */ + ulong b0 = block[0]; + ulong b1 = block[1]; + ulong b2 = block[2]; + ulong b3 = block[3]; + + /* + * First subkey injection. + */ + b0 += kw[0]; + b1 += kw[1] + t[0]; + b2 += kw[2] + t[1]; + b3 += kw[3]; + + /* + * Rounds loop, unrolled to 8 rounds per iteration. + * + * Unrolling to multiples of 4 avoids the mod 4 check for key injection, and allows + * inlining of the permutations, which cycle every of 2 rounds (avoiding array + * index/lookup). + * + * Unrolling to multiples of 8 avoids the mod 8 rotation constant lookup, and allows + * inlining constant rotation values (avoiding array index/lookup). + */ + + for (int d = 1; d < (ROUNDS_256 / 4); d += 2) + { + int dm5 = mod5[d]; + int dm3 = mod3[d]; + + /* + * 4 rounds of mix and permute. + * + * Permute schedule has a 2 round cycle, so permutes are inlined in the mix + * operations in each 4 round block. + */ + b1 = RotlXor(b1, ROTATION_0_0, b0 += b1); + b3 = RotlXor(b3, ROTATION_0_1, b2 += b3); + + b3 = RotlXor(b3, ROTATION_1_0, b0 += b3); + b1 = RotlXor(b1, ROTATION_1_1, b2 += b1); + + b1 = RotlXor(b1, ROTATION_2_0, b0 += b1); + b3 = RotlXor(b3, ROTATION_2_1, b2 += b3); + + b3 = RotlXor(b3, ROTATION_3_0, b0 += b3); + b1 = RotlXor(b1, ROTATION_3_1, b2 += b1); + + /* + * Subkey injection for first 4 rounds. + */ + b0 += kw[dm5]; + b1 += kw[dm5 + 1] + t[dm3]; + b2 += kw[dm5 + 2] + t[dm3 + 1]; + b3 += kw[dm5 + 3] + (uint)d; + + /* + * 4 more rounds of mix/permute + */ + b1 = RotlXor(b1, ROTATION_4_0, b0 += b1); + b3 = RotlXor(b3, ROTATION_4_1, b2 += b3); + + b3 = RotlXor(b3, ROTATION_5_0, b0 += b3); + b1 = RotlXor(b1, ROTATION_5_1, b2 += b1); + + b1 = RotlXor(b1, ROTATION_6_0, b0 += b1); + b3 = RotlXor(b3, ROTATION_6_1, b2 += b3); + + b3 = RotlXor(b3, ROTATION_7_0, b0 += b3); + b1 = RotlXor(b1, ROTATION_7_1, b2 += b1); + + /* + * Subkey injection for next 4 rounds. + */ + b0 += kw[dm5 + 1]; + b1 += kw[dm5 + 2] + t[dm3 + 1]; + b2 += kw[dm5 + 3] + t[dm3 + 2]; + b3 += kw[dm5 + 4] + (uint)d + 1; + } + + /* + * Output cipher state. + */ + outWords[0] = b0; + outWords[1] = b1; + outWords[2] = b2; + outWords[3] = b3; + } + + internal override void DecryptBlock(ulong[] block, ulong[] state) + { + ulong[] kw = this.kw; + ulong[] t = this.t; + int[] mod5 = MOD5; + int[] mod3 = MOD3; + + /* Help the JIT avoid index bounds checks */ + if (kw.Length != 9) + { + throw new ArgumentException(); + } + if (t.Length != 5) + { + throw new ArgumentException(); + } + + ulong b0 = block[0]; + ulong b1 = block[1]; + ulong b2 = block[2]; + ulong b3 = block[3]; + + for (int d = (ROUNDS_256 / 4) - 1; d >= 1; d -= 2) + { + int dm5 = mod5[d]; + int dm3 = mod3[d]; + + /* Reverse key injection for second 4 rounds */ + b0 -= kw[dm5 + 1]; + b1 -= kw[dm5 + 2] + t[dm3 + 1]; + b2 -= kw[dm5 + 3] + t[dm3 + 2]; + b3 -= kw[dm5 + 4] + (uint)d + 1; + + /* Reverse second 4 mix/permute rounds */ + + b3 = XorRotr(b3, ROTATION_7_0, b0); + b0 -= b3; + b1 = XorRotr(b1, ROTATION_7_1, b2); + b2 -= b1; + + b1 = XorRotr(b1, ROTATION_6_0, b0); + b0 -= b1; + b3 = XorRotr(b3, ROTATION_6_1, b2); + b2 -= b3; + + b3 = XorRotr(b3, ROTATION_5_0, b0); + b0 -= b3; + b1 = XorRotr(b1, ROTATION_5_1, b2); + b2 -= b1; + + b1 = XorRotr(b1, ROTATION_4_0, b0); + b0 -= b1; + b3 = XorRotr(b3, ROTATION_4_1, b2); + b2 -= b3; + + /* Reverse key injection for first 4 rounds */ + b0 -= kw[dm5]; + b1 -= kw[dm5 + 1] + t[dm3]; + b2 -= kw[dm5 + 2] + t[dm3 + 1]; + b3 -= kw[dm5 + 3] + (uint)d; + + /* Reverse first 4 mix/permute rounds */ + b3 = XorRotr(b3, ROTATION_3_0, b0); + b0 -= b3; + b1 = XorRotr(b1, ROTATION_3_1, b2); + b2 -= b1; + + b1 = XorRotr(b1, ROTATION_2_0, b0); + b0 -= b1; + b3 = XorRotr(b3, ROTATION_2_1, b2); + b2 -= b3; + + b3 = XorRotr(b3, ROTATION_1_0, b0); + b0 -= b3; + b1 = XorRotr(b1, ROTATION_1_1, b2); + b2 -= b1; + + b1 = XorRotr(b1, ROTATION_0_0, b0); + b0 -= b1; + b3 = XorRotr(b3, ROTATION_0_1, b2); + b2 -= b3; + } + + /* + * First subkey uninjection. + */ + b0 -= kw[0]; + b1 -= kw[1] + t[0]; + b2 -= kw[2] + t[1]; + b3 -= kw[3]; + + /* + * Output cipher state. + */ + state[0] = b0; + state[1] = b1; + state[2] = b2; + state[3] = b3; + } + + } + + private sealed class Threefish512Cipher + : ThreefishCipher + { + /** + * Mix rotation constants defined in Skein 1.3 specification + */ + private const int ROTATION_0_0 = 46, ROTATION_0_1 = 36, ROTATION_0_2 = 19, ROTATION_0_3 = 37; + private const int ROTATION_1_0 = 33, ROTATION_1_1 = 27, ROTATION_1_2 = 14, ROTATION_1_3 = 42; + private const int ROTATION_2_0 = 17, ROTATION_2_1 = 49, ROTATION_2_2 = 36, ROTATION_2_3 = 39; + private const int ROTATION_3_0 = 44, ROTATION_3_1 = 9, ROTATION_3_2 = 54, ROTATION_3_3 = 56; + + private const int ROTATION_4_0 = 39, ROTATION_4_1 = 30, ROTATION_4_2 = 34, ROTATION_4_3 = 24; + private const int ROTATION_5_0 = 13, ROTATION_5_1 = 50, ROTATION_5_2 = 10, ROTATION_5_3 = 17; + private const int ROTATION_6_0 = 25, ROTATION_6_1 = 29, ROTATION_6_2 = 39, ROTATION_6_3 = 43; + private const int ROTATION_7_0 = 8, ROTATION_7_1 = 35, ROTATION_7_2 = 56, ROTATION_7_3 = 22; + + internal Threefish512Cipher(ulong[] kw, ulong[] t) + : base(kw, t) + { + } + + internal override void EncryptBlock(ulong[] block, ulong[] outWords) + { + ulong[] kw = this.kw; + ulong[] t = this.t; + int[] mod9 = MOD9; + int[] mod3 = MOD3; + + /* Help the JIT avoid index bounds checks */ + if (kw.Length != 17) + { + throw new ArgumentException(); + } + if (t.Length != 5) + { + throw new ArgumentException(); + } + + /* + * Read 8 words of plaintext data, not using arrays for cipher state + */ + ulong b0 = block[0]; + ulong b1 = block[1]; + ulong b2 = block[2]; + ulong b3 = block[3]; + ulong b4 = block[4]; + ulong b5 = block[5]; + ulong b6 = block[6]; + ulong b7 = block[7]; + + /* + * First subkey injection. + */ + b0 += kw[0]; + b1 += kw[1]; + b2 += kw[2]; + b3 += kw[3]; + b4 += kw[4]; + b5 += kw[5] + t[0]; + b6 += kw[6] + t[1]; + b7 += kw[7]; + + /* + * Rounds loop, unrolled to 8 rounds per iteration. + * + * Unrolling to multiples of 4 avoids the mod 4 check for key injection, and allows + * inlining of the permutations, which cycle every of 4 rounds (avoiding array + * index/lookup). + * + * Unrolling to multiples of 8 avoids the mod 8 rotation constant lookup, and allows + * inlining constant rotation values (avoiding array index/lookup). + */ + + for (int d = 1; d < (ROUNDS_512 / 4); d += 2) + { + int dm9 = mod9[d]; + int dm3 = mod3[d]; + + /* + * 4 rounds of mix and permute. + * + * Permute schedule has a 4 round cycle, so permutes are inlined in the mix + * operations in each 4 round block. + */ + b1 = RotlXor(b1, ROTATION_0_0, b0 += b1); + b3 = RotlXor(b3, ROTATION_0_1, b2 += b3); + b5 = RotlXor(b5, ROTATION_0_2, b4 += b5); + b7 = RotlXor(b7, ROTATION_0_3, b6 += b7); + + b1 = RotlXor(b1, ROTATION_1_0, b2 += b1); + b7 = RotlXor(b7, ROTATION_1_1, b4 += b7); + b5 = RotlXor(b5, ROTATION_1_2, b6 += b5); + b3 = RotlXor(b3, ROTATION_1_3, b0 += b3); + + b1 = RotlXor(b1, ROTATION_2_0, b4 += b1); + b3 = RotlXor(b3, ROTATION_2_1, b6 += b3); + b5 = RotlXor(b5, ROTATION_2_2, b0 += b5); + b7 = RotlXor(b7, ROTATION_2_3, b2 += b7); + + b1 = RotlXor(b1, ROTATION_3_0, b6 += b1); + b7 = RotlXor(b7, ROTATION_3_1, b0 += b7); + b5 = RotlXor(b5, ROTATION_3_2, b2 += b5); + b3 = RotlXor(b3, ROTATION_3_3, b4 += b3); + + /* + * Subkey injection for first 4 rounds. + */ + b0 += kw[dm9]; + b1 += kw[dm9 + 1]; + b2 += kw[dm9 + 2]; + b3 += kw[dm9 + 3]; + b4 += kw[dm9 + 4]; + b5 += kw[dm9 + 5] + t[dm3]; + b6 += kw[dm9 + 6] + t[dm3 + 1]; + b7 += kw[dm9 + 7] + (uint)d; + + /* + * 4 more rounds of mix/permute + */ + b1 = RotlXor(b1, ROTATION_4_0, b0 += b1); + b3 = RotlXor(b3, ROTATION_4_1, b2 += b3); + b5 = RotlXor(b5, ROTATION_4_2, b4 += b5); + b7 = RotlXor(b7, ROTATION_4_3, b6 += b7); + + b1 = RotlXor(b1, ROTATION_5_0, b2 += b1); + b7 = RotlXor(b7, ROTATION_5_1, b4 += b7); + b5 = RotlXor(b5, ROTATION_5_2, b6 += b5); + b3 = RotlXor(b3, ROTATION_5_3, b0 += b3); + + b1 = RotlXor(b1, ROTATION_6_0, b4 += b1); + b3 = RotlXor(b3, ROTATION_6_1, b6 += b3); + b5 = RotlXor(b5, ROTATION_6_2, b0 += b5); + b7 = RotlXor(b7, ROTATION_6_3, b2 += b7); + + b1 = RotlXor(b1, ROTATION_7_0, b6 += b1); + b7 = RotlXor(b7, ROTATION_7_1, b0 += b7); + b5 = RotlXor(b5, ROTATION_7_2, b2 += b5); + b3 = RotlXor(b3, ROTATION_7_3, b4 += b3); + + /* + * Subkey injection for next 4 rounds. + */ + b0 += kw[dm9 + 1]; + b1 += kw[dm9 + 2]; + b2 += kw[dm9 + 3]; + b3 += kw[dm9 + 4]; + b4 += kw[dm9 + 5]; + b5 += kw[dm9 + 6] + t[dm3 + 1]; + b6 += kw[dm9 + 7] + t[dm3 + 2]; + b7 += kw[dm9 + 8] + (uint)d + 1; + } + + /* + * Output cipher state. + */ + outWords[0] = b0; + outWords[1] = b1; + outWords[2] = b2; + outWords[3] = b3; + outWords[4] = b4; + outWords[5] = b5; + outWords[6] = b6; + outWords[7] = b7; + } + + internal override void DecryptBlock(ulong[] block, ulong[] state) + { + ulong[] kw = this.kw; + ulong[] t = this.t; + int[] mod9 = MOD9; + int[] mod3 = MOD3; + + /* Help the JIT avoid index bounds checks */ + if (kw.Length != 17) + { + throw new ArgumentException(); + } + if (t.Length != 5) + { + throw new ArgumentException(); + } + + ulong b0 = block[0]; + ulong b1 = block[1]; + ulong b2 = block[2]; + ulong b3 = block[3]; + ulong b4 = block[4]; + ulong b5 = block[5]; + ulong b6 = block[6]; + ulong b7 = block[7]; + + for (int d = (ROUNDS_512 / 4) - 1; d >= 1; d -= 2) + { + int dm9 = mod9[d]; + int dm3 = mod3[d]; + + /* Reverse key injection for second 4 rounds */ + b0 -= kw[dm9 + 1]; + b1 -= kw[dm9 + 2]; + b2 -= kw[dm9 + 3]; + b3 -= kw[dm9 + 4]; + b4 -= kw[dm9 + 5]; + b5 -= kw[dm9 + 6] + t[dm3 + 1]; + b6 -= kw[dm9 + 7] + t[dm3 + 2]; + b7 -= kw[dm9 + 8] + (uint)d + 1; + + /* Reverse second 4 mix/permute rounds */ + + b1 = XorRotr(b1, ROTATION_7_0, b6); + b6 -= b1; + b7 = XorRotr(b7, ROTATION_7_1, b0); + b0 -= b7; + b5 = XorRotr(b5, ROTATION_7_2, b2); + b2 -= b5; + b3 = XorRotr(b3, ROTATION_7_3, b4); + b4 -= b3; + + b1 = XorRotr(b1, ROTATION_6_0, b4); + b4 -= b1; + b3 = XorRotr(b3, ROTATION_6_1, b6); + b6 -= b3; + b5 = XorRotr(b5, ROTATION_6_2, b0); + b0 -= b5; + b7 = XorRotr(b7, ROTATION_6_3, b2); + b2 -= b7; + + b1 = XorRotr(b1, ROTATION_5_0, b2); + b2 -= b1; + b7 = XorRotr(b7, ROTATION_5_1, b4); + b4 -= b7; + b5 = XorRotr(b5, ROTATION_5_2, b6); + b6 -= b5; + b3 = XorRotr(b3, ROTATION_5_3, b0); + b0 -= b3; + + b1 = XorRotr(b1, ROTATION_4_0, b0); + b0 -= b1; + b3 = XorRotr(b3, ROTATION_4_1, b2); + b2 -= b3; + b5 = XorRotr(b5, ROTATION_4_2, b4); + b4 -= b5; + b7 = XorRotr(b7, ROTATION_4_3, b6); + b6 -= b7; + + /* Reverse key injection for first 4 rounds */ + b0 -= kw[dm9]; + b1 -= kw[dm9 + 1]; + b2 -= kw[dm9 + 2]; + b3 -= kw[dm9 + 3]; + b4 -= kw[dm9 + 4]; + b5 -= kw[dm9 + 5] + t[dm3]; + b6 -= kw[dm9 + 6] + t[dm3 + 1]; + b7 -= kw[dm9 + 7] + (uint)d; + + /* Reverse first 4 mix/permute rounds */ + b1 = XorRotr(b1, ROTATION_3_0, b6); + b6 -= b1; + b7 = XorRotr(b7, ROTATION_3_1, b0); + b0 -= b7; + b5 = XorRotr(b5, ROTATION_3_2, b2); + b2 -= b5; + b3 = XorRotr(b3, ROTATION_3_3, b4); + b4 -= b3; + + b1 = XorRotr(b1, ROTATION_2_0, b4); + b4 -= b1; + b3 = XorRotr(b3, ROTATION_2_1, b6); + b6 -= b3; + b5 = XorRotr(b5, ROTATION_2_2, b0); + b0 -= b5; + b7 = XorRotr(b7, ROTATION_2_3, b2); + b2 -= b7; + + b1 = XorRotr(b1, ROTATION_1_0, b2); + b2 -= b1; + b7 = XorRotr(b7, ROTATION_1_1, b4); + b4 -= b7; + b5 = XorRotr(b5, ROTATION_1_2, b6); + b6 -= b5; + b3 = XorRotr(b3, ROTATION_1_3, b0); + b0 -= b3; + + b1 = XorRotr(b1, ROTATION_0_0, b0); + b0 -= b1; + b3 = XorRotr(b3, ROTATION_0_1, b2); + b2 -= b3; + b5 = XorRotr(b5, ROTATION_0_2, b4); + b4 -= b5; + b7 = XorRotr(b7, ROTATION_0_3, b6); + b6 -= b7; + } + + /* + * First subkey uninjection. + */ + b0 -= kw[0]; + b1 -= kw[1]; + b2 -= kw[2]; + b3 -= kw[3]; + b4 -= kw[4]; + b5 -= kw[5] + t[0]; + b6 -= kw[6] + t[1]; + b7 -= kw[7]; + + /* + * Output cipher state. + */ + state[0] = b0; + state[1] = b1; + state[2] = b2; + state[3] = b3; + state[4] = b4; + state[5] = b5; + state[6] = b6; + state[7] = b7; + } + } + + private sealed class Threefish1024Cipher + : ThreefishCipher + { + /** + * Mix rotation constants defined in Skein 1.3 specification + */ + private const int ROTATION_0_0 = 24, ROTATION_0_1 = 13, ROTATION_0_2 = 8, ROTATION_0_3 = 47; + private const int ROTATION_0_4 = 8, ROTATION_0_5 = 17, ROTATION_0_6 = 22, ROTATION_0_7 = 37; + private const int ROTATION_1_0 = 38, ROTATION_1_1 = 19, ROTATION_1_2 = 10, ROTATION_1_3 = 55; + private const int ROTATION_1_4 = 49, ROTATION_1_5 = 18, ROTATION_1_6 = 23, ROTATION_1_7 = 52; + private const int ROTATION_2_0 = 33, ROTATION_2_1 = 4, ROTATION_2_2 = 51, ROTATION_2_3 = 13; + private const int ROTATION_2_4 = 34, ROTATION_2_5 = 41, ROTATION_2_6 = 59, ROTATION_2_7 = 17; + private const int ROTATION_3_0 = 5, ROTATION_3_1 = 20, ROTATION_3_2 = 48, ROTATION_3_3 = 41; + private const int ROTATION_3_4 = 47, ROTATION_3_5 = 28, ROTATION_3_6 = 16, ROTATION_3_7 = 25; + + private const int ROTATION_4_0 = 41, ROTATION_4_1 = 9, ROTATION_4_2 = 37, ROTATION_4_3 = 31; + private const int ROTATION_4_4 = 12, ROTATION_4_5 = 47, ROTATION_4_6 = 44, ROTATION_4_7 = 30; + private const int ROTATION_5_0 = 16, ROTATION_5_1 = 34, ROTATION_5_2 = 56, ROTATION_5_3 = 51; + private const int ROTATION_5_4 = 4, ROTATION_5_5 = 53, ROTATION_5_6 = 42, ROTATION_5_7 = 41; + private const int ROTATION_6_0 = 31, ROTATION_6_1 = 44, ROTATION_6_2 = 47, ROTATION_6_3 = 46; + private const int ROTATION_6_4 = 19, ROTATION_6_5 = 42, ROTATION_6_6 = 44, ROTATION_6_7 = 25; + private const int ROTATION_7_0 = 9, ROTATION_7_1 = 48, ROTATION_7_2 = 35, ROTATION_7_3 = 52; + private const int ROTATION_7_4 = 23, ROTATION_7_5 = 31, ROTATION_7_6 = 37, ROTATION_7_7 = 20; + + public Threefish1024Cipher(ulong[] kw, ulong[] t) + : base(kw, t) + { + } + + internal override void EncryptBlock(ulong[] block, ulong[] outWords) + { + ulong[] kw = this.kw; + ulong[] t = this.t; + int[] mod17 = MOD17; + int[] mod3 = MOD3; + + /* Help the JIT avoid index bounds checks */ + if (kw.Length != 33) + { + throw new ArgumentException(); + } + if (t.Length != 5) + { + throw new ArgumentException(); + } + + /* + * Read 16 words of plaintext data, not using arrays for cipher state + */ + ulong b0 = block[0]; + ulong b1 = block[1]; + ulong b2 = block[2]; + ulong b3 = block[3]; + ulong b4 = block[4]; + ulong b5 = block[5]; + ulong b6 = block[6]; + ulong b7 = block[7]; + ulong b8 = block[8]; + ulong b9 = block[9]; + ulong b10 = block[10]; + ulong b11 = block[11]; + ulong b12 = block[12]; + ulong b13 = block[13]; + ulong b14 = block[14]; + ulong b15 = block[15]; + + /* + * First subkey injection. + */ + b0 += kw[0]; + b1 += kw[1]; + b2 += kw[2]; + b3 += kw[3]; + b4 += kw[4]; + b5 += kw[5]; + b6 += kw[6]; + b7 += kw[7]; + b8 += kw[8]; + b9 += kw[9]; + b10 += kw[10]; + b11 += kw[11]; + b12 += kw[12]; + b13 += kw[13] + t[0]; + b14 += kw[14] + t[1]; + b15 += kw[15]; + + /* + * Rounds loop, unrolled to 8 rounds per iteration. + * + * Unrolling to multiples of 4 avoids the mod 4 check for key injection, and allows + * inlining of the permutations, which cycle every of 4 rounds (avoiding array + * index/lookup). + * + * Unrolling to multiples of 8 avoids the mod 8 rotation constant lookup, and allows + * inlining constant rotation values (avoiding array index/lookup). + */ + + for (int d = 1; d < (ROUNDS_1024 / 4); d += 2) + { + int dm17 = mod17[d]; + int dm3 = mod3[d]; + + /* + * 4 rounds of mix and permute. + * + * Permute schedule has a 4 round cycle, so permutes are inlined in the mix + * operations in each 4 round block. + */ + b1 = RotlXor(b1, ROTATION_0_0, b0 += b1); + b3 = RotlXor(b3, ROTATION_0_1, b2 += b3); + b5 = RotlXor(b5, ROTATION_0_2, b4 += b5); + b7 = RotlXor(b7, ROTATION_0_3, b6 += b7); + b9 = RotlXor(b9, ROTATION_0_4, b8 += b9); + b11 = RotlXor(b11, ROTATION_0_5, b10 += b11); + b13 = RotlXor(b13, ROTATION_0_6, b12 += b13); + b15 = RotlXor(b15, ROTATION_0_7, b14 += b15); + + b9 = RotlXor(b9, ROTATION_1_0, b0 += b9); + b13 = RotlXor(b13, ROTATION_1_1, b2 += b13); + b11 = RotlXor(b11, ROTATION_1_2, b6 += b11); + b15 = RotlXor(b15, ROTATION_1_3, b4 += b15); + b7 = RotlXor(b7, ROTATION_1_4, b10 += b7); + b3 = RotlXor(b3, ROTATION_1_5, b12 += b3); + b5 = RotlXor(b5, ROTATION_1_6, b14 += b5); + b1 = RotlXor(b1, ROTATION_1_7, b8 += b1); + + b7 = RotlXor(b7, ROTATION_2_0, b0 += b7); + b5 = RotlXor(b5, ROTATION_2_1, b2 += b5); + b3 = RotlXor(b3, ROTATION_2_2, b4 += b3); + b1 = RotlXor(b1, ROTATION_2_3, b6 += b1); + b15 = RotlXor(b15, ROTATION_2_4, b12 += b15); + b13 = RotlXor(b13, ROTATION_2_5, b14 += b13); + b11 = RotlXor(b11, ROTATION_2_6, b8 += b11); + b9 = RotlXor(b9, ROTATION_2_7, b10 += b9); + + b15 = RotlXor(b15, ROTATION_3_0, b0 += b15); + b11 = RotlXor(b11, ROTATION_3_1, b2 += b11); + b13 = RotlXor(b13, ROTATION_3_2, b6 += b13); + b9 = RotlXor(b9, ROTATION_3_3, b4 += b9); + b1 = RotlXor(b1, ROTATION_3_4, b14 += b1); + b5 = RotlXor(b5, ROTATION_3_5, b8 += b5); + b3 = RotlXor(b3, ROTATION_3_6, b10 += b3); + b7 = RotlXor(b7, ROTATION_3_7, b12 += b7); + + /* + * Subkey injection for first 4 rounds. + */ + b0 += kw[dm17]; + b1 += kw[dm17 + 1]; + b2 += kw[dm17 + 2]; + b3 += kw[dm17 + 3]; + b4 += kw[dm17 + 4]; + b5 += kw[dm17 + 5]; + b6 += kw[dm17 + 6]; + b7 += kw[dm17 + 7]; + b8 += kw[dm17 + 8]; + b9 += kw[dm17 + 9]; + b10 += kw[dm17 + 10]; + b11 += kw[dm17 + 11]; + b12 += kw[dm17 + 12]; + b13 += kw[dm17 + 13] + t[dm3]; + b14 += kw[dm17 + 14] + t[dm3 + 1]; + b15 += kw[dm17 + 15] + (uint)d; + + /* + * 4 more rounds of mix/permute + */ + b1 = RotlXor(b1, ROTATION_4_0, b0 += b1); + b3 = RotlXor(b3, ROTATION_4_1, b2 += b3); + b5 = RotlXor(b5, ROTATION_4_2, b4 += b5); + b7 = RotlXor(b7, ROTATION_4_3, b6 += b7); + b9 = RotlXor(b9, ROTATION_4_4, b8 += b9); + b11 = RotlXor(b11, ROTATION_4_5, b10 += b11); + b13 = RotlXor(b13, ROTATION_4_6, b12 += b13); + b15 = RotlXor(b15, ROTATION_4_7, b14 += b15); + + b9 = RotlXor(b9, ROTATION_5_0, b0 += b9); + b13 = RotlXor(b13, ROTATION_5_1, b2 += b13); + b11 = RotlXor(b11, ROTATION_5_2, b6 += b11); + b15 = RotlXor(b15, ROTATION_5_3, b4 += b15); + b7 = RotlXor(b7, ROTATION_5_4, b10 += b7); + b3 = RotlXor(b3, ROTATION_5_5, b12 += b3); + b5 = RotlXor(b5, ROTATION_5_6, b14 += b5); + b1 = RotlXor(b1, ROTATION_5_7, b8 += b1); + + b7 = RotlXor(b7, ROTATION_6_0, b0 += b7); + b5 = RotlXor(b5, ROTATION_6_1, b2 += b5); + b3 = RotlXor(b3, ROTATION_6_2, b4 += b3); + b1 = RotlXor(b1, ROTATION_6_3, b6 += b1); + b15 = RotlXor(b15, ROTATION_6_4, b12 += b15); + b13 = RotlXor(b13, ROTATION_6_5, b14 += b13); + b11 = RotlXor(b11, ROTATION_6_6, b8 += b11); + b9 = RotlXor(b9, ROTATION_6_7, b10 += b9); + + b15 = RotlXor(b15, ROTATION_7_0, b0 += b15); + b11 = RotlXor(b11, ROTATION_7_1, b2 += b11); + b13 = RotlXor(b13, ROTATION_7_2, b6 += b13); + b9 = RotlXor(b9, ROTATION_7_3, b4 += b9); + b1 = RotlXor(b1, ROTATION_7_4, b14 += b1); + b5 = RotlXor(b5, ROTATION_7_5, b8 += b5); + b3 = RotlXor(b3, ROTATION_7_6, b10 += b3); + b7 = RotlXor(b7, ROTATION_7_7, b12 += b7); + + /* + * Subkey injection for next 4 rounds. + */ + b0 += kw[dm17 + 1]; + b1 += kw[dm17 + 2]; + b2 += kw[dm17 + 3]; + b3 += kw[dm17 + 4]; + b4 += kw[dm17 + 5]; + b5 += kw[dm17 + 6]; + b6 += kw[dm17 + 7]; + b7 += kw[dm17 + 8]; + b8 += kw[dm17 + 9]; + b9 += kw[dm17 + 10]; + b10 += kw[dm17 + 11]; + b11 += kw[dm17 + 12]; + b12 += kw[dm17 + 13]; + b13 += kw[dm17 + 14] + t[dm3 + 1]; + b14 += kw[dm17 + 15] + t[dm3 + 2]; + b15 += kw[dm17 + 16] + (uint)d + 1; + + } + + /* + * Output cipher state. + */ + outWords[0] = b0; + outWords[1] = b1; + outWords[2] = b2; + outWords[3] = b3; + outWords[4] = b4; + outWords[5] = b5; + outWords[6] = b6; + outWords[7] = b7; + outWords[8] = b8; + outWords[9] = b9; + outWords[10] = b10; + outWords[11] = b11; + outWords[12] = b12; + outWords[13] = b13; + outWords[14] = b14; + outWords[15] = b15; + } + + internal override void DecryptBlock(ulong[] block, ulong[] state) + { + ulong[] kw = this.kw; + ulong[] t = this.t; + int[] mod17 = MOD17; + int[] mod3 = MOD3; + + /* Help the JIT avoid index bounds checks */ + if (kw.Length != 33) + { + throw new ArgumentException(); + } + if (t.Length != 5) + { + throw new ArgumentException(); + } + + ulong b0 = block[0]; + ulong b1 = block[1]; + ulong b2 = block[2]; + ulong b3 = block[3]; + ulong b4 = block[4]; + ulong b5 = block[5]; + ulong b6 = block[6]; + ulong b7 = block[7]; + ulong b8 = block[8]; + ulong b9 = block[9]; + ulong b10 = block[10]; + ulong b11 = block[11]; + ulong b12 = block[12]; + ulong b13 = block[13]; + ulong b14 = block[14]; + ulong b15 = block[15]; + + for (int d = (ROUNDS_1024 / 4) - 1; d >= 1; d -= 2) + { + int dm17 = mod17[d]; + int dm3 = mod3[d]; + + /* Reverse key injection for second 4 rounds */ + b0 -= kw[dm17 + 1]; + b1 -= kw[dm17 + 2]; + b2 -= kw[dm17 + 3]; + b3 -= kw[dm17 + 4]; + b4 -= kw[dm17 + 5]; + b5 -= kw[dm17 + 6]; + b6 -= kw[dm17 + 7]; + b7 -= kw[dm17 + 8]; + b8 -= kw[dm17 + 9]; + b9 -= kw[dm17 + 10]; + b10 -= kw[dm17 + 11]; + b11 -= kw[dm17 + 12]; + b12 -= kw[dm17 + 13]; + b13 -= kw[dm17 + 14] + t[dm3 + 1]; + b14 -= kw[dm17 + 15] + t[dm3 + 2]; + b15 -= kw[dm17 + 16] + (uint)d + 1; + + /* Reverse second 4 mix/permute rounds */ + b15 = XorRotr(b15, ROTATION_7_0, b0); + b0 -= b15; + b11 = XorRotr(b11, ROTATION_7_1, b2); + b2 -= b11; + b13 = XorRotr(b13, ROTATION_7_2, b6); + b6 -= b13; + b9 = XorRotr(b9, ROTATION_7_3, b4); + b4 -= b9; + b1 = XorRotr(b1, ROTATION_7_4, b14); + b14 -= b1; + b5 = XorRotr(b5, ROTATION_7_5, b8); + b8 -= b5; + b3 = XorRotr(b3, ROTATION_7_6, b10); + b10 -= b3; + b7 = XorRotr(b7, ROTATION_7_7, b12); + b12 -= b7; + + b7 = XorRotr(b7, ROTATION_6_0, b0); + b0 -= b7; + b5 = XorRotr(b5, ROTATION_6_1, b2); + b2 -= b5; + b3 = XorRotr(b3, ROTATION_6_2, b4); + b4 -= b3; + b1 = XorRotr(b1, ROTATION_6_3, b6); + b6 -= b1; + b15 = XorRotr(b15, ROTATION_6_4, b12); + b12 -= b15; + b13 = XorRotr(b13, ROTATION_6_5, b14); + b14 -= b13; + b11 = XorRotr(b11, ROTATION_6_6, b8); + b8 -= b11; + b9 = XorRotr(b9, ROTATION_6_7, b10); + b10 -= b9; + + b9 = XorRotr(b9, ROTATION_5_0, b0); + b0 -= b9; + b13 = XorRotr(b13, ROTATION_5_1, b2); + b2 -= b13; + b11 = XorRotr(b11, ROTATION_5_2, b6); + b6 -= b11; + b15 = XorRotr(b15, ROTATION_5_3, b4); + b4 -= b15; + b7 = XorRotr(b7, ROTATION_5_4, b10); + b10 -= b7; + b3 = XorRotr(b3, ROTATION_5_5, b12); + b12 -= b3; + b5 = XorRotr(b5, ROTATION_5_6, b14); + b14 -= b5; + b1 = XorRotr(b1, ROTATION_5_7, b8); + b8 -= b1; + + b1 = XorRotr(b1, ROTATION_4_0, b0); + b0 -= b1; + b3 = XorRotr(b3, ROTATION_4_1, b2); + b2 -= b3; + b5 = XorRotr(b5, ROTATION_4_2, b4); + b4 -= b5; + b7 = XorRotr(b7, ROTATION_4_3, b6); + b6 -= b7; + b9 = XorRotr(b9, ROTATION_4_4, b8); + b8 -= b9; + b11 = XorRotr(b11, ROTATION_4_5, b10); + b10 -= b11; + b13 = XorRotr(b13, ROTATION_4_6, b12); + b12 -= b13; + b15 = XorRotr(b15, ROTATION_4_7, b14); + b14 -= b15; + + /* Reverse key injection for first 4 rounds */ + b0 -= kw[dm17]; + b1 -= kw[dm17 + 1]; + b2 -= kw[dm17 + 2]; + b3 -= kw[dm17 + 3]; + b4 -= kw[dm17 + 4]; + b5 -= kw[dm17 + 5]; + b6 -= kw[dm17 + 6]; + b7 -= kw[dm17 + 7]; + b8 -= kw[dm17 + 8]; + b9 -= kw[dm17 + 9]; + b10 -= kw[dm17 + 10]; + b11 -= kw[dm17 + 11]; + b12 -= kw[dm17 + 12]; + b13 -= kw[dm17 + 13] + t[dm3]; + b14 -= kw[dm17 + 14] + t[dm3 + 1]; + b15 -= kw[dm17 + 15] + (uint)d; + + /* Reverse first 4 mix/permute rounds */ + b15 = XorRotr(b15, ROTATION_3_0, b0); + b0 -= b15; + b11 = XorRotr(b11, ROTATION_3_1, b2); + b2 -= b11; + b13 = XorRotr(b13, ROTATION_3_2, b6); + b6 -= b13; + b9 = XorRotr(b9, ROTATION_3_3, b4); + b4 -= b9; + b1 = XorRotr(b1, ROTATION_3_4, b14); + b14 -= b1; + b5 = XorRotr(b5, ROTATION_3_5, b8); + b8 -= b5; + b3 = XorRotr(b3, ROTATION_3_6, b10); + b10 -= b3; + b7 = XorRotr(b7, ROTATION_3_7, b12); + b12 -= b7; + + b7 = XorRotr(b7, ROTATION_2_0, b0); + b0 -= b7; + b5 = XorRotr(b5, ROTATION_2_1, b2); + b2 -= b5; + b3 = XorRotr(b3, ROTATION_2_2, b4); + b4 -= b3; + b1 = XorRotr(b1, ROTATION_2_3, b6); + b6 -= b1; + b15 = XorRotr(b15, ROTATION_2_4, b12); + b12 -= b15; + b13 = XorRotr(b13, ROTATION_2_5, b14); + b14 -= b13; + b11 = XorRotr(b11, ROTATION_2_6, b8); + b8 -= b11; + b9 = XorRotr(b9, ROTATION_2_7, b10); + b10 -= b9; + + b9 = XorRotr(b9, ROTATION_1_0, b0); + b0 -= b9; + b13 = XorRotr(b13, ROTATION_1_1, b2); + b2 -= b13; + b11 = XorRotr(b11, ROTATION_1_2, b6); + b6 -= b11; + b15 = XorRotr(b15, ROTATION_1_3, b4); + b4 -= b15; + b7 = XorRotr(b7, ROTATION_1_4, b10); + b10 -= b7; + b3 = XorRotr(b3, ROTATION_1_5, b12); + b12 -= b3; + b5 = XorRotr(b5, ROTATION_1_6, b14); + b14 -= b5; + b1 = XorRotr(b1, ROTATION_1_7, b8); + b8 -= b1; + + b1 = XorRotr(b1, ROTATION_0_0, b0); + b0 -= b1; + b3 = XorRotr(b3, ROTATION_0_1, b2); + b2 -= b3; + b5 = XorRotr(b5, ROTATION_0_2, b4); + b4 -= b5; + b7 = XorRotr(b7, ROTATION_0_3, b6); + b6 -= b7; + b9 = XorRotr(b9, ROTATION_0_4, b8); + b8 -= b9; + b11 = XorRotr(b11, ROTATION_0_5, b10); + b10 -= b11; + b13 = XorRotr(b13, ROTATION_0_6, b12); + b12 -= b13; + b15 = XorRotr(b15, ROTATION_0_7, b14); + b14 -= b15; + } + + /* + * First subkey uninjection. + */ + b0 -= kw[0]; + b1 -= kw[1]; + b2 -= kw[2]; + b3 -= kw[3]; + b4 -= kw[4]; + b5 -= kw[5]; + b6 -= kw[6]; + b7 -= kw[7]; + b8 -= kw[8]; + b9 -= kw[9]; + b10 -= kw[10]; + b11 -= kw[11]; + b12 -= kw[12]; + b13 -= kw[13] + t[0]; + b14 -= kw[14] + t[1]; + b15 -= kw[15]; + + /* + * Output cipher state. + */ + state[0] = b0; + state[1] = b1; + state[2] = b2; + state[3] = b3; + state[4] = b4; + state[5] = b5; + state[6] = b6; + state[7] = b7; + state[8] = b8; + state[9] = b9; + state[10] = b10; + state[11] = b11; + state[12] = b12; + state[13] = b13; + state[14] = b14; + state[15] = b15; + } + + } + + } +} \ No newline at end of file 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
index d467fbba5..1c2802a80 100644 --- a/crypto/src/crypto/engines/VMPCEngine.cs +++ b/crypto/src/crypto/engines/VMPCEngine.cs
@@ -4,136 +4,137 @@ 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); - } - } + 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; + + if (!(ivParams.Parameters is KeyParameter)) + throw new ArgumentException("VMPC Init parameters must include a key"); + + KeyParameter key = (KeyParameter)ivParams.Parameters; + + 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/XSalsa20Engine.cs b/crypto/src/crypto/engines/XSalsa20Engine.cs new file mode 100644
index 000000000..fc6630905 --- /dev/null +++ b/crypto/src/crypto/engines/XSalsa20Engine.cs
@@ -0,0 +1,71 @@ +using System; + +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /// <summary> + /// Implementation of Daniel J. Bernstein's XSalsa20 stream cipher - Salsa20 with an extended nonce. + /// </summary> + /// <remarks> + /// XSalsa20 requires a 256 bit key, and a 192 bit nonce. + /// </remarks> + public class XSalsa20Engine + : Salsa20Engine + { + + public override string AlgorithmName + { + get { return "XSalsa20"; } + } + + protected override int NonceSize + { + get { return 24; } + } + + /// <summary> + /// XSalsa20 key generation: process 256 bit input key and 128 bits of the input nonce + /// using a core Salsa20 function without input addition to produce 256 bit working key + /// and use that with the remaining 64 bits of nonce to initialize a standard Salsa20 engine state. + /// </summary> + protected override void SetKey(byte[] keyBytes, byte[] ivBytes) + { + if (keyBytes.Length != 32) + { + throw new ArgumentException(AlgorithmName + " requires a 256 bit key"); + } + + // Set key for HSalsa20 + base.SetKey(keyBytes, ivBytes); + + // Pack next 64 bits of IV into engine state instead of counter + engineState[8] = Pack.LE_To_UInt32(ivBytes, 8); + engineState[9] = Pack.LE_To_UInt32(ivBytes, 12); + + // Process engine state to generate Salsa20 key + uint[] hsalsa20Out = new uint[engineState.Length]; + SalsaCore(20, engineState, hsalsa20Out); + + // Set new key, removing addition in last round of salsaCore + engineState[1] = hsalsa20Out[0] - engineState[0]; + engineState[2] = hsalsa20Out[5] - engineState[5]; + engineState[3] = hsalsa20Out[10] - engineState[10]; + engineState[4] = hsalsa20Out[15] - engineState[15]; + + engineState[11] = hsalsa20Out[6] - engineState[6]; + engineState[12] = hsalsa20Out[7] - engineState[7]; + engineState[13] = hsalsa20Out[8] - engineState[8]; + engineState[14] = hsalsa20Out[9] - engineState[9]; + + // Last 64 bits of input IV + engineState[6] = Pack.LE_To_UInt32(ivBytes, 16); + engineState[7] = Pack.LE_To_UInt32(ivBytes, 20); + + // Counter reset + ResetCounter(); + } + + } +} + 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
index 0366401d1..bca420711 100644 --- a/crypto/src/crypto/generators/BaseKdfBytesGenerator.cs +++ b/crypto/src/crypto/generators/BaseKdfBytesGenerator.cs
@@ -2,140 +2,131 @@ using System; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; 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; - } - } + /** + * 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. + */ + public BaseKdfBytesGenerator(int counterStart, IDigest digest) + { + this.counterStart = counterStart; + this.digest = digest; + } + + public virtual 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 virtual 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 virtual 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()]; + + byte[] C = new byte[4]; + Pack.UInt32_To_BE((uint)counterStart, C, 0); + + uint counterBase = (uint)(counterStart & ~0xFF); + + for (int i = 0; i < cThreshold; i++) + { + digest.BlockUpdate(shared, 0, shared.Length); + digest.BlockUpdate(C, 0, 4); + + 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); + } + + if (++C[3] == 0) + { + counterBase += 0x100; + Pack.UInt32_To_BE(counterBase, C, 0); + } + } + + 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
index 756e8482a..68aba64f7 100644 --- a/crypto/src/crypto/generators/DHKeyGeneratorHelper.cs +++ b/crypto/src/crypto/generators/DHKeyGeneratorHelper.cs
@@ -2,52 +2,71 @@ using System; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC.Multiplier; 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); - } - } + 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) + { + int minWeight = limit >> 2; + for (;;) + { + BigInteger x = new BigInteger(limit, random).SetBit(limit - 1); + if (WNafUtilities.GetNafWeight(x) >= minWeight) + { + return x; + } + } + } + + BigInteger min = BigInteger.Two; + int m = dhParams.M; + if (m != 0) + { + min = BigInteger.One.ShiftLeft(m - 1); + } + + BigInteger q = dhParams.Q; + if (q == null) + { + q = dhParams.P; + } + BigInteger max = q.Subtract(BigInteger.Two); + + { + int minWeight = max.BitLength >> 2; + for (;;) + { + BigInteger x = BigIntegers.CreateRandomInRange(min, max, random); + if (WNafUtilities.GetNafWeight(x) >= minWeight) + { + return x; + } + } + } + } + + 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
index 7860cbe33..bf2de2add 100644 --- a/crypto/src/crypto/generators/DHParametersHelper.cs +++ b/crypto/src/crypto/generators/DHParametersHelper.cs
@@ -1,215 +1,137 @@ using System; using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC.Multiplier; 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) - */ + internal class DHParametersHelper + { + private static readonly BigInteger Six = BigInteger.ValueOf(6); + + private static readonly int[][] primeLists = BigInteger.primeLists; + private static readonly int[] primeProducts = BigInteger.primeProducts; + private static readonly BigInteger[] BigPrimeProducts = ConstructBigPrimeProducts(primeProducts); + + private static BigInteger[] ConstructBigPrimeProducts(int[] primeProducts) + { + BigInteger[] bpp = new BigInteger[primeProducts.Length]; + for (int i = 0; i < bpp.Length; ++i) + { + bpp[i] = BigInteger.ValueOf(primeProducts[i]); + } + return bpp; + } + + /* + * 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; + int minWeight = size >> 2; + + if (size <= 32) + { + for (;;) + { + q = new BigInteger(qLength, 2, random); + + p = q.ShiftLeft(1).Add(BigInteger.One); + + if (!p.IsProbablePrime(certainty)) + continue; + + if (certainty > 2 && !q.IsProbablePrime(certainty - 2)) + continue; + + 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(BigPrimeProducts[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)) + continue; + + if (certainty > 2 && !q.RabinMillerTest(certainty - 2, random)) + continue; + + /* + * Require a minimum weight of the NAF representation, since low-weight primes may be + * weak against a version of the number-field-sieve for the discrete-logarithm-problem. + * + * See "The number field sieve for integers of low weight", Oliver Schirokauer. + */ + if (WNafUtilities.GetNafWeight(p) < minWeight) + continue; + + 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); @@ -217,18 +139,18 @@ namespace Org.BouncyCastle.Crypto.Generators // 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); + /* + * 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)); + g = h.ModPow(BigInteger.Two, p); + } + while (g.Equals(BigInteger.One)); - return g; - } - } + 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
index bb8ec591b..1c9ce5a16 100644 --- a/crypto/src/crypto/generators/DsaKeyPairGenerator.cs +++ b/crypto/src/crypto/generators/DsaKeyPairGenerator.cs
@@ -1,8 +1,10 @@ using System; -using Org.BouncyCastle.Math; -using Org.BouncyCastle.Security; + using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC.Multiplier; +using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Generators @@ -11,51 +13,60 @@ 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>. + * in <i>FIPS 186-3 B.1 FFC Key Pair Generation</i>. */ public class DsaKeyPairGenerator - : IAsymmetricCipherKeyPairGenerator + : IAsymmetricCipherKeyPairGenerator { + private static readonly BigInteger One = BigInteger.One; + private DsaKeyGenerationParameters param; - public void Init( - KeyGenerationParameters parameters) + public void Init( + KeyGenerationParameters parameters) { - if (parameters == null) - throw new ArgumentNullException("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) + // Note: If we start accepting instances of KeyGenerationParameters, + // must apply constraint checking on strength (see DsaParametersGenerator.Init) - this.param = (DsaKeyGenerationParameters) parameters; + this.param = (DsaKeyGenerationParameters) parameters; } - public AsymmetricCipherKeyPair GenerateKeyPair() + public AsymmetricCipherKeyPair GenerateKeyPair() { - DsaParameters dsaParams = param.Parameters; + DsaParameters dsaParams = param.Parameters; - BigInteger x = GeneratePrivateKey(dsaParams.Q, param.Random); - BigInteger y = CalculatePublicKey(dsaParams.P, dsaParams.G, x); + 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)); + 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); - } - } + private static BigInteger GeneratePrivateKey(BigInteger q, SecureRandom random) + { + // B.1.2 Key Pair Generation by Testing Candidates + int minWeight = q.BitLength >> 2; + for (;;) + { + // TODO Prefer this method? (change test cases that used fixed random) + // B.1.1 Key Pair Generation Using Extra Random Bits + //BigInteger x = new BigInteger(q.BitLength + 64, random).Mod(q.Subtract(One)).Add(One); + + BigInteger x = BigIntegers.CreateRandomInRange(One, q.Subtract(One), random); + if (WNafUtilities.GetNafWeight(x) >= minWeight) + { + return x; + } + } + } + + 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
index 3e9d4f021..cf6343a16 100644 --- a/crypto/src/crypto/generators/DsaParametersGenerator.cs +++ b/crypto/src/crypto/generators/DsaParametersGenerator.cs
@@ -9,15 +9,27 @@ 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. + * Generate suitable parameters for DSA, in line with FIPS 186-2, or FIPS 186-3. */ public class DsaParametersGenerator { - private int L, N; + private IDigest digest; + private int L, N; private int certainty; private SecureRandom random; + private bool use186_3; + private int usageIndex; + + public DsaParametersGenerator() + : this(new Sha1Digest()) + { + } + + public DsaParametersGenerator(IDigest digest) + { + this.digest = digest; + } /** * initialise the key generator. @@ -26,32 +38,51 @@ namespace Org.BouncyCastle.Crypto.Generators * @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( + public virtual 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; - } + if (!IsValidDsaStrength(size)) + throw new ArgumentException("size must be from 512 - 1024 and a multiple of 64", "size"); + + this.use186_3 = false; + this.L = size; + this.N = GetDefaultN(size); + this.certainty = certainty; + this.random = random; + } + + /** + * Initialise the key generator for DSA 2. + * <p> + * Use this init method if you need to generate parameters for DSA 2 keys. + * </p> + * + * @param params DSA 2 key generation parameters. + */ + public virtual void Init(DsaParameterGenerationParameters parameters) + { + // TODO Should we enforce the minimum 'certainty' values as per C.3 Table C.1? + this.use186_3 = true; + this.L = parameters.L; + this.N = parameters.N; + this.certainty = parameters.Certainty; + this.random = parameters.Random; + this.usageIndex = parameters.UsageIndex; + + if ((L < 1024 || L > 3072) || L % 1024 != 0) + throw new ArgumentException("Values must be between 1024 and 3072 and a multiple of 1024", "L"); + if (L == 1024 && N != 160) + throw new ArgumentException("N must be 160 for L = 1024"); + if (L == 2048 && (N != 224 && N != 256)) + throw new ArgumentException("N must be 224 or 256 for L = 2048"); + if (L == 3072 && N != 256) + throw new ArgumentException("N must be 256 for L = 3072"); + + if (digest.GetDigestSize() * 8 < N) + throw new InvalidOperationException("Digest output size too small for value of N"); + } // /** // * add value to b, returning the result in a. The a value is treated @@ -76,280 +107,284 @@ namespace Org.BouncyCastle.Crypto.Generators // } // } - /** - * 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() - { + /** + * which Generates the p and g values from the given parameters, + * returning the DsaParameters object. + * <p> + * Note: can take a while...</p> + */ + public virtual DsaParameters GenerateParameters() + { + return use186_3 + ? GenerateParameters_FIPS186_3() + : GenerateParameters_FIPS186_2(); + } + + protected virtual 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() - { + int n = (L - 1) / 160; + byte[] w = new byte[L / 8]; + + if (!(digest is Sha1Digest)) + throw new InvalidOperationException("can only use SHA-1 for generating FIPS 186-2 parameters"); + + for (;;) + { + random.NextBytes(seed); + + Hash(digest, seed, part1); + Array.Copy(seed, 0, part2, 0, seed.Length); + Inc(part2); + Hash(digest, part2, part2); + + for (int i = 0; i != u.Length; i++) + { + u[i] = (byte)(part1[i] ^ part2[i]); + } + + u[0] |= (byte)0x80; + u[19] |= (byte)0x01; + + BigInteger q = new BigInteger(1, u); + + if (!q.IsProbablePrime(certainty)) + continue; + + byte[] offset = Arrays.Clone(seed); + Inc(offset); + + for (int counter = 0; counter < 4096; ++counter) + { + for (int k = 0; k < n; k++) + { + Inc(offset); + Hash(digest, offset, part1); + Array.Copy(part1, 0, w, w.Length - (k + 1) * part1.Length, part1.Length); + } + + Inc(offset); + Hash(digest, 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)); + } + } + } + } + + protected virtual 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>. + */ + protected virtual 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; + IDigest d = digest; + int outlen = d.GetDigestSize() * 8; // 1. Check that the (L, N) pair is in the list of acceptable (L, N pairs) (see Section 4.2). If // the pair is not in the list, then return INVALID. - // Note: checked at initialisation - + // 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]; + // 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; + int n = (L - 1) / outlen; // 4. b = L – 1 – (n ∗ outlen). - int b = (L - 1) % outlen; + int b = (L - 1) % outlen; - byte[] output = new byte[d.GetDigestSize()]; - for (;;) - { + byte[] output = new byte[d.GetDigestSize()]; + for (;;) + { // 5. Get an arbitrary sequence of seedlen bits as the domain_parameter_seed. - random.NextBytes(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)); + 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)); + 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)) - { + // TODO Review C.3 for primality checking + if (!q.IsProbablePrime(certainty)) + { // 9. If q is not a prime, then go to step 5. - continue; - } + continue; + } // 10. offset = 1. - // Note: 'offset' value managed incrementally - byte[] offset = Arrays.Clone(seed); + // 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) - { + 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)); - } + // 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)); + BigInteger X = W.Add(BigInteger.One.ShiftLeft(L - 1)); // 11.4 c = X mod 2q. - BigInteger c = X.Mod(q.ShiftLeft(1)); + 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)); + 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.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)) - { + // 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)); - } + // TODO Make configurable (8-bit unsigned)? + + if (usageIndex >= 0) + { + BigInteger g = CalculateGenerator_FIPS186_3_Verifiable(d, p, q, seed, usageIndex); + if (g != null) + return new DsaParameters(p, q, g, new DsaValidationParameters(seed, counter, usageIndex)); + } + + { + BigInteger g = CalculateGenerator_FIPS186_3_Unverifiable(p, q, random); + + return new DsaParameters(p, q, g, new DsaValidationParameters(seed, counter)); + } + } // 11.9 offset = offset + n + 1. Comment: Increment offset; then, as part of // the loop in step 11, increment counter; if // counter < 4L, repeat steps 11.1 through 11.8. - // Note: 'offset' value already incremented in inner loop - } + // 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; - } - } - } + } + } + + protected virtual BigInteger CalculateGenerator_FIPS186_3_Unverifiable(BigInteger p, BigInteger q, + SecureRandom r) + { + return CalculateGenerator_FIPS186_2(p, q, r); + } + + protected virtual 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; + } + + protected 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; + } + + protected 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
index d1e4b7cf6..6e777c74c 100644 --- a/crypto/src/crypto/generators/ECKeyPairGenerator.cs +++ b/crypto/src/crypto/generators/ECKeyPairGenerator.cs
@@ -1,5 +1,4 @@ using System; -using System.Globalization; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.Nist; @@ -7,168 +6,162 @@ using Org.BouncyCastle.Asn1.Sec; using Org.BouncyCastle.Asn1.TeleTrust; using Org.BouncyCastle.Asn1.X9; using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.EC; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Math.EC.Multiplier; using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Generators { public class ECKeyPairGenerator - : IAsymmetricCipherKeyPairGenerator + : IAsymmetricCipherKeyPairGenerator { - private readonly string algorithm; + private readonly string algorithm; - private ECDomainParameters parameters; - private DerObjectIdentifier publicKeyParamSet; + private ECDomainParameters parameters; + private DerObjectIdentifier publicKeyParamSet; private SecureRandom random; - public ECKeyPairGenerator() - : this("EC") - { - } + public ECKeyPairGenerator() + : this("EC") + { + } - public ECKeyPairGenerator( - string algorithm) - { - if (algorithm == null) - throw new ArgumentNullException("algorithm"); + public ECKeyPairGenerator( + string algorithm) + { + if (algorithm == null) + throw new ArgumentNullException("algorithm"); - this.algorithm = VerifyAlgorithmName(algorithm); - } + this.algorithm = ECKeyParameters.VerifyAlgorithmName(algorithm); + } - public void Init( + 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 + 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; + + if (this.random == null) + { + this.random = new SecureRandom(); + } + } + + /** + * 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; + int minWeight = n.BitLength >> 2; - do + for (;;) { d = new BigInteger(n.BitLength, random); + + if (d.CompareTo(BigInteger.Two) < 0 || d.CompareTo(n) >= 0) + continue; + + /* + * Require a minimum weight of the NAF representation, since low-weight primes may be + * weak against a version of the number-field-sieve for the discrete-logarithm-problem. + * + * See "The number field sieve for integers of low weight", Oliver Schirokauer. + */ + if (WNafUtilities.GetNafWeight(d) < minWeight) + continue; + + break; } - 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); - } - } + + ECPoint q = CreateBasePointMultiplier().Multiply(parameters.G, 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)); + } + + protected virtual ECMultiplier CreateBasePointMultiplier() + { + return new FixedPointCombMultiplier(); + } + + internal static X9ECParameters FindECCurveByOid(DerObjectIdentifier oid) + { + // TODO ECGost3410NamedCurves support (returns ECDomainParameters though) + + X9ECParameters ecP = CustomNamedCurves.GetByOid(oid); + if (ecP == null) + { + ecP = ECNamedCurveTable.GetByOid(oid); + } + return ecP; + } + + internal static ECPublicKeyParameters GetCorrespondingPublicKey( + ECPrivateKeyParameters privKey) + { + ECDomainParameters ec = privKey.Parameters; + ECPoint q = new FixedPointCombMultiplier().Multiply(ec.G, privKey.D); + + if (privKey.PublicKeyParamSet != null) + { + return new ECPublicKeyParameters(privKey.AlgorithmName, q, privKey.PublicKeyParamSet); + } + + return new ECPublicKeyParameters(privKey.AlgorithmName, q, ec); + } + } } 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
index 5878da64b..013b81810 100644 --- a/crypto/src/crypto/generators/GOST3410KeyPairGenerator.cs +++ b/crypto/src/crypto/generators/GOST3410KeyPairGenerator.cs
@@ -3,71 +3,86 @@ using System; using Org.BouncyCastle.Asn1.CryptoPro; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC.Multiplier; 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; + /** + * 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); + 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? - } + if (parameters.Strength != kgp.Parameters.P.BitLength - 1) + { + // TODO Should we complain? + } - this.param = kgp; - } - } + this.param = kgp; + } + } - public AsymmetricCipherKeyPair GenerateKeyPair() - { - SecureRandom random = param.Random; - Gost3410Parameters gost3410Params = param.Parameters; + 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 q = gost3410Params.Q, x; - BigInteger p = gost3410Params.P; - BigInteger a = gost3410Params.A; + int minWeight = 64; + for (;;) + { + x = new BigInteger(256, random); - // calculate the public key. - BigInteger y = a.ModPow(x, p); + if (x.SignValue < 1 || x.CompareTo(q) >= 0) + continue; - if (param.PublicKeyParamSet != null) - { - return new AsymmetricCipherKeyPair( - new Gost3410PublicKeyParameters(y, param.PublicKeyParamSet), - new Gost3410PrivateKeyParameters(x, param.PublicKeyParamSet)); - } + /* + * Require a minimum weight of the NAF representation, since low-weight primes may be + * weak against a version of the number-field-sieve for the discrete-logarithm-problem. + * + * See "The number field sieve for integers of low weight", Oliver Schirokauer. + */ + if (WNafUtilities.GetNafWeight(x) < minWeight) + continue; - return new AsymmetricCipherKeyPair( - new Gost3410PublicKeyParameters(y, gost3410Params), - new Gost3410PrivateKeyParameters(x, gost3410Params)); - } - } + break; + } + + 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
index 2b4fb7efd..0ddf6c166 100644 --- a/crypto/src/crypto/generators/Kdf1BytesGenerator.cs +++ b/crypto/src/crypto/generators/Kdf1BytesGenerator.cs
@@ -18,8 +18,7 @@ namespace Org.BouncyCastle.Crypto.Generators * * @param digest the digest to be used as the source of derived keys. */ - public Kdf1BytesGenerator( - IDigest digest) + public Kdf1BytesGenerator(IDigest digest) : base(0, digest) { } diff --git a/crypto/src/crypto/generators/Kdf2BytesGenerator.cs b/crypto/src/crypto/generators/Kdf2BytesGenerator.cs
index be1cd158e..8a6821980 100644 --- a/crypto/src/crypto/generators/Kdf2BytesGenerator.cs +++ b/crypto/src/crypto/generators/Kdf2BytesGenerator.cs
@@ -19,8 +19,7 @@ namespace Org.BouncyCastle.Crypto.Generators * * @param digest the digest to be used as the source of derived keys. */ - public Kdf2BytesGenerator( - IDigest digest) + 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
index a00a6c8a6..7011cf253 100644 --- a/crypto/src/crypto/generators/NaccacheSternKeyPairGenerator.cs +++ b/crypto/src/crypto/generators/NaccacheSternKeyPairGenerator.cs
@@ -55,7 +55,7 @@ namespace Org.BouncyCastle.Crypto.Generators if (debug) { - System.Diagnostics.Debug.WriteLine("Fetching first " + param.CountSmallPrimes + " primes."); + Console.WriteLine("Fetching first " + param.CountSmallPrimes + " primes."); } IList smallPrimes = findFirstPrimes(param.CountSmallPrimes); @@ -94,7 +94,7 @@ namespace Org.BouncyCastle.Crypto.Generators long tries = 0; if (debug) { - System.Diagnostics.Debug.WriteLine("generating p and q"); + Console.WriteLine("generating p and q"); } BigInteger _2au = a.Multiply(u).ShiftLeft(1); @@ -126,7 +126,7 @@ namespace Org.BouncyCastle.Crypto.Generators 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); + Console.WriteLine("sigma.gcd(_p.mult(_q)) != 1!\n _p: " + _p +"\n _q: "+ _q ); continue; } @@ -134,7 +134,7 @@ namespace Org.BouncyCastle.Crypto.Generators { if (debug) { - System.Diagnostics.Debug.WriteLine("key size too small. Should be " + strength + " but is actually " + Console.WriteLine("key size too small. Should be " + strength + " but is actually " + p.Multiply(q).BitLength); } continue; @@ -144,7 +144,7 @@ namespace Org.BouncyCastle.Crypto.Generators if (debug) { - System.Diagnostics.Debug.WriteLine("needed " + tries + " tries to generate p and q."); + Console.WriteLine("needed " + tries + " tries to generate p and q."); } BigInteger n = p.Multiply(q); @@ -153,7 +153,7 @@ namespace Org.BouncyCastle.Crypto.Generators tries = 0; if (debug) { - System.Diagnostics.Debug.WriteLine("generating g"); + Console.WriteLine("generating g"); } for (;;) { @@ -193,7 +193,7 @@ namespace Org.BouncyCastle.Crypto.Generators { if (debug) { - System.Diagnostics.Debug.WriteLine("g has order phi(n)/" + smallPrimes[i] + "\n g: " + g); + Console.WriteLine("g has order phi(n)/" + smallPrimes[i] + "\n g: " + g); } divisible = true; break; @@ -212,7 +212,7 @@ namespace Org.BouncyCastle.Crypto.Generators { if (debug) { - System.Diagnostics.Debug.WriteLine("g has order phi(n)/4\n g:" + g); + Console.WriteLine("g has order phi(n)/4\n g:" + g); } continue; } @@ -221,7 +221,7 @@ namespace Org.BouncyCastle.Crypto.Generators { if (debug) { - System.Diagnostics.Debug.WriteLine("g has order phi(n)/p'\n g: " + g); + Console.WriteLine("g has order phi(n)/p'\n g: " + g); } continue; } @@ -229,7 +229,7 @@ namespace Org.BouncyCastle.Crypto.Generators { if (debug) { - System.Diagnostics.Debug.WriteLine("g has order phi(n)/q'\n g: " + g); + Console.WriteLine("g has order phi(n)/q'\n g: " + g); } continue; } @@ -237,7 +237,7 @@ namespace Org.BouncyCastle.Crypto.Generators { if (debug) { - System.Diagnostics.Debug.WriteLine("g has order phi(n)/a\n g: " + g); + Console.WriteLine("g has order phi(n)/a\n g: " + g); } continue; } @@ -245,7 +245,7 @@ namespace Org.BouncyCastle.Crypto.Generators { if (debug) { - System.Diagnostics.Debug.WriteLine("g has order phi(n)/b\n g: " + g); + Console.WriteLine("g has order phi(n)/b\n g: " + g); } continue; } @@ -253,21 +253,21 @@ namespace Org.BouncyCastle.Crypto.Generators } 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(""); + Console.WriteLine("needed " + tries + " tries to generate g"); + Console.WriteLine(); + Console.WriteLine("found new NaccacheStern cipher variables:"); + Console.WriteLine("smallPrimes: " + CollectionUtilities.ToString(smallPrimes)); + Console.WriteLine("sigma:...... " + sigma + " (" + sigma.BitLength + " bits)"); + Console.WriteLine("a:.......... " + a); + Console.WriteLine("b:.......... " + b); + Console.WriteLine("p':......... " + _p); + Console.WriteLine("q':......... " + _q); + Console.WriteLine("p:.......... " + p); + Console.WriteLine("q:.......... " + q); + Console.WriteLine("n:.......... " + n); + Console.WriteLine("phi(n):..... " + phi_n); + Console.WriteLine("g:.......... " + g); + Console.WriteLine(); } return new AsymmetricCipherKeyPair(new NaccacheSternKeyParameters(false, g, n, sigma.BitLength), @@ -290,7 +290,7 @@ namespace Org.BouncyCastle.Crypto.Generators * the ArrayList to be permuted * @param rand * the source of Randomness for permutation - * @return a new ArrayList with the permuted elements. + * @return a new IList with the permuted elements. */ private static IList permuteList( IList arr, 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
index d2da3f6fc..cbbfd1b3b 100644 --- a/crypto/src/crypto/generators/Pkcs12ParametersGenerator.cs +++ b/crypto/src/crypto/generators/Pkcs12ParametersGenerator.cs
@@ -7,239 +7,239 @@ 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); - } - } + /** + * 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; + byte[] A = new byte[u]; + + for (int i = 1; i <= c; i++) + { + 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
index 58d7b5c37..10352abbc 100644 --- a/crypto/src/crypto/generators/Pkcs5S2ParametersGenerator.cs +++ b/crypto/src/crypto/generators/Pkcs5S2ParametersGenerator.cs
@@ -10,163 +10,171 @@ 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); - } - } + /** + * 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; + private readonly byte[] state; + + /** + * construct a Pkcs5 Scheme 2 Parameters generator. + */ + public Pkcs5S2ParametersGenerator() + : this(new Sha1Digest()) + { + } + + public Pkcs5S2ParametersGenerator(IDigest digest) + { + this.hMac = new HMac(digest); + this.state = new byte[hMac.GetMacSize()]; + } + + private void F( + byte[] S, + int c, + byte[] iBuf, + byte[] outBytes, + int outOff) + { + if (c == 0) + throw new ArgumentException("iteration count must be at least 1."); + + 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.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]; + int outPos = 0; + + ICipherParameters param = new KeyParameter(mPassword); + + hMac.Init(param); + + for (int i = 1; i <= l; i++) + { + // Increment the value in 'iBuf' + int pos = 3; + while (++iBuf[pos] == 0) + { + --pos; + } + + F(mSalt, mIterationCount, iBuf, outBytes, outPos); + outPos += hLen; + } + + return outBytes; + } + + /** + * Generate a key parameter derived from the password, salt, and iteration + * count we are currently initialised with. + * + * @param keySize the size of the key we want (in bits) + * @return a KeyParameter object. + */ + [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/Poly1305KeyGenerator.cs b/crypto/src/crypto/generators/Poly1305KeyGenerator.cs new file mode 100644
index 000000000..5deb50f07 --- /dev/null +++ b/crypto/src/crypto/generators/Poly1305KeyGenerator.cs
@@ -0,0 +1,123 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Macs; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Generators +{ + /// <summary> + /// Generates keys for the Poly1305 MAC. + /// </summary> + /// <remarks> + /// Poly1305 keys are 256 bit keys consisting of a 128 bit secret key used for the underlying block + /// cipher followed by a 128 bit {@code r} value used for the polynomial portion of the Mac. <br/> + /// The {@code r} value has a specific format with some bits required to be cleared, resulting in an + /// effective 106 bit key. <br/> + /// A separately generated 256 bit key can be modified to fit the Poly1305 key format by using the + /// {@link #clamp(byte[])} method to clear the required bits. + /// </remarks> + /// <seealso cref="Poly1305"/> + public class Poly1305KeyGenerator + : CipherKeyGenerator + { + private const byte R_MASK_LOW_2 = (byte)0xFC; + private const byte R_MASK_HIGH_4 = (byte)0x0F; + + /// <summary> + /// Initialises the key generator. + /// </summary> + /// <remarks> + /// Poly1305 keys are always 256 bits, so the key length in the provided parameters is ignored. + /// </remarks> + protected override void engineInit(KeyGenerationParameters param) + { + // Poly1305 keys are always 256 bits + this.random = param.Random; + this.strength = 32; + } + + /// <summary> + /// Generates a 256 bit key in the format required for Poly1305 - e.g. + /// <code>k[0] ... k[15], r[0] ... r[15]</code> with the required bits in <code>r</code> cleared + /// as per <see cref="Clamp(byte[])"/>. + /// </summary> + protected override byte[] engineGenerateKey() + { + byte[] key = base.engineGenerateKey(); + Clamp(key); + return key; + } + + /// <summary> + /// Modifies an existing 32 byte key value to comply with the requirements of the Poly1305 key by + /// clearing required bits in the <code>r</code> (second 16 bytes) portion of the key.<br/> + /// Specifically: + /// <ul> + /// <li>r[3], r[7], r[11], r[15] have top four bits clear (i.e., are {0, 1, . . . , 15})</li> + /// <li>r[4], r[8], r[12] have bottom two bits clear (i.e., are in {0, 4, 8, . . . , 252})</li> + /// </ul> + /// </summary> + /// <param name="key">a 32 byte key value <code>k[0] ... k[15], r[0] ... r[15]</code></param> + public static void Clamp(byte[] key) + { + /* + * Key is k[0] ... k[15], r[0] ... r[15] as per poly1305_aes_clamp in ref impl. + */ + if (key.Length != 32) + { + throw new ArgumentException("Poly1305 key must be 256 bits."); + } + + /* + * r[3], r[7], r[11], r[15] have top four bits clear (i.e., are {0, 1, . . . , 15}) + */ + key[19] &= R_MASK_HIGH_4; + key[23] &= R_MASK_HIGH_4; + key[27] &= R_MASK_HIGH_4; + key[31] &= R_MASK_HIGH_4; + + /* + * r[4], r[8], r[12] have bottom two bits clear (i.e., are in {0, 4, 8, . . . , 252}). + */ + key[20] &= R_MASK_LOW_2; + key[24] &= R_MASK_LOW_2; + key[28] &= R_MASK_LOW_2; + } + + /// <summary> + /// Checks a 32 byte key for compliance with the Poly1305 key requirements, e.g. + /// <code>k[0] ... k[15], r[0] ... r[15]</code> with the required bits in <code>r</code> cleared + /// as per <see cref="Clamp(byte[])"/>. + /// </summary> + /// <param name="key">Key.</param> + /// <exception cref="System.ArgumentException">if the key is of the wrong length, or has invalid bits set + /// in the <code>r</code> portion of the key.</exception> + public static void CheckKey(byte[] key) + { + if (key.Length != 32) + { + throw new ArgumentException("Poly1305 key must be 256 bits."); + } + + checkMask(key[19], R_MASK_HIGH_4); + checkMask(key[23], R_MASK_HIGH_4); + checkMask(key[27], R_MASK_HIGH_4); + checkMask(key[31], R_MASK_HIGH_4); + + checkMask(key[20], R_MASK_LOW_2); + checkMask(key[24], R_MASK_LOW_2); + checkMask(key[28], R_MASK_LOW_2); + } + + private static void checkMask(byte b, byte mask) + { + if ((b & (~mask)) != 0) + { + throw new ArgumentException("Invalid format for r portion of Poly1305 key."); + } + } + + } +} \ No newline at end of file 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
index 3074aed04..e870f1c08 100644 --- a/crypto/src/crypto/generators/RsaKeyPairGenerator.cs +++ b/crypto/src/crypto/generators/RsaKeyPairGenerator.cs
@@ -3,6 +3,7 @@ using System; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC.Multiplier; namespace Org.BouncyCastle.Crypto.Generators { @@ -10,107 +11,95 @@ namespace Org.BouncyCastle.Crypto.Generators * an RSA key pair generator. */ public class RsaKeyPairGenerator - : IAsymmetricCipherKeyPairGenerator + : IAsymmetricCipherKeyPairGenerator { - private static readonly BigInteger DefaultPublicExponent = BigInteger.ValueOf(0x10001); - private const int DefaultTests = 12; + private static readonly BigInteger DefaultPublicExponent = BigInteger.ValueOf(0x10001); + private const int DefaultTests = 12; - private RsaKeyGenerationParameters param; + private RsaKeyGenerationParameters param; - public void Init( + public void Init( KeyGenerationParameters parameters) { - if (parameters is RsaKeyGenerationParameters) - { - this.param = (RsaKeyGenerationParameters)parameters; - } - else - { - this.param = new RsaKeyGenerationParameters( - DefaultPublicExponent, parameters.Random, parameters.Strength, DefaultTests); - } + if (parameters is RsaKeyGenerationParameters) + { + this.param = (RsaKeyGenerationParameters)parameters; + } + else + { + this.param = new RsaKeyGenerationParameters( + DefaultPublicExponent, parameters.Random, parameters.Strength, DefaultTests); + } } - public AsymmetricCipherKeyPair GenerateKeyPair() + 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); + int strength = param.Strength; + int qBitlength = strength >> 1; + int pBitlength = strength - qBitlength; + int mindiffbits = strength / 3; + int minWeight = strength >> 2; - if (p.Mod(e).Equals(BigInteger.One)) - continue; + e = param.PublicExponent; - if (!p.IsProbablePrime(param.Certainty)) - continue; + // 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") - if (e.Gcd(p.Subtract(BigInteger.One)).Equals(BigInteger.One)) - break; - } + p = ChooseRandomPrime(pBitlength, e); // // 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); + q = ChooseRandomPrime(qBitlength, e); - 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; - } + // p and q should not be too close together (or equal!) + BigInteger diff = q.Subtract(p).Abs(); + if (diff.BitLength < mindiffbits) + continue; // // calculate the modulus // n = p.Multiply(q); - if (n.BitLength == param.Strength) - break; + if (n.BitLength != strength) + { + // + // if we get here our primes aren't big enough, make the largest + // of the two p and try again + // + p = p.Max(q); + continue; + } + + /* + * Require a minimum weight of the NAF representation, since low-weight composites may + * be weak against a version of the number-field-sieve for factoring. + * + * See "The number field sieve for integers of low weight", Oliver Schirokauer. + */ + if (WNafUtilities.GetNafWeight(n) < minWeight) + { + p = ChooseRandomPrime(pBitlength, e); + continue; + } - // - // if we Get here our primes aren't big enough, make the largest - // of the two p and try again - // - p = p.Max(q); + break; } - if (p.CompareTo(q) < 0) - { - phi = p; - p = q; - q = phi; - } + if (p.CompareTo(q) < 0) + { + phi = p; + p = q; + q = phi; + } pSub1 = p.Subtract(BigInteger.One); qSub1 = q.Subtract(BigInteger.One); @@ -134,6 +123,28 @@ namespace Org.BouncyCastle.Crypto.Generators new RsaKeyParameters(false, n, e), new RsaPrivateCrtKeyParameters(n, e, d, p, q, dP, dQ, qInv)); } - } + /// <summary>Choose a random prime value for use with RSA</summary> + /// <param name="bitlength">the bit-length of the returned prime</param> + /// <param name="e">the RSA public exponent</param> + /// <returns>a prime p, with (p-1) relatively prime to e</returns> + protected virtual BigInteger ChooseRandomPrime(int bitlength, BigInteger e) + { + for (;;) + { + BigInteger p = new BigInteger(bitlength, 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)) + continue; + + return p; + } + } + } } 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
index bf7effb0a..b6920854d 100644 --- a/crypto/src/crypto/io/CipherStream.cs +++ b/crypto/src/crypto/io/CipherStream.cs
@@ -201,18 +201,15 @@ namespace Org.BouncyCastle.Crypto.IO set { throw new NotSupportedException(); } } - protected override void Dispose(bool disposing) + public override void Close() { - if (disposing) - { - if (outCipher != null) - { - byte[] data = outCipher.DoFinal(); - stream.Write(data, 0, data.Length); - stream.Flush(); - } - stream.Dispose(); - } + if (outCipher != null) + { + byte[] data = outCipher.DoFinal(); + stream.Write(data, 0, data.Length); + stream.Flush(); + } + stream.Close(); } public override void Flush() diff --git a/crypto/src/crypto/io/DigestStream.cs b/crypto/src/crypto/io/DigestStream.cs
index a5b31f95c..c819a409a 100644 --- a/crypto/src/crypto/io/DigestStream.cs +++ b/crypto/src/crypto/io/DigestStream.cs
@@ -110,13 +110,10 @@ namespace Org.BouncyCastle.Crypto.IO set { stream.Position = value; } } - protected override void Dispose(bool disposing) - { - if (disposing) - { - stream.Dispose(); - } - } + public override void Close() + { + stream.Close(); + } public override void Flush() { diff --git a/crypto/src/crypto/io/MacStream.cs b/crypto/src/crypto/io/MacStream.cs
index 419eafb77..51cf1832e 100644 --- a/crypto/src/crypto/io/MacStream.cs +++ b/crypto/src/crypto/io/MacStream.cs
@@ -109,13 +109,10 @@ namespace Org.BouncyCastle.Crypto.IO set { stream.Position = value; } } - protected override void Dispose(bool disposing) - { - if (disposing) - { - stream.Dispose(); - } - } + public override void Close() + { + stream.Close(); + } public override void Flush() { diff --git a/crypto/src/crypto/io/SignerStream.cs b/crypto/src/crypto/io/SignerStream.cs
index 8be8ca84a..49dfb38c6 100644 --- a/crypto/src/crypto/io/SignerStream.cs +++ b/crypto/src/crypto/io/SignerStream.cs
@@ -110,13 +110,10 @@ namespace Org.BouncyCastle.Crypto.IO set { stream.Position = value; } } - protected override void Dispose(bool disposing) - { - if (disposing) - { - stream.Dispose(); - } - } + public override void Close() + { + stream.Close(); + } public override void Flush() { diff --git a/crypto/src/crypto/macs/CMac.cs b/crypto/src/crypto/macs/CMac.cs
index ea1ce88f5..682c12bac 100644 --- a/crypto/src/crypto/macs/CMac.cs +++ b/crypto/src/crypto/macs/CMac.cs
@@ -2,239 +2,256 @@ using System; using Org.BouncyCastle.Crypto.Modes; using Org.BouncyCastle.Crypto.Paddings; +using Org.BouncyCastle.Crypto.Parameters; 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(); - } - } + /** + * 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 static int ShiftLeft(byte[] block, byte[] output) + { + int i = block.Length; + uint bit = 0; + while (--i >= 0) + { + uint b = block[i]; + output[i] = (byte)((b << 1) | bit); + bit = (b >> 7) & 1; + } + return (int)bit; + } + + private static byte[] DoubleLu(byte[] input) + { + byte[] ret = new byte[input.Length]; + int carry = ShiftLeft(input, ret); + int xor = input.Length == 16 ? CONSTANT_128 : CONSTANT_64; + + /* + * NOTE: This construction is an attempt at a constant-time implementation. + */ + ret[input.Length - 1] ^= (byte)(xor >> ((1 - carry) << 3)); + + return ret; + } + + public void Init( + ICipherParameters parameters) + { + if (parameters is KeyParameter) + { + 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); + } + else if (parameters != null) + { + // CMAC mode does not permit IV to underlying CBC mode + throw new ArgumentException("CMac mode only permits key to be set.", "parameters"); + } + + Reset(); + } + + 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/GMac.cs b/crypto/src/crypto/macs/GMac.cs new file mode 100644
index 000000000..f2c3990c6 --- /dev/null +++ b/crypto/src/crypto/macs/GMac.cs
@@ -0,0 +1,112 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Macs +{ + /// <summary> + /// The GMAC specialisation of Galois/Counter mode (GCM) detailed in NIST Special Publication + /// 800-38D. + /// </summary> + /// <remarks> + /// GMac is an invocation of the GCM mode where no data is encrypted (i.e. all input data to the Mac + /// is processed as additional authenticated data with the underlying GCM block cipher). + /// </remarks> + public class GMac + : IMac + { + private readonly GcmBlockCipher cipher; + private readonly int macSizeBits; + + /// <summary> + /// Creates a GMAC based on the operation of a block cipher in GCM mode. + /// </summary> + /// <remarks> + /// This will produce an authentication code the length of the block size of the cipher. + /// </remarks> + /// <param name="cipher">the cipher to be used in GCM mode to generate the MAC.</param> + public GMac(GcmBlockCipher cipher) + : this(cipher, 128) + { + } + + /// <summary> + /// Creates a GMAC based on the operation of a 128 bit block cipher in GCM mode. + /// </summary> + /// <remarks> + /// This will produce an authentication code the length of the block size of the cipher. + /// </remarks> + /// <param name="cipher">the cipher to be used in GCM mode to generate the MAC.</param> + /// <param name="macSizeBits">the mac size to generate, in bits. Must be a multiple of 8, between 32 and 128 (inclusive). + /// Sizes less than 96 are not recommended, but are supported for specialized applications.</param> + public GMac(GcmBlockCipher cipher, int macSizeBits) + { + this.cipher = cipher; + this.macSizeBits = macSizeBits; + } + + /// <summary> + /// Initialises the GMAC - requires a <see cref="Org.BouncyCastle.Crypto.Parameters.ParametersWithIV"/> + /// providing a <see cref="Org.BouncyCastle.Crypto.Parameters.KeyParameter"/> and a nonce. + /// </summary> + public void Init(ICipherParameters parameters) + { + if (parameters is ParametersWithIV) + { + ParametersWithIV param = (ParametersWithIV)parameters; + + byte[] iv = param.GetIV(); + KeyParameter keyParam = (KeyParameter)param.Parameters; + + // GCM is always operated in encrypt mode to calculate MAC + cipher.Init(true, new AeadParameters(keyParam, macSizeBits, iv)); + } + else + { + throw new ArgumentException("GMAC requires ParametersWithIV"); + } + } + + public string AlgorithmName + { + get { return cipher.GetUnderlyingCipher().AlgorithmName + "-GMAC"; } + } + + public int GetMacSize() + { + return macSizeBits / 8; + } + + public void Update(byte input) + { + cipher.ProcessAadByte(input); + } + + public void BlockUpdate(byte[] input, int inOff, int len) + { + cipher.ProcessAadBytes(input, inOff, len); + } + + public int DoFinal(byte[] output, int outOff) + { + try + { + return cipher.DoFinal(output, outOff); + } + catch (InvalidCipherTextException e) + { + // Impossible in encrypt mode + throw new InvalidOperationException(e.ToString()); + } + } + + public void Reset() + { + 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
index 3f9b0cef0..460f3c5a0 100644 --- a/crypto/src/crypto/macs/HMac.cs +++ b/crypto/src/crypto/macs/HMac.cs
@@ -3,6 +3,7 @@ using System.Collections; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Macs { @@ -20,32 +21,32 @@ namespace Org.BouncyCastle.Crypto.Macs private readonly IDigest digest; private readonly int digestSize; private readonly int blockLength; + private IMemoable ipadState; + private IMemoable opadState; private readonly byte[] inputPad; - private readonly byte[] outputPad; + private readonly byte[] outputBuf; - public HMac( - IDigest digest) + 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]; + this.outputBuf = new byte[blockLength + digestSize]; } - public string AlgorithmName + public virtual string AlgorithmName { get { return digest.AlgorithmName + "/HMAC"; } } - public IDigest GetUnderlyingDigest() + public virtual IDigest GetUnderlyingDigest() { return digest; } - public void Init( - ICipherParameters parameters) + public virtual void Init(ICipherParameters parameters) { digest.Reset(); @@ -54,7 +55,7 @@ namespace Org.BouncyCastle.Crypto.Macs if (keyLength > blockLength) { - digest.BlockUpdate(key, 0, key.Length); + digest.BlockUpdate(key, 0, keyLength); digest.DoFinal(inputPad, 0); keyLength = digestSize; @@ -65,48 +66,67 @@ namespace Org.BouncyCastle.Crypto.Macs } Array.Clear(inputPad, keyLength, blockLength - keyLength); - Array.Copy(inputPad, 0, outputPad, 0, blockLength); + Array.Copy(inputPad, 0, outputBuf, 0, blockLength); - xor(inputPad, IPAD); - xor(outputPad, OPAD); + XorPad(inputPad, blockLength, IPAD); + XorPad(outputBuf, blockLength, OPAD); + + if (digest is IMemoable) + { + opadState = ((IMemoable)digest).Copy(); + + ((IDigest)opadState).BlockUpdate(outputBuf, 0, blockLength); + } - // Initialise the digest digest.BlockUpdate(inputPad, 0, inputPad.Length); + + if (digest is IMemoable) + { + ipadState = ((IMemoable)digest).Copy(); + } } - public int GetMacSize() + public virtual int GetMacSize() { return digestSize; } - public void Update( - byte input) + public virtual void Update(byte input) { digest.Update(input); } - public void BlockUpdate( - byte[] input, - int inOff, - int len) + public virtual void BlockUpdate(byte[] input, int inOff, int len) { digest.BlockUpdate(input, inOff, len); } - public int DoFinal( - byte[] output, - int outOff) + public virtual 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); + digest.DoFinal(outputBuf, blockLength); + + if (opadState != null) + { + ((IMemoable)digest).Reset(opadState); + digest.BlockUpdate(outputBuf, blockLength, digest.GetDigestSize()); + } + else + { + digest.BlockUpdate(outputBuf, 0, outputBuf.Length); + } + + int len = digest.DoFinal(output, outOff); + + Array.Clear(outputBuf, blockLength, digestSize); + + if (ipadState != null) + { + ((IMemoable)digest).Reset(ipadState); + } + else + { + digest.BlockUpdate(inputPad, 0, inputPad.Length); + } return len; } @@ -114,7 +134,7 @@ namespace Org.BouncyCastle.Crypto.Macs /** * Reset the mac generator. */ - public void Reset() + public virtual void Reset() { // Reset underlying digest digest.Reset(); @@ -123,11 +143,11 @@ namespace Org.BouncyCastle.Crypto.Macs digest.BlockUpdate(inputPad, 0, inputPad.Length); } - private static void xor(byte[] a, byte n) + private static void XorPad(byte[] pad, int len, byte n) { - for (int i = 0; i < a.Length; ++i) + for (int i = 0; i < len; ++i) { - a[i] ^= n; + pad[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/Poly1305.cs b/crypto/src/crypto/macs/Poly1305.cs new file mode 100644
index 000000000..1a951ca04 --- /dev/null +++ b/crypto/src/crypto/macs/Poly1305.cs
@@ -0,0 +1,291 @@ +using System; + +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Macs +{ + + /// <summary> + /// Poly1305 message authentication code, designed by D. J. Bernstein. + /// </summary> + /// <remarks> + /// Poly1305 computes a 128-bit (16 bytes) authenticator, using a 128 bit nonce and a 256 bit key + /// consisting of a 128 bit key applied to an underlying cipher, and a 128 bit key (with 106 + /// effective key bits) used in the authenticator. + /// + /// The polynomial calculation in this implementation is adapted from the public domain <a + /// href="https://github.com/floodyberry/poly1305-donna">poly1305-donna-unrolled</a> C implementation + /// by Andrew M (@floodyberry). + /// </remarks> + /// <seealso cref="Org.BouncyCastle.Crypto.Generators.Poly1305KeyGenerator"/> + public class Poly1305 + : IMac + { + private const int BLOCK_SIZE = 16; + + private readonly IBlockCipher cipher; + + private readonly byte[] singleByte = new byte[1]; + + // Initialised state + + /** Polynomial key */ + private uint r0, r1, r2, r3, r4; + + /** Precomputed 5 * r[1..4] */ + private uint s1, s2, s3, s4; + + /** Encrypted nonce */ + private uint k0, k1, k2, k3; + + // Accumulating state + + /** Current block of buffered input */ + private byte[] currentBlock = new byte[BLOCK_SIZE]; + + /** Current offset in input buffer */ + private int currentBlockOffset = 0; + + /** Polynomial accumulator */ + private uint h0, h1, h2, h3, h4; + + /** + * Constructs a Poly1305 MAC, where the key passed to init() will be used directly. + */ + public Poly1305() + { + this.cipher = null; + } + + /** + * Constructs a Poly1305 MAC, using a 128 bit block cipher. + */ + public Poly1305(IBlockCipher cipher) + { + if (cipher.GetBlockSize() != BLOCK_SIZE) + { + throw new ArgumentException("Poly1305 requires a 128 bit block cipher."); + } + this.cipher = cipher; + } + + /// <summary> + /// Initialises the Poly1305 MAC. + /// </summary> + /// <param name="parameters">a {@link ParametersWithIV} containing a 128 bit nonce and a {@link KeyParameter} with + /// a 256 bit key complying to the {@link Poly1305KeyGenerator Poly1305 key format}.</param> + public void Init(ICipherParameters parameters) + { + byte[] nonce = null; + + if (cipher != null) + { + if (!(parameters is ParametersWithIV)) + throw new ArgumentException("Poly1305 requires an IV when used with a block cipher.", "parameters"); + + ParametersWithIV ivParams = (ParametersWithIV)parameters; + nonce = ivParams.GetIV(); + parameters = ivParams.Parameters; + } + + if (!(parameters is KeyParameter)) + throw new ArgumentException("Poly1305 requires a key."); + + KeyParameter keyParams = (KeyParameter)parameters; + + SetKey(keyParams.GetKey(), nonce); + + Reset(); + } + + private void SetKey(byte[] key, byte[] nonce) + { + if (cipher != null && (nonce == null || nonce.Length != BLOCK_SIZE)) + throw new ArgumentException("Poly1305 requires a 128 bit IV."); + + Poly1305KeyGenerator.CheckKey(key); + + // Extract r portion of key + uint t0 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 0); + uint t1 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 4); + uint t2 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 8); + uint t3 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 12); + + r0 = t0 & 0x3ffffff; t0 >>= 26; t0 |= t1 << 6; + r1 = t0 & 0x3ffff03; t1 >>= 20; t1 |= t2 << 12; + r2 = t1 & 0x3ffc0ff; t2 >>= 14; t2 |= t3 << 18; + r3 = t2 & 0x3f03fff; t3 >>= 8; + r4 = t3 & 0x00fffff; + + // Precompute multipliers + s1 = r1 * 5; + s2 = r2 * 5; + s3 = r3 * 5; + s4 = r4 * 5; + + byte[] kBytes; + if (cipher == null) + { + kBytes = key; + } + else + { + // Compute encrypted nonce + kBytes = new byte[BLOCK_SIZE]; + cipher.Init(true, new KeyParameter(key, 0, BLOCK_SIZE)); + cipher.ProcessBlock(nonce, 0, kBytes, 0); + } + + k0 = Pack.LE_To_UInt32(kBytes, 0); + k1 = Pack.LE_To_UInt32(kBytes, 4); + k2 = Pack.LE_To_UInt32(kBytes, 8); + k3 = Pack.LE_To_UInt32(kBytes, 12); + } + + public string AlgorithmName + { + get { return cipher == null ? "Poly1305" : "Poly1305-" + cipher.AlgorithmName; } + } + + public int GetMacSize() + { + return BLOCK_SIZE; + } + + public void Update(byte input) + { + singleByte[0] = input; + BlockUpdate(singleByte, 0, 1); + } + + public void BlockUpdate(byte[] input, int inOff, int len) + { + int copied = 0; + while (len > copied) + { + if (currentBlockOffset == BLOCK_SIZE) + { + processBlock(); + currentBlockOffset = 0; + } + + int toCopy = System.Math.Min((len - copied), BLOCK_SIZE - currentBlockOffset); + Array.Copy(input, copied + inOff, currentBlock, currentBlockOffset, toCopy); + copied += toCopy; + currentBlockOffset += toCopy; + } + + } + + private void processBlock() + { + if (currentBlockOffset < BLOCK_SIZE) + { + currentBlock[currentBlockOffset] = 1; + for (int i = currentBlockOffset + 1; i < BLOCK_SIZE; i++) + { + currentBlock[i] = 0; + } + } + + ulong t0 = Pack.LE_To_UInt32(currentBlock, 0); + ulong t1 = Pack.LE_To_UInt32(currentBlock, 4); + ulong t2 = Pack.LE_To_UInt32(currentBlock, 8); + ulong t3 = Pack.LE_To_UInt32(currentBlock, 12); + + h0 += (uint)(t0 & 0x3ffffffU); + h1 += (uint)((((t1 << 32) | t0) >> 26) & 0x3ffffff); + h2 += (uint)((((t2 << 32) | t1) >> 20) & 0x3ffffff); + h3 += (uint)((((t3 << 32) | t2) >> 14) & 0x3ffffff); + h4 += (uint)(t3 >> 8); + + if (currentBlockOffset == BLOCK_SIZE) + { + h4 += (1 << 24); + } + + ulong tp0 = mul32x32_64(h0,r0) + mul32x32_64(h1,s4) + mul32x32_64(h2,s3) + mul32x32_64(h3,s2) + mul32x32_64(h4,s1); + ulong tp1 = mul32x32_64(h0,r1) + mul32x32_64(h1,r0) + mul32x32_64(h2,s4) + mul32x32_64(h3,s3) + mul32x32_64(h4,s2); + ulong tp2 = mul32x32_64(h0,r2) + mul32x32_64(h1,r1) + mul32x32_64(h2,r0) + mul32x32_64(h3,s4) + mul32x32_64(h4,s3); + ulong tp3 = mul32x32_64(h0,r3) + mul32x32_64(h1,r2) + mul32x32_64(h2,r1) + mul32x32_64(h3,r0) + mul32x32_64(h4,s4); + ulong tp4 = mul32x32_64(h0,r4) + mul32x32_64(h1,r3) + mul32x32_64(h2,r2) + mul32x32_64(h3,r1) + mul32x32_64(h4,r0); + + ulong b; + h0 = (uint)tp0 & 0x3ffffff; b = (tp0 >> 26); + tp1 += b; h1 = (uint)tp1 & 0x3ffffff; b = (tp1 >> 26); + tp2 += b; h2 = (uint)tp2 & 0x3ffffff; b = (tp2 >> 26); + tp3 += b; h3 = (uint)tp3 & 0x3ffffff; b = (tp3 >> 26); + tp4 += b; h4 = (uint)tp4 & 0x3ffffff; b = (tp4 >> 26); + h0 += (uint)(b * 5); + } + + public int DoFinal(byte[] output, int outOff) + { + if (outOff + BLOCK_SIZE > output.Length) + { + throw new DataLengthException("Output buffer is too short."); + } + + if (currentBlockOffset > 0) + { + // Process padded block + processBlock(); + } + + ulong f0, f1, f2, f3; + + uint b = h0 >> 26; + h0 = h0 & 0x3ffffff; + h1 += b; b = h1 >> 26; h1 = h1 & 0x3ffffff; + h2 += b; b = h2 >> 26; h2 = h2 & 0x3ffffff; + h3 += b; b = h3 >> 26; h3 = h3 & 0x3ffffff; + h4 += b; b = h4 >> 26; h4 = h4 & 0x3ffffff; + h0 += b * 5; + + uint g0, g1, g2, g3, g4; + g0 = h0 + 5; b = g0 >> 26; g0 &= 0x3ffffff; + g1 = h1 + b; b = g1 >> 26; g1 &= 0x3ffffff; + g2 = h2 + b; b = g2 >> 26; g2 &= 0x3ffffff; + g3 = h3 + b; b = g3 >> 26; g3 &= 0x3ffffff; + g4 = h4 + b - (1 << 26); + + b = (g4 >> 31) - 1; + uint nb = ~b; + h0 = (h0 & nb) | (g0 & b); + h1 = (h1 & nb) | (g1 & b); + h2 = (h2 & nb) | (g2 & b); + h3 = (h3 & nb) | (g3 & b); + h4 = (h4 & nb) | (g4 & b); + + f0 = ((h0 ) | (h1 << 26)) + (ulong)k0; + f1 = ((h1 >> 6 ) | (h2 << 20)) + (ulong)k1; + f2 = ((h2 >> 12) | (h3 << 14)) + (ulong)k2; + f3 = ((h3 >> 18) | (h4 << 8 )) + (ulong)k3; + + Pack.UInt32_To_LE((uint)f0, output, outOff); + f1 += (f0 >> 32); + Pack.UInt32_To_LE((uint)f1, output, outOff + 4); + f2 += (f1 >> 32); + Pack.UInt32_To_LE((uint)f2, output, outOff + 8); + f3 += (f2 >> 32); + Pack.UInt32_To_LE((uint)f3, output, outOff + 12); + + Reset(); + return BLOCK_SIZE; + } + + public void Reset() + { + currentBlockOffset = 0; + + h0 = h1 = h2 = h3 = h4 = 0; + } + + private static ulong mul32x32_64(uint i1, uint i2) + { + return ((ulong)i1) * i2; + } + } +} diff --git a/crypto/src/crypto/macs/SipHash.cs b/crypto/src/crypto/macs/SipHash.cs new file mode 100644
index 000000000..e1a19fa5b --- /dev/null +++ b/crypto/src/crypto/macs/SipHash.cs
@@ -0,0 +1,199 @@ +using System; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Macs +{ + /// <summary> + /// Implementation of SipHash as specified in "SipHash: a fast short-input PRF", by Jean-Philippe + /// Aumasson and Daniel J. Bernstein (https://131002.net/siphash/siphash.pdf). + /// </summary> + /// <remarks> + /// "SipHash is a family of PRFs SipHash-c-d where the integer parameters c and d are the number of + /// compression rounds and the number of finalization rounds. A compression round is identical to a + /// finalization round and this round function is called SipRound. Given a 128-bit key k and a + /// (possibly empty) byte string m, SipHash-c-d returns a 64-bit value..." + /// </remarks> + public class SipHash + : IMac + { + protected readonly int c, d; + + protected long k0, k1; + protected long v0, v1, v2, v3; + + protected long m = 0; + protected int wordPos = 0; + protected int wordCount = 0; + + /// <summary>SipHash-2-4</summary> + public SipHash() + : this(2, 4) + { + } + + /// <summary>SipHash-c-d</summary> + /// <param name="c">the number of compression rounds</param> + /// <param name="d">the number of finalization rounds</param> + public SipHash(int c, int d) + { + this.c = c; + this.d = d; + } + + public virtual string AlgorithmName + { + get { return "SipHash-" + c + "-" + d; } + } + + public virtual int GetMacSize() + { + return 8; + } + + public virtual void Init(ICipherParameters parameters) + { + KeyParameter keyParameter = parameters as KeyParameter; + if (keyParameter == null) + throw new ArgumentException("must be an instance of KeyParameter", "parameters"); + byte[] key = keyParameter.GetKey(); + if (key.Length != 16) + throw new ArgumentException("must be a 128-bit key", "parameters"); + + this.k0 = (long)Pack.LE_To_UInt64(key, 0); + this.k1 = (long)Pack.LE_To_UInt64(key, 8); + + Reset(); + } + + public virtual void Update(byte input) + { + m = (long)(((ulong)m >> 8) | ((ulong)input << 56)); + + if (++wordPos == 8) + { + ProcessMessageWord(); + wordPos = 0; + } + } + + public virtual void BlockUpdate(byte[] input, int offset, int length) + { + int i = 0, fullWords = length & ~7; + if (wordPos == 0) + { + for (; i < fullWords; i += 8) + { + m = (long)Pack.LE_To_UInt64(input, offset + i); + ProcessMessageWord(); + } + for (; i < length; ++i) + { + m = (long)(((ulong)m >> 8) | ((ulong)input[offset + i] << 56)); + } + wordPos = length - fullWords; + } + else + { + int bits = wordPos << 3; + for (; i < fullWords; i += 8) + { + ulong n = Pack.LE_To_UInt64(input, offset + i); + m = (long)((n << bits) | ((ulong)m >> -bits)); + ProcessMessageWord(); + m = (long)n; + } + for (; i < length; ++i) + { + m = (long)(((ulong)m >> 8) | ((ulong)input[offset + i] << 56)); + + if (++wordPos == 8) + { + ProcessMessageWord(); + wordPos = 0; + } + } + } + } + + public virtual long DoFinal() + { + // NOTE: 2 distinct shifts to avoid "64-bit shift" when wordPos == 0 + m = (long)((ulong)m >> ((7 - wordPos) << 3)); + m = (long)((ulong)m >> 8); + m = (long)((ulong)m | ((ulong)((wordCount << 3) + wordPos) << 56)); + + ProcessMessageWord(); + + v2 ^= 0xffL; + + ApplySipRounds(d); + + long result = v0 ^ v1 ^ v2 ^ v3; + + Reset(); + + return result; + } + + public virtual int DoFinal(byte[] output, int outOff) + { + long result = DoFinal(); + Pack.UInt64_To_LE((ulong)result, output, outOff); + return 8; + } + + public virtual void Reset() + { + v0 = k0 ^ 0x736f6d6570736575L; + v1 = k1 ^ 0x646f72616e646f6dL; + v2 = k0 ^ 0x6c7967656e657261L; + v3 = k1 ^ 0x7465646279746573L; + + m = 0; + wordPos = 0; + wordCount = 0; + } + + protected virtual void ProcessMessageWord() + { + ++wordCount; + v3 ^= m; + ApplySipRounds(c); + v0 ^= m; + } + + protected virtual void ApplySipRounds(int n) + { + long r0 = v0, r1 = v1, r2 = v2, r3 = v3; + + for (int r = 0; r < n; ++r) + { + r0 += r1; + r2 += r3; + r1 = RotateLeft(r1, 13); + r3 = RotateLeft(r3, 16); + r1 ^= r0; + r3 ^= r2; + r0 = RotateLeft(r0, 32); + r2 += r1; + r0 += r3; + r1 = RotateLeft(r1, 17); + r3 = RotateLeft(r3, 21); + r1 ^= r2; + r3 ^= r0; + r2 = RotateLeft(r2, 32); + } + + v0 = r0; v1 = r1; v2 = r2; v3 = r3; + } + + protected static long RotateLeft(long x, int n) + { + ulong ux = (ulong)x; + ux = (ux << n) | (ux >> -n); + return (long)ux; + } + } +} diff --git a/crypto/src/crypto/macs/SkeinMac.cs b/crypto/src/crypto/macs/SkeinMac.cs new file mode 100644
index 000000000..1d61a41ca --- /dev/null +++ b/crypto/src/crypto/macs/SkeinMac.cs
@@ -0,0 +1,117 @@ +using System; + +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Macs +{ + + /// <summary> + /// Implementation of the Skein parameterised MAC function in 256, 512 and 1024 bit block sizes, + /// based on the <see cref="Org.BouncyCastle.Crypto.Engines.ThreefishEngine">Threefish</see> tweakable block cipher. + /// </summary> + /// <remarks> + /// This is the 1.3 version of Skein defined in the Skein hash function submission to the NIST SHA-3 + /// competition in October 2010. + /// <p/> + /// Skein was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir + /// Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker. + /// </remarks> + /// <seealso cref="Org.BouncyCastle.Crypto.Digests.SkeinEngine"/> + /// <seealso cref="Org.BouncyCastle.Crypto.Parameters.SkeinParameters"/> + public class SkeinMac + : IMac + { + /// <summary> + /// 256 bit block size - Skein-256 + /// </summary> + public const int SKEIN_256 = SkeinEngine.SKEIN_256; + /// <summary> + /// 512 bit block size - Skein-512 + /// </summary> + public const int SKEIN_512 = SkeinEngine.SKEIN_512; + /// <summary> + /// 1024 bit block size - Skein-1024 + /// </summary> + public const int SKEIN_1024 = SkeinEngine.SKEIN_1024; + + private readonly SkeinEngine engine; + + /// <summary> + /// Constructs a Skein MAC with an internal state size and output size. + /// </summary> + /// <param name="stateSizeBits">the internal state size in bits - one of <see cref="SKEIN_256"/> <see cref="SKEIN_512"/> or + /// <see cref="SKEIN_1024"/>.</param> + /// <param name="digestSizeBits">the output/MAC size to produce in bits, which must be an integral number of + /// bytes.</param> + public SkeinMac(int stateSizeBits, int digestSizeBits) + { + this.engine = new SkeinEngine(stateSizeBits, digestSizeBits); + } + + public SkeinMac(SkeinMac mac) + { + this.engine = new SkeinEngine(mac.engine); + } + + public string AlgorithmName + { + get { return "Skein-MAC-" + (engine.BlockSize * 8) + "-" + (engine.OutputSize * 8); } + } + + /// <summary> + /// Optionally initialises the Skein digest with the provided parameters. + /// </summary> + /// See <see cref="Org.BouncyCastle.Crypto.Parameters.SkeinParameters"></see> for details on the parameterisation of the Skein hash function. + /// <param name="parameters">the parameters to apply to this engine, or <code>null</code> to use no parameters.</param> + public void Init(ICipherParameters parameters) + { + SkeinParameters skeinParameters; + if (parameters is SkeinParameters) + { + skeinParameters = (SkeinParameters)parameters; + } + else if (parameters is KeyParameter) + { + skeinParameters = new SkeinParameters.Builder().SetKey(((KeyParameter)parameters).GetKey()).Build(); + } + else + { + throw new ArgumentException("Invalid parameter passed to Skein MAC init - " + + parameters.GetType().Name); + } + if (skeinParameters.GetKey() == null) + { + throw new ArgumentException("Skein MAC requires a key parameter."); + } + engine.Init(skeinParameters); + } + + public int GetMacSize() + { + return engine.OutputSize; + } + + public void Reset() + { + engine.Reset(); + } + + public void Update(byte inByte) + { + engine.Update(inByte); + } + + public void BlockUpdate(byte[] input, int inOff, int len) + { + engine.Update(input, inOff, len); + } + + public int DoFinal(byte[] output, int outOff) + { + return engine.DoFinal(output, outOff); + } + + } +} 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
index 0bbc0cb24..9345fd8c2 100644 --- a/crypto/src/crypto/modes/CbcBlockCipher.cs +++ b/crypto/src/crypto/modes/CbcBlockCipher.cs
@@ -55,6 +55,8 @@ namespace Org.BouncyCastle.Crypto.Modes bool forEncryption, ICipherParameters parameters) { + bool oldEncrypting = this.encrypting; + this.encrypting = forEncryption; if (parameters is ParametersWithIV) @@ -74,7 +76,15 @@ namespace Org.BouncyCastle.Crypto.Modes Reset(); - cipher.Init(encrypting, parameters); + // if null it's an IV changed only. + if (parameters != null) + { + cipher.Init(encrypting, parameters); + } + else if (oldEncrypting != encrypting) + { + throw new ArgumentException("cannot change encrypting state without providing key."); + } } /** diff --git a/crypto/src/crypto/modes/CcmBlockCipher.cs b/crypto/src/crypto/modes/CcmBlockCipher.cs
index abfde237e..653d75cb9 100644 --- a/crypto/src/crypto/modes/CcmBlockCipher.cs +++ b/crypto/src/crypto/modes/CcmBlockCipher.cs
@@ -13,21 +13,22 @@ namespace Org.BouncyCastle.Crypto.Modes * NIST Special Publication 800-38C. * <p> * <b>Note</b>: this mode is a packet mode - it needs all the data up front. - * </p> + * </p> */ public class CcmBlockCipher - : IAeadBlockCipher + : IAeadBlockCipher { - private static readonly int BlockSize = 16; + private static readonly int BlockSize = 16; - private readonly IBlockCipher cipher; + 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(); + private byte[] nonce; + private byte[] initialAssociatedText; + private int macSize; + private ICipherParameters keyParam; + private readonly MemoryStream associatedText = new MemoryStream(); + private readonly MemoryStream data = new MemoryStream(); /** * Basic constructor. @@ -35,7 +36,7 @@ namespace Org.BouncyCastle.Crypto.Modes * @param cipher the block cipher to be used. */ public CcmBlockCipher( - IBlockCipher cipher) + IBlockCipher cipher) { this.cipher = cipher; this.macBlock = new byte[BlockSize]; @@ -55,88 +56,104 @@ namespace Org.BouncyCastle.Crypto.Modes } public virtual void Init( - bool forEncryption, - ICipherParameters parameters) + 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"); - } + this.forEncryption = forEncryption; + + if (parameters is AeadParameters) + { + AeadParameters param = (AeadParameters) parameters; + + nonce = param.GetNonce(); + initialAssociatedText = param.GetAssociatedText(); + macSize = param.MacSize / 8; + keyParam = param.Key; + } + else if (parameters is ParametersWithIV) + { + ParametersWithIV param = (ParametersWithIV) parameters; + + nonce = param.GetIV(); + initialAssociatedText = null; + macSize = macBlock.Length / 2; + keyParam = param.Parameters; + } + else + { + throw new ArgumentException("invalid parameters passed to CCM"); + } + + if (nonce == null || nonce.Length < 7 || nonce.Length > 13) + { + throw new ArgumentException("nonce must have length from 7 to 13 octets"); + } } - public virtual string AlgorithmName + 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); - } - - /** + public virtual int GetBlockSize() + { + return cipher.GetBlockSize(); + } + + public virtual void ProcessAadByte(byte input) + { + associatedText.WriteByte(input); + } + + public virtual void ProcessAadBytes(byte[] inBytes, int inOff, int len) + { + // TODO: Process AAD online + associatedText.Write(inBytes, inOff, len); + } + + 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[] enc = ProcessPacket(data.GetBuffer(), 0, (int)data.Position); + + Array.Copy(enc, 0, outBytes, outOff, enc.Length); + + Reset(); + + return enc.Length; + } + + public virtual void Reset() + { + cipher.Reset(); + associatedText.SetLength(0); + data.SetLength(0); + } + + /** * Returns a byte array containing the mac calculated as part of the * last encrypt or decrypt operation. * @@ -144,53 +161,64 @@ namespace Org.BouncyCastle.Crypto.Modes */ public virtual byte[] GetMac() { - byte[] mac = new byte[macSize]; + byte[] mac = new byte[macSize]; + + Array.Copy(macBlock, 0, mac, 0, mac.Length); - Array.Copy(macBlock, 0, mac, 0, mac.Length); + return mac; + } - return mac; + public virtual int GetUpdateOutputSize( + int len) + { + return 0; } - 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) + public int GetOutputSize( + int len) { + int totalData = (int)data.Length + len; + + if (forEncryption) + { + return totalData + macSize; + } + + return totalData < macSize ? 0 : totalData - macSize; + } + + public byte[] ProcessPacket( + byte[] input, + int inOff, + int inLen) + { + // TODO: handle null keyParam (e.g. via RepeatedKeySpec) + // Need to keep the CTR and CBC Mac parts around and reset if (keyParam == null) throw new InvalidOperationException("CCM cipher unitialized."); - IBlockCipher ctrCipher = new SicBlockCipher(cipher); - byte[] iv = new byte[BlockSize]; - byte[] output; + int n = nonce.Length; + int q = 15 - n; + if (q < 4) + { + int limitLen = 1 << (8 * q); + if (inLen >= limitLen) + throw new InvalidOperationException("CCM packet too large for choice of q."); + } - iv[0] = (byte)(((15 - nonce.Length) - 1) & 0x7); + byte[] iv = new byte[BlockSize]; + iv[0] = (byte)((q - 1) & 0x7); + nonce.CopyTo(iv, 1); - Array.Copy(nonce, 0, iv, 1, nonce.Length); + IBlockCipher ctrCipher = new SicBlockCipher(cipher); + ctrCipher.Init(forEncryption, new ParametersWithIV(keyParam, iv)); - ctrCipher.Init(forEncryption, new ParametersWithIV(keyParam, iv)); + int index = inOff; + int outOff = 0; + byte[] output; - if (forEncryption) + if (forEncryption) { - int index = inOff; - int outOff = 0; - output = new byte[inLen + macSize]; calculateMac(input, inOff, inLen, macBlock); @@ -218,9 +246,6 @@ namespace Org.BouncyCastle.Crypto.Modes } else { - int index = inOff; - int outOff = 0; - output = new byte[inLen - macSize]; Array.Copy(input, inOff + inLen - macSize, macBlock, 0, macSize); @@ -251,25 +276,25 @@ namespace Org.BouncyCastle.Crypto.Modes calculateMac(output, 0, output.Length, calculatedMacBlock); - if (!Arrays.ConstantTimeAreEqual(macBlock, calculatedMacBlock)) + if (!Arrays.ConstantTimeAreEqual(macBlock, calculatedMacBlock)) throw new InvalidCipherTextException("mac check in CCM failed"); } - return output; + return output; } - private int calculateMac(byte[] data, int dataOff, int dataLen, byte[] macBlock) + private int calculateMac(byte[] data, int dataOff, int dataLen, byte[] macBlock) { - IMac cMac = new CbcBlockCipherMac(cipher, macSize * 8); + IMac cMac = new CbcBlockCipherMac(cipher, macSize * 8); - cMac.Init(keyParam); + cMac.Init(keyParam); - // + // // build b0 // byte[] b0 = new byte[16]; - if (hasAssociatedText()) + if (HasAssociatedText()) { b0[0] |= 0x40; } @@ -294,14 +319,15 @@ namespace Org.BouncyCastle.Crypto.Modes // // process associated text // - if (hasAssociatedText()) + if (HasAssociatedText()) { int extra; - if (associatedText.Length < ((1 << 16) - (1 << 8))) + int textLength = GetAssociatedTextLength(); + if (textLength < ((1 << 16) - (1 << 8))) { - cMac.Update((byte)(associatedText.Length >> 8)); - cMac.Update((byte)associatedText.Length); + cMac.Update((byte)(textLength >> 8)); + cMac.Update((byte)textLength); extra = 2; } @@ -309,20 +335,27 @@ namespace Org.BouncyCastle.Crypto.Modes { 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); + cMac.Update((byte)(textLength >> 24)); + cMac.Update((byte)(textLength >> 16)); + cMac.Update((byte)(textLength >> 8)); + cMac.Update((byte)textLength); extra = 6; } - cMac.BlockUpdate(associatedText, 0, associatedText.Length); + if (initialAssociatedText != null) + { + cMac.BlockUpdate(initialAssociatedText, 0, initialAssociatedText.Length); + } + if (associatedText.Position > 0) + { + cMac.BlockUpdate(associatedText.GetBuffer(), 0, (int)associatedText.Position); + } - extra = (extra + associatedText.Length) % 16; + extra = (extra + textLength) % 16; if (extra != 0) { - for (int i = 0; i != 16 - extra; i++) + for (int i = extra; i < 16; ++i) { cMac.Update((byte)0x00); } @@ -337,9 +370,14 @@ namespace Org.BouncyCastle.Crypto.Modes return cMac.DoFinal(macBlock, 0); } - private bool hasAssociatedText() - { - return associatedText != null && associatedText.Length != 0; - } + private int GetAssociatedTextLength() + { + return (int)associatedText.Length + ((initialAssociatedText == null) ? 0 : initialAssociatedText.Length); + } + + private bool HasAssociatedText() + { + return GetAssociatedTextLength() > 0; + } } } diff --git a/crypto/src/crypto/modes/CfbBlockCipher.cs b/crypto/src/crypto/modes/CfbBlockCipher.cs
index b400a72f4..433716535 100644 --- a/crypto/src/crypto/modes/CfbBlockCipher.cs +++ b/crypto/src/crypto/modes/CfbBlockCipher.cs
@@ -71,8 +71,14 @@ namespace Org.BouncyCastle.Crypto.Modes parameters = ivParam.Parameters; } Reset(); - cipher.Init(true, parameters); + + // if it's null, key is to be reused. + if (parameters != null) + { + cipher.Init(true, parameters); + } } + /** * return the algorithm name and mode. * 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
index b3016d79c..5ccc69b66 100644 --- a/crypto/src/crypto/modes/EAXBlockCipher.cs +++ b/crypto/src/crypto/modes/EAXBlockCipher.cs
@@ -40,6 +40,9 @@ namespace Org.BouncyCastle.Crypto.Modes private byte[] bufBlock; private int bufOff; + private bool cipherInitialized; + private byte[] initialAssociatedText; + /** * Constructor that accepts an instance of a block cipher engine. * @@ -62,6 +65,11 @@ namespace Org.BouncyCastle.Crypto.Modes get { return cipher.GetUnderlyingCipher().AlgorithmName + "/EAX"; } } + public IBlockCipher GetUnderlyingCipher() + { + return cipher; + } + public virtual int GetBlockSize() { return cipher.GetBlockSize(); @@ -73,7 +81,7 @@ namespace Org.BouncyCastle.Crypto.Modes { this.forEncryption = forEncryption; - byte[] nonce, associatedText; + byte[] nonce; ICipherParameters keyParam; if (parameters is AeadParameters) @@ -81,7 +89,7 @@ namespace Org.BouncyCastle.Crypto.Modes AeadParameters param = (AeadParameters) parameters; nonce = param.GetNonce(); - associatedText = param.GetAssociatedText(); + initialAssociatedText = param.GetAssociatedText(); macSize = param.MacSize / 8; keyParam = param.Key; } @@ -90,7 +98,7 @@ namespace Org.BouncyCastle.Crypto.Modes ParametersWithIV param = (ParametersWithIV) parameters; nonce = param.GetIV(); - associatedText = new byte[0]; + initialAssociatedText = null; macSize = mac.GetMacSize() / 2; keyParam = param.Parameters; } @@ -99,26 +107,45 @@ namespace Org.BouncyCastle.Crypto.Modes throw new ArgumentException("invalid parameters passed to EAX"); } - byte[] tag = new byte[blockSize]; + 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); + // Key reuse implemented in CBC mode of underlying CMac + mac.Init(keyParam); - 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.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); + tag[blockSize - 1] = (byte)Tag.H; + mac.BlockUpdate(tag, 0, blockSize); - cipher.Init(true, new ParametersWithIV(keyParam, nonceMac)); + if (initialAssociatedText != null) + { + ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length); + } + + // Same BlockCipher underlies this and the mac, so reuse last key on cipher + cipher.Init(true, new ParametersWithIV(null, nonceMac)); } - private void calculateMac() + private void InitCipher() + { + if (cipherInitialized) + { + return; + } + + cipherInitialized = true; + + mac.DoFinal(associatedTextMac, 0); + + byte[] tag = new byte[blockSize]; + tag[blockSize - 1] = (byte)Tag.C; + mac.BlockUpdate(tag, 0, blockSize); + } + + private void CalculateMac() { byte[] outC = new byte[blockSize]; mac.DoFinal(outC, 0); @@ -137,7 +164,7 @@ namespace Org.BouncyCastle.Crypto.Modes private void Reset( bool clearMac) { - cipher.Reset(); + cipher.Reset(); // TODO Redundant since the mac will reset it? mac.Reset(); bufOff = 0; @@ -148,44 +175,75 @@ namespace Org.BouncyCastle.Crypto.Modes 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[] tag = new byte[blockSize]; + tag[blockSize - 1] = (byte)Tag.H; + mac.BlockUpdate(tag, 0, blockSize); + + cipherInitialized = false; + + if (initialAssociatedText != null) + { + ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length); + } + } + + public virtual void ProcessAadByte(byte input) + { + if (cipherInitialized) + { + throw new InvalidOperationException("AAD data cannot be added after encryption/decription processing has begun."); + } + mac.Update(input); + } + + public void ProcessAadBytes(byte[] inBytes, int inOff, int len) + { + if (cipherInitialized) + { + throw new InvalidOperationException("AAD data cannot be added after encryption/decription processing has begun."); + } + mac.BlockUpdate(inBytes, inOff, len); + } + + public virtual int ProcessByte( byte input, byte[] outBytes, int outOff) { - return process(input, outBytes, outOff); + InitCipher(); + + return Process(input, outBytes, outOff); } - public virtual int ProcessBytes( + public virtual int ProcessBytes( byte[] inBytes, int inOff, int len, byte[] outBytes, int outOff) { - int resultLen = 0; + InitCipher(); + + int resultLen = 0; for (int i = 0; i != len; i++) { - resultLen += process(inBytes[inOff + i], outBytes, outOff + resultLen); + resultLen += Process(inBytes[inOff + i], outBytes, outOff + resultLen); } - return resultLen; + return resultLen; } public virtual int DoFinal( byte[] outBytes, int outOff) { - int extra = bufOff; + InitCipher(); + + int extra = bufOff; byte[] tmp = new byte[bufBlock.Length]; - bufOff = 0; + bufOff = 0; if (forEncryption) { @@ -196,7 +254,7 @@ namespace Org.BouncyCastle.Crypto.Modes mac.BlockUpdate(tmp, 0, extra); - calculateMac(); + CalculateMac(); Array.Copy(macBlock, 0, outBytes, outOff + extra, macSize); @@ -216,9 +274,9 @@ namespace Org.BouncyCastle.Crypto.Modes Array.Copy(tmp, 0, outBytes, outOff, extra - macSize); } - calculateMac(); + CalculateMac(); - if (!verifyMac(bufBlock, extra - macSize)) + if (!VerifyMac(bufBlock, extra - macSize)) throw new InvalidCipherTextException("mac check in EAX failed"); Reset(false); @@ -236,24 +294,35 @@ namespace Org.BouncyCastle.Crypto.Modes return mac; } - public virtual int GetUpdateOutputSize( + public virtual int GetUpdateOutputSize( int len) { - return ((len + bufOff) / blockSize) * blockSize; - } + int totalData = len + bufOff; + if (!forEncryption) + { + if (totalData < macSize) + { + return 0; + } + totalData -= macSize; + } + return totalData - totalData % blockSize; + } public virtual int GetOutputSize( int len) { - if (forEncryption) - { - return len + bufOff + macSize; - } + int totalData = len + bufOff; - return len + bufOff - macSize; - } + if (forEncryption) + { + return totalData + macSize; + } - private int process( + return totalData < macSize ? 0 : totalData - macSize; + } + + private int Process( byte b, byte[] outBytes, int outOff) @@ -286,17 +355,16 @@ namespace Org.BouncyCastle.Crypto.Modes return 0; } - private bool verifyMac(byte[] mac, int off) + private bool VerifyMac(byte[] mac, int off) { - for (int i = 0; i < macSize; i++) - { - if (macBlock[i] != mac[off + i]) - { - return false; - } - } + int nonEqual = 0; + + for (int i = 0; i < macSize; i++) + { + nonEqual |= (macBlock[i] ^ mac[off + i]); + } - return true; + return nonEqual == 0; } } } diff --git a/crypto/src/crypto/modes/GCMBlockCipher.cs b/crypto/src/crypto/modes/GCMBlockCipher.cs
index 6a3a4463d..2e2ac2eca 100644 --- a/crypto/src/crypto/modes/GCMBlockCipher.cs +++ b/crypto/src/crypto/modes/GCMBlockCipher.cs
@@ -4,397 +4,513 @@ 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 + /// <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 readonly IBlockCipher cipher; + private readonly IGcmMultiplier multiplier; + private IGcmExponentiator exp; + + // These fields are set by Init and not modified by processing + private bool forEncryption; + private int macSize; + private byte[] nonce; + private byte[] initialAssociatedText; + private byte[] H; + private byte[] J0; + + // These fields are modified during processing + private byte[] bufBlock; + private byte[] macBlock; + private byte[] S, S_at, S_atPre; + private byte[] counter; + private int bufOff; + private ulong totalLength; + private byte[] atBlock; + private int atBlockPos; + private ulong atLength; + private ulong atLengthPre; + + 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 IBlockCipher GetUnderlyingCipher() + { + return cipher; + } + + public virtual int GetBlockSize() + { + return BlockSize; + } + + /// <remarks> + /// MAC sizes from 32 bits to 128 bits (must be a multiple of 8) are supported. The default is 128 bits. + /// Sizes less than 96 are not recommended, but are supported for specialized applications. + /// </remarks> + public virtual void Init( + bool forEncryption, + ICipherParameters parameters) + { + this.forEncryption = forEncryption; + this.macBlock = null; + + KeyParameter keyParam; + + if (parameters is AeadParameters) + { + AeadParameters param = (AeadParameters)parameters; + + nonce = param.GetNonce(); + initialAssociatedText = param.GetAssociatedText(); + + int macSizeBits = param.MacSize; + if (macSizeBits < 32 || 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(); + initialAssociatedText = 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"); + } + + // TODO Restrict macSize to 16 if nonce length not 12? + + // Cipher always used in forward mode + // if keyParam is null we're reusing the last key. + if (keyParam != null) + { + cipher.Init(true, keyParam); + + this.H = new byte[BlockSize]; + cipher.ProcessBlock(H, 0, H, 0); + + // if keyParam is null we're reusing the last key and the multiplier doesn't need re-init + multiplier.Init(H); + exp = null; + } + else if (this.H == null) + { + throw new ArgumentException("Key must be specified in initial init"); + } + + this.J0 = new byte[BlockSize]; + + if (nonce.Length == 12) + { + Array.Copy(nonce, 0, J0, 0, nonce.Length); + this.J0[BlockSize - 1] = 0x01; + } + else + { + gHASH(J0, nonce, nonce.Length); + byte[] X = new byte[BlockSize]; + Pack.UInt64_To_BE((ulong)nonce.Length * 8UL, X, 8); + gHASHBlock(J0, X); + } + + this.S = new byte[BlockSize]; + this.S_at = new byte[BlockSize]; + this.S_atPre = new byte[BlockSize]; + this.atBlock = new byte[BlockSize]; + this.atBlockPos = 0; + this.atLength = 0; + this.atLengthPre = 0; + this.counter = Arrays.Clone(J0); + this.bufOff = 0; + this.totalLength = 0; + + if (initialAssociatedText != null) + { + ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length); + } + } + + public virtual byte[] GetMac() + { + return Arrays.Clone(macBlock); + } + + public virtual int GetOutputSize( + int len) + { + int totalData = len + bufOff; + + if (forEncryption) + { + return totalData + macSize; + } + + return totalData < macSize ? 0 : totalData - macSize; + } + + public virtual int GetUpdateOutputSize( + int len) + { + int totalData = len + bufOff; + if (!forEncryption) + { + if (totalData < macSize) + { + return 0; + } + totalData -= macSize; + } + return totalData - totalData % BlockSize; + } + + public virtual void ProcessAadByte(byte input) + { + atBlock[atBlockPos] = input; + if (++atBlockPos == BlockSize) + { + // Hash each block as it fills + gHASHBlock(S_at, atBlock); + atBlockPos = 0; + atLength += BlockSize; + } + } + + public virtual void ProcessAadBytes(byte[] inBytes, int inOff, int len) + { + for (int i = 0; i < len; ++i) + { + atBlock[atBlockPos] = inBytes[inOff + i]; + if (++atBlockPos == BlockSize) + { + // Hash each block as it fills + gHASHBlock(S_at, atBlock); + atBlockPos = 0; + atLength += BlockSize; + } + } + } + + private void InitCipher() + { + if (atLength > 0) + { + Array.Copy(S_at, 0, S_atPre, 0, BlockSize); + atLengthPre = atLength; + } + + // Finish hash for partial AAD block + if (atBlockPos > 0) + { + gHASHPartial(S_atPre, atBlock, 0, atBlockPos); + atLengthPre += (uint)atBlockPos; + } + + if (atLengthPre > 0) + { + Array.Copy(S_atPre, 0, S, 0, BlockSize); + } + } + + public virtual int ProcessByte( + byte input, + byte[] output, + int outOff) + { + bufBlock[bufOff] = input; + if (++bufOff == bufBlock.Length) + { + OutputBlock(output, outOff); + return BlockSize; + } + return 0; + } + + public virtual int ProcessBytes( + byte[] input, + int inOff, + int len, + byte[] output, + int outOff) + { + int resultLen = 0; + + for (int i = 0; i < len; ++i) + { + bufBlock[bufOff] = input[inOff + i]; + if (++bufOff == bufBlock.Length) + { + OutputBlock(output, outOff + resultLen); + resultLen += BlockSize; + } + } + + return resultLen; + } + + private void OutputBlock(byte[] output, int offset) + { + if (totalLength == 0) + { + InitCipher(); + } + gCTRBlock(bufBlock, output, offset); + if (forEncryption) + { + bufOff = 0; + } + else + { + Array.Copy(bufBlock, BlockSize, bufBlock, 0, macSize); + bufOff = macSize; + } + } + + public int DoFinal(byte[] output, int outOff) + { + if (totalLength == 0) + { + InitCipher(); + } + + int extra = bufOff; + if (!forEncryption) + { + if (extra < macSize) + throw new InvalidCipherTextException("data too short"); + + extra -= macSize; + } + + if (extra > 0) + { + gCTRPartial(bufBlock, 0, extra, output, outOff); + } + + atLength += (uint)atBlockPos; + + if (atLength > atLengthPre) + { + /* + * Some AAD was sent after the cipher started. We determine the difference b/w the hash value + * we actually used when the cipher started (S_atPre) and the final hash value calculated (S_at). + * Then we carry this difference forward by multiplying by H^c, where c is the number of (full or + * partial) cipher-text blocks produced, and adjust the current hash. + */ + + // Finish hash for partial AAD block + if (atBlockPos > 0) + { + gHASHPartial(S_at, atBlock, 0, atBlockPos); + } + + // Find the difference between the AAD hashes + if (atLengthPre > 0) + { + GcmUtilities.Xor(S_at, S_atPre); + } + + // Number of cipher-text blocks produced + long c = (long)(((totalLength * 8) + 127) >> 7); + + // Calculate the adjustment factor + byte[] H_c = new byte[16]; + if (exp == null) + { + exp = new Tables1kGcmExponentiator(); + exp.Init(H); + } + exp.ExponentiateX(c, H_c); + + // Carry the difference forward + GcmUtilities.Multiply(S_at, H_c); + + // Adjust the current hash + GcmUtilities.Xor(S, S_at); + } + + // Final gHASH + byte[] X = new byte[BlockSize]; + Pack.UInt64_To_BE(atLength * 8UL, X, 0); + Pack.UInt64_To_BE(totalLength * 8UL, X, 8); + + gHASHBlock(S, X); + + // 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) + { + cipher.Reset(); + + S = new byte[BlockSize]; + S_at = new byte[BlockSize]; + S_atPre = new byte[BlockSize]; + atBlock = new byte[BlockSize]; + atBlockPos = 0; + atLength = 0; + atLengthPre = 0; + counter = Arrays.Clone(J0); + bufOff = 0; + totalLength = 0; + + if (bufBlock != null) + { + Arrays.Fill(bufBlock, 0); + } + + if (clearMac) + { + macBlock = null; + } + + if (initialAssociatedText != null) + { + ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length); + } + } + + private void gCTRBlock(byte[] block, byte[] output, int outOff) + { + byte[] tmp = GetNextCounterBlock(); + + GcmUtilities.Xor(tmp, block); + Array.Copy(tmp, 0, output, outOff, BlockSize); + + gHASHBlock(S, forEncryption ? tmp : block); + + totalLength += BlockSize; + } + + private void gCTRPartial(byte[] buf, int off, int len, byte[] output, int outOff) + { + byte[] tmp = GetNextCounterBlock(); + + GcmUtilities.Xor(tmp, buf, off, len); + Array.Copy(tmp, 0, output, outOff, len); + + gHASHPartial(S, forEncryption ? tmp : buf, 0, len); + + totalLength += (uint)len; + } + + private void gHASH(byte[] Y, byte[] b, int len) + { + for (int pos = 0; pos < len; pos += BlockSize) + { + int num = System.Math.Min(len - pos, BlockSize); + gHASHPartial(Y, b, pos, num); + } + } + + private void gHASHBlock(byte[] Y, byte[] b) + { + GcmUtilities.Xor(Y, b); + multiplier.MultiplyH(Y); + } + + private void gHASHPartial(byte[] Y, byte[] b, int off, int len) + { + GcmUtilities.Xor(Y, b, off, len); + multiplier.MultiplyH(Y); + } + + private byte[] GetNextCounterBlock() + { + for (int i = 15; i >= 12; --i) + { + if (++counter[i] != 0) break; + } + + byte[] tmp = new byte[BlockSize]; + // TODO Sure would be nice if ciphers could operate on int[] + cipher.ProcessBlock(counter, 0, tmp, 0); + return tmp; + } + } +} diff --git a/crypto/src/crypto/modes/GOFBBlockCipher.cs b/crypto/src/crypto/modes/GOFBBlockCipher.cs
index 7db843115..a91562549 100644 --- a/crypto/src/crypto/modes/GOFBBlockCipher.cs +++ b/crypto/src/crypto/modes/GOFBBlockCipher.cs
@@ -98,7 +98,11 @@ namespace Org.BouncyCastle.Crypto.Modes Reset(); - cipher.Init(true, parameters); + // if it's null, key is to be reused. + if (parameters != null) + { + cipher.Init(true, parameters); + } } /** diff --git a/crypto/src/crypto/modes/IAeadBlockCipher.cs b/crypto/src/crypto/modes/IAeadBlockCipher.cs
index ca7dab44c..52c4ff428 100644 --- a/crypto/src/crypto/modes/IAeadBlockCipher.cs +++ b/crypto/src/crypto/modes/IAeadBlockCipher.cs
@@ -11,6 +11,9 @@ namespace Org.BouncyCastle.Crypto.Modes /// <summary>The name of the algorithm this cipher implements.</summary> string AlgorithmName { get; } + /// <summary>The block cipher underlying this algorithm.</summary> + IBlockCipher GetUnderlyingCipher(); + /// <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> @@ -20,7 +23,19 @@ namespace Org.BouncyCastle.Crypto.Modes /// <returns>The block size for this cipher, in bytes.</returns> int GetBlockSize(); - /** + /// <summary>Add a single byte to the associated data check.</summary> + /// <remarks>If the implementation supports it, this will be an online operation and will not retain the associated data.</remarks> + /// <param name="input">The byte to be processed.</param> + void ProcessAadByte(byte input); + + /// <summary>Add a sequence of bytes to the associated data check.</summary> + /// <remarks>If the implementation supports it, this will be an online operation and will not retain the associated data.</remarks> + /// <param name="inBytes">The input byte array.</param> + /// <param name="inOff">The offset into the input array where the data to be processed starts.</param> + /// <param name="len">The number of bytes to be processed.</param> + void ProcessAadBytes(byte[] inBytes, int inOff, int len); + + /** * Encrypt/decrypt a single byte. * * @param input the byte to be processed. diff --git a/crypto/src/crypto/modes/OCBBlockCipher.cs b/crypto/src/crypto/modes/OCBBlockCipher.cs new file mode 100644
index 000000000..54359dfe8 --- /dev/null +++ b/crypto/src/crypto/modes/OCBBlockCipher.cs
@@ -0,0 +1,558 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Modes +{ + /** + * An implementation of <a href="http://tools.ietf.org/html/rfc7253">RFC 7253 on The OCB + * Authenticated-Encryption Algorithm</a>, licensed per: + * + * <blockquote><p><a href="http://www.cs.ucdavis.edu/~rogaway/ocb/license1.pdf">License for + * Open-Source Software Implementations of OCB</a> (Jan 9, 2013) - 'License 1'<br/> + * Under this license, you are authorized to make, use, and distribute open-source software + * implementations of OCB. This license terminates for you if you sue someone over their open-source + * software implementation of OCB claiming that you have a patent covering their implementation. + * </p><p> + * This is a non-binding summary of a legal document (the link above). The parameters of the license + * are specified in the license document and that document is controlling.</p></blockquote> + */ + public class OcbBlockCipher + : IAeadBlockCipher + { + private const int BLOCK_SIZE = 16; + + private readonly IBlockCipher hashCipher; + private readonly IBlockCipher mainCipher; + + /* + * CONFIGURATION + */ + private bool forEncryption; + private int macSize; + private byte[] initialAssociatedText; + + /* + * KEY-DEPENDENT + */ + // NOTE: elements are lazily calculated + private IList L; + private byte[] L_Asterisk, L_Dollar; + + /* + * NONCE-DEPENDENT + */ + private byte[] KtopInput = null; + private byte[] Stretch = new byte[24]; + private byte[] OffsetMAIN_0 = new byte[16]; + + /* + * PER-ENCRYPTION/DECRYPTION + */ + private byte[] hashBlock, mainBlock; + private int hashBlockPos, mainBlockPos; + private long hashBlockCount, mainBlockCount; + private byte[] OffsetHASH; + private byte[] Sum; + private byte[] OffsetMAIN = new byte[16]; + private byte[] Checksum; + + // NOTE: The MAC value is preserved after doFinal + private byte[] macBlock; + + public OcbBlockCipher(IBlockCipher hashCipher, IBlockCipher mainCipher) + { + if (hashCipher == null) + throw new ArgumentNullException("hashCipher"); + if (hashCipher.GetBlockSize() != BLOCK_SIZE) + throw new ArgumentException("must have a block size of " + BLOCK_SIZE, "hashCipher"); + if (mainCipher == null) + throw new ArgumentNullException("mainCipher"); + if (mainCipher.GetBlockSize() != BLOCK_SIZE) + throw new ArgumentException("must have a block size of " + BLOCK_SIZE, "mainCipher"); + + if (!hashCipher.AlgorithmName.Equals(mainCipher.AlgorithmName)) + throw new ArgumentException("'hashCipher' and 'mainCipher' must be the same algorithm"); + + this.hashCipher = hashCipher; + this.mainCipher = mainCipher; + } + + public virtual IBlockCipher GetUnderlyingCipher() + { + return mainCipher; + } + + public virtual string AlgorithmName + { + get { return mainCipher.AlgorithmName + "/OCB"; } + } + + public virtual void Init(bool forEncryption, ICipherParameters parameters) + { + bool oldForEncryption = this.forEncryption; + this.forEncryption = forEncryption; + this.macBlock = null; + + KeyParameter keyParameter; + + byte[] N; + if (parameters is AeadParameters) + { + AeadParameters aeadParameters = (AeadParameters) parameters; + + N = aeadParameters.GetNonce(); + initialAssociatedText = aeadParameters.GetAssociatedText(); + + int macSizeBits = aeadParameters.MacSize; + if (macSizeBits < 64 || macSizeBits > 128 || macSizeBits % 8 != 0) + throw new ArgumentException("Invalid value for MAC size: " + macSizeBits); + + macSize = macSizeBits / 8; + keyParameter = aeadParameters.Key; + } + else if (parameters is ParametersWithIV) + { + ParametersWithIV parametersWithIV = (ParametersWithIV) parameters; + + N = parametersWithIV.GetIV(); + initialAssociatedText = null; + macSize = 16; + keyParameter = (KeyParameter) parametersWithIV.Parameters; + } + else + { + throw new ArgumentException("invalid parameters passed to OCB"); + } + + this.hashBlock = new byte[16]; + this.mainBlock = new byte[forEncryption ? BLOCK_SIZE : (BLOCK_SIZE + macSize)]; + + if (N == null) + { + N = new byte[0]; + } + + if (N.Length > 15) + { + throw new ArgumentException("IV must be no more than 15 bytes"); + } + + /* + * KEY-DEPENDENT INITIALISATION + */ + + if (keyParameter != null) + { + // hashCipher always used in forward mode + hashCipher.Init(true, keyParameter); + mainCipher.Init(forEncryption, keyParameter); + KtopInput = null; + } + else if (oldForEncryption != forEncryption) + { + throw new ArgumentException("cannot change encrypting state without providing key."); + } + + this.L_Asterisk = new byte[16]; + hashCipher.ProcessBlock(L_Asterisk, 0, L_Asterisk, 0); + + this.L_Dollar = OCB_double(L_Asterisk); + + this.L = Platform.CreateArrayList(); + this.L.Add(OCB_double(L_Dollar)); + + /* + * NONCE-DEPENDENT AND PER-ENCRYPTION/DECRYPTION INITIALISATION + */ + + int bottom = ProcessNonce(N); + + int bits = bottom % 8, bytes = bottom / 8; + if (bits == 0) + { + Array.Copy(Stretch, bytes, OffsetMAIN_0, 0, 16); + } + else + { + for (int i = 0; i < 16; ++i) + { + uint b1 = Stretch[bytes]; + uint b2 = Stretch[++bytes]; + this.OffsetMAIN_0[i] = (byte) ((b1 << bits) | (b2 >> (8 - bits))); + } + } + + this.hashBlockPos = 0; + this.mainBlockPos = 0; + + this.hashBlockCount = 0; + this.mainBlockCount = 0; + + this.OffsetHASH = new byte[16]; + this.Sum = new byte[16]; + Array.Copy(OffsetMAIN_0, 0, OffsetMAIN, 0, 16); + this.Checksum = new byte[16]; + + if (initialAssociatedText != null) + { + ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length); + } + } + + protected virtual int ProcessNonce(byte[] N) + { + byte[] nonce = new byte[16]; + Array.Copy(N, 0, nonce, nonce.Length - N.Length, N.Length); + nonce[0] = (byte)(macSize << 4); + nonce[15 - N.Length] |= 1; + + int bottom = nonce[15] & 0x3F; + nonce[15] &= 0xC0; + + /* + * When used with incrementing nonces, the cipher is only applied once every 64 inits. + */ + if (KtopInput == null || !Arrays.AreEqual(nonce, KtopInput)) + { + byte[] Ktop = new byte[16]; + KtopInput = nonce; + hashCipher.ProcessBlock(KtopInput, 0, Ktop, 0); + Array.Copy(Ktop, 0, Stretch, 0, 16); + for (int i = 0; i < 8; ++i) + { + Stretch[16 + i] = (byte)(Ktop[i] ^ Ktop[i + 1]); + } + } + + return bottom; + } + + public virtual int GetBlockSize() + { + return BLOCK_SIZE; + } + + public virtual byte[] GetMac() + { + return Arrays.Clone(macBlock); + } + + public virtual int GetOutputSize(int len) + { + int totalData = len + mainBlockPos; + if (forEncryption) + { + return totalData + macSize; + } + return totalData < macSize ? 0 : totalData - macSize; + } + + public virtual int GetUpdateOutputSize(int len) + { + int totalData = len + mainBlockPos; + if (!forEncryption) + { + if (totalData < macSize) + { + return 0; + } + totalData -= macSize; + } + return totalData - totalData % BLOCK_SIZE; + } + + public virtual void ProcessAadByte(byte input) + { + hashBlock[hashBlockPos] = input; + if (++hashBlockPos == hashBlock.Length) + { + ProcessHashBlock(); + } + } + + public virtual void ProcessAadBytes(byte[] input, int off, int len) + { + for (int i = 0; i < len; ++i) + { + hashBlock[hashBlockPos] = input[off + i]; + if (++hashBlockPos == hashBlock.Length) + { + ProcessHashBlock(); + } + } + } + + public virtual int ProcessByte(byte input, byte[] output, int outOff) + { + mainBlock[mainBlockPos] = input; + if (++mainBlockPos == mainBlock.Length) + { + ProcessMainBlock(output, outOff); + return BLOCK_SIZE; + } + return 0; + } + + public virtual int ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff) + { + int resultLen = 0; + + for (int i = 0; i < len; ++i) + { + mainBlock[mainBlockPos] = input[inOff + i]; + if (++mainBlockPos == mainBlock.Length) + { + ProcessMainBlock(output, outOff + resultLen); + resultLen += BLOCK_SIZE; + } + } + + return resultLen; + } + + public virtual int DoFinal(byte[] output, int outOff) + { + /* + * For decryption, get the tag from the end of the message + */ + byte[] tag = null; + if (!forEncryption) { + if (mainBlockPos < macSize) + throw new InvalidCipherTextException("data too short"); + + mainBlockPos -= macSize; + tag = new byte[macSize]; + Array.Copy(mainBlock, mainBlockPos, tag, 0, macSize); + } + + /* + * HASH: Process any final partial block; compute final hash value + */ + if (hashBlockPos > 0) + { + OCB_extend(hashBlock, hashBlockPos); + UpdateHASH(L_Asterisk); + } + + /* + * OCB-ENCRYPT/OCB-DECRYPT: Process any final partial block + */ + if (mainBlockPos > 0) + { + if (forEncryption) + { + OCB_extend(mainBlock, mainBlockPos); + Xor(Checksum, mainBlock); + } + + Xor(OffsetMAIN, L_Asterisk); + + byte[] Pad = new byte[16]; + hashCipher.ProcessBlock(OffsetMAIN, 0, Pad, 0); + + Xor(mainBlock, Pad); + + Array.Copy(mainBlock, 0, output, outOff, mainBlockPos); + + if (!forEncryption) + { + OCB_extend(mainBlock, mainBlockPos); + Xor(Checksum, mainBlock); + } + } + + /* + * OCB-ENCRYPT/OCB-DECRYPT: Compute raw tag + */ + Xor(Checksum, OffsetMAIN); + Xor(Checksum, L_Dollar); + hashCipher.ProcessBlock(Checksum, 0, Checksum, 0); + Xor(Checksum, Sum); + + this.macBlock = new byte[macSize]; + Array.Copy(Checksum, 0, macBlock, 0, macSize); + + /* + * Validate or append tag and reset this cipher for the next run + */ + int resultLen = mainBlockPos; + + if (forEncryption) + { + // Append tag to the message + Array.Copy(macBlock, 0, output, outOff + resultLen, macSize); + resultLen += macSize; + } + else + { + // Compare the tag from the message with the calculated one + if (!Arrays.ConstantTimeAreEqual(macBlock, tag)) + throw new InvalidCipherTextException("mac check in OCB failed"); + } + + Reset(false); + + return resultLen; + } + + public virtual void Reset() + { + Reset(true); + } + + protected virtual void Clear(byte[] bs) + { + if (bs != null) + { + Array.Clear(bs, 0, bs.Length); + } + } + + protected virtual byte[] GetLSub(int n) + { + while (n >= L.Count) + { + L.Add(OCB_double((byte[]) L[L.Count - 1])); + } + return (byte[])L[n]; + } + + protected virtual void ProcessHashBlock() + { + /* + * HASH: Process any whole blocks + */ + UpdateHASH(GetLSub(OCB_ntz(++hashBlockCount))); + hashBlockPos = 0; + } + + protected virtual void ProcessMainBlock(byte[] output, int outOff) + { + /* + * OCB-ENCRYPT/OCB-DECRYPT: Process any whole blocks + */ + + if (forEncryption) + { + Xor(Checksum, mainBlock); + mainBlockPos = 0; + } + + Xor(OffsetMAIN, GetLSub(OCB_ntz(++mainBlockCount))); + + Xor(mainBlock, OffsetMAIN); + mainCipher.ProcessBlock(mainBlock, 0, mainBlock, 0); + Xor(mainBlock, OffsetMAIN); + + Array.Copy(mainBlock, 0, output, outOff, 16); + + if (!forEncryption) + { + Xor(Checksum, mainBlock); + Array.Copy(mainBlock, BLOCK_SIZE, mainBlock, 0, macSize); + mainBlockPos = macSize; + } + } + + protected virtual void Reset(bool clearMac) + { + hashCipher.Reset(); + mainCipher.Reset(); + + Clear(hashBlock); + Clear(mainBlock); + + hashBlockPos = 0; + mainBlockPos = 0; + + hashBlockCount = 0; + mainBlockCount = 0; + + Clear(OffsetHASH); + Clear(Sum); + Array.Copy(OffsetMAIN_0, 0, OffsetMAIN, 0, 16); + Clear(Checksum); + + if (clearMac) + { + macBlock = null; + } + + if (initialAssociatedText != null) + { + ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length); + } + } + + protected virtual void UpdateHASH(byte[] LSub) + { + Xor(OffsetHASH, LSub); + Xor(hashBlock, OffsetHASH); + hashCipher.ProcessBlock(hashBlock, 0, hashBlock, 0); + Xor(Sum, hashBlock); + } + + protected static byte[] OCB_double(byte[] block) + { + byte[] result = new byte[16]; + int carry = ShiftLeft(block, result); + + /* + * NOTE: This construction is an attempt at a constant-time implementation. + */ + result[15] ^= (byte)(0x87 >> ((1 - carry) << 3)); + + return result; + } + + protected static void OCB_extend(byte[] block, int pos) + { + block[pos] = (byte) 0x80; + while (++pos < 16) + { + block[pos] = 0; + } + } + + protected static int OCB_ntz(long x) + { + if (x == 0) + { + return 64; + } + + int n = 0; + ulong ux = (ulong)x; + while ((ux & 1UL) == 0UL) + { + ++n; + ux >>= 1; + } + return n; + } + + protected static int ShiftLeft(byte[] block, byte[] output) + { + int i = 16; + uint bit = 0; + while (--i >= 0) + { + uint b = block[i]; + output[i] = (byte) ((b << 1) | bit); + bit = (b >> 7) & 1; + } + return (int)bit; + } + + protected static void Xor(byte[] block, byte[] val) + { + for (int i = 15; i >= 0; --i) + { + block[i] ^= val[i]; + } + } + } +} diff --git a/crypto/src/crypto/modes/OfbBlockCipher.cs b/crypto/src/crypto/modes/OfbBlockCipher.cs
index 9408a74d4..a99f8c5d7 100644 --- a/crypto/src/crypto/modes/OfbBlockCipher.cs +++ b/crypto/src/crypto/modes/OfbBlockCipher.cs
@@ -85,10 +85,14 @@ namespace Org.BouncyCastle.Crypto.Modes Reset(); - cipher.Init(true, parameters); + // if it's null, key is to be reused. + if (parameters != null) + { + cipher.Init(true, parameters); + } } - /** + /** * return the algorithm name and mode. * * @return the name of the underlying algorithm followed by "/OFB" 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
index c45026e82..da7ed7859 100644 --- a/crypto/src/crypto/modes/SicBlockCipher.cs +++ b/crypto/src/crypto/modes/SicBlockCipher.cs
@@ -5,106 +5,111 @@ 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; + /** + * 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]; - } + /** + * 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; - } + /** + * 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); + 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"); - } - } + Reset(); - public string AlgorithmName - { - get { return cipher.AlgorithmName + "/SIC"; } - } + // if null it's an IV changed only. + if (ivParam.Parameters != null) + { + cipher.Init(true, ivParam.Parameters); + } + } + else + { + throw new ArgumentException("SIC mode requires ParametersWithIV", "parameters"); + } + } - public bool IsPartialBlockOkay - { - get { return true; } - } + public string AlgorithmName + { + get { return cipher.AlgorithmName + "/SIC"; } + } - public int GetBlockSize() - { - return cipher.GetBlockSize(); - } + public bool IsPartialBlockOkay + { + get { return true; } + } - public int ProcessBlock( - byte[] input, - int inOff, - byte[] output, - int outOff) - { - cipher.ProcessBlock(counter, 0, counterOut, 0); + public int GetBlockSize() + { + return cipher.GetBlockSize(); + } - // - // 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]); - } + public int ProcessBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + cipher.ProcessBlock(counter, 0, counterOut, 0); - // Increment the counter - int j = counter.Length; - while (--j >= 0 && ++counter[j] == 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]); + } - return counter.Length; - } + // Increment the counter + int j = counter.Length; + while (--j >= 0 && ++counter[j] == 0) + { + } - public void Reset() - { - Array.Copy(IV, 0, counter, 0, counter.Length); - cipher.Reset(); - } - } + 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
index 4076de990..85e3ac9b1 100644 --- a/crypto/src/crypto/modes/gcm/BasicGcmMultiplier.cs +++ b/crypto/src/crypto/modes/gcm/BasicGcmMultiplier.cs
@@ -1,5 +1,7 @@ using System; +using Org.BouncyCastle.Utilities; + namespace Org.BouncyCastle.Crypto.Modes.Gcm { public class BasicGcmMultiplier @@ -9,10 +11,10 @@ namespace Org.BouncyCastle.Crypto.Modes.Gcm public void Init(byte[] H) { - this.H = (byte[])H.Clone(); + this.H = Arrays.Clone(H); } - public void MultiplyH(byte[] x) + 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
index 8da125641..71e63c8fd 100644 --- a/crypto/src/crypto/modes/gcm/GcmUtilities.cs +++ b/crypto/src/crypto/modes/gcm/GcmUtilities.cs
@@ -5,145 +5,233 @@ 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) - { + 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[] output = new uint[4]; + Pack.BE_To_UInt32(bs, 0, output); + return output; + } + + internal static void AsUints(byte[] bs, uint[] output) + { + Pack.BE_To_UInt32(bs, 0, output); + } + + 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 MultiplyP(uint[] x, uint[] output) + { + bool lsb = (x[3] & 1) != 0; + ShiftRight(x, output); + if (lsb) + { + output[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]; - } - } - } + 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 MultiplyP8(uint[] x, uint[] output) + { + uint lsw = x[3]; + ShiftRightN(x, 8, output); + for (int i = 7; i >= 0; --i) + { + if ((lsw & (1 << i)) != 0) + { + output[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); + } + } + + static void ShiftRight(byte[] block, byte[] output) + { + int i = 0; + byte bit = 0; + for (;;) + { + byte b = block[i]; + output[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 ShiftRight(uint[] block, uint[] output) + { + int i = 0; + uint bit = 0; + for (; ; ) + { + uint b = block[i]; + output[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 ShiftRightN(uint[] block, int n, uint[] output) + { + int i = 0; + uint bit = 0; + for (; ; ) + { + uint b = block[i]; + output[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(byte[] block, byte[] val, int off, int len) + { + while (--len >= 0) + { + block[len] ^= val[off + len]; + } + } + + internal static void Xor(byte[] block, byte[] val, byte[] output) + { + for (int i = 15; i >= 0; --i) + { + output[i] = (byte)(block[i] ^ val[i]); + } + } + + internal static void Xor(uint[] block, uint[] val) + { + for (int i = 3; i >= 0; --i) + { + block[i] ^= val[i]; + } + } + + internal static void Xor(uint[] block, uint[] val, uint[] output) + { + for (int i = 3; i >= 0; --i) + { + output[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
index 9425a3d9d..44933bba7 100644 --- a/crypto/src/crypto/modes/gcm/Tables1kGcmExponentiator.cs +++ b/crypto/src/crypto/modes/gcm/Tables1kGcmExponentiator.cs
@@ -1,4 +1,5 @@ using System; +using System.Collections; using Org.BouncyCastle.Utilities; @@ -7,38 +8,53 @@ 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][]; + // A lookup table of the power-of-two powers of 'x' + // - lookupPowX2[i] = x^(2^i) + private IList lookupPowX2; - public void Init(byte[] x) + public void Init(byte[] x) { - lookupPowX2[0] = GcmUtilities.OneAsBytes(); - lookupPowX2[1] = Arrays.Clone(x); + if (lookupPowX2 != null && Arrays.AreEqual(x, (byte[])lookupPowX2[0])) + { + return; + } - for (int i = 2; i != 64; ++i) - { - byte[] tmp = Arrays.Clone(lookupPowX2[i - 1]); - GcmUtilities.Multiply(tmp, tmp); - lookupPowX2[i] = tmp; - } + lookupPowX2 = Platform.CreateArrayList(8); + lookupPowX2.Add(Arrays.Clone(x)); } 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; - } + int bit = 0; + while (pow > 0) + { + if ((pow & 1L) != 0) + { + EnsureAvailable(bit); + GcmUtilities.Multiply(y, (byte[])lookupPowX2[bit]); + } + ++bit; + pow >>= 1; + } Array.Copy(y, 0, output, 0, 16); } - } + + private void EnsureAvailable(int bit) + { + int count = lookupPowX2.Count; + if (count <= bit) + { + byte[] tmp = (byte[])lookupPowX2[count - 1]; + do + { + tmp = Arrays.Clone(tmp); + GcmUtilities.Multiply(tmp, tmp); + lookupPowX2.Add(tmp); + } + while (++count <= bit); + } + } + } } diff --git a/crypto/src/crypto/modes/gcm/Tables64kGcmMultiplier.cs b/crypto/src/crypto/modes/gcm/Tables64kGcmMultiplier.cs
index f089dfe8d..707b0be2e 100644 --- a/crypto/src/crypto/modes/gcm/Tables64kGcmMultiplier.cs +++ b/crypto/src/crypto/modes/gcm/Tables64kGcmMultiplier.cs
@@ -1,64 +1,77 @@ using System; using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Modes.Gcm { - public class Tables64kGcmMultiplier - : IGcmMultiplier - { - private readonly uint[][][] M = new uint[16][][]; + public class Tables64kGcmMultiplier + : IGcmMultiplier + { + private byte[] H; + private uint[][][] M; - 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; - } - } + public void Init(byte[] H) + { + if (M == null) + { + M = new uint[16][][]; + } + else if (Arrays.AreEqual(this.H, H)) + { + return; + } - if (++i == 16) return; + this.H = Arrays.Clone(H); - 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; - } - } - } + 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; + } + } - 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]; - } + if (++i == 16) return; - Pack.UInt32_To_BE(z, x, 0); - } - } + 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
index 91d58fab8..5f3d6c86c 100644 --- a/crypto/src/crypto/modes/gcm/Tables8kGcmMultiplier.cs +++ b/crypto/src/crypto/modes/gcm/Tables8kGcmMultiplier.cs
@@ -1,90 +1,103 @@ using System; using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.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); + public class Tables8kGcmMultiplier + : IGcmMultiplier + { + private byte[] H; + private uint[][][] M; - for (int j = 4; j >= 1; j >>= 1) - { - uint[] tmp = (uint[])M[1][j + j].Clone(); - GcmUtilities.MultiplyP(tmp); - M[1][j] = tmp; - } + public void Init(byte[] H) + { + if (M == null) + { + M = new uint[32][][]; + } + else if (Arrays.AreEqual(this.H, H)) + { + return; + } - { - uint[] tmp = (uint[])M[1][1].Clone(); - GcmUtilities.MultiplyP(tmp); - M[0][8] = tmp; - } + this.H = Arrays.Clone(H); - for (int j = 4; j >= 1; j >>= 1) - { - uint[] tmp = (uint[])M[0][j + j].Clone(); - GcmUtilities.MultiplyP(tmp); - M[0][j] = tmp; - } + 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 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; - } - } + for (int j = 4; j >= 1; j >>= 1) + { + uint[] tmp = (uint[])M[1][j + j].Clone(); + GcmUtilities.MultiplyP(tmp); + M[1][j] = tmp; + } - if (++i == 32) return; + { + uint[] tmp = (uint[])M[1][1].Clone(); + GcmUtilities.MultiplyP(tmp); + M[0][8] = tmp; + } - 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; - } - } - } - } + for (int j = 4; j >= 1; j >>= 1) + { + uint[] tmp = (uint[])M[0][j + j].Clone(); + GcmUtilities.MultiplyP(tmp); + M[0][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]; - } + 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; + } + } - Pack.UInt32_To_BE(z, x, 0); - } - } + 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
index 06b2f5c38..825d6b7f2 100644 --- a/crypto/src/crypto/parameters/AEADParameters.cs +++ b/crypto/src/crypto/parameters/AEADParameters.cs
@@ -10,7 +10,19 @@ namespace Org.BouncyCastle.Crypto.Parameters 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 + */ + public AeadParameters(KeyParameter key, int macSize, byte[] nonce) + : this(key, macSize, nonce, null) + { + } + + /** * Base constructor. * * @param key key to be used by underlying cipher diff --git a/crypto/src/crypto/parameters/CcmParameters.cs b/crypto/src/crypto/parameters/CcmParameters.cs
index 8dc981e1f..d4459081c 100644 --- a/crypto/src/crypto/parameters/CcmParameters.cs +++ b/crypto/src/crypto/parameters/CcmParameters.cs
@@ -2,6 +2,7 @@ using System; namespace Org.BouncyCastle.Crypto.Parameters { + [Obsolete("Use AeadParameters")] public class CcmParameters : AeadParameters { 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/DSAParameterGenerationParameters.cs b/crypto/src/crypto/parameters/DSAParameterGenerationParameters.cs new file mode 100644
index 000000000..7427574c8 --- /dev/null +++ b/crypto/src/crypto/parameters/DSAParameterGenerationParameters.cs
@@ -0,0 +1,74 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class DsaParameterGenerationParameters + { + public const int DigitalSignatureUsage = 1; + public const int KeyEstablishmentUsage = 2; + + private readonly int l; + private readonly int n; + private readonly int certainty; + private readonly SecureRandom random; + private readonly int usageIndex; + + /** + * Construct without a usage index, this will do a random construction of G. + * + * @param L desired length of prime P in bits (the effective key size). + * @param N desired length of prime Q in bits. + * @param certainty certainty level for prime number generation. + * @param random the source of randomness to use. + */ + public DsaParameterGenerationParameters(int L, int N, int certainty, SecureRandom random) + : this(L, N, certainty, random, -1) + { + } + + /** + * Construct for a specific usage index - this has the effect of using verifiable canonical generation of G. + * + * @param L desired length of prime P in bits (the effective key size). + * @param N desired length of prime Q in bits. + * @param certainty certainty level for prime number generation. + * @param random the source of randomness to use. + * @param usageIndex a valid usage index. + */ + public DsaParameterGenerationParameters(int L, int N, int certainty, SecureRandom random, int usageIndex) + { + this.l = L; + this.n = N; + this.certainty = certainty; + this.random = random; + this.usageIndex = usageIndex; + } + + public virtual int L + { + get { return l; } + } + + public virtual int N + { + get { return n; } + } + + public virtual int UsageIndex + { + get { return usageIndex; } + } + + public virtual int Certainty + { + get { return certainty; } + } + + public virtual SecureRandom Random + { + get { return random; } + } + } +} 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
index b9cdc4a79..c2f84c785 100644 --- a/crypto/src/crypto/parameters/DsaValidationParameters.cs +++ b/crypto/src/crypto/parameters/DsaValidationParameters.cs
@@ -8,52 +8,65 @@ namespace Org.BouncyCastle.Crypto.Parameters { private readonly byte[] seed; private readonly int counter; + private readonly int usageIndex; - public DsaValidationParameters( + public DsaValidationParameters(byte[] seed, int counter) + : this(seed, counter, -1) + { + } + + public DsaValidationParameters( byte[] seed, - int counter) + int counter, + int usageIndex) { - if (seed == null) - throw new ArgumentNullException("seed"); + if (seed == null) + throw new ArgumentNullException("seed"); - this.seed = (byte[]) seed.Clone(); + this.seed = (byte[]) seed.Clone(); this.counter = counter; + this.usageIndex = usageIndex; + } + + public virtual byte[] GetSeed() + { + return (byte[]) seed.Clone(); } - public byte[] GetSeed() + public virtual int Counter { - return (byte[]) seed.Clone(); + get { return counter; } } - public int Counter - { - get { return counter; } - } + public virtual int UsageIndex + { + get { return usageIndex; } + } - public override bool Equals( + public override bool Equals( object obj) { - if (obj == this) - return true; + if (obj == this) + return true; - DsaValidationParameters other = obj as DsaValidationParameters; + DsaValidationParameters other = obj as DsaValidationParameters; - if (other == null) - return false; + if (other == null) + return false; - return Equals(other); - } + return Equals(other); + } - protected bool Equals( - DsaValidationParameters other) - { - return counter == other.counter - && Arrays.AreEqual(seed, other.seed); - } + protected virtual bool Equals( + DsaValidationParameters other) + { + return counter == other.counter + && Arrays.AreEqual(seed, other.seed); + } - public override int GetHashCode() + public override int GetHashCode() { - return counter.GetHashCode() ^ Arrays.GetHashCode(seed); - } + return counter.GetHashCode() ^ Arrays.GetHashCode(seed); + } } } diff --git a/crypto/src/crypto/parameters/ECDomainParameters.cs b/crypto/src/crypto/parameters/ECDomainParameters.cs
index c6a3e4e72..619971a6c 100644 --- a/crypto/src/crypto/parameters/ECDomainParameters.cs +++ b/crypto/src/crypto/parameters/ECDomainParameters.cs
@@ -14,11 +14,11 @@ namespace Org.BouncyCastle.Crypto.Parameters internal BigInteger n; internal BigInteger h; - public ECDomainParameters( + public ECDomainParameters( ECCurve curve, ECPoint g, BigInteger n) - : this(curve, g, n, BigInteger.One) + : this(curve, g, n, BigInteger.One) { } @@ -27,34 +27,34 @@ namespace Org.BouncyCastle.Crypto.Parameters ECPoint g, BigInteger n, BigInteger h) - : this(curve, g, n, h, null) - { + : this(curve, g, n, h, null) + { } - public ECDomainParameters( + 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; + 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.Normalize(); this.n = n; this.h = h; this.seed = Arrays.Clone(seed); } - public ECCurve Curve + public ECCurve Curve { get { return curve; } } @@ -76,40 +76,40 @@ namespace Org.BouncyCastle.Crypto.Parameters public byte[] GetSeed() { - return Arrays.Clone(seed); + return Arrays.Clone(seed); } - public override bool Equals( - object obj) + public override bool Equals( + object obj) { - if (obj == this) - return true; + if (obj == this) + return true; - ECDomainParameters other = obj as ECDomainParameters; + ECDomainParameters other = obj as ECDomainParameters; - if (other == null) - return false; + if (other == null) + return false; - return Equals(other); + 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); } - 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() + public override int GetHashCode() { return curve.GetHashCode() - ^ g.GetHashCode() - ^ n.GetHashCode() - ^ h.GetHashCode() - ^ Arrays.GetHashCode(seed); + ^ 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
index 4d4622ced..70b3543da 100644 --- a/crypto/src/crypto/parameters/ECKeyParameters.cs +++ b/crypto/src/crypto/parameters/ECKeyParameters.cs
@@ -1,145 +1,136 @@ using System; -using System.Globalization; +using System.Collections; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.CryptoPro; using Org.BouncyCastle.Asn1.X9; using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Collections; namespace Org.BouncyCastle.Crypto.Parameters { public abstract class ECKeyParameters - : AsymmetricKeyParameter + : AsymmetricKeyParameter { - private readonly string algorithm; - private readonly ECDomainParameters parameters; - private readonly DerObjectIdentifier publicKeyParamSet; + private static readonly string[] algorithms = { "EC", "ECDSA", "ECDH", "ECDHC", "ECGOST3410", "ECMQV" }; - protected ECKeyParameters( - string algorithm, + private readonly string algorithm; + private readonly ECDomainParameters parameters; + private readonly DerObjectIdentifier publicKeyParamSet; + + protected ECKeyParameters( + string algorithm, bool isPrivate, ECDomainParameters parameters) - : base(isPrivate) + : 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 (parameters == null) - throw new ArgumentNullException("parameters"); + 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; + } - this.algorithm = VerifyAlgorithmName(algorithm); - this.parameters = parameters; + public string AlgorithmName + { + get { return algorithm; } } - 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 + public ECDomainParameters Parameters { - get { return 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; - } - } + 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); + } + + internal static string VerifyAlgorithmName(string algorithm) + { + string upper = Platform.ToUpperInvariant(algorithm); + if (Array.IndexOf(algorithms, algorithm, 0, algorithms.Length) < 0) + 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
index e6352d5d1..4d0fa1fc6 100644 --- a/crypto/src/crypto/parameters/ECPrivateKeyParameters.cs +++ b/crypto/src/crypto/parameters/ECPrivateKeyParameters.cs
@@ -7,81 +7,81 @@ using Org.BouncyCastle.Math; namespace Org.BouncyCastle.Crypto.Parameters { public class ECPrivateKeyParameters - : ECKeyParameters + : 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) + 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 (obj == this) - return true; + if (d == null) + throw new ArgumentNullException("d"); - ECPrivateKeyParameters other = obj as ECPrivateKeyParameters; + this.d = d; + } - if (other == null) - return false; + public ECPrivateKeyParameters( + string algorithm, + BigInteger d, + ECDomainParameters parameters) + : base(algorithm, true, parameters) + { + if (d == null) + throw new ArgumentNullException("d"); - return Equals(other); + this.d = d; } - protected bool Equals( - ECPrivateKeyParameters other) - { - return d.Equals(other.d) && base.Equals(other); - } + 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() + 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
index 9e71c2a25..1eb665da9 100644 --- a/crypto/src/crypto/parameters/ECPublicKeyParameters.cs +++ b/crypto/src/crypto/parameters/ECPublicKeyParameters.cs
@@ -7,78 +7,78 @@ using Org.BouncyCastle.Math.EC; namespace Org.BouncyCastle.Crypto.Parameters { public class ECPublicKeyParameters - : ECKeyParameters + : 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) + public ECPublicKeyParameters( + ECPoint q, + ECDomainParameters parameters) + : this("EC", q, parameters) { - if (q == null) - throw new ArgumentNullException("q"); + } + + [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; - } + this.q = q.Normalize(); + } - public ECPublicKeyParameters( - string algorithm, - ECPoint q, - DerObjectIdentifier publicKeyParamSet) - : base(algorithm, false, publicKeyParamSet) + public ECPublicKeyParameters( + string algorithm, + ECPoint q, + ECDomainParameters parameters) + : base(algorithm, false, parameters) { - if (q == null) - throw new ArgumentNullException("q"); + if (q == null) + throw new ArgumentNullException("q"); - this.q = q; - } + this.q = q.Normalize(); + } - public ECPoint Q + public ECPublicKeyParameters( + string algorithm, + ECPoint q, + DerObjectIdentifier publicKeyParamSet) + : base(algorithm, false, publicKeyParamSet) { - get { return q; } + if (q == null) + throw new ArgumentNullException("q"); + + this.q = q.Normalize(); } - public override bool Equals(object obj) + public ECPoint Q { - if (obj == this) - return true; + get { return q; } + } + + public override bool Equals(object obj) + { + if (obj == this) + return true; - ECPublicKeyParameters other = obj as ECPublicKeyParameters; + ECPublicKeyParameters other = obj as ECPublicKeyParameters; - if (other == null) - return false; + if (other == null) + return false; - return Equals(other); + return Equals(other); } - protected bool Equals( - ECPublicKeyParameters other) - { - return q.Equals(other.q) && base.Equals(other); - } + protected bool Equals( + ECPublicKeyParameters other) + { + return q.Equals(other.q) && base.Equals(other); + } - public override int GetHashCode() + 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
index 42a0454a1..0e1fe14b7 100644 --- a/crypto/src/crypto/parameters/NaccacheSternPrivateKeyParameters.cs +++ b/crypto/src/crypto/parameters/NaccacheSternPrivateKeyParameters.cs
@@ -16,7 +16,7 @@ namespace Org.BouncyCastle.Crypto.Parameters private readonly BigInteger phiN; private readonly IList smallPrimes; -#if !(SILVERLIGHT || PORTABLE) +#if !SILVERLIGHT [Obsolete] public NaccacheSternPrivateKeyParameters( BigInteger g, @@ -63,7 +63,7 @@ namespace Org.BouncyCastle.Crypto.Parameters get { return phiN; } } -#if !(SILVERLIGHT || PORTABLE) +#if !SILVERLIGHT [Obsolete("Use 'SmallPrimesList' instead")] public ArrayList SmallPrimes { diff --git a/crypto/src/crypto/parameters/ParametersWithIV.cs b/crypto/src/crypto/parameters/ParametersWithIV.cs
index e00abce58..11a8b77a0 100644 --- a/crypto/src/crypto/parameters/ParametersWithIV.cs +++ b/crypto/src/crypto/parameters/ParametersWithIV.cs
@@ -21,8 +21,7 @@ namespace Org.BouncyCastle.Crypto.Parameters int ivOff, int ivLen) { - if (parameters == null) - throw new ArgumentNullException("parameters"); + // NOTE: 'parameters' may be null to imply key re-use if (iv == null) throw new ArgumentNullException("iv"); 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/parameters/SkeinParameters.cs b/crypto/src/crypto/parameters/SkeinParameters.cs new file mode 100644
index 000000000..a4e3e8e2a --- /dev/null +++ b/crypto/src/crypto/parameters/SkeinParameters.cs
@@ -0,0 +1,285 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + + /// <summary> + /// Parameters for the Skein hash function - a series of byte[] strings identified by integer tags. + /// </summary> + /// <remarks> + /// Parameterised Skein can be used for: + /// <ul> + /// <li>MAC generation, by providing a <see cref="SkeinParameters.Builder.SetKey(byte[])">key</see>.</li> + /// <li>Randomised hashing, by providing a <see cref="SkeinParameters.Builder.SetNonce(byte[])">nonce</see>.</li> + /// <li>A hash function for digital signatures, associating a + /// <see cref="SkeinParameters.Builder.SetPublicKey(byte[])">public key</see> with the message digest.</li> + /// <li>A key derivation function, by providing a + /// <see cref="SkeinParameters.Builder.SetKeyIdentifier(byte[])">key identifier</see>.</li> + /// <li>Personalised hashing, by providing a + /// <see cref="SkeinParameters.Builder.SetPersonalisation(DateTime,string,string)">recommended format</see> or + /// <see cref="SkeinParameters.Builder.SetPersonalisation(byte[])">arbitrary</see> personalisation string.</li> + /// </ul> + /// </remarks> + /// <seealso cref="Org.BouncyCastle.Crypto.Digests.SkeinEngine"/> + /// <seealso cref="Org.BouncyCastle.Crypto.Digests.SkeinDigest"/> + /// <seealso cref="Org.BouncyCastle.Crypto.Macs.SkeinMac"/> + public class SkeinParameters + : ICipherParameters + { + /// <summary> + /// The parameter type for a secret key, supporting MAC or KDF functions: 0 + /// </summary> + public const int PARAM_TYPE_KEY = 0; + + /// <summary> + /// The parameter type for the Skein configuration block: 4 + /// </summary> + public const int PARAM_TYPE_CONFIG = 4; + + /// <summary> + /// The parameter type for a personalisation string: 8 + /// </summary> + public const int PARAM_TYPE_PERSONALISATION = 8; + + /// <summary> + /// The parameter type for a public key: 12 + /// </summary> + public const int PARAM_TYPE_PUBLIC_KEY = 12; + + /// <summary> + /// The parameter type for a key identifier string: 16 + /// </summary> + public const int PARAM_TYPE_KEY_IDENTIFIER = 16; + + /// <summary> + /// The parameter type for a nonce: 20 + /// </summary> + public const int PARAM_TYPE_NONCE = 20; + + /// <summary> + /// The parameter type for the message: 48 + /// </summary> + public const int PARAM_TYPE_MESSAGE = 48; + + /// <summary> + /// The parameter type for the output transformation: 63 + /// </summary> + public const int PARAM_TYPE_OUTPUT = 63; + + private IDictionary parameters; + + public SkeinParameters() + : this(Platform.CreateHashtable()) + + { + } + + private SkeinParameters(IDictionary parameters) + { + this.parameters = parameters; + } + + /// <summary> + /// Obtains a map of type (int) to value (byte[]) for the parameters tracked in this object. + /// </summary> + public IDictionary GetParameters() + { + return parameters; + } + + /// <summary> + /// Obtains the value of the <see cref="PARAM_TYPE_KEY">key parameter</see>, or <code>null</code> if not + /// set. + /// </summary> + /// <returns>The key.</returns> + public byte[] GetKey() + { + return (byte[])parameters[PARAM_TYPE_KEY]; + } + + /// <summary> + /// Obtains the value of the <see cref="PARAM_TYPE_PERSONALISATION">personalisation parameter</see>, or + /// <code>null</code> if not set. + /// </summary> + public byte[] GetPersonalisation() + { + return (byte[])parameters[PARAM_TYPE_PERSONALISATION]; + } + + /// <summary> + /// Obtains the value of the <see cref="PARAM_TYPE_PUBLIC_KEY">public key parameter</see>, or + /// <code>null</code> if not set. + /// </summary> + public byte[] GetPublicKey() + { + return (byte[])parameters[PARAM_TYPE_PUBLIC_KEY]; + } + + /// <summary> + /// Obtains the value of the <see cref="PARAM_TYPE_KEY_IDENTIFIER">key identifier parameter</see>, or + /// <code>null</code> if not set. + /// </summary> + public byte[] GetKeyIdentifier() + { + return (byte[])parameters[PARAM_TYPE_KEY_IDENTIFIER]; + } + + /// <summary> + /// Obtains the value of the <see cref="PARAM_TYPE_NONCE">nonce parameter</see>, or <code>null</code> if + /// not set. + /// </summary> + public byte[] GetNonce() + { + return (byte[])parameters[PARAM_TYPE_NONCE]; + } + + /// <summary> + /// A builder for <see cref="SkeinParameters"/>. + /// </summary> + public class Builder + { + private IDictionary parameters = Platform.CreateHashtable(); + + public Builder() + { + } + + public Builder(IDictionary paramsMap) + { + IEnumerator keys = paramsMap.Keys.GetEnumerator(); + while (keys.MoveNext()) + { + int key = (int)keys.Current; + parameters.Add(key, paramsMap[key]); + } + } + + public Builder(SkeinParameters parameters) + { + IEnumerator keys = parameters.parameters.Keys.GetEnumerator(); + while (keys.MoveNext()) + { + int key = (int)keys.Current; + this.parameters.Add(key, parameters.parameters[key]); + } + } + + /// <summary> + /// Sets a parameters to apply to the Skein hash function. + /// </summary> + /// <remarks> + /// Parameter types must be in the range 0,5..62, and cannot use the value 48 + /// (reserved for message body). + /// <p/> + /// Parameters with type &lt; 48 are processed before + /// the message content, parameters with type &gt; 48 + /// are processed after the message and prior to output. + /// </remarks> + /// <param name="type">the type of the parameter, in the range 5..62.</param> + /// <param name="value">the byte sequence of the parameter.</param> + public Builder Set(int type, byte[] value) + { + if (value == null) + { + throw new ArgumentException("Parameter value must not be null."); + } + if ((type != PARAM_TYPE_KEY) + && (type <= PARAM_TYPE_CONFIG || type >= PARAM_TYPE_OUTPUT || type == PARAM_TYPE_MESSAGE)) + { + throw new ArgumentException("Parameter types must be in the range 0,5..47,49..62."); + } + if (type == PARAM_TYPE_CONFIG) + { + throw new ArgumentException("Parameter type " + PARAM_TYPE_CONFIG + + " is reserved for internal use."); + } + this.parameters.Add(type, value); + return this; + } + + /// <summary> + /// Sets the <see cref="SkeinParameters.PARAM_TYPE_KEY"/> parameter. + /// </summary> + public Builder SetKey(byte[] key) + { + return Set(PARAM_TYPE_KEY, key); + } + + /// <summary> + /// Sets the <see cref="SkeinParameters.PARAM_TYPE_PERSONALISATION"/> parameter. + /// </summary> + public Builder SetPersonalisation(byte[] personalisation) + { + return Set(PARAM_TYPE_PERSONALISATION, personalisation); + } + + /// <summary> + /// Implements the recommended personalisation format for Skein defined in Section 4.11 of + /// the Skein 1.3 specification. + /// </summary> + /// <remarks> + /// The format is <code>YYYYMMDD email@address distinguisher</code>, encoded to a byte + /// sequence using UTF-8 encoding. + /// </remarks> + /// <param name="date">the date the personalised application of the Skein was defined.</param> + /// <param name="emailAddress">the email address of the creation of the personalised application.</param> + /// <param name="distinguisher">an arbitrary personalisation string distinguishing the application.</param> + public Builder SetPersonalisation(DateTime date, string emailAddress, string distinguisher) + { + try + { + MemoryStream bout = new MemoryStream(); + StreamWriter outBytes = new StreamWriter(bout, System.Text.Encoding.UTF8); + outBytes.Write(date.ToString("YYYYMMDD")); + outBytes.Write(" "); + outBytes.Write(emailAddress); + outBytes.Write(" "); + outBytes.Write(distinguisher); + outBytes.Close(); + return Set(PARAM_TYPE_PERSONALISATION, bout.ToArray()); + } + catch (IOException e) + { + throw new InvalidOperationException("Byte I/O failed.", e); + } + } + + /// <summary> + /// Sets the <see cref="SkeinParameters.PARAM_TYPE_KEY_IDENTIFIER"/> parameter. + /// </summary> + public Builder SetPublicKey(byte[] publicKey) + { + return Set(PARAM_TYPE_PUBLIC_KEY, publicKey); + } + + /// <summary> + /// Sets the <see cref="SkeinParameters.PARAM_TYPE_KEY_IDENTIFIER"/> parameter. + /// </summary> + public Builder SetKeyIdentifier(byte[] keyIdentifier) + { + return Set(PARAM_TYPE_KEY_IDENTIFIER, keyIdentifier); + } + + /// <summary> + /// Sets the <see cref="SkeinParameters.PARAM_TYPE_NONCE"/> parameter. + /// </summary> + public Builder SetNonce(byte[] nonce) + { + return Set(PARAM_TYPE_NONCE, nonce); + } + + /// <summary> + /// Constructs a new <see cref="SkeinParameters"/> instance with the parameters provided to this + /// builder. + /// </summary> + public SkeinParameters Build() + { + return new SkeinParameters(parameters); + } + } + } +} \ No newline at end of file diff --git a/crypto/src/crypto/parameters/TweakableBlockCipherParameters.cs b/crypto/src/crypto/parameters/TweakableBlockCipherParameters.cs new file mode 100644
index 000000000..f75726600 --- /dev/null +++ b/crypto/src/crypto/parameters/TweakableBlockCipherParameters.cs
@@ -0,0 +1,40 @@ +using System; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + + /// <summary> + /// Parameters for tweakable block ciphers. + /// </summary> + public class TweakableBlockCipherParameters + : ICipherParameters + { + private readonly byte[] tweak; + private readonly KeyParameter key; + + public TweakableBlockCipherParameters(KeyParameter key, byte[] tweak) + { + this.key = key; + this.tweak = Arrays.Clone(tweak); + } + + /// <summary> + /// Gets the key. + /// </summary> + /// <value>the key to use, or <code>null</code> to use the current key.</value> + public KeyParameter Key + { + get { return key; } + } + + /// <summary> + /// Gets the tweak value. + /// </summary> + /// <value>The tweak to use, or <code>null</code> to use the current tweak.</value> + public byte[] Tweak + { + get { return tweak; } + } + } +} \ No newline at end of file diff --git a/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs b/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs
index 9e9e29cf1..e1a0d4012 100644 --- a/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs +++ b/crypto/src/crypto/prng/CryptoApiRandomGenerator.cs
@@ -1,4 +1,4 @@ -#if !(NETCF_1_0 || PORTABLE) +#if !NETCF_1_0 using System; using System.Security.Cryptography; 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
index 9f918ea6e..c29ad2c9a 100644 --- a/crypto/src/crypto/prng/ThreadedSeedGenerator.cs +++ b/crypto/src/crypto/prng/ThreadedSeedGenerator.cs
@@ -34,7 +34,27 @@ namespace Org.BouncyCastle.Crypto.Prng int numBytes, bool fast) { - this.counter = 0; +#if SILVERLIGHT + return DoGenerateSeed(numBytes, fast); +#else + ThreadPriority originalPriority = Thread.CurrentThread.Priority; + try + { + Thread.CurrentThread.Priority = ThreadPriority.Normal; + return DoGenerateSeed(numBytes, fast); + } + finally + { + Thread.CurrentThread.Priority = originalPriority; + } +#endif + } + + private byte[] DoGenerateSeed( + int numBytes, + bool fast) + { + this.counter = 0; this.stop = false; byte[] result = new byte[numBytes]; @@ -45,13 +65,11 @@ namespace Org.BouncyCastle.Crypto.Prng for (int i = 0; i < end; i++) { - var waitEvent = new ManualResetEvent(false); - while (this.counter == last) { try { - waitEvent.WaitOne(1); + Thread.Sleep(1); } catch (Exception) { diff --git a/crypto/src/crypto/prng/VMPCRandomGenerator.cs b/crypto/src/crypto/prng/VMPCRandomGenerator.cs
index 2ab079999..64f287d13 100644 --- a/crypto/src/crypto/prng/VMPCRandomGenerator.cs +++ b/crypto/src/crypto/prng/VMPCRandomGenerator.cs
@@ -1,115 +1,114 @@ +using System; + +using Org.BouncyCastle.Crypto.Utilities; + namespace Org.BouncyCastle.Crypto.Prng { - public class VmpcRandomGenerator - : IRandomGenerator - { - private byte n = 0; + 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> + /// 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; + /// <remarks>Value generated in the same way as <c>P</c>.</remarks> + private byte s = (byte) 0xbe; - public VmpcRandomGenerator() - { - } + 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(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 AddSeedMaterial(long seed) + { + AddSeedMaterial(Pack.UInt64_To_BE((ulong)seed)); + } - public virtual void NextBytes(byte[] bytes) - { - NextBytes(bytes, 0, bytes.Length); - } + 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); - } - } - } - } + 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
index 419b1972e..bb28addfc 100644 --- a/crypto/src/crypto/signers/DsaSigner.cs +++ b/crypto/src/crypto/signers/DsaSigner.cs
@@ -1,136 +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; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; 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); - } - } + /** + * The Digital Signature Algorithm - as described in "Handbook of Applied + * Cryptography", pages 452 - 453. + */ + public class DsaSigner + : IDsa + { + protected readonly IDsaKCalculator kCalculator; + + protected DsaKeyParameters key = null; + protected SecureRandom random = null; + + /** + * Default configuration, random K values. + */ + public DsaSigner() + { + this.kCalculator = new RandomDsaKCalculator(); + } + + /** + * Configuration with an alternate, possibly deterministic calculator of K. + * + * @param kCalculator a K value calculator. + */ + public DsaSigner(IDsaKCalculator kCalculator) + { + this.kCalculator = kCalculator; + } + + public virtual string AlgorithmName + { + get { return "DSA"; } + } + + public virtual void Init(bool forSigning, ICipherParameters parameters) + { + SecureRandom providedRandom = null; + + if (forSigning) + { + if (parameters is ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)parameters; + + providedRandom = rParam.Random; + parameters = rParam.Parameters; + } + + 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; + } + + this.random = InitSecureRandom(forSigning && !kCalculator.IsDeterministic, providedRandom); + } + + /** + * 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 virtual BigInteger[] GenerateSignature(byte[] message) + { + DsaParameters parameters = key.Parameters; + BigInteger q = parameters.Q; + BigInteger m = CalculateE(q, message); + BigInteger x = ((DsaPrivateKeyParameters)key).X; + + if (kCalculator.IsDeterministic) + { + kCalculator.Init(q, x, message); + } + else + { + kCalculator.Init(q, random); + } + + BigInteger k = kCalculator.NextK(); + + BigInteger r = parameters.G.ModPow(k, parameters.P).Mod(q); + + k = k.ModInverse(q).Multiply(m.Add(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 virtual 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); + } + + protected virtual BigInteger CalculateE(BigInteger n, byte[] message) + { + int length = System.Math.Min(message.Length, n.BitLength / 8); + + return new BigInteger(1, message, 0, length); + } + + protected virtual SecureRandom InitSecureRandom(bool needed, SecureRandom provided) + { + return !needed ? null : (provided != null) ? provided : new SecureRandom(); + } + } } diff --git a/crypto/src/crypto/signers/ECDsaSigner.cs b/crypto/src/crypto/signers/ECDsaSigner.cs
index 4254e5590..9821732c2 100644 --- a/crypto/src/crypto/signers/ECDsaSigner.cs +++ b/crypto/src/crypto/signers/ECDsaSigner.cs
@@ -1,156 +1,185 @@ using System; + +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Math.EC.Multiplier; 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; - } - } + /** + * EC-DSA as described in X9.62 + */ + public class ECDsaSigner + : IDsa + { + protected readonly IDsaKCalculator kCalculator; + + protected ECKeyParameters key = null; + protected SecureRandom random = null; + + /** + * Default configuration, random K values. + */ + public ECDsaSigner() + { + this.kCalculator = new RandomDsaKCalculator(); + } + + /** + * Configuration with an alternate, possibly deterministic calculator of K. + * + * @param kCalculator a K value calculator. + */ + public ECDsaSigner(IDsaKCalculator kCalculator) + { + this.kCalculator = kCalculator; + } + + public virtual string AlgorithmName + { + get { return "ECDSA"; } + } + + public virtual void Init(bool forSigning, ICipherParameters parameters) + { + SecureRandom providedRandom = null; + + if (forSigning) + { + if (parameters is ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)parameters; + + providedRandom = rParam.Random; + parameters = rParam.Parameters; + } + + 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; + } + + this.random = InitSecureRandom(forSigning && !kCalculator.IsDeterministic, providedRandom); + } + + // 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 virtual BigInteger[] GenerateSignature(byte[] message) + { + ECDomainParameters ec = key.Parameters; + BigInteger n = ec.N; + BigInteger e = CalculateE(n, message); + BigInteger d = ((ECPrivateKeyParameters)key).D; + + if (kCalculator.IsDeterministic) + { + kCalculator.Init(n, d, message); + } + else + { + kCalculator.Init(n, random); + } + + BigInteger r, s; + + ECMultiplier basePointMultiplier = CreateBasePointMultiplier(); + + // 5.3.2 + do // Generate s + { + BigInteger k; + do // Generate r + { + k = kCalculator.NextK(); + + ECPoint p = basePointMultiplier.Multiply(ec.G, k).Normalize(); + + // 5.3.3 + r = p.AffineXCoord.ToBigInteger().Mod(n); + } + while (r.SignValue == 0); + + s = k.ModInverse(n).Multiply(e.Add(d.Multiply(r))).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 virtual 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).Normalize(); + + if (point.IsInfinity) + return false; + + BigInteger v = point.AffineXCoord.ToBigInteger().Mod(n); + + return v.Equals(r); + } + + protected virtual 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; + } + + protected virtual ECMultiplier CreateBasePointMultiplier() + { + return new FixedPointCombMultiplier(); + } + + protected virtual SecureRandom InitSecureRandom(bool needed, SecureRandom provided) + { + return !needed ? null : (provided != null) ? provided : new SecureRandom(); + } + } } diff --git a/crypto/src/crypto/signers/ECGOST3410Signer.cs b/crypto/src/crypto/signers/ECGOST3410Signer.cs
index d68b83f67..6027aa9b9 100644 --- a/crypto/src/crypto/signers/ECGOST3410Signer.cs +++ b/crypto/src/crypto/signers/ECGOST3410Signer.cs
@@ -4,151 +4,159 @@ using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Math.EC.Multiplier; 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); - } - } + /** + * 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); + + ECDomainParameters ec = key.Parameters; + BigInteger n = ec.N; + BigInteger d = ((ECPrivateKeyParameters)key).D; + + BigInteger r, s = null; + + ECMultiplier basePointMultiplier = CreateBasePointMultiplier(); + + do // generate s + { + BigInteger k; + do // generate r + { + do + { + k = new BigInteger(n.BitLength, random); + } + while (k.SignValue == 0); + + ECPoint p = basePointMultiplier.Multiply(ec.G, k).Normalize(); + + r = p.AffineXCoord.ToBigInteger().Mod(n); + } + while (r.SignValue == 0); + + 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).Normalize(); + + if (point.IsInfinity) + return false; + + BigInteger R = point.AffineXCoord.ToBigInteger().Mod(n); + + return R.Equals(r); + } + + protected virtual ECMultiplier CreateBasePointMultiplier() + { + return new FixedPointCombMultiplier(); + } + } } diff --git a/crypto/src/crypto/signers/ECNRSigner.cs b/crypto/src/crypto/signers/ECNRSigner.cs
index 63865d731..cae15bdbf 100644 --- a/crypto/src/crypto/signers/ECNRSigner.cs +++ b/crypto/src/crypto/signers/ECNRSigner.cs
@@ -9,178 +9,180 @@ 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); - } - } + /** + * 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.AffineXCoord.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; + } + + // 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).Normalize(); + + if (P.IsInfinity) + return false; + + BigInteger x = P.AffineXCoord.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/HMacDsaKCalculator.cs b/crypto/src/crypto/signers/HMacDsaKCalculator.cs new file mode 100644
index 000000000..8231197b9 --- /dev/null +++ b/crypto/src/crypto/signers/HMacDsaKCalculator.cs
@@ -0,0 +1,150 @@ +using System; + +using Org.BouncyCastle.Crypto.Macs; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Signers +{ + /** + * A deterministic K calculator based on the algorithm in section 3.2 of RFC 6979. + */ + public class HMacDsaKCalculator + : IDsaKCalculator + { + private readonly HMac hMac; + private readonly byte[] K; + private readonly byte[] V; + + private BigInteger n; + + /** + * Base constructor. + * + * @param digest digest to build the HMAC on. + */ + public HMacDsaKCalculator(IDigest digest) + { + this.hMac = new HMac(digest); + this.V = new byte[hMac.GetMacSize()]; + this.K = new byte[hMac.GetMacSize()]; + } + + public virtual bool IsDeterministic + { + get { return true; } + } + + public virtual void Init(BigInteger n, SecureRandom random) + { + throw new InvalidOperationException("Operation not supported"); + } + + public void Init(BigInteger n, BigInteger d, byte[] message) + { + this.n = n; + + Arrays.Fill(V, (byte)0x01); + Arrays.Fill(K, (byte)0); + + byte[] x = new byte[(n.BitLength + 7) / 8]; + byte[] dVal = BigIntegers.AsUnsignedByteArray(d); + + Array.Copy(dVal, 0, x, x.Length - dVal.Length, dVal.Length); + + byte[] m = new byte[(n.BitLength + 7) / 8]; + + BigInteger mInt = BitsToInt(message); + + if (mInt.CompareTo(n) >= 0) + { + mInt = mInt.Subtract(n); + } + + byte[] mVal = BigIntegers.AsUnsignedByteArray(mInt); + + Array.Copy(mVal, 0, m, m.Length - mVal.Length, mVal.Length); + + hMac.Init(new KeyParameter(K)); + + hMac.BlockUpdate(V, 0, V.Length); + hMac.Update((byte)0x00); + hMac.BlockUpdate(x, 0, x.Length); + hMac.BlockUpdate(m, 0, m.Length); + + hMac.DoFinal(K, 0); + + hMac.Init(new KeyParameter(K)); + + hMac.BlockUpdate(V, 0, V.Length); + + hMac.DoFinal(V, 0); + + hMac.BlockUpdate(V, 0, V.Length); + hMac.Update((byte)0x01); + hMac.BlockUpdate(x, 0, x.Length); + hMac.BlockUpdate(m, 0, m.Length); + + hMac.DoFinal(K, 0); + + hMac.Init(new KeyParameter(K)); + + hMac.BlockUpdate(V, 0, V.Length); + + hMac.DoFinal(V, 0); + } + + public virtual BigInteger NextK() + { + byte[] t = new byte[((n.BitLength + 7) / 8)]; + + for (;;) + { + int tOff = 0; + + while (tOff < t.Length) + { + hMac.BlockUpdate(V, 0, V.Length); + + hMac.DoFinal(V, 0); + + int len = System.Math.Min(t.Length - tOff, V.Length); + Array.Copy(V, 0, t, tOff, len); + tOff += len; + } + + BigInteger k = BitsToInt(t); + + if (k.SignValue > 0 && k.CompareTo(n) < 0) + { + return k; + } + + hMac.BlockUpdate(V, 0, V.Length); + hMac.Update((byte)0x00); + + hMac.DoFinal(K, 0); + + hMac.Init(new KeyParameter(K)); + + hMac.BlockUpdate(V, 0, V.Length); + + hMac.DoFinal(V, 0); + } + } + + private BigInteger BitsToInt(byte[] t) + { + BigInteger v = new BigInteger(1, t); + + if (t.Length * 8 > n.BitLength) + { + v = v.ShiftRight(t.Length * 8 - n.BitLength); + } + + return v; + } + } +} diff --git a/crypto/src/crypto/signers/IDsaKCalculator.cs b/crypto/src/crypto/signers/IDsaKCalculator.cs new file mode 100644
index 000000000..645186d41 --- /dev/null +++ b/crypto/src/crypto/signers/IDsaKCalculator.cs
@@ -0,0 +1,44 @@ +using System; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Signers +{ + /** + * Interface define calculators of K values for DSA/ECDSA. + */ + public interface IDsaKCalculator + { + /** + * Return true if this calculator is deterministic, false otherwise. + * + * @return true if deterministic, otherwise false. + */ + bool IsDeterministic { get; } + + /** + * Non-deterministic initialiser. + * + * @param n the order of the DSA group. + * @param random a source of randomness. + */ + void Init(BigInteger n, SecureRandom random); + + /** + * Deterministic initialiser. + * + * @param n the order of the DSA group. + * @param d the DSA private value. + * @param message the message being signed. + */ + void Init(BigInteger n, BigInteger d, byte[] message); + + /** + * Return the next valid value of K. + * + * @return a K value. + */ + BigInteger NextK(); + } +} diff --git a/crypto/src/crypto/signers/Iso9796d2PssSigner.cs b/crypto/src/crypto/signers/Iso9796d2PssSigner.cs
index 48cd719e9..d4f6c5522 100644 --- a/crypto/src/crypto/signers/Iso9796d2PssSigner.cs +++ b/crypto/src/crypto/signers/Iso9796d2PssSigner.cs
@@ -1,4 +1,5 @@ using System; +using System.Collections; using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Parameters; @@ -7,570 +8,610 @@ 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; - } - } + /// <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; + public const int TrailerSha256 = 0x34CC; + public const int TrailerSha512 = 0x35CC; + public const int TrailerSha384 = 0x36CC; + public const int TrailerWhirlpool = 0x37CC; + + private static readonly IDictionary trailerMap = Platform.CreateHashtable(); + + static Iso9796d2PssSigner() + { + 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 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; + + private byte[] preSig; + private byte[] preBlock; + private int preMStart; + private int preTLength; + + /// <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 + { + string digestAlg = digest.AlgorithmName; + if (!trailerMap.Contains(digestAlg)) + throw new ArgumentException("no valid trailer for digest"); + + trailer = (int)trailerMap[digestAlg]; + } + } + + /// <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) + { + byte[] block = cipher.ProcessBlock(signature, 0, signature.Length); + + // + // adjust block size for leading zeroes if necessary + // + if (block.Length < (keyBits + 7) / 8) + { + byte[] tmp = new byte[(keyBits + 7) / 8]; + + Array.Copy(block, 0, tmp, tmp.Length - block.Length, block.Length); + ClearBlock(block); + block = tmp; + } + + int tLength; + + if (((block[block.Length - 1] & 0xFF) ^ 0xBC) == 0) + { + tLength = 1; + } + else + { + int sigTrail = ((block[block.Length - 2] & 0xFF) << 8) | (block[block.Length - 1] & 0xFF); + + string digestAlg = digest.AlgorithmName; + if (!trailerMap.Contains(digestAlg)) + throw new ArgumentException("unrecognised hash in signature"); + + if (sigTrail != (int)trailerMap[digestAlg]) + throw new InvalidOperationException("signer initialised with wrong digest for trailer " + sigTrail); + + 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); + } + + fullMessage = (mStart > 1); + + recoveredMessage = new byte[dbMask.Length - mStart - saltLength]; + + Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length); + recoveredMessage.CopyTo(mBuf, 0); + + preSig = signature; + preBlock = block; + preMStart = mStart; + preTLength = tLength; + } + + /// <summary> update the internal digest with the byte b</summary> + public virtual void Update( + byte input) + { + if (preSig == null && 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) + { + if (preSig == null) + { + 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; + if (preSig != null) + { + preSig = null; + ClearBlock(preBlock); + preBlock = null; + } + } + + /// <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) + { + // + // calculate H(m2) + // + byte[] m2Hash = new byte[hLen]; + digest.DoFinal(m2Hash, 0); + + byte[] block; + int tLength; + int mStart = 0; + + if (preSig == null) + { + try + { + UpdateWithRecoveredMessage(signature); + } + catch (Exception) + { + return false; + } + } + else + { + if (!Arrays.AreEqual(preSig, signature)) + { + throw new InvalidOperationException("UpdateWithRecoveredMessage called on different signature"); + } + } + + block = preBlock; + mStart = preMStart; + tLength = preTLength; + + preSig = null; + preBlock = null; + + // + // check the hashes + // + byte[] C = new byte[8]; + LtoOSP(recoveredMessage.Length * 8, C); + + digest.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; + + 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
index 8ff87e8ee..cfb8942e6 100644 --- a/crypto/src/crypto/signers/Iso9796d2Signer.cs +++ b/crypto/src/crypto/signers/Iso9796d2Signer.cs
@@ -8,550 +8,556 @@ 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; - } - } + /// <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; + recoveredMessage.CopyTo(mBuf, 0); + } + + /// <summary> update the internal digest with the byte b</summary> + public void Update( + byte input) + { + digest.Update(input); + + if (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) + { + while (length > 0 && messageLength < mBuf.Length) + { + //for (int i = 0; i < length && (i + messageLength) < mBuf.Length; i++) + //{ + // mBuf[messageLength + i] = input[inOff + i]; + //} + this.Update(input[inOff]); + inOff++; + length--; + } + + digest.BlockUpdate(input, inOff, length); + 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; + + if (preSig != null) + { + preSig = null; + ClearBlock(preBlock); + preBlock = null; + } + } + + /// <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; + + if (preSig == null) + { + 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"); + + 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) + { + if (!IsSameAs(mBuf, 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/RandomDsaKCalculator.cs b/crypto/src/crypto/signers/RandomDsaKCalculator.cs new file mode 100644
index 000000000..022cc268d --- /dev/null +++ b/crypto/src/crypto/signers/RandomDsaKCalculator.cs
@@ -0,0 +1,44 @@ +using System; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Signers +{ + public class RandomDsaKCalculator + : IDsaKCalculator + { + private BigInteger q; + private SecureRandom random; + + public virtual bool IsDeterministic + { + get { return false; } + } + + public virtual void Init(BigInteger n, SecureRandom random) + { + this.q = n; + this.random = random; + } + + public virtual void Init(BigInteger n, BigInteger d, byte[] message) + { + throw new InvalidOperationException("Operation not supported"); + } + + public virtual BigInteger NextK() + { + int qBitLength = q.BitLength; + + BigInteger k; + do + { + k = new BigInteger(qBitLength, random); + } + while (k.SignValue < 1 || k.CompareTo(q) >= 0); + + return k; + } + } +} diff --git a/crypto/src/crypto/signers/RsaDigestSigner.cs b/crypto/src/crypto/signers/RsaDigestSigner.cs
index f57bfc83d..9af4e7145 100644 --- a/crypto/src/crypto/signers/RsaDigestSigner.cs +++ b/crypto/src/crypto/signers/RsaDigestSigner.cs
@@ -19,16 +19,16 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Signers { public class RsaDigestSigner - : ISigner + : ISigner { private readonly IAsymmetricBlockCipher rsaEngine = new Pkcs1Encoding(new RsaBlindedEngine()); private readonly AlgorithmIdentifier algId; - private readonly IDigest digest; - private bool forSigning; + private readonly IDigest digest; + private bool forSigning; - private static readonly IDictionary oidMap = Platform.CreateHashtable(); + private static readonly IDictionary oidMap = Platform.CreateHashtable(); - /// <summary> + /// <summary> /// Load oid table. /// </summary> static RsaDigestSigner() @@ -48,37 +48,37 @@ namespace Org.BouncyCastle.Crypto.Signers oidMap["MD5"] = PkcsObjectIdentifiers.MD5; } - public RsaDigestSigner( - IDigest digest) + public RsaDigestSigner(IDigest digest) + : this(digest, (DerObjectIdentifier)oidMap[digest.AlgorithmName]) { - 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 RsaDigestSigner(IDigest digest, DerObjectIdentifier digestOid) + : this(digest, new AlgorithmIdentifier(digestOid, DerNull.Instance)) + { } - public string AlgorithmName + public RsaDigestSigner(IDigest digest, AlgorithmIdentifier algId) + { + this.digest = digest; + this.algId = algId; + } + + [Obsolete] + 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) + bool forSigning, + ICipherParameters parameters) { this.forSigning = forSigning; AsymmetricKeyParameter k; @@ -95,10 +95,10 @@ namespace Org.BouncyCastle.Crypto.Signers if (forSigning && !k.IsPrivate) throw new InvalidKeyException("Signing requires private key."); - if (!forSigning && k.IsPrivate) + if (!forSigning && k.IsPrivate) throw new InvalidKeyException("Verification requires public key."); - Reset(); + Reset(); rsaEngine.Init(forSigning, parameters); } @@ -107,7 +107,7 @@ namespace Org.BouncyCastle.Crypto.Signers * update the internal digest with the byte b */ public void Update( - byte input) + byte input) { digest.Update(input); } @@ -116,9 +116,9 @@ namespace Org.BouncyCastle.Crypto.Signers * update the internal digest with the byte array in */ public void BlockUpdate( - byte[] input, - int inOff, - int length) + byte[] input, + int inOff, + int length) { digest.BlockUpdate(input, inOff, length); } @@ -132,79 +132,69 @@ namespace Org.BouncyCastle.Crypto.Signers if (!forSigning) throw new InvalidOperationException("RsaDigestSigner not initialised for signature generation."); - byte[] hash = new byte[digest.GetDigestSize()]; + byte[] hash = new byte[digest.GetDigestSize()]; digest.DoFinal(hash, 0); - byte[] data = DerEncode(hash); + 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) + 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; + 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) + { + return Arrays.ConstantTimeAreEqual(sig, expected); + } + else if (sig.Length == expected.Length - 2) // NULL left out + { + int sigOffset = sig.Length - hash.Length - 2; + int expectedOffset = expected.Length - hash.Length - 2; + + expected[1] -= 2; // adjust lengths + expected[3] -= 2; + + int nonEqual = 0; + + for (int i = 0; i < hash.Length; i++) + { + nonEqual |= (sig[sigOffset + i] ^ expected[expectedOffset + i]); + } + + for (int i = 0; i < sigOffset; i++) + { + nonEqual |= (sig[i] ^ expected[i]); // check header less NULL + } + + return nonEqual == 0; + } + else + { + return false; + } } public void Reset() @@ -212,17 +202,17 @@ namespace Org.BouncyCastle.Crypto.Signers digest.Reset(); } - private byte[] DerEncode(byte[] hash) - { - if (algId == null) - { - // For raw RSA, the DigestInfo must be prepared externally - return hash; - } + 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); + DigestInfo dInfo = new DigestInfo(algId, hash); - return dInfo.GetDerEncoded(); - } + return dInfo.GetDerEncoded(); + } } } diff --git a/crypto/src/crypto/tls/AbstractTlsAgreementCredentials.cs b/crypto/src/crypto/tls/AbstractTlsAgreementCredentials.cs new file mode 100644
index 000000000..2d7af80e8 --- /dev/null +++ b/crypto/src/crypto/tls/AbstractTlsAgreementCredentials.cs
@@ -0,0 +1,12 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class AbstractTlsAgreementCredentials + : AbstractTlsCredentials, TlsAgreementCredentials + { + /// <exception cref="IOException"></exception> + public abstract byte[] GenerateAgreement(AsymmetricKeyParameter peerPublicKey); + } +} diff --git a/crypto/src/crypto/tls/AbstractTlsCipherFactory.cs b/crypto/src/crypto/tls/AbstractTlsCipherFactory.cs new file mode 100644
index 000000000..141ee6507 --- /dev/null +++ b/crypto/src/crypto/tls/AbstractTlsCipherFactory.cs
@@ -0,0 +1,15 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class AbstractTlsCipherFactory + : TlsCipherFactory + { + /// <exception cref="IOException"></exception> + public virtual TlsCipher CreateCipher(TlsContext context, int encryptionAlgorithm, int macAlgorithm) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } +} diff --git a/crypto/src/crypto/tls/AbstractTlsClient.cs b/crypto/src/crypto/tls/AbstractTlsClient.cs new file mode 100644
index 000000000..9484afa7d --- /dev/null +++ b/crypto/src/crypto/tls/AbstractTlsClient.cs
@@ -0,0 +1,232 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class AbstractTlsClient + : AbstractTlsPeer, TlsClient + { + protected TlsCipherFactory mCipherFactory; + + protected TlsClientContext mContext; + + protected IList mSupportedSignatureAlgorithms; + protected int[] mNamedCurves; + protected byte[] mClientECPointFormats, mServerECPointFormats; + + protected int mSelectedCipherSuite; + protected short mSelectedCompressionMethod; + + public AbstractTlsClient() + : this(new DefaultTlsCipherFactory()) + { + } + + public AbstractTlsClient(TlsCipherFactory cipherFactory) + { + this.mCipherFactory = cipherFactory; + } + + public virtual void Init(TlsClientContext context) + { + this.mContext = context; + } + + public virtual TlsSession GetSessionToResume() + { + return null; + } + + /** + * RFC 5246 E.1. "TLS clients that wish to negotiate with older servers MAY send any value + * {03,XX} as the record layer version number. Typical values would be {03,00}, the lowest + * version number supported by the client, and the value of ClientHello.client_version. No + * single value will guarantee interoperability with all old servers, but this is a complex + * topic beyond the scope of this document." + */ + public virtual ProtocolVersion ClientHelloRecordLayerVersion + { + get + { + // "{03,00}" + // return ProtocolVersion.SSLv3; + + // "the lowest version number supported by the client" + // return getMinimumVersion(); + + // "the value of ClientHello.client_version" + return ClientVersion; + } + } + + public virtual ProtocolVersion ClientVersion + { + get { return ProtocolVersion.TLSv12; } + } + + public virtual IDictionary GetClientExtensions() + { + IDictionary clientExtensions = null; + + ProtocolVersion clientVersion = mContext.ClientVersion; + + /* + * RFC 5246 7.4.1.4.1. Note: this extension is not meaningful for TLS versions prior to 1.2. + * Clients MUST NOT offer it if they are offering prior versions. + */ + if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(clientVersion)) + { + // TODO Provide a way for the user to specify the acceptable hash/signature algorithms. + + byte[] hashAlgorithms = new byte[]{ HashAlgorithm.sha512, HashAlgorithm.sha384, HashAlgorithm.sha256, + HashAlgorithm.sha224, HashAlgorithm.sha1 }; + + // TODO Sort out ECDSA signatures and add them as the preferred option here + byte[] signatureAlgorithms = new byte[]{ SignatureAlgorithm.rsa }; + + this.mSupportedSignatureAlgorithms = Platform.CreateArrayList(); + for (int i = 0; i < hashAlgorithms.Length; ++i) + { + for (int j = 0; j < signatureAlgorithms.Length; ++j) + { + this.mSupportedSignatureAlgorithms.Add(new SignatureAndHashAlgorithm(hashAlgorithms[i], + signatureAlgorithms[j])); + } + } + + /* + * RFC 5264 7.4.3. Currently, DSA [DSS] may only be used with SHA-1. + */ + this.mSupportedSignatureAlgorithms.Add(new SignatureAndHashAlgorithm(HashAlgorithm.sha1, + SignatureAlgorithm.dsa)); + + clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(clientExtensions); + + TlsUtilities.AddSignatureAlgorithmsExtension(clientExtensions, mSupportedSignatureAlgorithms); + } + + if (TlsEccUtilities.ContainsEccCipherSuites(GetCipherSuites())) + { + /* + * RFC 4492 5.1. A client that proposes ECC cipher suites in its ClientHello message + * appends these extensions (along with any others), enumerating the curves it supports + * and the point formats it can parse. Clients SHOULD send both the Supported Elliptic + * Curves Extension and the Supported Point Formats Extension. + */ + /* + * TODO Could just add all the curves since we support them all, but users may not want + * to use unnecessarily large fields. Need configuration options. + */ + this.mNamedCurves = new int[]{ NamedCurve.secp256r1, NamedCurve.secp384r1 }; + this.mClientECPointFormats = new byte[]{ ECPointFormat.uncompressed, + ECPointFormat.ansiX962_compressed_prime, ECPointFormat.ansiX962_compressed_char2, }; + + clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(clientExtensions); + + TlsEccUtilities.AddSupportedEllipticCurvesExtension(clientExtensions, mNamedCurves); + TlsEccUtilities.AddSupportedPointFormatsExtension(clientExtensions, mClientECPointFormats); + } + + return clientExtensions; + } + + public virtual ProtocolVersion MinimumVersion + { + get { return ProtocolVersion.TLSv10; } + } + + public virtual void NotifyServerVersion(ProtocolVersion serverVersion) + { + if (!MinimumVersion.IsEqualOrEarlierVersionOf(serverVersion)) + throw new TlsFatalAlert(AlertDescription.protocol_version); + } + + public abstract int[] GetCipherSuites(); + + public virtual byte[] GetCompressionMethods() + { + return new byte[]{ CompressionMethod.cls_null }; + } + + public virtual void NotifySessionID(byte[] sessionID) + { + // Currently ignored + } + + public virtual void NotifySelectedCipherSuite(int selectedCipherSuite) + { + this.mSelectedCipherSuite = selectedCipherSuite; + } + + public virtual void NotifySelectedCompressionMethod(byte selectedCompressionMethod) + { + this.mSelectedCompressionMethod = selectedCompressionMethod; + } + + public virtual void ProcessServerExtensions(IDictionary serverExtensions) + { + /* + * TlsProtocol implementation validates that any server extensions received correspond to + * client extensions sent. By default, we don't send any, and this method is not called. + */ + if (serverExtensions != null) + { + /* + * RFC 5246 7.4.1.4.1. Servers MUST NOT send this extension. + */ + if (serverExtensions.Contains(ExtensionType.signature_algorithms)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + int[] namedCurves = TlsEccUtilities.GetSupportedEllipticCurvesExtension(serverExtensions); + if (namedCurves != null) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + this.mServerECPointFormats = TlsEccUtilities.GetSupportedPointFormatsExtension(serverExtensions); + if (this.mServerECPointFormats != null && !TlsEccUtilities.IsEccCipherSuite(this.mSelectedCipherSuite)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + + public virtual void ProcessServerSupplementalData(IList serverSupplementalData) + { + if (serverSupplementalData != null) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + public abstract TlsKeyExchange GetKeyExchange(); + + public abstract TlsAuthentication GetAuthentication(); + + public virtual IList GetClientSupplementalData() + { + return null; + } + + public override TlsCompression GetCompression() + { + switch (mSelectedCompressionMethod) + { + case CompressionMethod.cls_null: + return new TlsNullCompression(); + + case CompressionMethod.DEFLATE: + return new TlsDeflateCompression(); + + default: + /* + * Note: internal error here; the TlsProtocol implementation verifies that the + * server-selected compression method was in the list of client-offered compression + * methods, so if we now can't produce an implementation, we shouldn't have offered it! + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public virtual void NotifyNewSessionTicket(NewSessionTicket newSessionTicket) + { + } + } +} diff --git a/crypto/src/crypto/tls/AbstractTlsContext.cs b/crypto/src/crypto/tls/AbstractTlsContext.cs new file mode 100644
index 000000000..83150d37e --- /dev/null +++ b/crypto/src/crypto/tls/AbstractTlsContext.cs
@@ -0,0 +1,132 @@ +using System; +using System.Threading; + +using Org.BouncyCastle.Crypto.Prng; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + internal abstract class AbstractTlsContext + : TlsContext + { + private static long counter = Times.NanoTime(); + + private static long NextCounterValue() + { + return Interlocked.Increment(ref counter); + } + + private readonly IRandomGenerator mNonceRandom; + private readonly SecureRandom mSecureRandom; + private readonly SecurityParameters mSecurityParameters; + + private ProtocolVersion mClientVersion = null; + private ProtocolVersion mServerVersion = null; + private TlsSession mSession = null; + private object mUserObject = null; + + internal AbstractTlsContext(SecureRandom secureRandom, SecurityParameters securityParameters) + { + IDigest d = TlsUtilities.CreateHash(HashAlgorithm.sha256); + byte[] seed = new byte[d.GetDigestSize()]; + secureRandom.NextBytes(seed); + + this.mNonceRandom = new DigestRandomGenerator(d); + mNonceRandom.AddSeedMaterial(NextCounterValue()); + mNonceRandom.AddSeedMaterial(Times.NanoTime()); + mNonceRandom.AddSeedMaterial(seed); + + this.mSecureRandom = secureRandom; + this.mSecurityParameters = securityParameters; + } + + public virtual IRandomGenerator NonceRandomGenerator + { + get { return mNonceRandom; } + } + + public virtual SecureRandom SecureRandom + { + get { return mSecureRandom; } + } + + public virtual SecurityParameters SecurityParameters + { + get { return mSecurityParameters; } + } + + public abstract bool IsServer { get; } + + public virtual ProtocolVersion ClientVersion + { + get { return mClientVersion; } + } + + internal virtual void SetClientVersion(ProtocolVersion clientVersion) + { + this.mClientVersion = clientVersion; + } + + public virtual ProtocolVersion ServerVersion + { + get { return mServerVersion; } + } + + internal virtual void SetServerVersion(ProtocolVersion serverVersion) + { + this.mServerVersion = serverVersion; + } + + public virtual TlsSession ResumableSession + { + get { return mSession; } + } + + internal virtual void SetResumableSession(TlsSession session) + { + this.mSession = session; + } + + public virtual object UserObject + { + get { return mUserObject; } + set { this.mUserObject = value; } + } + + public virtual byte[] ExportKeyingMaterial(string asciiLabel, byte[] context_value, int length) + { + if (context_value != null && !TlsUtilities.IsValidUint16(context_value.Length)) + throw new ArgumentException("must have length less than 2^16 (or be null)", "context_value"); + + SecurityParameters sp = SecurityParameters; + byte[] cr = sp.ClientRandom, sr = sp.ServerRandom; + + int seedLength = cr.Length + sr.Length; + if (context_value != null) + { + seedLength += (2 + context_value.Length); + } + + byte[] seed = new byte[seedLength]; + int seedPos = 0; + + Array.Copy(cr, 0, seed, seedPos, cr.Length); + seedPos += cr.Length; + Array.Copy(sr, 0, seed, seedPos, sr.Length); + seedPos += sr.Length; + if (context_value != null) + { + TlsUtilities.WriteUint16(context_value.Length, seed, seedPos); + seedPos += 2; + Array.Copy(context_value, 0, seed, seedPos, context_value.Length); + seedPos += context_value.Length; + } + + if (seedPos != seedLength) + throw new InvalidOperationException("error in calculation of seed for export"); + + return TlsUtilities.PRF(this, sp.MasterSecret, asciiLabel, seed, length); + } + } +} diff --git a/crypto/src/crypto/tls/AbstractTlsCredentials.cs b/crypto/src/crypto/tls/AbstractTlsCredentials.cs new file mode 100644
index 000000000..6411b811c --- /dev/null +++ b/crypto/src/crypto/tls/AbstractTlsCredentials.cs
@@ -0,0 +1,10 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class AbstractTlsCredentials + : TlsCredentials + { + public abstract Certificate Certificate { get; } + } +} diff --git a/crypto/src/crypto/tls/AbstractTlsEncryptionCredentials.cs b/crypto/src/crypto/tls/AbstractTlsEncryptionCredentials.cs new file mode 100644
index 000000000..05b129c60 --- /dev/null +++ b/crypto/src/crypto/tls/AbstractTlsEncryptionCredentials.cs
@@ -0,0 +1,12 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class AbstractTlsEncryptionCredentials + : AbstractTlsCredentials, TlsEncryptionCredentials + { + /// <exception cref="IOException"></exception> + public abstract byte[] DecryptPreMasterSecret(byte[] encryptedPreMasterSecret); + } +} diff --git a/crypto/src/crypto/tls/AbstractTlsKeyExchange.cs b/crypto/src/crypto/tls/AbstractTlsKeyExchange.cs new file mode 100644
index 000000000..155ac94d8 --- /dev/null +++ b/crypto/src/crypto/tls/AbstractTlsKeyExchange.cs
@@ -0,0 +1,166 @@ +using System; +using System.Collections; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class AbstractTlsKeyExchange + : TlsKeyExchange + { + protected readonly int mKeyExchange; + protected IList mSupportedSignatureAlgorithms; + + protected TlsContext context; + + protected AbstractTlsKeyExchange(int keyExchange, IList supportedSignatureAlgorithms) + { + this.mKeyExchange = keyExchange; + this.mSupportedSignatureAlgorithms = supportedSignatureAlgorithms; + } + + public virtual void Init(TlsContext context) + { + this.context = context; + + ProtocolVersion clientVersion = context.ClientVersion; + + if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(clientVersion)) + { + /* + * RFC 5264 7.4.1.4.1. If the client does not send the signature_algorithms extension, + * the server MUST do the following: + * + * - If the negotiated key exchange algorithm is one of (RSA, DHE_RSA, DH_RSA, RSA_PSK, + * ECDH_RSA, ECDHE_RSA), behave as if client had sent the value {sha1,rsa}. + * + * - If the negotiated key exchange algorithm is one of (DHE_DSS, DH_DSS), behave as if + * the client had sent the value {sha1,dsa}. + * + * - If the negotiated key exchange algorithm is one of (ECDH_ECDSA, ECDHE_ECDSA), + * behave as if the client had sent value {sha1,ecdsa}. + */ + if (this.mSupportedSignatureAlgorithms == null) + { + switch (mKeyExchange) + { + case KeyExchangeAlgorithm.DH_DSS: + case KeyExchangeAlgorithm.DHE_DSS: + case KeyExchangeAlgorithm.SRP_DSS: + { + this.mSupportedSignatureAlgorithms = TlsUtilities.GetDefaultDssSignatureAlgorithms(); + break; + } + + case KeyExchangeAlgorithm.ECDH_ECDSA: + case KeyExchangeAlgorithm.ECDHE_ECDSA: + { + this.mSupportedSignatureAlgorithms = TlsUtilities.GetDefaultECDsaSignatureAlgorithms(); + break; + } + + case KeyExchangeAlgorithm.DH_RSA: + case KeyExchangeAlgorithm.DHE_RSA: + case KeyExchangeAlgorithm.ECDH_RSA: + case KeyExchangeAlgorithm.ECDHE_RSA: + case KeyExchangeAlgorithm.RSA: + case KeyExchangeAlgorithm.RSA_PSK: + case KeyExchangeAlgorithm.SRP_RSA: + { + this.mSupportedSignatureAlgorithms = TlsUtilities.GetDefaultRsaSignatureAlgorithms(); + break; + } + + case KeyExchangeAlgorithm.DHE_PSK: + case KeyExchangeAlgorithm.ECDHE_PSK: + case KeyExchangeAlgorithm.PSK: + case KeyExchangeAlgorithm.SRP: + break; + + default: + throw new InvalidOperationException("unsupported key exchange algorithm"); + } + } + + } + else if (this.mSupportedSignatureAlgorithms != null) + { + throw new InvalidOperationException("supported_signature_algorithms not allowed for " + clientVersion); + } + } + + public abstract void SkipServerCredentials(); + + public virtual void ProcessServerCertificate(Certificate serverCertificate) + { + if (mSupportedSignatureAlgorithms == null) + { + /* + * TODO RFC 2264 7.4.2. Unless otherwise specified, the signing algorithm for the + * certificate must be the same as the algorithm for the certificate key. + */ + } + else + { + /* + * TODO RFC 5264 7.4.2. If the client provided a "signature_algorithms" extension, then + * all certificates provided by the server MUST be signed by a hash/signature algorithm + * pair that appears in that extension. + */ + } + } + + public virtual void ProcessServerCredentials(TlsCredentials serverCredentials) + { + ProcessServerCertificate(serverCredentials.Certificate); + } + + public virtual bool RequiresServerKeyExchange + { + get { return false; } + } + + public virtual byte[] GenerateServerKeyExchange() + { + if (RequiresServerKeyExchange) + throw new TlsFatalAlert(AlertDescription.internal_error); + + return null; + } + + public virtual void SkipServerKeyExchange() + { + if (RequiresServerKeyExchange) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + public virtual void ProcessServerKeyExchange(Stream input) + { + if (!RequiresServerKeyExchange) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + } + + public abstract void ValidateCertificateRequest(CertificateRequest certificateRequest); + + public virtual void SkipClientCredentials() + { + } + + public abstract void ProcessClientCredentials(TlsCredentials clientCredentials); + + public virtual void ProcessClientCertificate(Certificate clientCertificate) + { + } + + public abstract void GenerateClientKeyExchange(Stream output); + + public virtual void ProcessClientKeyExchange(Stream input) + { + // Key exchange implementation MUST support client key exchange + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + public abstract byte[] GeneratePremasterSecret(); + } +} diff --git a/crypto/src/crypto/tls/AbstractTlsPeer.cs b/crypto/src/crypto/tls/AbstractTlsPeer.cs new file mode 100644
index 000000000..81a53386c --- /dev/null +++ b/crypto/src/crypto/tls/AbstractTlsPeer.cs
@@ -0,0 +1,48 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class AbstractTlsPeer + : TlsPeer + { + public virtual bool ShouldUseGmtUnixTime() + { + /* + * draft-mathewson-no-gmtunixtime-00 2. For the reasons we discuss above, we recommend that + * TLS implementors MUST by default set the entire value the ClientHello.Random and + * ServerHello.Random fields, including gmt_unix_time, to a cryptographically random + * sequence. + */ + return false; + } + + public virtual void NotifySecureRenegotiation(bool secureRenegotiation) + { + if (!secureRenegotiation) + { + /* + * RFC 5746 3.4/3.6. In this case, some clients/servers may want to terminate the handshake instead + * of continuing; see Section 4.1/4.3 for discussion. + */ + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + } + + public abstract TlsCompression GetCompression(); + + public abstract TlsCipher GetCipher(); + + public virtual void NotifyAlertRaised(byte alertLevel, byte alertDescription, string message, Exception cause) + { + } + + public virtual void NotifyAlertReceived(byte alertLevel, byte alertDescription) + { + } + + public virtual void NotifyHandshakeComplete() + { + } + } +} diff --git a/crypto/src/crypto/tls/AbstractTlsServer.cs b/crypto/src/crypto/tls/AbstractTlsServer.cs new file mode 100644
index 000000000..47542c796 --- /dev/null +++ b/crypto/src/crypto/tls/AbstractTlsServer.cs
@@ -0,0 +1,318 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class AbstractTlsServer + : AbstractTlsPeer, TlsServer + { + protected TlsCipherFactory mCipherFactory; + + protected TlsServerContext mContext; + + protected ProtocolVersion mClientVersion; + protected int[] mOfferedCipherSuites; + protected byte[] mOfferedCompressionMethods; + protected IDictionary mClientExtensions; + + protected bool mEncryptThenMacOffered; + protected short mMaxFragmentLengthOffered; + protected bool mTruncatedHMacOffered; + protected IList mSupportedSignatureAlgorithms; + protected bool mEccCipherSuitesOffered; + protected int[] mNamedCurves; + protected byte[] mClientECPointFormats, mServerECPointFormats; + + protected ProtocolVersion mServerVersion; + protected int mSelectedCipherSuite; + protected byte mSelectedCompressionMethod; + protected IDictionary mServerExtensions; + + public AbstractTlsServer() + : this(new DefaultTlsCipherFactory()) + { + } + + public AbstractTlsServer(TlsCipherFactory cipherFactory) + { + this.mCipherFactory = cipherFactory; + } + + protected virtual bool AllowEncryptThenMac + { + get { return true; } + } + + protected virtual bool AllowTruncatedHMac + { + get { return false; } + } + + protected virtual IDictionary CheckServerExtensions() + { + return this.mServerExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(this.mServerExtensions); + } + + protected abstract int[] GetCipherSuites(); + + protected byte[] GetCompressionMethods() + { + return new byte[] { CompressionMethod.cls_null }; + } + + protected virtual ProtocolVersion MaximumVersion + { + get { return ProtocolVersion.TLSv11; } + } + + protected virtual ProtocolVersion MinimumVersion + { + get { return ProtocolVersion.TLSv10; } + } + + protected virtual bool SupportsClientEccCapabilities(int[] namedCurves, byte[] ecPointFormats) + { + // NOTE: BC supports all the current set of point formats so we don't check them here + + if (namedCurves == null) + { + /* + * RFC 4492 4. A client that proposes ECC cipher suites may choose not to include these + * extensions. In this case, the server is free to choose any one of the elliptic curves + * or point formats [...]. + */ + return TlsEccUtilities.HasAnySupportedNamedCurves(); + } + + for (int i = 0; i < namedCurves.Length; ++i) + { + int namedCurve = namedCurves[i]; + if (NamedCurve.IsValid(namedCurve) + && (!NamedCurve.RefersToASpecificNamedCurve(namedCurve) || TlsEccUtilities.IsSupportedNamedCurve(namedCurve))) + { + return true; + } + } + + return false; + } + + public virtual void Init(TlsServerContext context) + { + this.mContext = context; + } + + public virtual void NotifyClientVersion(ProtocolVersion clientVersion) + { + this.mClientVersion = clientVersion; + } + + public virtual void NotifyOfferedCipherSuites(int[] offeredCipherSuites) + { + this.mOfferedCipherSuites = offeredCipherSuites; + this.mEccCipherSuitesOffered = TlsEccUtilities.ContainsEccCipherSuites(this.mOfferedCipherSuites); + } + + public virtual void NotifyOfferedCompressionMethods(byte[] offeredCompressionMethods) + { + this.mOfferedCompressionMethods = offeredCompressionMethods; + } + + public virtual void ProcessClientExtensions(IDictionary clientExtensions) + { + this.mClientExtensions = clientExtensions; + + if (clientExtensions != null) + { + this.mEncryptThenMacOffered = TlsExtensionsUtilities.HasEncryptThenMacExtension(clientExtensions); + this.mMaxFragmentLengthOffered = TlsExtensionsUtilities.GetMaxFragmentLengthExtension(clientExtensions); + this.mTruncatedHMacOffered = TlsExtensionsUtilities.HasTruncatedHMacExtension(clientExtensions); + + this.mSupportedSignatureAlgorithms = TlsUtilities.GetSignatureAlgorithmsExtension(clientExtensions); + if (this.mSupportedSignatureAlgorithms != null) + { + /* + * RFC 5246 7.4.1.4.1. Note: this extension is not meaningful for TLS versions prior + * to 1.2. Clients MUST NOT offer it if they are offering prior versions. + */ + if (!TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(mClientVersion)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + this.mNamedCurves = TlsEccUtilities.GetSupportedEllipticCurvesExtension(clientExtensions); + this.mClientECPointFormats = TlsEccUtilities.GetSupportedPointFormatsExtension(clientExtensions); + } + + /* + * RFC 4429 4. The client MUST NOT include these extensions in the ClientHello message if it + * does not propose any ECC cipher suites. + */ + if (!this.mEccCipherSuitesOffered && (this.mNamedCurves != null || this.mClientECPointFormats != null)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + public virtual ProtocolVersion GetServerVersion() + { + if (MinimumVersion.IsEqualOrEarlierVersionOf(mClientVersion)) + { + ProtocolVersion maximumVersion = MaximumVersion; + if (mClientVersion.IsEqualOrEarlierVersionOf(maximumVersion)) + { + return mServerVersion = mClientVersion; + } + if (mClientVersion.IsLaterVersionOf(maximumVersion)) + { + return mServerVersion = maximumVersion; + } + } + throw new TlsFatalAlert(AlertDescription.protocol_version); + } + + public virtual int GetSelectedCipherSuite() + { + /* + * TODO RFC 5246 7.4.3. In order to negotiate correctly, the server MUST check any candidate + * cipher suites against the "signature_algorithms" extension before selecting them. This is + * somewhat inelegant but is a compromise designed to minimize changes to the original + * cipher suite design. + */ + + /* + * RFC 4429 5.1. A server that receives a ClientHello containing one or both of these + * extensions MUST use the client's enumerated capabilities to guide its selection of an + * appropriate cipher suite. One of the proposed ECC cipher suites must be negotiated only + * if the server can successfully complete the handshake while using the curves and point + * formats supported by the client [...]. + */ + bool eccCipherSuitesEnabled = SupportsClientEccCapabilities(this.mNamedCurves, this.mClientECPointFormats); + + int[] cipherSuites = GetCipherSuites(); + for (int i = 0; i < cipherSuites.Length; ++i) + { + int cipherSuite = cipherSuites[i]; + + if (Arrays.Contains(this.mOfferedCipherSuites, cipherSuite) + && (eccCipherSuitesEnabled || !TlsEccUtilities.IsEccCipherSuite(cipherSuite)) + && TlsUtilities.IsValidCipherSuiteForVersion(cipherSuite, mServerVersion)) + { + return this.mSelectedCipherSuite = cipherSuite; + } + } + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + + public virtual byte GetSelectedCompressionMethod() + { + byte[] compressionMethods = GetCompressionMethods(); + for (int i = 0; i < compressionMethods.Length; ++i) + { + if (Arrays.Contains(mOfferedCompressionMethods, compressionMethods[i])) + { + return this.mSelectedCompressionMethod = compressionMethods[i]; + } + } + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + + // IDictionary is (Int32 -> byte[]) + public virtual IDictionary GetServerExtensions() + { + if (this.mEncryptThenMacOffered && AllowEncryptThenMac) + { + /* + * draft-ietf-tls-encrypt-then-mac-03 3. If a server receives an encrypt-then-MAC + * request extension from a client and then selects a stream or AEAD cipher suite, it + * MUST NOT send an encrypt-then-MAC response extension back to the client. + */ + if (TlsUtilities.IsBlockCipherSuite(this.mSelectedCipherSuite)) + { + TlsExtensionsUtilities.AddEncryptThenMacExtension(CheckServerExtensions()); + } + } + + if (this.mMaxFragmentLengthOffered >= 0) + { + TlsExtensionsUtilities.AddMaxFragmentLengthExtension(CheckServerExtensions(), (byte)this.mMaxFragmentLengthOffered); + } + + if (this.mTruncatedHMacOffered && AllowTruncatedHMac) + { + TlsExtensionsUtilities.AddTruncatedHMacExtension(CheckServerExtensions()); + } + + if (this.mClientECPointFormats != null && TlsEccUtilities.IsEccCipherSuite(this.mSelectedCipherSuite)) + { + /* + * RFC 4492 5.2. A server that selects an ECC cipher suite in response to a ClientHello + * message including a Supported Point Formats Extension appends this extension (along + * with others) to its ServerHello message, enumerating the point formats it can parse. + */ + this.mServerECPointFormats = new byte[]{ ECPointFormat.uncompressed, + ECPointFormat.ansiX962_compressed_prime, ECPointFormat.ansiX962_compressed_char2, }; + + TlsEccUtilities.AddSupportedPointFormatsExtension(CheckServerExtensions(), mServerECPointFormats); + } + + return mServerExtensions; + } + + public virtual IList GetServerSupplementalData() + { + return null; + } + + public abstract TlsCredentials GetCredentials(); + + public virtual CertificateStatus GetCertificateStatus() + { + return null; + } + + public abstract TlsKeyExchange GetKeyExchange(); + + public virtual CertificateRequest GetCertificateRequest() + { + return null; + } + + public virtual void ProcessClientSupplementalData(IList clientSupplementalData) + { + if (clientSupplementalData != null) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + public virtual void NotifyClientCertificate(Certificate clientCertificate) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + public override TlsCompression GetCompression() + { + switch (mSelectedCompressionMethod) + { + case CompressionMethod.cls_null: + return new TlsNullCompression(); + + default: + /* + * Note: internal error here; we selected the compression method, so if we now can't + * produce an implementation, we shouldn't have chosen it! + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public virtual NewSessionTicket GetNewSessionTicket() + { + /* + * RFC 5077 3.3. If the server determines that it does not want to include a ticket after it + * has included the SessionTicket extension in the ServerHello, then it sends a zero-length + * ticket in the NewSessionTicket handshake message. + */ + return new NewSessionTicket(0L, TlsUtilities.EmptyBytes); + } + } +} diff --git a/crypto/src/crypto/tls/AbstractTlsSigner.cs b/crypto/src/crypto/tls/AbstractTlsSigner.cs new file mode 100644
index 000000000..1f4aabf74 --- /dev/null +++ b/crypto/src/crypto/tls/AbstractTlsSigner.cs
@@ -0,0 +1,50 @@ +using System; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class AbstractTlsSigner + : TlsSigner + { + protected TlsContext mContext; + + public virtual void Init(TlsContext context) + { + this.mContext = context; + } + + public virtual byte[] GenerateRawSignature(AsymmetricKeyParameter privateKey, byte[] md5AndSha1) + { + return GenerateRawSignature(null, privateKey, md5AndSha1); + } + + public abstract byte[] GenerateRawSignature(SignatureAndHashAlgorithm algorithm, + AsymmetricKeyParameter privateKey, byte[] hash); + + public virtual bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5AndSha1) + { + return VerifyRawSignature(null, sigBytes, publicKey, md5AndSha1); + } + + public abstract bool VerifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes, + AsymmetricKeyParameter publicKey, byte[] hash); + + public virtual ISigner CreateSigner(AsymmetricKeyParameter privateKey) + { + return CreateSigner(null, privateKey); + } + + public abstract ISigner CreateSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey); + + public virtual ISigner CreateVerifyer(AsymmetricKeyParameter publicKey) + { + return CreateVerifyer(null, publicKey); + } + + public abstract ISigner CreateVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey); + + public abstract bool IsValidPublicKey(AsymmetricKeyParameter publicKey); + } +} diff --git a/crypto/src/crypto/tls/AbstractTlsSignerCredentials.cs b/crypto/src/crypto/tls/AbstractTlsSignerCredentials.cs new file mode 100644
index 000000000..886c46c6e --- /dev/null +++ b/crypto/src/crypto/tls/AbstractTlsSignerCredentials.cs
@@ -0,0 +1,20 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class AbstractTlsSignerCredentials + : AbstractTlsCredentials, TlsSignerCredentials + { + /// <exception cref="IOException"></exception> + public abstract byte[] GenerateCertificateSignature(byte[] hash); + + public virtual SignatureAndHashAlgorithm SignatureAndHashAlgorithm + { + get + { + throw new InvalidOperationException("TlsSignerCredentials implementation does not support (D)TLS 1.2+"); + } + } + } +} diff --git a/crypto/src/crypto/tls/AlertDescription.cs b/crypto/src/crypto/tls/AlertDescription.cs
index e1229a4a3..e09da6cab 100644 --- a/crypto/src/crypto/tls/AlertDescription.cs +++ b/crypto/src/crypto/tls/AlertDescription.cs
@@ -1,47 +1,217 @@ 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, - } + /// <summary> + /// RFC 5246 7.2 + /// </summary> + public abstract class AlertDescription + { + /** + * This message notifies the recipient that the sender will not send any more messages on this + * connection. Note that as of TLS 1.1, failure to properly close a connection no longer + * requires that a session not be resumed. This is a change from TLS 1.0 ("The session becomes + * unresumable if any connection is terminated without proper close_notify messages with level + * equal to warning.") to conform with widespread implementation practice. + */ + public const byte close_notify = 0; + + /** + * An inappropriate message was received. This alert is always fatal and should never be + * observed in communication between proper implementations. + */ + public const byte unexpected_message = 10; + + /** + * This alert is returned if a record is received with an incorrect MAC. This alert also MUST be + * returned if an alert is sent because a TLSCiphertext decrypted in an invalid way: either it + * wasn't an even multiple of the block length, or its padding values, when checked, weren't + * correct. This message is always fatal and should never be observed in communication between + * proper implementations (except when messages were corrupted in the network). + */ + public const byte bad_record_mac = 20; + + /** + * This alert was used in some earlier versions of TLS, and may have permitted certain attacks + * against the CBC mode [CBCATT]. It MUST NOT be sent by compliant implementations. + */ + public const byte decryption_failed = 21; + + /** + * A TLSCiphertext record was received that had a length more than 2^14+2048 bytes, or a record + * decrypted to a TLSCompressed record with more than 2^14+1024 bytes. This message is always + * fatal and should never be observed in communication between proper implementations (except + * when messages were corrupted in the network). + */ + public const byte record_overflow = 22; + + /** + * The decompression function received improper input (e.g., data that would expand to excessive + * length). This message is always fatal and should never be observed in communication between + * proper implementations. + */ + public const byte decompression_failure = 30; + + /** + * Reception of a handshake_failure alert message indicates that the sender was unable to + * negotiate an acceptable set of security parameters given the options available. This is a + * fatal error. + */ + public const byte handshake_failure = 40; + + /** + * This alert was used in SSLv3 but not any version of TLS. It MUST NOT be sent by compliant + * implementations. + */ + public const byte no_certificate = 41; + + /** + * A certificate was corrupt, contained signatures that did not verify correctly, etc. + */ + public const byte bad_certificate = 42; + + /** + * A certificate was of an unsupported type. + */ + public const byte unsupported_certificate = 43; + + /** + * A certificate was revoked by its signer. + */ + public const byte certificate_revoked = 44; + + /** + * A certificate has expired or is not currently valid. + */ + public const byte certificate_expired = 45; + + /** + * Some other (unspecified) issue arose in processing the certificate, rendering it + * unacceptable. + */ + public const byte certificate_unknown = 46; + + /** + * A field in the handshake was out of range or inconsistent with other fields. This message is + * always fatal. + */ + public const byte illegal_parameter = 47; + + /** + * A valid certificate chain or partial chain was received, but the certificate was not accepted + * because the CA certificate could not be located or couldn't be matched with a known, trusted + * CA. This message is always fatal. + */ + public const byte unknown_ca = 48; + + /** + * A valid certificate was received, but when access control was applied, the sender decided not + * to proceed with negotiation. This message is always fatal. + */ + public const byte access_denied = 49; + + /** + * A message could not be decoded because some field was out of the specified range or the + * length of the message was incorrect. This message is always fatal and should never be + * observed in communication between proper implementations (except when messages were corrupted + * in the network). + */ + public const byte decode_error = 50; + + /** + * A handshake cryptographic operation failed, including being unable to correctly verify a + * signature or validate a Finished message. This message is always fatal. + */ + public const byte decrypt_error = 51; + + /** + * This alert was used in some earlier versions of TLS. It MUST NOT be sent by compliant + * implementations. + */ + public const byte export_restriction = 60; + + /** + * The protocol version the client has attempted to negotiate is recognized but not supported. + * (For example, old protocol versions might be avoided for security reasons.) This message is + * always fatal. + */ + public const byte protocol_version = 70; + + /** + * Returned instead of handshake_failure when a negotiation has failed specifically because the + * server requires ciphers more secure than those supported by the client. This message is + * always fatal. + */ + public const byte insufficient_security = 71; + + /** + * An internal error unrelated to the peer or the correctness of the protocol (such as a memory + * allocation failure) makes it impossible to continue. This message is always fatal. + */ + public const byte internal_error = 80; + + /** + * This handshake is being canceled for some reason unrelated to a protocol failure. If the user + * cancels an operation after the handshake is complete, just closing the connection by sending + * a close_notify is more appropriate. This alert should be followed by a close_notify. This + * message is generally a warning. + */ + public const byte user_canceled = 90; + + /** + * Sent by the client in response to a hello request or by the server in response to a client + * hello after initial handshaking. Either of these would normally lead to renegotiation; when + * that is not appropriate, the recipient should respond with this alert. At that point, the + * original requester can decide whether to proceed with the connection. One case where this + * would be appropriate is where a server has spawned a process to satisfy a request; the + * process might receive security parameters (key length, authentication, etc.) at startup, and + * it might be difficult to communicate changes to these parameters after that point. This + * message is always a warning. + */ + public const byte no_renegotiation = 100; + + /** + * Sent by clients that receive an extended server hello containing an extension that they did + * not put in the corresponding client hello. This message is always fatal. + */ + public const byte unsupported_extension = 110; + + /* + * RFC 3546 + */ + + /** + * This alert is sent by servers who are unable to retrieve a certificate chain from the URL + * supplied by the client (see Section 3.3). This message MAY be fatal - for example if client + * authentication is required by the server for the handshake to continue and the server is + * unable to retrieve the certificate chain, it may send a fatal alert. + */ + public const byte certificate_unobtainable = 111; + + /** + * This alert is sent by servers that receive a server_name extension request, but do not + * recognize the server name. This message MAY be fatal. + */ + public const byte unrecognized_name = 112; + + /** + * This alert is sent by clients that receive an invalid certificate status response (see + * Section 3.6). This message is always fatal. + */ + public const byte bad_certificate_status_response = 113; + + /** + * This alert is sent by servers when a certificate hash does not match a client provided + * certificate_hash. This message is always fatal. + */ + public const byte bad_certificate_hash_value = 114; + + /* + * RFC 4279 + */ + + /** + * If the server does not recognize the PSK identity, it MAY respond with an + * "unknown_psk_identity" alert message. + */ + public const byte unknown_psk_identity = 115; + } } diff --git a/crypto/src/crypto/tls/AlertLevel.cs b/crypto/src/crypto/tls/AlertLevel.cs
index afb04308b..d77251dfb 100644 --- a/crypto/src/crypto/tls/AlertLevel.cs +++ b/crypto/src/crypto/tls/AlertLevel.cs
@@ -1,11 +1,11 @@ namespace Org.BouncyCastle.Crypto.Tls { - /// <summary> - /// RFC 2246 7.2 - /// </summary> - public enum AlertLevel : byte - { - warning = 1, - fatal = 2, - } + /// <summary> + /// RFC 5246 7.2 + /// </summary> + public abstract class AlertLevel + { + public const byte warning = 1; + public const byte fatal = 2; + } } diff --git a/crypto/src/crypto/tls/BulkCipherAlgorithm.cs b/crypto/src/crypto/tls/BulkCipherAlgorithm.cs new file mode 100644
index 000000000..07ff8dc07 --- /dev/null +++ b/crypto/src/crypto/tls/BulkCipherAlgorithm.cs
@@ -0,0 +1,25 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <summary>RFC 2246</summary> + /// <remarks> + /// Note that the values here are implementation-specific and arbitrary. It is recommended not to + /// depend on the particular values (e.g. serialization). + /// </remarks> + public abstract class BulkCipherAlgorithm + { + public const int cls_null = 0; + public const int rc4 = 1; + public const int rc2 = 2; + public const int des = 3; + public const int cls_3des = 4; + public const int des40 = 5; + + /* + * RFC 4346 + */ + public const int aes = 6; + public const int idea = 7; + } +} diff --git a/crypto/src/crypto/tls/ByteQueue.cs b/crypto/src/crypto/tls/ByteQueue.cs
index 96062402b..f9398bbaf 100644 --- a/crypto/src/crypto/tls/ByteQueue.cs +++ b/crypto/src/crypto/tls/ByteQueue.cs
@@ -2,124 +2,146 @@ 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; - } + /// <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 initial size for our buffer. + */ + private const int DefaultCapacity = 1024; - /** - * The buffer where we store our data. - */ - private byte[] databuf = new byte[ByteQueue.InitBufSize]; + /** + * The buffer where we store our data. + */ + private byte[] databuf; - /** - * How many bytes at the beginning of the buffer are skipped. - */ - private int skipped = 0; + /** + * 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; + /** + * 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); - } + public ByteQueue() + : this(DefaultCapacity) + { + } - /// <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; - } + public ByteQueue(int capacity) + { + this.databuf = new byte[capacity]; + } - /// <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); - } + /// <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 ((buf.Length - offset) < len) + { + throw new ArgumentException("Buffer size of " + buf.Length + " is too small for a read of " + len + " bytes"); + } + if ((available - skip) < len) + { + throw new InvalidOperationException("Not enough data to read"); + } + Array.Copy(databuf, skipped + skip, buf, offset, len); + } - /* - * Skip the data. - */ - available -= i; - skipped += i; + /// <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) + { + int desiredSize = ByteQueue.NextTwoPow(available + len); + if (desiredSize > databuf.Length) + { + byte[] tmp = new byte[desiredSize]; + Array.Copy(databuf, skipped, tmp, 0, available); + databuf = tmp; + } + else + { + Array.Copy(databuf, skipped, databuf, 0, available); + } + skipped = 0; + } - /* - * 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; - } - } + Array.Copy(data, offset, databuf, skipped + available, len); + available += len; + } - /// <summary>The number of bytes which are available in this buffer.</summary> - public int Available - { - get { return available; } - } - } + /// <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 InvalidOperationException("Cannot remove " + i + " bytes, only got " + available); + } + + /* + * Skip the data. + */ + available -= i; + skipped += i; + } + + public void RemoveData(byte[] buf, int off, int len, int skip) + { + Read(buf, off, len, skip); + RemoveData(skip + len); + } + + public byte[] RemoveData(int len, int skip) + { + byte[] buf = new byte[len]; + RemoveData(buf, 0, len, skip); + return buf; + } + + /// <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/CertChainType.cs b/crypto/src/crypto/tls/CertChainType.cs new file mode 100644
index 000000000..cbb183441 --- /dev/null +++ b/crypto/src/crypto/tls/CertChainType.cs
@@ -0,0 +1,18 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /* + * RFC 3546 3.3. + */ + public abstract class CertChainType + { + public const byte individual_certs = 0; + public const byte pkipath = 1; + + public static bool IsValid(byte certChainType) + { + return certChainType >= individual_certs && certChainType <= pkipath; + } + } +} diff --git a/crypto/src/crypto/tls/Certificate.cs b/crypto/src/crypto/tls/Certificate.cs
index e4df041e2..c59616c95 100644 --- a/crypto/src/crypto/tls/Certificate.cs +++ b/crypto/src/crypto/tls/Certificate.cs
@@ -8,104 +8,129 @@ 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) + /** + * Parsing and encoding of a <i>Certificate</i> struct from RFC 4346. + * <p/> + * <pre> + * opaque ASN.1Cert&lt;2^24-1&gt;; + * + * struct { + * ASN.1Cert certificate_list&lt;0..2^24-1&gt;; + * } Certificate; + * </pre> + * + * @see Org.BouncyCastle.Asn1.X509.X509CertificateStructure + */ + public class Certificate + { + public static readonly Certificate EmptyChain = new Certificate(new X509CertificateStructure[0]); + + /** + * The certificates. + */ + protected readonly X509CertificateStructure[] mCertificateList; + + public Certificate(X509CertificateStructure[] certificateList) + { + if (certificateList == null) + throw new ArgumentNullException("certificateList"); + + this.mCertificateList = certificateList; + } + + /** + * @return an array of {@link org.bouncycastle.asn1.x509.Certificate} representing a certificate + * chain. + */ + public virtual X509CertificateStructure[] GetCertificateList() + { + return CloneCertificateList(); + } + + public virtual X509CertificateStructure GetCertificateAt(int index) + { + return mCertificateList[index]; + } + + public virtual int Length + { + get { return mCertificateList.Length; } + } + + /** + * @return <code>true</code> if this certificate chain contains no certificates, or + * <code>false</code> otherwise. + */ + public virtual bool IsEmpty + { + get { return mCertificateList.Length == 0; } + } + + /** + * Encode this {@link Certificate} to a {@link Stream}. + * + * @param output the {@link Stream} to encode to. + * @throws IOException + */ + public virtual void Encode(Stream output) + { + IList derEncodings = Platform.CreateArrayList(mCertificateList.Length); + + int totalLength = 0; + foreach (Asn1Encodable asn1Cert in mCertificateList) + { + byte[] derEncoding = asn1Cert.GetEncoded(Asn1Encodable.Der); + derEncodings.Add(derEncoding); + totalLength += derEncoding.Length + 3; + } + + TlsUtilities.CheckUint24(totalLength); + TlsUtilities.WriteUint24(totalLength, output); + + foreach (byte[] derEncoding in derEncodings) { - certs[i] = (X509CertificateStructure)tmp[i]; + TlsUtilities.WriteOpaque24(derEncoding, output); } - 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; } - } - } + } + + /** + * Parse a {@link Certificate} from a {@link Stream}. + * + * @param input the {@link Stream} to parse from. + * @return a {@link Certificate} object. + * @throws IOException + */ + public static Certificate Parse(Stream input) + { + int totalLength = TlsUtilities.ReadUint24(input); + if (totalLength == 0) + { + return EmptyChain; + } + + byte[] certListData = TlsUtilities.ReadFully(totalLength, input); + + MemoryStream buf = new MemoryStream(certListData, false); + + IList certificate_list = Platform.CreateArrayList(); + while (buf.Position < buf.Length) + { + byte[] derEncoding = TlsUtilities.ReadOpaque24(buf); + Asn1Object asn1Cert = TlsUtilities.ReadDerObject(derEncoding); + certificate_list.Add(X509CertificateStructure.GetInstance(asn1Cert)); + } + + X509CertificateStructure[] certificateList = new X509CertificateStructure[certificate_list.Count]; + for (int i = 0; i < certificate_list.Count; ++i) + { + certificateList[i] = (X509CertificateStructure)certificate_list[i]; + } + return new Certificate(certificateList); + } + + protected virtual X509CertificateStructure[] CloneCertificateList() + { + return (X509CertificateStructure[])mCertificateList.Clone(); + } + } } diff --git a/crypto/src/crypto/tls/CertificateRequest.cs b/crypto/src/crypto/tls/CertificateRequest.cs
index 49d8ba6fb..f3dcb3bbd 100644 --- a/crypto/src/crypto/tls/CertificateRequest.cs +++ b/crypto/src/crypto/tls/CertificateRequest.cs
@@ -1,28 +1,156 @@ 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 { - 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 + /** + * Parsing and encoding of a <i>CertificateRequest</i> struct from RFC 4346. + * <p/> + * <pre> + * struct { + * ClientCertificateType certificate_types&lt;1..2^8-1&gt;; + * DistinguishedName certificate_authorities&lt;3..2^16-1&gt; + * } CertificateRequest; + * </pre> + * + * @see ClientCertificateType + * @see X509Name + */ + public class CertificateRequest + { + protected readonly byte[] mCertificateTypes; + protected readonly IList mSupportedSignatureAlgorithms; + protected readonly IList mCertificateAuthorities; + + /** + * @param certificateTypes see {@link ClientCertificateType} for valid constants. + * @param certificateAuthorities an {@link IList} of {@link X509Name}. + */ + public CertificateRequest(byte[] certificateTypes, IList supportedSignatureAlgorithms, + IList certificateAuthorities) + { + this.mCertificateTypes = certificateTypes; + this.mSupportedSignatureAlgorithms = supportedSignatureAlgorithms; + this.mCertificateAuthorities = certificateAuthorities; + } + + /** + * @return an array of certificate types + * @see {@link ClientCertificateType} + */ + public virtual byte[] CertificateTypes + { + get { return mCertificateTypes; } + } + + /** + * @return an {@link IList} of {@link SignatureAndHashAlgorithm} (or null before TLS 1.2). + */ + public virtual IList SupportedSignatureAlgorithms + { + get { return mSupportedSignatureAlgorithms; } + } + + /** + * @return an {@link IList} of {@link X509Name} + */ + public virtual IList CertificateAuthorities + { + get { return mCertificateAuthorities; } + } + + /** + * Encode this {@link CertificateRequest} to a {@link Stream}. + * + * @param output the {@link Stream} to encode to. + * @throws IOException + */ + public virtual void Encode(Stream output) + { + if (mCertificateTypes == null || mCertificateTypes.Length == 0) + { + TlsUtilities.WriteUint8(0, output); + } + else + { + TlsUtilities.WriteUint8ArrayWithUint8Length(mCertificateTypes, output); + } + + if (mSupportedSignatureAlgorithms != null) + { + // TODO Check whether SignatureAlgorithm.anonymous is allowed here + TlsUtilities.EncodeSupportedSignatureAlgorithms(mSupportedSignatureAlgorithms, false, output); + } + + if (mCertificateAuthorities == null || mCertificateAuthorities.Count < 1) + { + TlsUtilities.WriteUint16(0, output); + } + else + { + IList derEncodings = Platform.CreateArrayList(mCertificateAuthorities.Count); + + int totalLength = 0; + foreach (Asn1Encodable certificateAuthority in mCertificateAuthorities) + { + byte[] derEncoding = certificateAuthority.GetEncoded(Asn1Encodable.Der); + derEncodings.Add(derEncoding); + totalLength += derEncoding.Length + 2; + } + + TlsUtilities.CheckUint16(totalLength); + TlsUtilities.WriteUint16(totalLength, output); + + foreach (byte[] derEncoding in derEncodings) + { + TlsUtilities.WriteOpaque16(derEncoding, output); + } + } + } + + /** + * Parse a {@link CertificateRequest} from a {@link Stream}. + * + * @param context + * the {@link TlsContext} of the current connection. + * @param input + * the {@link Stream} to parse from. + * @return a {@link CertificateRequest} object. + * @throws IOException + */ + public static CertificateRequest Parse(TlsContext context, Stream input) + { + int numTypes = TlsUtilities.ReadUint8(input); + byte[] certificateTypes = new byte[numTypes]; + for (int i = 0; i < numTypes; ++i) + { + certificateTypes[i] = TlsUtilities.ReadUint8(input); + } + + IList supportedSignatureAlgorithms = null; + if (TlsUtilities.IsTlsV12(context)) + { + // TODO Check whether SignatureAlgorithm.anonymous is allowed here + supportedSignatureAlgorithms = TlsUtilities.ParseSupportedSignatureAlgorithms(false, input); + } + + IList certificateAuthorities = Platform.CreateArrayList(); + byte[] certAuthData = TlsUtilities.ReadOpaque16(input); + MemoryStream bis = new MemoryStream(certAuthData, false); + while (bis.Position < bis.Length) + { + byte[] derEncoding = TlsUtilities.ReadOpaque16(bis); + Asn1Object asn1 = TlsUtilities.ReadDerObject(derEncoding); + // TODO Switch to X500Name when available + certificateAuthorities.Add(X509Name.GetInstance(asn1)); + } + + return new CertificateRequest(certificateTypes, supportedSignatureAlgorithms, certificateAuthorities); + } + } +} diff --git a/crypto/src/crypto/tls/CertificateStatus.cs b/crypto/src/crypto/tls/CertificateStatus.cs new file mode 100644
index 000000000..0f95475b9 --- /dev/null +++ b/crypto/src/crypto/tls/CertificateStatus.cs
@@ -0,0 +1,102 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Ocsp; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class CertificateStatus + { + protected readonly byte mStatusType; + protected readonly object mResponse; + + public CertificateStatus(byte statusType, object response) + { + if (!IsCorrectType(statusType, response)) + throw new ArgumentException("not an instance of the correct type", "response"); + + this.mStatusType = statusType; + this.mResponse = response; + } + + public virtual byte StatusType + { + get { return mStatusType; } + } + + public virtual object Response + { + get { return mResponse; } + } + + public virtual OcspResponse GetOcspResponse() + { + if (!IsCorrectType(CertificateStatusType.ocsp, mResponse)) + throw new InvalidOperationException("'response' is not an OcspResponse"); + + return (OcspResponse)mResponse; + } + + /** + * Encode this {@link CertificateStatus} to a {@link Stream}. + * + * @param output + * the {@link Stream} to encode to. + * @throws IOException + */ + public virtual void Encode(Stream output) + { + TlsUtilities.WriteUint8(mStatusType, output); + + switch (mStatusType) + { + case CertificateStatusType.ocsp: + byte[] derEncoding = ((OcspResponse)mResponse).GetEncoded(Asn1Encodable.Der); + TlsUtilities.WriteOpaque24(derEncoding, output); + break; + default: + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + /** + * Parse a {@link CertificateStatus} from a {@link Stream}. + * + * @param input + * the {@link Stream} to parse from. + * @return a {@link CertificateStatus} object. + * @throws IOException + */ + public static CertificateStatus Parse(Stream input) + { + byte status_type = TlsUtilities.ReadUint8(input); + object response; + + switch (status_type) + { + case CertificateStatusType.ocsp: + { + byte[] derEncoding = TlsUtilities.ReadOpaque24(input); + response = OcspResponse.GetInstance(TlsUtilities.ReadDerObject(derEncoding)); + break; + } + default: + throw new TlsFatalAlert(AlertDescription.decode_error); + } + + return new CertificateStatus(status_type, response); + } + + protected static bool IsCorrectType(byte statusType, object response) + { + switch (statusType) + { + case CertificateStatusType.ocsp: + return response is OcspResponse; + default: + throw new ArgumentException("unsupported value", "statusType"); + } + } + } +} diff --git a/crypto/src/crypto/tls/CertificateStatusRequest.cs b/crypto/src/crypto/tls/CertificateStatusRequest.cs new file mode 100644
index 000000000..9587d7df8 --- /dev/null +++ b/crypto/src/crypto/tls/CertificateStatusRequest.cs
@@ -0,0 +1,95 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class CertificateStatusRequest + { + protected readonly byte mStatusType; + protected readonly object mRequest; + + public CertificateStatusRequest(byte statusType, Object request) + { + if (!IsCorrectType(statusType, request)) + throw new ArgumentException("not an instance of the correct type", "request"); + + this.mStatusType = statusType; + this.mRequest = request; + } + + public virtual byte StatusType + { + get { return mStatusType; } + } + + public virtual object Request + { + get { return mRequest; } + } + + public virtual OcspStatusRequest GetOcspStatusRequest() + { + if (!IsCorrectType(CertificateStatusType.ocsp, mRequest)) + throw new InvalidOperationException("'request' is not an OCSPStatusRequest"); + + return (OcspStatusRequest)mRequest; + } + + /** + * Encode this {@link CertificateStatusRequest} to a {@link Stream}. + * + * @param output + * the {@link Stream} to encode to. + * @throws IOException + */ + public virtual void Encode(Stream output) + { + TlsUtilities.WriteUint8(mStatusType, output); + + switch (mStatusType) + { + case CertificateStatusType.ocsp: + ((OcspStatusRequest)mRequest).Encode(output); + break; + default: + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + /** + * Parse a {@link CertificateStatusRequest} from a {@link Stream}. + * + * @param input + * the {@link Stream} to parse from. + * @return a {@link CertificateStatusRequest} object. + * @throws IOException + */ + public static CertificateStatusRequest Parse(Stream input) + { + byte status_type = TlsUtilities.ReadUint8(input); + object result; + + switch (status_type) + { + case CertificateStatusType.ocsp: + result = OcspStatusRequest.Parse(input); + break; + default: + throw new TlsFatalAlert(AlertDescription.decode_error); + } + + return new CertificateStatusRequest(status_type, result); + } + + protected static bool IsCorrectType(byte statusType, object request) + { + switch (statusType) + { + case CertificateStatusType.ocsp: + return request is OcspStatusRequest; + default: + throw new ArgumentException("unsupported value", "statusType"); + } + } + } +} diff --git a/crypto/src/crypto/tls/CertificateStatusType.cs b/crypto/src/crypto/tls/CertificateStatusType.cs new file mode 100644
index 000000000..54b741b42 --- /dev/null +++ b/crypto/src/crypto/tls/CertificateStatusType.cs
@@ -0,0 +1,12 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class CertificateStatusType + { + /* + * RFC 3546 3.6 + */ + public const byte ocsp = 1; + } +} diff --git a/crypto/src/crypto/tls/CertificateUrl.cs b/crypto/src/crypto/tls/CertificateUrl.cs new file mode 100644
index 000000000..a951b8063 --- /dev/null +++ b/crypto/src/crypto/tls/CertificateUrl.cs
@@ -0,0 +1,124 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /* + * RFC 3546 3.3 + */ + public class CertificateUrl + { + protected readonly byte mType; + protected readonly IList mUrlAndHashList; + + /** + * @param type + * see {@link CertChainType} for valid constants. + * @param urlAndHashList + * a {@link IList} of {@link UrlAndHash}. + */ + public CertificateUrl(byte type, IList urlAndHashList) + { + if (!CertChainType.IsValid(type)) + throw new ArgumentException("not a valid CertChainType value", "type"); + if (urlAndHashList == null || urlAndHashList.Count < 1) + throw new ArgumentException("must have length > 0", "urlAndHashList"); + + this.mType = type; + this.mUrlAndHashList = urlAndHashList; + } + + /** + * @return {@link CertChainType} + */ + public virtual byte Type + { + get { return mType; } + } + + /** + * @return an {@link IList} of {@link UrlAndHash} + */ + public virtual IList UrlAndHashList + { + get { return mUrlAndHashList; } + } + + /** + * Encode this {@link CertificateUrl} to a {@link Stream}. + * + * @param output the {@link Stream} to encode to. + * @throws IOException + */ + public virtual void Encode(Stream output) + { + TlsUtilities.WriteUint8(this.mType, output); + + ListBuffer16 buf = new ListBuffer16(); + foreach (UrlAndHash urlAndHash in this.mUrlAndHashList) + { + urlAndHash.Encode(buf); + } + buf.EncodeTo(output); + } + + /** + * Parse a {@link CertificateUrl} from a {@link Stream}. + * + * @param context + * the {@link TlsContext} of the current connection. + * @param input + * the {@link Stream} to parse from. + * @return a {@link CertificateUrl} object. + * @throws IOException + */ + public static CertificateUrl parse(TlsContext context, Stream input) + { + byte type = TlsUtilities.ReadUint8(input); + if (!CertChainType.IsValid(type)) + throw new TlsFatalAlert(AlertDescription.decode_error); + + int totalLength = TlsUtilities.ReadUint16(input); + if (totalLength < 1) + throw new TlsFatalAlert(AlertDescription.decode_error); + + byte[] urlAndHashListData = TlsUtilities.ReadFully(totalLength, input); + + MemoryStream buf = new MemoryStream(urlAndHashListData, false); + + IList url_and_hash_list = Platform.CreateArrayList(); + while (buf.Position < buf.Length) + { + UrlAndHash url_and_hash = UrlAndHash.Parse(context, buf); + url_and_hash_list.Add(url_and_hash); + } + + return new CertificateUrl(type, url_and_hash_list); + } + + // TODO Could be more generally useful + internal class ListBuffer16 + : MemoryStream + { + internal ListBuffer16() + { + // Reserve space for length + TlsUtilities.WriteUint16(0, this); + } + + internal void EncodeTo(Stream output) + { + // Patch actual length back in + long length = Length - 2; + TlsUtilities.CheckUint16(length); + this.Position = 0; + TlsUtilities.WriteUint16((int)length, this); + this.WriteTo(output); + this.Close(); + } + } + } +} diff --git a/crypto/src/crypto/tls/Chacha20Poly1305.cs b/crypto/src/crypto/tls/Chacha20Poly1305.cs new file mode 100644
index 000000000..e4e4c7ee2 --- /dev/null +++ b/crypto/src/crypto/tls/Chacha20Poly1305.cs
@@ -0,0 +1,153 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Macs; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class Chacha20Poly1305 + : TlsCipher + { + protected readonly TlsContext context; + + protected readonly ChaChaEngine encryptCipher; + protected readonly ChaChaEngine decryptCipher; + + /// <exception cref="IOException"></exception> + public Chacha20Poly1305(TlsContext context) + { + if (!TlsUtilities.IsTlsV12(context)) + throw new TlsFatalAlert(AlertDescription.internal_error); + + this.context = context; + + byte[] key_block = TlsUtilities.CalculateKeyBlock(context, 64); + + KeyParameter client_write_key = new KeyParameter(key_block, 0, 32); + KeyParameter server_write_key = new KeyParameter(key_block, 32, 32); + + this.encryptCipher = new ChaChaEngine(20); + this.decryptCipher = new ChaChaEngine(20); + + KeyParameter encryptKey, decryptKey; + if (context.IsServer) + { + encryptKey = server_write_key; + decryptKey = client_write_key; + } + else + { + encryptKey = client_write_key; + decryptKey = server_write_key; + } + + byte[] dummyNonce = new byte[8]; + + this.encryptCipher.Init(true, new ParametersWithIV(encryptKey, dummyNonce)); + this.decryptCipher.Init(false, new ParametersWithIV(decryptKey, dummyNonce)); + } + + public virtual int GetPlaintextLimit(int ciphertextLimit) + { + return ciphertextLimit - 16; + } + + /// <exception cref="IOException"></exception> + public virtual byte[] EncodePlaintext(long seqNo, byte type, byte[] plaintext, int offset, int len) + { + int ciphertextLength = len + 16; + + KeyParameter macKey = InitRecordMac(encryptCipher, true, seqNo); + + byte[] output = new byte[ciphertextLength]; + encryptCipher.ProcessBytes(plaintext, offset, len, output, 0); + + byte[] additionalData = GetAdditionalData(seqNo, type, len); + byte[] mac = CalculateRecordMac(macKey, additionalData, output, 0, len); + Array.Copy(mac, 0, output, len, mac.Length); + + return output; + } + + /// <exception cref="IOException"></exception> + public virtual byte[] DecodeCiphertext(long seqNo, byte type, byte[] ciphertext, int offset, int len) + { + if (GetPlaintextLimit(len) < 0) + throw new TlsFatalAlert(AlertDescription.decode_error); + + int plaintextLength = len - 16; + + byte[] receivedMAC = Arrays.CopyOfRange(ciphertext, offset + plaintextLength, offset + len); + + KeyParameter macKey = InitRecordMac(decryptCipher, false, seqNo); + + byte[] additionalData = GetAdditionalData(seqNo, type, plaintextLength); + byte[] calculatedMAC = CalculateRecordMac(macKey, additionalData, ciphertext, offset, plaintextLength); + + if (!Arrays.ConstantTimeAreEqual(calculatedMAC, receivedMAC)) + throw new TlsFatalAlert(AlertDescription.bad_record_mac); + + byte[] output = new byte[plaintextLength]; + decryptCipher.ProcessBytes(ciphertext, offset, plaintextLength, output, 0); + + return output; + } + + protected virtual KeyParameter InitRecordMac(ChaChaEngine cipher, bool forEncryption, long seqNo) + { + byte[] nonce = new byte[8]; + TlsUtilities.WriteUint64(seqNo, nonce, 0); + + cipher.Init(forEncryption, new ParametersWithIV(null, nonce)); + + byte[] firstBlock = new byte[64]; + cipher.ProcessBytes(firstBlock, 0, firstBlock.Length, firstBlock, 0); + + // NOTE: The BC implementation puts 'r' after 'k' + Array.Copy(firstBlock, 0, firstBlock, 32, 16); + KeyParameter macKey = new KeyParameter(firstBlock, 16, 32); + Poly1305KeyGenerator.Clamp(macKey.GetKey()); + return macKey; + } + + protected virtual byte[] CalculateRecordMac(KeyParameter macKey, byte[] additionalData, byte[] buf, int off, int len) + { + IMac mac = new Poly1305(); + mac.Init(macKey); + + UpdateRecordMac(mac, additionalData, 0, additionalData.Length); + UpdateRecordMac(mac, buf, off, len); + return MacUtilities.DoFinal(mac); + } + + protected virtual void UpdateRecordMac(IMac mac, byte[] buf, int off, int len) + { + mac.BlockUpdate(buf, off, len); + + byte[] longLen = Pack.UInt64_To_LE((ulong)len); + mac.BlockUpdate(longLen, 0, longLen.Length); + } + + /// <exception cref="IOException"></exception> + protected virtual byte[] GetAdditionalData(long seqNo, byte type, int len) + { + /* + * additional_data = seq_num + TLSCompressed.type + TLSCompressed.version + + * TLSCompressed.length + */ + byte[] additional_data = new byte[13]; + TlsUtilities.WriteUint64(seqNo, additional_data, 0); + TlsUtilities.WriteUint8(type, additional_data, 8); + TlsUtilities.WriteVersion(context.ServerVersion, additional_data, 9); + TlsUtilities.WriteUint16(len, additional_data, 11); + + return additional_data; + } + } +} diff --git a/crypto/src/crypto/tls/ChangeCipherSpec.cs b/crypto/src/crypto/tls/ChangeCipherSpec.cs new file mode 100644
index 000000000..323de9162 --- /dev/null +++ b/crypto/src/crypto/tls/ChangeCipherSpec.cs
@@ -0,0 +1,9 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class ChangeCipherSpec + { + public const byte change_cipher_spec = 1; + } +} diff --git a/crypto/src/crypto/tls/CipherSuite.cs b/crypto/src/crypto/tls/CipherSuite.cs
index 6e1f7a545..f034ab802 100644 --- a/crypto/src/crypto/tls/CipherSuite.cs +++ b/crypto/src/crypto/tls/CipherSuite.cs
@@ -1,136 +1,352 @@ 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, - } + /// <summary> + /// RFC 2246 A.5 + /// </summary> + public abstract class CipherSuite + { + public const int TLS_NULL_WITH_NULL_NULL = 0x0000; + public const int TLS_RSA_WITH_NULL_MD5 = 0x0001; + public const int TLS_RSA_WITH_NULL_SHA = 0x0002; + public const int TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x0003; + public const int TLS_RSA_WITH_RC4_128_MD5 = 0x0004; + public const int TLS_RSA_WITH_RC4_128_SHA = 0x0005; + public const int TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0x0006; + public const int TLS_RSA_WITH_IDEA_CBC_SHA = 0x0007; + public const int TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0008; + public const int TLS_RSA_WITH_DES_CBC_SHA = 0x0009; + public const int TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A; + public const int TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x000B; + public const int TLS_DH_DSS_WITH_DES_CBC_SHA = 0x000C; + public const int TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x000D; + public const int TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x000E; + public const int TLS_DH_RSA_WITH_DES_CBC_SHA = 0x000F; + public const int TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x0010; + public const int TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x0011; + public const int TLS_DHE_DSS_WITH_DES_CBC_SHA = 0x0012; + public const int TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x0013; + public const int TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0014; + public const int TLS_DHE_RSA_WITH_DES_CBC_SHA = 0x0015; + public const int TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x0016; + public const int TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x0017; + public const int TLS_DH_anon_WITH_RC4_128_MD5 = 0x0018; + public const int TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0x0019; + public const int TLS_DH_anon_WITH_DES_CBC_SHA = 0x001A; + public const int TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0x001B; + + /* + * Note: The cipher suite values { 0x00, 0x1C } and { 0x00, 0x1D } are reserved to avoid + * collision with Fortezza-based cipher suites in SSL 3. + */ + + /* + * RFC 3268 + */ + public const int TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F; + public const int TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0x0030; + public const int TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0x0031; + public const int TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x0032; + public const int TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033; + public const int TLS_DH_anon_WITH_AES_128_CBC_SHA = 0x0034; + public const int TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035; + public const int TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0x0036; + public const int TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0x0037; + public const int TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x0038; + public const int TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039; + public const int TLS_DH_anon_WITH_AES_256_CBC_SHA = 0x003A; + + /* + * RFC 5932 + */ + public const int TLS_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0041; + public const int TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0042; + public const int TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0043; + public const int TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0044; + public const int TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0045; + public const int TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA = 0x0046; + + public const int TLS_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0084; + public const int TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0085; + public const int TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0086; + public const int TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0087; + public const int TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0088; + public const int TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA = 0x0089; + + public const int TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BA; + public const int TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BB; + public const int TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BC; + public const int TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BD; + public const int TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BE; + public const int TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BF; + + public const int TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C0; + public const int TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C1; + public const int TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C2; + public const int TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C3; + public const int TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C4; + public const int TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C5; + + /* + * RFC 4162 + */ + public const int TLS_RSA_WITH_SEED_CBC_SHA = 0x0096; + public const int TLS_DH_DSS_WITH_SEED_CBC_SHA = 0x0097; + public const int TLS_DH_RSA_WITH_SEED_CBC_SHA = 0x0098; + public const int TLS_DHE_DSS_WITH_SEED_CBC_SHA = 0x0099; + public const int TLS_DHE_RSA_WITH_SEED_CBC_SHA = 0x009A; + public const int TLS_DH_anon_WITH_SEED_CBC_SHA = 0x009B; + + /* + * RFC 4279 + */ + public const int TLS_PSK_WITH_RC4_128_SHA = 0x008A; + public const int TLS_PSK_WITH_3DES_EDE_CBC_SHA = 0x008B; + public const int TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C; + public const int TLS_PSK_WITH_AES_256_CBC_SHA = 0x008D; + public const int TLS_DHE_PSK_WITH_RC4_128_SHA = 0x008E; + public const int TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA = 0x008F; + public const int TLS_DHE_PSK_WITH_AES_128_CBC_SHA = 0x0090; + public const int TLS_DHE_PSK_WITH_AES_256_CBC_SHA = 0x0091; + public const int TLS_RSA_PSK_WITH_RC4_128_SHA = 0x0092; + public const int TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA = 0x0093; + public const int TLS_RSA_PSK_WITH_AES_128_CBC_SHA = 0x0094; + public const int TLS_RSA_PSK_WITH_AES_256_CBC_SHA = 0x0095; + + /* + * RFC 4492 + */ + public const int TLS_ECDH_ECDSA_WITH_NULL_SHA = 0xC001; + public const int TLS_ECDH_ECDSA_WITH_RC4_128_SHA = 0xC002; + public const int TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC003; + public const int TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA = 0xC004; + public const int TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA = 0xC005; + public const int TLS_ECDHE_ECDSA_WITH_NULL_SHA = 0xC006; + public const int TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = 0xC007; + public const int TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC008; + public const int TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009; + public const int TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A; + public const int TLS_ECDH_RSA_WITH_NULL_SHA = 0xC00B; + public const int TLS_ECDH_RSA_WITH_RC4_128_SHA = 0xC00C; + public const int TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA = 0xC00D; + public const int TLS_ECDH_RSA_WITH_AES_128_CBC_SHA = 0xC00E; + public const int TLS_ECDH_RSA_WITH_AES_256_CBC_SHA = 0xC00F; + public const int TLS_ECDHE_RSA_WITH_NULL_SHA = 0xC010; + public const int TLS_ECDHE_RSA_WITH_RC4_128_SHA = 0xC011; + public const int TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = 0xC012; + public const int TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013; + public const int TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014; + public const int TLS_ECDH_anon_WITH_NULL_SHA = 0xC015; + public const int TLS_ECDH_anon_WITH_RC4_128_SHA = 0xC016; + public const int TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA = 0xC017; + public const int TLS_ECDH_anon_WITH_AES_128_CBC_SHA = 0xC018; + public const int TLS_ECDH_anon_WITH_AES_256_CBC_SHA = 0xC019; + + /* + * RFC 4785 + */ + public const int TLS_PSK_WITH_NULL_SHA = 0x002C; + public const int TLS_DHE_PSK_WITH_NULL_SHA = 0x002D; + public const int TLS_RSA_PSK_WITH_NULL_SHA = 0x002E; + + /* + * RFC 5054 + */ + public const int TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA = 0xC01A; + public const int TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA = 0xC01B; + public const int TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA = 0xC01C; + public const int TLS_SRP_SHA_WITH_AES_128_CBC_SHA = 0xC01D; + public const int TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA = 0xC01E; + public const int TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA = 0xC01F; + public const int TLS_SRP_SHA_WITH_AES_256_CBC_SHA = 0xC020; + public const int TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA = 0xC021; + public const int TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA = 0xC022; + + /* + * RFC 5246 + */ + public const int TLS_RSA_WITH_NULL_SHA256 = 0x003B; + public const int TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x003C; + public const int TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x003D; + public const int TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = 0x003E; + public const int TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = 0x003F; + public const int TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x0040; + public const int TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067; + public const int TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = 0x0068; + public const int TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = 0x0069; + public const int TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x006A; + public const int TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B; + public const int TLS_DH_anon_WITH_AES_128_CBC_SHA256 = 0x006C; + public const int TLS_DH_anon_WITH_AES_256_CBC_SHA256 = 0x006D; + + /* + * RFC 5288 + */ + public const int TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C; + public const int TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009D; + public const int TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E; + public const int TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F; + public const int TLS_DH_RSA_WITH_AES_128_GCM_SHA256 = 0x00A0; + public const int TLS_DH_RSA_WITH_AES_256_GCM_SHA384 = 0x00A1; + public const int TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = 0x00A2; + public const int TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = 0x00A3; + public const int TLS_DH_DSS_WITH_AES_128_GCM_SHA256 = 0x00A4; + public const int TLS_DH_DSS_WITH_AES_256_GCM_SHA384 = 0x00A5; + public const int TLS_DH_anon_WITH_AES_128_GCM_SHA256 = 0x00A6; + public const int TLS_DH_anon_WITH_AES_256_GCM_SHA384 = 0x00A7; + + /* + * RFC 5289 + */ + public const int TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023; + public const int TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024; + public const int TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC025; + public const int TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC026; + public const int TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027; + public const int TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028; + public const int TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 = 0xC029; + public const int TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 = 0xC02A; + public const int TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B; + public const int TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C; + public const int TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02D; + public const int TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02E; + public const int TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F; + public const int TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030; + public const int TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 = 0xC031; + public const int TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 = 0xC032; + + /* + * RFC 5487 + */ + public const int TLS_PSK_WITH_AES_128_GCM_SHA256 = 0x00A8; + public const int TLS_PSK_WITH_AES_256_GCM_SHA384 = 0x00A9; + public const int TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 = 0x00AA; + public const int TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 = 0x00AB; + public const int TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 = 0x00AC; + public const int TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 = 0x00AD; + public const int TLS_PSK_WITH_AES_128_CBC_SHA256 = 0x00AE; + public const int TLS_PSK_WITH_AES_256_CBC_SHA384 = 0x00AF; + public const int TLS_PSK_WITH_NULL_SHA256 = 0x00B0; + public const int TLS_PSK_WITH_NULL_SHA384 = 0x00B1; + public const int TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 = 0x00B2; + public const int TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 = 0x00B3; + public const int TLS_DHE_PSK_WITH_NULL_SHA256 = 0x00B4; + public const int TLS_DHE_PSK_WITH_NULL_SHA384 = 0x00B5; + public const int TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 = 0x00B6; + public const int TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 = 0x00B7; + public const int TLS_RSA_PSK_WITH_NULL_SHA256 = 0x00B8; + public const int TLS_RSA_PSK_WITH_NULL_SHA384 = 0x00B9; + + /* + * RFC 5489 + */ + public const int TLS_ECDHE_PSK_WITH_RC4_128_SHA = 0xC033; + public const int TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA = 0xC034; + public const int TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA = 0xC035; + public const int TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA = 0xC036; + public const int TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 = 0xC037; + public const int TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 = 0xC038; + public const int TLS_ECDHE_PSK_WITH_NULL_SHA = 0xC039; + public const int TLS_ECDHE_PSK_WITH_NULL_SHA256 = 0xC03A; + public const int TLS_ECDHE_PSK_WITH_NULL_SHA384 = 0xC03B; + + /* + * RFC 5746 + */ + public const int TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF; + + /* + * RFC 6367 + */ + public const int TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 = 0xC072; + public const int TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 = 0xC073; + public const int TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 = 0xC074; + public const int TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 = 0xC075; + public const int TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0xC076; + public const int TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 = 0xC077; + public const int TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0xC078; + public const int TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 = 0xC079; + + public const int TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC07A; + public const int TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC07B; + public const int TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC07C; + public const int TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC07D; + public const int TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC07E; + public const int TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC07F; + public const int TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256 = 0xC080; + public const int TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384 = 0xC081; + public const int TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256 = 0xC082; + public const int TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384 = 0xC083; + public const int TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256 = 0xC084; + public const int TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384 = 0xC085; + public const int TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC086; + public const int TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC087; + public const int TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC088; + public const int TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC089; + public const int TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC08A; + public const int TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC08B; + public const int TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC08C; + public const int TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC08D; + + public const int TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 = 0xC08E; + public const int TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 = 0xC08F; + public const int TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 = 0xC090; + public const int TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 = 0xC091; + public const int TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 = 0xC092; + public const int TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 = 0xC093; + public const int TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 = 0xC094; + public const int TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 = 0xC095; + public const int TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 = 0xC096; + public const int TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 = 0xC097; + public const int TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 = 0xC098; + public const int TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 = 0xC099; + public const int TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 = 0xC09A; + public const int TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 = 0xC09B; + + /* + * RFC 6655 + */ + public const int TLS_RSA_WITH_AES_128_CCM = 0xC09C; + public const int TLS_RSA_WITH_AES_256_CCM = 0xC09D; + public const int TLS_DHE_RSA_WITH_AES_128_CCM = 0xC09E; + public const int TLS_DHE_RSA_WITH_AES_256_CCM = 0xC09F; + public const int TLS_RSA_WITH_AES_128_CCM_8 = 0xC0A0; + public const int TLS_RSA_WITH_AES_256_CCM_8 = 0xC0A1; + public const int TLS_DHE_RSA_WITH_AES_128_CCM_8 = 0xC0A2; + public const int TLS_DHE_RSA_WITH_AES_256_CCM_8 = 0xC0A3; + public const int TLS_PSK_WITH_AES_128_CCM = 0xC0A4; + public const int TLS_PSK_WITH_AES_256_CCM = 0xC0A5; + public const int TLS_DHE_PSK_WITH_AES_128_CCM = 0xC0A6; + public const int TLS_DHE_PSK_WITH_AES_256_CCM = 0xC0A7; + public const int TLS_PSK_WITH_AES_128_CCM_8 = 0xC0A8; + public const int TLS_PSK_WITH_AES_256_CCM_8 = 0xC0A9; + public const int TLS_PSK_DHE_WITH_AES_128_CCM_8 = 0xC0AA; + public const int TLS_PSK_DHE_WITH_AES_256_CCM_8 = 0xC0AB; + + /* + * draft-agl-tls-chacha20poly1305-04 + */ + public const int TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCC13; + public const int TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCC14; + public const int TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCC15; + + /* + * draft-josefsson-salsa20-tls-04 + */ + public const int TLS_RSA_WITH_ESTREAM_SALSA20_SHA1 = 0xE410; + public const int TLS_RSA_WITH_SALSA20_SHA1 = 0xE411; + public const int TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1 = 0xE412; + public const int TLS_ECDHE_RSA_WITH_SALSA20_SHA1 = 0xE413; + public const int TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1 = 0xE414; + public const int TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1 = 0xE415; + public const int TLS_PSK_WITH_ESTREAM_SALSA20_SHA1 = 0xE416; + public const int TLS_PSK_WITH_SALSA20_SHA1 = 0xE417; + public const int TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1 = 0xE418; + public const int TLS_ECDHE_PSK_WITH_SALSA20_SHA1 = 0xE419; + public const int TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1 = 0xE41A; + public const int TLS_RSA_PSK_WITH_SALSA20_SHA1 = 0xE41B; + public const int TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1 = 0xE41C; + public const int TLS_DHE_PSK_WITH_SALSA20_SHA1 = 0xE41D; + public const int TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1 = 0xE41E; + public const int TLS_DHE_RSA_WITH_SALSA20_SHA1 = 0xE41F; + } } diff --git a/crypto/src/crypto/tls/CipherType.cs b/crypto/src/crypto/tls/CipherType.cs new file mode 100644
index 000000000..b2ad7d8e1 --- /dev/null +++ b/crypto/src/crypto/tls/CipherType.cs
@@ -0,0 +1,20 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <summary>RFC 2246</summary> + /// <remarks> + /// Note that the values here are implementation-specific and arbitrary. It is recommended not to + /// depend on the particular values (e.g. serialization). + /// </remarks> + public abstract class CipherType + { + public const int stream = 0; + public const int block = 1; + + /* + * RFC 5246 + */ + public const int aead = 2; + } +} diff --git a/crypto/src/crypto/tls/ClientAuthenticationType.cs b/crypto/src/crypto/tls/ClientAuthenticationType.cs new file mode 100644
index 000000000..dd248f3df --- /dev/null +++ b/crypto/src/crypto/tls/ClientAuthenticationType.cs
@@ -0,0 +1,14 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class ClientAuthenticationType + { + /* + * RFC 5077 4 + */ + public const byte anonymous = 0; + public const byte certificate_based = 1; + public const byte psk = 2; + } +} diff --git a/crypto/src/crypto/tls/ClientCertificateType.cs b/crypto/src/crypto/tls/ClientCertificateType.cs
index 58f5d4276..a291a46e6 100644 --- a/crypto/src/crypto/tls/ClientCertificateType.cs +++ b/crypto/src/crypto/tls/ClientCertificateType.cs
@@ -1,20 +1,23 @@ 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, + public abstract class ClientCertificateType + { + /* + * RFC 4346 7.4.4 + */ + public const byte rsa_sign = 1; + public const byte dss_sign = 2; + public const byte rsa_fixed_dh = 3; + public const byte dss_fixed_dh = 4; + public const byte rsa_ephemeral_dh_RESERVED = 5; + public const byte dss_ephemeral_dh_RESERVED = 6; + public const byte fortezza_dms_RESERVED = 20; - /* - * RFC 4492 5.5 - */ - ecdsa_sign = 64, - rsa_fixed_ecdh = 65, - ecdsa_fixed_ecdh = 66, - } -} \ No newline at end of file + /* + * RFC 4492 5.5 + */ + public const byte ecdsa_sign = 64; + public const byte rsa_fixed_ecdh = 65; + public const byte ecdsa_fixed_ecdh = 66; + } +} diff --git a/crypto/src/crypto/tls/CombinedHash.cs b/crypto/src/crypto/tls/CombinedHash.cs
index 59ad87a7b..74a52d598 100644 --- a/crypto/src/crypto/tls/CombinedHash.cs +++ b/crypto/src/crypto/tls/CombinedHash.cs
@@ -1,82 +1,133 @@ using System; -using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Security; 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(); - } - } + /** + * A combined hash, which implements md5(m) || sha1(m). + */ + internal class CombinedHash + : TlsHandshakeHash + { + protected TlsContext mContext; + protected IDigest mMd5; + protected IDigest mSha1; + + internal CombinedHash() + { + this.mMd5 = TlsUtilities.CreateHash(HashAlgorithm.md5); + this.mSha1 = TlsUtilities.CreateHash(HashAlgorithm.sha1); + } + + internal CombinedHash(CombinedHash t) + { + this.mContext = t.mContext; + this.mMd5 = TlsUtilities.CloneHash(HashAlgorithm.md5, t.mMd5); + this.mSha1 = TlsUtilities.CloneHash(HashAlgorithm.sha1, t.mSha1); + } + + public virtual void Init(TlsContext context) + { + this.mContext = context; + } + + public virtual TlsHandshakeHash NotifyPrfDetermined() + { + return this; + } + + public virtual void TrackHashAlgorithm(byte hashAlgorithm) + { + throw new InvalidOperationException("CombinedHash only supports calculating the legacy PRF for handshake hash"); + } + + public virtual void SealHashAlgorithms() + { + } + + public virtual TlsHandshakeHash StopTracking() + { + return new CombinedHash(this); + } + + public virtual IDigest ForkPrfHash() + { + return new CombinedHash(this); + } + + public virtual byte[] GetFinalHash(byte hashAlgorithm) + { + throw new InvalidOperationException("CombinedHash doesn't support multiple hashes"); + } + + public virtual string AlgorithmName + { + get { return mMd5.AlgorithmName + " and " + mSha1.AlgorithmName; } + } + + public virtual int GetByteLength() + { + return System.Math.Max(mMd5.GetByteLength(), mSha1.GetByteLength()); + } + + public virtual int GetDigestSize() + { + return mMd5.GetDigestSize() + mSha1.GetDigestSize(); + } + + public virtual void Update(byte input) + { + mMd5.Update(input); + mSha1.Update(input); + } + + /** + * @see org.bouncycastle.crypto.Digest#update(byte[], int, int) + */ + public virtual void BlockUpdate(byte[] input, int inOff, int len) + { + mMd5.BlockUpdate(input, inOff, len); + mSha1.BlockUpdate(input, inOff, len); + } + + /** + * @see org.bouncycastle.crypto.Digest#doFinal(byte[], int) + */ + public virtual int DoFinal(byte[] output, int outOff) + { + if (mContext != null && TlsUtilities.IsSsl(mContext)) + { + Ssl3Complete(mMd5, Ssl3Mac.IPAD, Ssl3Mac.OPAD, 48); + Ssl3Complete(mSha1, Ssl3Mac.IPAD, Ssl3Mac.OPAD, 40); + } + + int i1 = mMd5.DoFinal(output, outOff); + int i2 = mSha1.DoFinal(output, outOff + i1); + return i1 + i2; + } + + /** + * @see org.bouncycastle.crypto.Digest#reset() + */ + public virtual void Reset() + { + mMd5.Reset(); + mSha1.Reset(); + } + + protected virtual void Ssl3Complete(IDigest d, byte[] ipad, byte[] opad, int padLength) + { + byte[] master_secret = mContext.SecurityParameters.masterSecret; + + d.BlockUpdate(master_secret, 0, master_secret.Length); + d.BlockUpdate(ipad, 0, padLength); + + byte[] tmp = DigestUtilities.DoFinal(d); + + d.BlockUpdate(master_secret, 0, master_secret.Length); + d.BlockUpdate(opad, 0, padLength); + d.BlockUpdate(tmp, 0, tmp.Length); + } + } } diff --git a/crypto/src/crypto/tls/CompressionMethod.cs b/crypto/src/crypto/tls/CompressionMethod.cs
index 4a127a63e..89c1f5ff4 100644 --- a/crypto/src/crypto/tls/CompressionMethod.cs +++ b/crypto/src/crypto/tls/CompressionMethod.cs
@@ -1,20 +1,22 @@ +using System; + namespace Org.BouncyCastle.Crypto.Tls { - /// <summary> - /// RFC 2246 6.1 - /// </summary> - public enum CompressionMethod : byte - { - NULL = 0, + /// <summary> + /// RFC 2246 6.1 + /// </summary> + public abstract class CompressionMethod + { + public const byte cls_null = 0; - /* - * RFC 3749 2 - */ - DEFLATE = 1 + /* + * RFC 3749 2 + */ + public const byte DEFLATE = 1; - /* - * Values from 224 decimal (0xE0) through 255 decimal (0xFF) - * inclusive are reserved for private use. - */ - } + /* + * Values from 224 decimal (0xE0) through 255 decimal (0xFF) + * inclusive are reserved for private use. + */ + } } diff --git a/crypto/src/crypto/tls/ConnectionEnd.cs b/crypto/src/crypto/tls/ConnectionEnd.cs new file mode 100644
index 000000000..afc9460f2 --- /dev/null +++ b/crypto/src/crypto/tls/ConnectionEnd.cs
@@ -0,0 +1,15 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <summary>RFC 2246</summary> + /// <remarks> + /// Note that the values here are implementation-specific and arbitrary. It is recommended not to + /// depend on the particular values (e.g. serialization). + /// </remarks> + public abstract class ConnectionEnd + { + public const int server = 0; + public const int client = 1; + } +} diff --git a/crypto/src/crypto/tls/ContentType.cs b/crypto/src/crypto/tls/ContentType.cs
index a664e3a38..d6ab43857 100644 --- a/crypto/src/crypto/tls/ContentType.cs +++ b/crypto/src/crypto/tls/ContentType.cs
@@ -1,13 +1,14 @@ 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, - } + /** + * RFC 2246 6.2.1 + */ + public abstract class ContentType + { + public const byte change_cipher_spec = 20; + public const byte alert = 21; + public const byte handshake = 22; + public const byte application_data = 23; + public const byte heartbeat = 24; + } } diff --git a/crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs b/crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs
index 2dfe526d1..5147a1990 100644 --- a/crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs +++ b/crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs
@@ -1,4 +1,5 @@ using System; +using System.IO; using Org.BouncyCastle.Crypto.Agreement; using Org.BouncyCastle.Crypto.Parameters; @@ -7,61 +8,62 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Tls { - public class DefaultTlsAgreementCredentials - : TlsAgreementCredentials - { - protected Certificate clientCert; - protected AsymmetricKeyParameter clientPrivateKey; + public class DefaultTlsAgreementCredentials + : AbstractTlsAgreementCredentials + { + protected readonly Certificate mCertificate; + protected readonly AsymmetricKeyParameter mPrivateKey; - protected IBasicAgreement basicAgreement; + protected readonly IBasicAgreement mBasicAgreement; + protected readonly bool mTruncateAgreement; - 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"); - } + public DefaultTlsAgreementCredentials(Certificate certificate, AsymmetricKeyParameter privateKey) + { + if (certificate == null) + throw new ArgumentNullException("certificate"); + if (certificate.IsEmpty) + throw new ArgumentException("cannot be empty", "certificate"); + if (privateKey == null) + throw new ArgumentNullException("privateKey"); + if (!privateKey.IsPrivate) + throw new ArgumentException("must be private", "privateKey"); - 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"); - } + if (privateKey is DHPrivateKeyParameters) + { + mBasicAgreement = new DHBasicAgreement(); + mTruncateAgreement = true; + } + else if (privateKey is ECPrivateKeyParameters) + { + mBasicAgreement = new ECDHBasicAgreement(); + mTruncateAgreement = false; + } + else + { + throw new ArgumentException("type not supported: " + privateKey.GetType().FullName, "privateKey"); + } - this.clientCert = clientCertificate; - this.clientPrivateKey = clientPrivateKey; - } + this.mCertificate = certificate; + this.mPrivateKey = privateKey; + } - public virtual Certificate Certificate - { - get { return clientCert; } - } + public override Certificate Certificate + { + get { return mCertificate; } + } - public virtual byte[] GenerateAgreement(AsymmetricKeyParameter serverPublicKey) - { - basicAgreement.Init(clientPrivateKey); - BigInteger agreementValue = basicAgreement.CalculateAgreement(serverPublicKey); - return BigIntegers.AsUnsignedByteArray(agreementValue); - } - } + /// <exception cref="IOException"></exception> + public override byte[] GenerateAgreement(AsymmetricKeyParameter peerPublicKey) + { + mBasicAgreement.Init(mPrivateKey); + BigInteger agreementValue = mBasicAgreement.CalculateAgreement(peerPublicKey); + + if (mTruncateAgreement) + { + return BigIntegers.AsUnsignedByteArray(agreementValue); + } + + return BigIntegers.AsUnsignedByteArray(mBasicAgreement.GetFieldSize(), agreementValue); + } + } } diff --git a/crypto/src/crypto/tls/DefaultTlsCipherFactory.cs b/crypto/src/crypto/tls/DefaultTlsCipherFactory.cs
index 53e3438d9..7c4213c25 100644 --- a/crypto/src/crypto/tls/DefaultTlsCipherFactory.cs +++ b/crypto/src/crypto/tls/DefaultTlsCipherFactory.cs
@@ -1,73 +1,225 @@ 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); - } - } - } + public class DefaultTlsCipherFactory + : AbstractTlsCipherFactory + { + /// <exception cref="IOException"></exception> + public override TlsCipher CreateCipher(TlsContext context, int encryptionAlgorithm, int macAlgorithm) + { + switch (encryptionAlgorithm) + { + case EncryptionAlgorithm.cls_3DES_EDE_CBC: + return CreateDesEdeCipher(context, macAlgorithm); + case EncryptionAlgorithm.AEAD_CHACHA20_POLY1305: + // NOTE: Ignores macAlgorithm + return CreateChaCha20Poly1305(context); + case EncryptionAlgorithm.AES_128_CBC: + return CreateAESCipher(context, 16, macAlgorithm); + case EncryptionAlgorithm.AES_128_CCM: + // NOTE: Ignores macAlgorithm + return CreateCipher_Aes_Ccm(context, 16, 16); + case EncryptionAlgorithm.AES_128_CCM_8: + // NOTE: Ignores macAlgorithm + return CreateCipher_Aes_Ccm(context, 16, 8); + case EncryptionAlgorithm.AES_256_CCM: + // NOTE: Ignores macAlgorithm + return CreateCipher_Aes_Ccm(context, 32, 16); + case EncryptionAlgorithm.AES_256_CCM_8: + // NOTE: Ignores macAlgorithm + return CreateCipher_Aes_Ccm(context, 32, 8); + case EncryptionAlgorithm.AES_128_GCM: + // NOTE: Ignores macAlgorithm + return CreateCipher_Aes_Gcm(context, 16, 16); + case EncryptionAlgorithm.AES_256_CBC: + return CreateAESCipher(context, 32, macAlgorithm); + case EncryptionAlgorithm.AES_256_GCM: + // NOTE: Ignores macAlgorithm + return CreateCipher_Aes_Gcm(context, 32, 16); + case EncryptionAlgorithm.CAMELLIA_128_CBC: + return CreateCamelliaCipher(context, 16, macAlgorithm); + case EncryptionAlgorithm.CAMELLIA_128_GCM: + // NOTE: Ignores macAlgorithm + return CreateCipher_Camellia_Gcm(context, 16, 16); + case EncryptionAlgorithm.CAMELLIA_256_CBC: + return CreateCamelliaCipher(context, 32, macAlgorithm); + case EncryptionAlgorithm.CAMELLIA_256_GCM: + // NOTE: Ignores macAlgorithm + return CreateCipher_Camellia_Gcm(context, 32, 16); + case EncryptionAlgorithm.ESTREAM_SALSA20: + return CreateSalsa20Cipher(context, 12, 32, macAlgorithm); + case EncryptionAlgorithm.NULL: + return CreateNullCipher(context, macAlgorithm); + case EncryptionAlgorithm.RC4_128: + return CreateRC4Cipher(context, 16, macAlgorithm); + case EncryptionAlgorithm.SALSA20: + return CreateSalsa20Cipher(context, 20, 32, macAlgorithm); + case EncryptionAlgorithm.SEED_CBC: + return CreateSeedCipher(context, macAlgorithm); + default: + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + /// <exception cref="IOException"></exception> + protected virtual TlsBlockCipher CreateAESCipher(TlsContext context, int cipherKeySize, int macAlgorithm) + { + return new TlsBlockCipher(context, CreateAesBlockCipher(), CreateAesBlockCipher(), + CreateHMacDigest(macAlgorithm), CreateHMacDigest(macAlgorithm), cipherKeySize); + } + + /// <exception cref="IOException"></exception> + protected virtual TlsBlockCipher CreateCamelliaCipher(TlsContext context, int cipherKeySize, int macAlgorithm) + { + return new TlsBlockCipher(context, CreateCamelliaBlockCipher(), + CreateCamelliaBlockCipher(), CreateHMacDigest(macAlgorithm), + CreateHMacDigest(macAlgorithm), cipherKeySize); + } + + /// <exception cref="IOException"></exception> + protected virtual TlsCipher CreateChaCha20Poly1305(TlsContext context) + { + return new Chacha20Poly1305(context); + } + + /// <exception cref="IOException"></exception> + protected virtual TlsAeadCipher CreateCipher_Aes_Ccm(TlsContext context, int cipherKeySize, int macSize) + { + return new TlsAeadCipher(context, CreateAeadBlockCipher_Aes_Ccm(), + CreateAeadBlockCipher_Aes_Ccm(), cipherKeySize, macSize); + } + + /// <exception cref="IOException"></exception> + protected virtual TlsAeadCipher CreateCipher_Aes_Gcm(TlsContext context, int cipherKeySize, int macSize) + { + return new TlsAeadCipher(context, CreateAeadBlockCipher_Aes_Gcm(), + CreateAeadBlockCipher_Aes_Gcm(), cipherKeySize, macSize); + } + + /// <exception cref="IOException"></exception> + protected virtual TlsAeadCipher CreateCipher_Camellia_Gcm(TlsContext context, int cipherKeySize, int macSize) + { + return new TlsAeadCipher(context, CreateAeadBlockCipher_Camellia_Gcm(), + CreateAeadBlockCipher_Camellia_Gcm(), cipherKeySize, macSize); + } + + /// <exception cref="IOException"></exception> + protected virtual TlsBlockCipher CreateDesEdeCipher(TlsContext context, int macAlgorithm) + { + return new TlsBlockCipher(context, CreateDesEdeBlockCipher(), CreateDesEdeBlockCipher(), + CreateHMacDigest(macAlgorithm), CreateHMacDigest(macAlgorithm), 24); + } + + /// <exception cref="IOException"></exception> + protected virtual TlsNullCipher CreateNullCipher(TlsContext context, int macAlgorithm) + { + return new TlsNullCipher(context, CreateHMacDigest(macAlgorithm), + CreateHMacDigest(macAlgorithm)); + } + + /// <exception cref="IOException"></exception> + protected virtual TlsStreamCipher CreateRC4Cipher(TlsContext context, int cipherKeySize, int macAlgorithm) + { + return new TlsStreamCipher(context, CreateRC4StreamCipher(), CreateRC4StreamCipher(), + CreateHMacDigest(macAlgorithm), CreateHMacDigest(macAlgorithm), cipherKeySize, false); + } + + /// <exception cref="IOException"></exception> + protected virtual TlsStreamCipher CreateSalsa20Cipher(TlsContext context, int rounds, int cipherKeySize, int macAlgorithm) + { + return new TlsStreamCipher(context, CreateSalsa20StreamCipher(rounds), CreateSalsa20StreamCipher(rounds), + CreateHMacDigest(macAlgorithm), CreateHMacDigest(macAlgorithm), cipherKeySize, true); + } + + /// <exception cref="IOException"></exception> + protected virtual TlsBlockCipher CreateSeedCipher(TlsContext context, int macAlgorithm) + { + return new TlsBlockCipher(context, CreateSeedBlockCipher(), CreateSeedBlockCipher(), + CreateHMacDigest(macAlgorithm), CreateHMacDigest(macAlgorithm), 16); + } + + protected virtual IBlockCipher CreateAesEngine() + { + return new AesEngine(); + } + + protected virtual IBlockCipher CreateCamelliaEngine() + { + return new CamelliaEngine(); + } + + protected virtual IBlockCipher CreateAesBlockCipher() + { + return new CbcBlockCipher(CreateAesEngine()); + } + + protected virtual IAeadBlockCipher CreateAeadBlockCipher_Aes_Ccm() + { + return new CcmBlockCipher(CreateAesEngine()); + } + + protected virtual IAeadBlockCipher CreateAeadBlockCipher_Aes_Gcm() + { + // TODO Consider allowing custom configuration of multiplier + return new GcmBlockCipher(CreateAesEngine()); + } + + protected virtual IAeadBlockCipher CreateAeadBlockCipher_Camellia_Gcm() + { + // TODO Consider allowing custom configuration of multiplier + return new GcmBlockCipher(CreateCamelliaEngine()); + } + + protected virtual IBlockCipher CreateCamelliaBlockCipher() + { + return new CbcBlockCipher(CreateCamelliaEngine()); + } + + protected virtual IBlockCipher CreateDesEdeBlockCipher() + { + return new CbcBlockCipher(new DesEdeEngine()); + } + + protected virtual IStreamCipher CreateRC4StreamCipher() + { + return new RC4Engine(); + } + + protected virtual IStreamCipher CreateSalsa20StreamCipher(int rounds) + { + return new Salsa20Engine(rounds); + } + + protected virtual IBlockCipher CreateSeedBlockCipher() + { + return new CbcBlockCipher(new SeedEngine()); + } + + /// <exception cref="IOException"></exception> + protected virtual IDigest CreateHMacDigest(int macAlgorithm) + { + switch (macAlgorithm) + { + case MacAlgorithm.cls_null: + return null; + case MacAlgorithm.hmac_md5: + return TlsUtilities.CreateHash(HashAlgorithm.md5); + case MacAlgorithm.hmac_sha1: + return TlsUtilities.CreateHash(HashAlgorithm.sha1); + case MacAlgorithm.hmac_sha256: + return TlsUtilities.CreateHash(HashAlgorithm.sha256); + case MacAlgorithm.hmac_sha384: + return TlsUtilities.CreateHash(HashAlgorithm.sha384); + case MacAlgorithm.hmac_sha512: + return TlsUtilities.CreateHash(HashAlgorithm.sha512); + default: + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + } } diff --git a/crypto/src/crypto/tls/DefaultTlsClient.cs b/crypto/src/crypto/tls/DefaultTlsClient.cs
index c5b59a06b..a2a04a33c 100644 --- a/crypto/src/crypto/tls/DefaultTlsClient.cs +++ b/crypto/src/crypto/tls/DefaultTlsClient.cs
@@ -11,249 +11,451 @@ 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() + public abstract class DefaultTlsClient + : AbstractTlsClient + { + public DefaultTlsClient() + : base() { - /* - * To offer DEFLATE compression, override this method: - * return new CompressionMethod[] { CompressionMethod.DEFLATE, CompressionMethod.NULL }; - */ + } - return new CompressionMethod[] { CompressionMethod.NULL }; + public DefaultTlsClient(TlsCipherFactory cipherFactory) + : base(cipherFactory) + { } - public virtual IDictionary GetClientExtensions() - { - return null; - } + public override int[] GetCipherSuites() + { + return new int[] + { + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256, + CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, + }; + } - public virtual void NotifySessionID(byte[] sessionID) - { - // Currently ignored - } + public override TlsKeyExchange GetKeyExchange() + { + switch (mSelectedCipherSuite) + { + case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA: + return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_DSS); + + case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA: + return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_RSA); + + case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA: + return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_DSS); + + case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA: + return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_RSA); + + case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA: + return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_ECDSA); + + case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA: + return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_RSA); + + case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1: + return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_ECDSA); + + case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1: + return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_RSA); + + case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_RSA_WITH_NULL_MD5: + case CipherSuite.TLS_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_RSA_WITH_NULL_SHA256: + case CipherSuite.TLS_RSA_WITH_RC4_128_MD5: + case CipherSuite.TLS_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA: + return CreateRsaKeyExchange(); + + default: + /* + * Note: internal error here; the TlsProtocol implementation verifies that the + * server-selected cipher suite was in the list of client-offered cipher suites, so if + * we now can't produce an implementation, we shouldn't have offered it! + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } - public virtual void NotifySelectedCipherSuite(CipherSuite selectedCipherSuite) - { - this.selectedCipherSuite = selectedCipherSuite; - } + public override TlsCipher GetCipher() + { + switch (mSelectedCipherSuite) + { + case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.cls_3DES_EDE_CBC, MacAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AEAD_CHACHA20_POLY1305, MacAlgorithm.cls_null); + + case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha256); + + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CCM, MacAlgorithm.cls_null); + + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CCM_8, MacAlgorithm.cls_null); + + case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_GCM, MacAlgorithm.cls_null); + + case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha256); + + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha384); + + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CCM, MacAlgorithm.cls_null); + + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CCM_8, MacAlgorithm.cls_null); + + case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_GCM, MacAlgorithm.cls_null); + + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_CBC, MacAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_CBC, MacAlgorithm.hmac_sha256); + + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_GCM, MacAlgorithm.cls_null); + + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha256); + + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_GCM, MacAlgorithm.cls_null); + + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha384); + + case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.ESTREAM_SALSA20, MacAlgorithm.hmac_sha1); + + case CipherSuite.TLS_RSA_WITH_NULL_MD5: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_md5); + + case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_RSA_WITH_NULL_SHA: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha1); + + case CipherSuite.TLS_RSA_WITH_NULL_SHA256: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha256); + + case CipherSuite.TLS_RSA_WITH_RC4_128_MD5: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.RC4_128, MacAlgorithm.hmac_md5); + + case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_RSA_WITH_RC4_128_SHA: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.RC4_128, MacAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.SALSA20, MacAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.SEED_CBC, MacAlgorithm.hmac_sha1); + + default: + /* + * Note: internal error here; the TlsProtocol implementation verifies that the + * server-selected cipher suite was in the list of client-offered cipher suites, so if + * we now can't produce an implementation, we shouldn't have offered it! + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + protected virtual TlsKeyExchange CreateDHKeyExchange(int keyExchange) + { + return new TlsDHKeyExchange(keyExchange, mSupportedSignatureAlgorithms, null); + } - public virtual void NotifySelectedCompressionMethod(CompressionMethod selectedCompressionMethod) + protected virtual TlsKeyExchange CreateDheKeyExchange(int keyExchange) { - this.selectedCompressionMethod = selectedCompressionMethod; + return new TlsDheKeyExchange(keyExchange, mSupportedSignatureAlgorithms, null); } - 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) + protected virtual TlsKeyExchange CreateECDHKeyExchange(int keyExchange) { - return new TlsECDHKeyExchange(context, keyExchange); + return new TlsECDHKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mNamedCurves, mClientECPointFormats, + mServerECPointFormats); } - protected virtual TlsKeyExchange CreateECDheKeyExchange(KeyExchangeAlgorithm keyExchange) + protected virtual TlsKeyExchange CreateECDheKeyExchange(int keyExchange) { - return new TlsECDheKeyExchange(context, keyExchange); + return new TlsECDheKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mNamedCurves, mClientECPointFormats, + mServerECPointFormats); } protected virtual TlsKeyExchange CreateRsaKeyExchange() - { - return new TlsRsaKeyExchange(context); - } + { + return new TlsRsaKeyExchange(mSupportedSignatureAlgorithms); + } } } diff --git a/crypto/src/crypto/tls/DefaultTlsEncryptionCredentials.cs b/crypto/src/crypto/tls/DefaultTlsEncryptionCredentials.cs new file mode 100644
index 000000000..34d15d146 --- /dev/null +++ b/crypto/src/crypto/tls/DefaultTlsEncryptionCredentials.cs
@@ -0,0 +1,51 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class DefaultTlsEncryptionCredentials + : AbstractTlsEncryptionCredentials + { + protected readonly TlsContext mContext; + protected readonly Certificate mCertificate; + protected readonly AsymmetricKeyParameter mPrivateKey; + + public DefaultTlsEncryptionCredentials(TlsContext context, Certificate certificate, + AsymmetricKeyParameter privateKey) + { + if (certificate == null) + throw new ArgumentNullException("certificate"); + if (certificate.IsEmpty) + throw new ArgumentException("cannot be empty", "certificate"); + if (privateKey == null) + throw new ArgumentNullException("'privateKey' cannot be null"); + if (!privateKey.IsPrivate) + throw new ArgumentException("must be private", "privateKey"); + + if (privateKey is RsaKeyParameters) + { + } + else + { + throw new ArgumentException("type not supported: " + privateKey.GetType().FullName, "privateKey"); + } + + this.mContext = context; + this.mCertificate = certificate; + this.mPrivateKey = privateKey; + } + + public override Certificate Certificate + { + get { return mCertificate; } + } + + /// <exception cref="IOException"></exception> + public override byte[] DecryptPreMasterSecret(byte[] encryptedPreMasterSecret) + { + return TlsRsaUtilities.SafeDecryptPreMasterSecret(mContext, (RsaKeyParameters)mPrivateKey, encryptedPreMasterSecret); + } + } +} diff --git a/crypto/src/crypto/tls/DefaultTlsServer.cs b/crypto/src/crypto/tls/DefaultTlsServer.cs new file mode 100644
index 000000000..017ed0d85 --- /dev/null +++ b/crypto/src/crypto/tls/DefaultTlsServer.cs
@@ -0,0 +1,548 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Crypto.Agreement; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class DefaultTlsServer + : AbstractTlsServer + { + public DefaultTlsServer() + : base() + { + } + + public DefaultTlsServer(TlsCipherFactory cipherFactory) + : base(cipherFactory) + { + } + + protected virtual TlsEncryptionCredentials GetRsaEncryptionCredentials() + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + protected virtual TlsSignerCredentials GetRsaSignerCredentials() + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + protected virtual DHParameters GetDHParameters() + { + return DHStandardGroups.rfc5114_1024_160; + } + + protected override int[] GetCipherSuites() + { + return new int[] + { + CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256, + CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256, + CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, + }; + } + + public override TlsCredentials GetCredentials() + { + switch (mSelectedCipherSuite) + { + case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_NULL_MD5: + case CipherSuite.TLS_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_RSA_WITH_NULL_SHA256: + case CipherSuite.TLS_RSA_WITH_RC4_128_MD5: + case CipherSuite.TLS_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA: + return GetRsaEncryptionCredentials(); + + case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1: + return GetRsaSignerCredentials(); + + default: + /* + * Note: internal error here; selected a key exchange we don't implement! + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public override TlsKeyExchange GetKeyExchange() + { + switch (mSelectedCipherSuite) + { + case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA: + return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_DSS); + + case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA: + return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_RSA); + + case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA: + return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_DSS); + + case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA: + return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_RSA); + + case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA: + return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_ECDSA); + + case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA: + return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_RSA); + + case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1: + return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_ECDSA); + + case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1: + return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_RSA); + + case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_RSA_WITH_NULL_MD5: + case CipherSuite.TLS_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_RSA_WITH_NULL_SHA256: + case CipherSuite.TLS_RSA_WITH_RC4_128_MD5: + case CipherSuite.TLS_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA: + return createRSAKeyExchange(); + + default: + /* + * Note: internal error here; selected a key exchange we don't implement! + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public override TlsCipher GetCipher() + { + switch (mSelectedCipherSuite) + { + case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.cls_3DES_EDE_CBC, MacAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AEAD_CHACHA20_POLY1305, MacAlgorithm.cls_null); + + case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha256); + + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CCM, MacAlgorithm.cls_null); + + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CCM_8, MacAlgorithm.cls_null); + + case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_GCM, MacAlgorithm.cls_null); + + case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha256); + + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha384); + + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CCM, MacAlgorithm.cls_null); + + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CCM_8, MacAlgorithm.cls_null); + + case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_GCM, MacAlgorithm.cls_null); + + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_CBC, MacAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_CBC, MacAlgorithm.hmac_sha256); + + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_GCM, MacAlgorithm.cls_null); + + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha256); + + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_GCM, MacAlgorithm.cls_null); + + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha384); + + case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.ESTREAM_SALSA20, MacAlgorithm.hmac_sha1); + + case CipherSuite.TLS_RSA_WITH_NULL_MD5: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_md5); + + case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_RSA_WITH_NULL_SHA: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha1); + + case CipherSuite.TLS_RSA_WITH_NULL_SHA256: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha256); + + case CipherSuite.TLS_RSA_WITH_RC4_128_MD5: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.RC4_128, MacAlgorithm.hmac_md5); + + case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_RSA_WITH_RC4_128_SHA: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.RC4_128, MacAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.SALSA20, MacAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.SEED_CBC, MacAlgorithm.hmac_sha1); + + default: + /* + * Note: internal error here; selected a cipher suite we don't implement! + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + protected virtual TlsKeyExchange CreateDHKeyExchange(int keyExchange) + { + return new TlsDHKeyExchange(keyExchange, mSupportedSignatureAlgorithms, GetDHParameters()); + } + + protected virtual TlsKeyExchange CreateDheKeyExchange(int keyExchange) + { + return new TlsDheKeyExchange(keyExchange, mSupportedSignatureAlgorithms, GetDHParameters()); + } + + protected virtual TlsKeyExchange CreateECDHKeyExchange(int keyExchange) + { + return new TlsECDHKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mNamedCurves, mClientECPointFormats, + mServerECPointFormats); + } + + protected virtual TlsKeyExchange CreateECDheKeyExchange(int keyExchange) + { + return new TlsECDheKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mNamedCurves, mClientECPointFormats, + mServerECPointFormats); + } + + protected virtual TlsKeyExchange createRSAKeyExchange() + { + return new TlsRsaKeyExchange(mSupportedSignatureAlgorithms); + } + } +} diff --git a/crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs b/crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs
index 23d607d85..c7a136573 100644 --- a/crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs +++ b/crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs
@@ -1,76 +1,92 @@ using System; +using System.IO; using Org.BouncyCastle.Crypto.Parameters; namespace Org.BouncyCastle.Crypto.Tls { - public class DefaultTlsSignerCredentials - : TlsSignerCredentials - { - protected TlsClientContext context; - protected Certificate clientCert; - protected AsymmetricKeyParameter clientPrivateKey; + public class DefaultTlsSignerCredentials + : AbstractTlsSignerCredentials + { + protected readonly TlsContext mContext; + protected readonly Certificate mCertificate; + protected readonly AsymmetricKeyParameter mPrivateKey; + protected readonly SignatureAndHashAlgorithm mSignatureAndHashAlgorithm; - protected TlsSigner clientSigner; + protected readonly TlsSigner mSigner; - 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"); - } + public DefaultTlsSignerCredentials(TlsContext context, Certificate certificate, AsymmetricKeyParameter privateKey) + : this(context, certificate, privateKey, null) + { + } - 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"); - } + public DefaultTlsSignerCredentials(TlsContext context, Certificate certificate, AsymmetricKeyParameter privateKey, + SignatureAndHashAlgorithm signatureAndHashAlgorithm) + { + if (certificate == null) + throw new ArgumentNullException("certificate"); + if (certificate.IsEmpty) + throw new ArgumentException("cannot be empty", "clientCertificate"); + if (privateKey == null) + throw new ArgumentNullException("privateKey"); + if (!privateKey.IsPrivate) + throw new ArgumentException("must be private", "privateKey"); + if (TlsUtilities.IsTlsV12(context) && signatureAndHashAlgorithm == null) + throw new ArgumentException("cannot be null for (D)TLS 1.2+", "signatureAndHashAlgorithm"); - this.context = context; - this.clientCert = clientCertificate; - this.clientPrivateKey = clientPrivateKey; - } + if (privateKey is RsaKeyParameters) + { + mSigner = new TlsRsaSigner(); + } + else if (privateKey is DsaPrivateKeyParameters) + { + mSigner = new TlsDssSigner(); + } + else if (privateKey is ECPrivateKeyParameters) + { + mSigner = new TlsECDsaSigner(); + } + else + { + throw new ArgumentException("type not supported: " + privateKey.GetType().FullName, "privateKey"); + } - public virtual Certificate Certificate - { - get { return clientCert; } - } + this.mSigner.Init(context); - public virtual byte[] GenerateCertificateSignature(byte[] md5andsha1) - { - try - { - return clientSigner.CalculateRawSignature(context.SecureRandom, clientPrivateKey, md5andsha1); - } - catch (CryptoException) - { - throw new TlsFatalAlert(AlertDescription.internal_error); - } - } - } + this.mContext = context; + this.mCertificate = certificate; + this.mPrivateKey = privateKey; + this.mSignatureAndHashAlgorithm = signatureAndHashAlgorithm; + } + + public override Certificate Certificate + { + get { return mCertificate; } + } + + /// <exception cref="IOException"></exception> + public override byte[] GenerateCertificateSignature(byte[] hash) + { + try + { + if (TlsUtilities.IsTlsV12(mContext)) + { + return mSigner.GenerateRawSignature(mSignatureAndHashAlgorithm, mPrivateKey, hash); + } + else + { + return mSigner.GenerateRawSignature(mPrivateKey, hash); + } + } + catch (CryptoException e) + { + throw new TlsFatalAlert(AlertDescription.internal_error, e); + } + } + + public override SignatureAndHashAlgorithm SignatureAndHashAlgorithm + { + get { return mSignatureAndHashAlgorithm; } + } + } } diff --git a/crypto/src/crypto/tls/DeferredHash.cs b/crypto/src/crypto/tls/DeferredHash.cs new file mode 100644
index 000000000..1112d4a3c --- /dev/null +++ b/crypto/src/crypto/tls/DeferredHash.cs
@@ -0,0 +1,201 @@ +using System; +using System.Collections; + +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /** + * Buffers input until the hash algorithm is determined. + */ + internal class DeferredHash + : TlsHandshakeHash + { + protected const int BUFFERING_HASH_LIMIT = 4; + + protected TlsContext mContext; + + private DigestInputBuffer mBuf; + private IDictionary mHashes; + private int mPrfHashAlgorithm; + + internal DeferredHash() + { + this.mBuf = new DigestInputBuffer(); + this.mHashes = Platform.CreateHashtable(); + this.mPrfHashAlgorithm = -1; + } + + private DeferredHash(byte prfHashAlgorithm, IDigest prfHash) + { + this.mBuf = null; + this.mHashes = Platform.CreateHashtable(); + this.mPrfHashAlgorithm = prfHashAlgorithm; + mHashes[prfHashAlgorithm] = prfHash; + } + + public virtual void Init(TlsContext context) + { + this.mContext = context; + } + + public virtual TlsHandshakeHash NotifyPrfDetermined() + { + int prfAlgorithm = mContext.SecurityParameters.PrfAlgorithm; + if (prfAlgorithm == PrfAlgorithm.tls_prf_legacy) + { + CombinedHash legacyHash = new CombinedHash(); + legacyHash.Init(mContext); + mBuf.UpdateDigest(legacyHash); + return legacyHash.NotifyPrfDetermined(); + } + + this.mPrfHashAlgorithm = TlsUtilities.GetHashAlgorithmForPrfAlgorithm(prfAlgorithm); + + CheckTrackingHash((byte)mPrfHashAlgorithm); + + return this; + } + + public virtual void TrackHashAlgorithm(byte hashAlgorithm) + { + if (mBuf == null) + throw new InvalidOperationException("Too late to track more hash algorithms"); + + CheckTrackingHash(hashAlgorithm); + } + + public virtual void SealHashAlgorithms() + { + CheckStopBuffering(); + } + + public virtual TlsHandshakeHash StopTracking() + { + byte prfHashAlgorithm = (byte)mPrfHashAlgorithm; + IDigest prfHash = TlsUtilities.CloneHash(prfHashAlgorithm, (IDigest)mHashes[prfHashAlgorithm]); + if (mBuf != null) + { + mBuf.UpdateDigest(prfHash); + } + DeferredHash result = new DeferredHash(prfHashAlgorithm, prfHash); + result.Init(mContext); + return result; + } + + public virtual IDigest ForkPrfHash() + { + CheckStopBuffering(); + + byte prfHashAlgorithm = (byte)mPrfHashAlgorithm; + if (mBuf != null) + { + IDigest prfHash = TlsUtilities.CreateHash(prfHashAlgorithm); + mBuf.UpdateDigest(prfHash); + return prfHash; + } + + return TlsUtilities.CloneHash(prfHashAlgorithm, (IDigest)mHashes[prfHashAlgorithm]); + } + + public virtual byte[] GetFinalHash(byte hashAlgorithm) + { + IDigest d = (IDigest)mHashes[hashAlgorithm]; + if (d == null) + throw new InvalidOperationException("HashAlgorithm " + hashAlgorithm + " is not being tracked"); + + d = TlsUtilities.CloneHash(hashAlgorithm, d); + if (mBuf != null) + { + mBuf.UpdateDigest(d); + } + + return DigestUtilities.DoFinal(d); + } + + public virtual string AlgorithmName + { + get { throw new InvalidOperationException("Use Fork() to get a definite IDigest"); } + } + + public virtual int GetByteLength() + { + throw new InvalidOperationException("Use Fork() to get a definite IDigest"); + } + + public virtual int GetDigestSize() + { + throw new InvalidOperationException("Use Fork() to get a definite IDigest"); + } + + public virtual void Update(byte input) + { + if (mBuf != null) + { + mBuf.WriteByte(input); + return; + } + + foreach (IDigest hash in mHashes.Values) + { + hash.Update(input); + } + } + + public virtual void BlockUpdate(byte[] input, int inOff, int len) + { + if (mBuf != null) + { + mBuf.Write(input, inOff, len); + return; + } + + foreach (IDigest hash in mHashes.Values) + { + hash.BlockUpdate(input, inOff, len); + } + } + + public virtual int DoFinal(byte[] output, int outOff) + { + throw new InvalidOperationException("Use Fork() to get a definite IDigest"); + } + + public virtual void Reset() + { + if (mBuf != null) + { + mBuf.SetLength(0); + return; + } + + foreach (IDigest hash in mHashes.Values) + { + hash.Reset(); + } + } + + protected virtual void CheckStopBuffering() + { + if (mBuf != null && mHashes.Count <= BUFFERING_HASH_LIMIT) + { + foreach (IDigest hash in mHashes.Values) + { + mBuf.UpdateDigest(hash); + } + + this.mBuf = null; + } + } + + protected virtual void CheckTrackingHash(byte hashAlgorithm) + { + if (!mHashes.Contains(hashAlgorithm)) + { + IDigest hash = TlsUtilities.CreateHash(hashAlgorithm); + mHashes[hashAlgorithm] = hash; + } + } + } +} diff --git a/crypto/src/crypto/tls/DigestInputBuffer.cs b/crypto/src/crypto/tls/DigestInputBuffer.cs new file mode 100644
index 000000000..547bcab54 --- /dev/null +++ b/crypto/src/crypto/tls/DigestInputBuffer.cs
@@ -0,0 +1,39 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + internal class DigestInputBuffer + : MemoryStream + { + internal void UpdateDigest(IDigest d) + { + WriteTo(new DigStream(d)); + } + + private class DigStream + : BaseOutputStream + { + private readonly IDigest d; + + internal DigStream(IDigest d) + { + this.d = d; + } + + public override void WriteByte(byte b) + { + d.Update(b); + } + + public override void Write(byte[] buf, int off, int len) + { + d.BlockUpdate(buf, off, len); + } + } + } +} diff --git a/crypto/src/crypto/tls/DigitallySigned.cs b/crypto/src/crypto/tls/DigitallySigned.cs new file mode 100644
index 000000000..8b7344fd9 --- /dev/null +++ b/crypto/src/crypto/tls/DigitallySigned.cs
@@ -0,0 +1,70 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class DigitallySigned + { + protected readonly SignatureAndHashAlgorithm mAlgorithm; + protected readonly byte[] mSignature; + + public DigitallySigned(SignatureAndHashAlgorithm algorithm, byte[] signature) + { + if (signature == null) + throw new ArgumentNullException("signature"); + + this.mAlgorithm = algorithm; + this.mSignature = signature; + } + + /** + * @return a {@link SignatureAndHashAlgorithm} (or null before TLS 1.2). + */ + public virtual SignatureAndHashAlgorithm Algorithm + { + get { return mAlgorithm; } + } + + public virtual byte[] Signature + { + get { return mSignature; } + } + + /** + * Encode this {@link DigitallySigned} to a {@link Stream}. + * + * @param output + * the {@link Stream} to encode to. + * @throws IOException + */ + public virtual void Encode(Stream output) + { + if (mAlgorithm != null) + { + mAlgorithm.Encode(output); + } + TlsUtilities.WriteOpaque16(mSignature, output); + } + + /** + * Parse a {@link DigitallySigned} from a {@link Stream}. + * + * @param context + * the {@link TlsContext} of the current connection. + * @param input + * the {@link Stream} to parse from. + * @return a {@link DigitallySigned} object. + * @throws IOException + */ + public static DigitallySigned Parse(TlsContext context, Stream input) + { + SignatureAndHashAlgorithm algorithm = null; + if (TlsUtilities.IsTlsV12(context)) + { + algorithm = SignatureAndHashAlgorithm.Parse(input); + } + byte[] signature = TlsUtilities.ReadOpaque16(input); + return new DigitallySigned(algorithm, signature); + } + } +} diff --git a/crypto/src/crypto/tls/ECBasisType.cs b/crypto/src/crypto/tls/ECBasisType.cs new file mode 100644
index 000000000..5416e17c0 --- /dev/null +++ b/crypto/src/crypto/tls/ECBasisType.cs
@@ -0,0 +1,16 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <summary>RFC 4492 5.4. (Errata ID: 2389)</summary> + public abstract class ECBasisType + { + public const byte ec_basis_trinomial = 1; + public const byte ec_basis_pentanomial = 2; + + public static bool IsValid(byte ecBasisType) + { + return ecBasisType >= ec_basis_trinomial && ecBasisType <= ec_basis_pentanomial; + } + } +} diff --git a/crypto/src/crypto/tls/ECCurveType.cs b/crypto/src/crypto/tls/ECCurveType.cs
index 15d5d7b42..1b352e9c4 100644 --- a/crypto/src/crypto/tls/ECCurveType.cs +++ b/crypto/src/crypto/tls/ECCurveType.cs
@@ -1,29 +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, + /// <summary> + /// RFC 4492 5.4 + /// </summary> + public abstract class ECCurveType + { + /** + * Indicates the elliptic curve domain parameters are conveyed verbosely, and the + * underlying finite field is a prime field. + */ + public const byte 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 the elliptic curve domain parameters are conveyed verbosely, and the + * underlying finite field is a characteristic-2 field. + */ + public const byte explicit_char2 = 2; - /** - * Indicates that a named curve is used. This option SHOULD be used when applicable. - */ - named_curve = 3, + /** + * Indicates that a named curve is used. This option SHOULD be used when applicable. + */ + public const byte named_curve = 3; - /* - * Values 248 through 255 are reserved for private use. - */ - } + /* + * 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
index 4e0dd0067..21b0fdd97 100644 --- a/crypto/src/crypto/tls/ECPointFormat.cs +++ b/crypto/src/crypto/tls/ECPointFormat.cs
@@ -1,16 +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, + /// <summary> + /// RFC 4492 5.1.2 + /// </summary> + public abstract class ECPointFormat + { + public const byte uncompressed = 0; + public const byte ansiX962_compressed_prime = 1; + public const byte ansiX962_compressed_char2 = 2; - /* - * reserved (248..255) - */ - } + /* + * reserved (248..255) + */ + } } diff --git a/crypto/src/crypto/tls/EncryptionAlgorithm.cs b/crypto/src/crypto/tls/EncryptionAlgorithm.cs
index 79d3b63b5..05d1c5d7a 100644 --- a/crypto/src/crypto/tls/EncryptionAlgorithm.cs +++ b/crypto/src/crypto/tls/EncryptionAlgorithm.cs
@@ -2,31 +2,68 @@ 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, - } + /// <summary>RFC 2246</summary> + /// <remarks> + /// Note that the values here are implementation-specific and arbitrary. It is recommended not to + /// depend on the particular values (e.g. serialization). + /// </remarks> + public abstract class EncryptionAlgorithm + { + public const int NULL = 0; + public const int RC4_40 = 1; + public const int RC4_128 = 2; + public const int RC2_CBC_40 = 3; + public const int IDEA_CBC = 4; + public const int DES40_CBC = 5; + public const int DES_CBC = 6; + public const int cls_3DES_EDE_CBC = 7; + + /* + * RFC 3268 + */ + public const int AES_128_CBC = 8; + public const int AES_256_CBC = 9; + + /* + * RFC 5289 + */ + public const int AES_128_GCM = 10; + public const int AES_256_GCM = 11; + + /* + * RFC 4132 + */ + public const int CAMELLIA_128_CBC = 12; + public const int CAMELLIA_256_CBC = 13; + + /* + * RFC 4162 + */ + public const int SEED_CBC = 14; + + /* + * RFC 6655 + */ + public const int AES_128_CCM = 15; + public const int AES_128_CCM_8 = 16; + public const int AES_256_CCM = 17; + public const int AES_256_CCM_8 = 18; + + /* + * RFC 6367 + */ + public const int CAMELLIA_128_GCM = 19; + public const int CAMELLIA_256_GCM = 20; + + /* + * draft-josefsson-salsa20-tls-04 + */ + public const int ESTREAM_SALSA20 = 100; + public const int SALSA20 = 101; + + /* + * draft-agl-tls-chacha20poly1305-04 + */ + public const int AEAD_CHACHA20_POLY1305 = 102; + } } diff --git a/crypto/src/crypto/tls/ExporterLabel.cs b/crypto/src/crypto/tls/ExporterLabel.cs new file mode 100644
index 000000000..f301ea3c0 --- /dev/null +++ b/crypto/src/crypto/tls/ExporterLabel.cs
@@ -0,0 +1,32 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <summary>RFC 5705</summary> + public abstract class ExporterLabel + { + /* + * RFC 5246 + */ + public const string client_finished = "client finished"; + public const string server_finished = "server finished"; + public const string master_secret = "master secret"; + public const string key_expansion = "key expansion"; + + /* + * RFC 5216 + */ + public const string client_EAP_encryption = "client EAP encryption"; + + /* + * RFC 5281 + */ + public const string ttls_keying_material = "ttls keying material"; + public const string ttls_challenge = "ttls challenge"; + + /* + * RFC 5764 + */ + public const string dtls_srtp = "EXTRACTOR-dtls_srtp"; + } +} diff --git a/crypto/src/crypto/tls/ExtensionType.cs b/crypto/src/crypto/tls/ExtensionType.cs
index f00e34e3f..929c134d5 100644 --- a/crypto/src/crypto/tls/ExtensionType.cs +++ b/crypto/src/crypto/tls/ExtensionType.cs
@@ -1,31 +1,61 @@ 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, - } + public abstract class ExtensionType + { + /* + * RFC 2546 2.3. + */ + public const int server_name = 0; + public const int max_fragment_length = 1; + public const int client_certificate_url = 2; + public const int trusted_ca_keys = 3; + public const int truncated_hmac = 4; + public const int status_request = 5; + + /* + * RFC 4681 + */ + public const int user_mapping = 6; + + /* + * RFC 4492 5.1. + */ + public const int elliptic_curves = 10; + public const int ec_point_formats = 11; + + /* + * RFC 5054 2.8.1. + */ + public const int srp = 12; + + /* + * RFC 5246 7.4.1.4. + */ + public const int signature_algorithms = 13; + + /* + * RFC 5764 9. + */ + public const int use_srtp = 14; + + /* + * RFC 6520 6. + */ + public const int heartbeat = 15; + + /* + * RFC 5077 7. + */ + public const int session_ticket = 35; + + /* + * draft-ietf-tls-encrypt-then-mac-03 + */ + public const int encrypt_then_mac = 22; + + /* + * RFC 5746 3.2. + */ + public const int renegotiation_info = 0xff01; + } } diff --git a/crypto/src/crypto/tls/HandshakeType.cs b/crypto/src/crypto/tls/HandshakeType.cs
index deedb1f84..e63042ac3 100644 --- a/crypto/src/crypto/tls/HandshakeType.cs +++ b/crypto/src/crypto/tls/HandshakeType.cs
@@ -1,19 +1,40 @@ 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, - } + public abstract class HandshakeType + { + /* + * RFC 2246 7.4 + */ + public const byte hello_request = 0; + public const byte client_hello = 1; + public const byte server_hello = 2; + public const byte certificate = 11; + public const byte server_key_exchange = 12; + public const byte certificate_request = 13; + public const byte server_hello_done = 14; + public const byte certificate_verify = 15; + public const byte client_key_exchange = 16; + public const byte finished = 20; + + /* + * RFC 3546 2.4 + */ + public const byte certificate_url = 21; + public const byte certificate_status = 22; + + /* + * (DTLS) RFC 4347 4.3.2 + */ + public const byte hello_verify_request = 3; + + /* + * RFC 4680 + */ + public const byte supplemental_data = 23; + + /* + * RFC 5077 + */ + public const byte session_ticket = 4; + } } diff --git a/crypto/src/crypto/tls/HashAlgorithm.cs b/crypto/src/crypto/tls/HashAlgorithm.cs new file mode 100644
index 000000000..ac6def26f --- /dev/null +++ b/crypto/src/crypto/tls/HashAlgorithm.cs
@@ -0,0 +1,16 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <summary>RFC 5246 7.4.1.4.1</summary> + public abstract class HashAlgorithm + { + public const byte none = 0; + public const byte md5 = 1; + public const byte sha1 = 2; + public const byte sha224 = 3; + public const byte sha256 = 4; + public const byte sha384 = 5; + public const byte sha512 = 6; + } +} diff --git a/crypto/src/crypto/tls/HeartbeatExtension.cs b/crypto/src/crypto/tls/HeartbeatExtension.cs new file mode 100644
index 000000000..049837266 --- /dev/null +++ b/crypto/src/crypto/tls/HeartbeatExtension.cs
@@ -0,0 +1,52 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class HeartbeatExtension + { + protected readonly byte mMode; + + public HeartbeatExtension(byte mode) + { + if (!HeartbeatMode.IsValid(mode)) + throw new ArgumentException("not a valid HeartbeatMode value", "mode"); + + this.mMode = mode; + } + + public virtual byte Mode + { + get { return mMode; } + } + + /** + * Encode this {@link HeartbeatExtension} to a {@link Stream}. + * + * @param output + * the {@link Stream} to encode to. + * @throws IOException + */ + public virtual void Encode(Stream output) + { + TlsUtilities.WriteUint8(mMode, output); + } + + /** + * Parse a {@link HeartbeatExtension} from a {@link Stream}. + * + * @param input + * the {@link Stream} to parse from. + * @return a {@link HeartbeatExtension} object. + * @throws IOException + */ + public static HeartbeatExtension Parse(Stream input) + { + byte mode = TlsUtilities.ReadUint8(input); + if (!HeartbeatMode.IsValid(mode)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + return new HeartbeatExtension(mode); + } + } +} diff --git a/crypto/src/crypto/tls/HeartbeatMessage.cs b/crypto/src/crypto/tls/HeartbeatMessage.cs new file mode 100644
index 000000000..f64a7baa4 --- /dev/null +++ b/crypto/src/crypto/tls/HeartbeatMessage.cs
@@ -0,0 +1,102 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class HeartbeatMessage + { + protected readonly byte mType; + protected readonly byte[] mPayload; + protected readonly int mPaddingLength; + + public HeartbeatMessage(byte type, byte[] payload, int paddingLength) + { + if (!HeartbeatMessageType.IsValid(type)) + throw new ArgumentException("not a valid HeartbeatMessageType value", "type"); + if (payload == null || payload.Length >= (1 << 16)) + throw new ArgumentException("must have length < 2^16", "payload"); + if (paddingLength < 16) + throw new ArgumentException("must be at least 16", "paddingLength"); + + this.mType = type; + this.mPayload = payload; + this.mPaddingLength = paddingLength; + } + + /** + * Encode this {@link HeartbeatMessage} to a {@link Stream}. + * + * @param output + * the {@link Stream} to encode to. + * @throws IOException + */ + public virtual void Encode(TlsContext context, Stream output) + { + TlsUtilities.WriteUint8(mType, output); + + TlsUtilities.CheckUint16(mPayload.Length); + TlsUtilities.WriteUint16(mPayload.Length, output); + output.Write(mPayload, 0, mPayload.Length); + + byte[] padding = new byte[mPaddingLength]; + context.NonceRandomGenerator.NextBytes(padding); + output.Write(padding, 0, padding.Length); + } + + /** + * Parse a {@link HeartbeatMessage} from a {@link Stream}. + * + * @param input + * the {@link Stream} to parse from. + * @return a {@link HeartbeatMessage} object. + * @throws IOException + */ + public static HeartbeatMessage Parse(Stream input) + { + byte type = TlsUtilities.ReadUint8(input); + if (!HeartbeatMessageType.IsValid(type)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + int payload_length = TlsUtilities.ReadUint16(input); + + PayloadBuffer buf = new PayloadBuffer(); + Streams.PipeAll(input, buf); + + byte[] payload = buf.ToTruncatedByteArray(payload_length); + if (payload == null) + { + /* + * RFC 6520 4. If the payload_length of a received HeartbeatMessage is too large, the + * received HeartbeatMessage MUST be discarded silently. + */ + return null; + } + + TlsUtilities.CheckUint16(buf.Length); + int padding_length = (int)buf.Length - payload.Length; + + /* + * RFC 6520 4. The padding of a received HeartbeatMessage message MUST be ignored + */ + return new HeartbeatMessage(type, payload, padding_length); + } + + internal class PayloadBuffer + : MemoryStream + { + internal byte[] ToTruncatedByteArray(int payloadLength) + { + /* + * RFC 6520 4. The padding_length MUST be at least 16. + */ + int minimumCount = payloadLength + 16; + if (Length < minimumCount) + return null; + return Arrays.CopyOf(GetBuffer(), payloadLength); + } + } + } +} diff --git a/crypto/src/crypto/tls/HeartbeatMessageType.cs b/crypto/src/crypto/tls/HeartbeatMessageType.cs new file mode 100644
index 000000000..57a4b86be --- /dev/null +++ b/crypto/src/crypto/tls/HeartbeatMessageType.cs
@@ -0,0 +1,18 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /* + * RFC 6520 3. + */ + public abstract class HeartbeatMessageType + { + public const byte heartbeat_request = 1; + public const byte heartbeat_response = 2; + + public static bool IsValid(byte heartbeatMessageType) + { + return heartbeatMessageType >= heartbeat_request && heartbeatMessageType <= heartbeat_response; + } + } +} diff --git a/crypto/src/crypto/tls/HeartbeatMode.cs b/crypto/src/crypto/tls/HeartbeatMode.cs new file mode 100644
index 000000000..f1570a84d --- /dev/null +++ b/crypto/src/crypto/tls/HeartbeatMode.cs
@@ -0,0 +1,18 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /* + * RFC 6520 + */ + public abstract class HeartbeatMode + { + public const byte peer_allowed_to_send = 1; + public const byte peer_not_allowed_to_send = 2; + + public static bool IsValid(byte heartbeatMode) + { + return heartbeatMode >= peer_allowed_to_send && heartbeatMode <= peer_not_allowed_to_send; + } + } +} diff --git a/crypto/src/crypto/tls/KeyExchangeAlgorithm.cs b/crypto/src/crypto/tls/KeyExchangeAlgorithm.cs
index 3fdbeb2a6..9b1b3ba5e 100644 --- a/crypto/src/crypto/tls/KeyExchangeAlgorithm.cs +++ b/crypto/src/crypto/tls/KeyExchangeAlgorithm.cs
@@ -2,35 +2,53 @@ 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, - } + /// <summary>RFC 2246</summary> + /// <remarks> + /// Note that the values here are implementation-specific and arbitrary. It is recommended not to + /// depend on the particular values (e.g. serialization). + /// </remarks> + public abstract class KeyExchangeAlgorithm + { + public const int NULL = 0; + public const int RSA = 1; + public const int RSA_EXPORT = 2; + public const int DHE_DSS = 3; + public const int DHE_DSS_EXPORT = 4; + public const int DHE_RSA = 5; + public const int DHE_RSA_EXPORT = 6; + public const int DH_DSS = 7; + public const int DH_DSS_EXPORT = 8; + public const int DH_RSA = 9; + public const int DH_RSA_EXPORT = 10; + public const int DH_anon = 11; + public const int DH_anon_EXPORT = 12; + + /* + * RFC 4279 + */ + public const int PSK = 13; + public const int DHE_PSK = 14; + public const int RSA_PSK = 15; + + /* + * RFC 4429 + */ + public const int ECDH_ECDSA = 16; + public const int ECDHE_ECDSA = 17; + public const int ECDH_RSA = 18; + public const int ECDHE_RSA = 19; + public const int ECDH_anon = 20; + + /* + * RFC 5054 + */ + public const int SRP = 21; + public const int SRP_DSS = 22; + public const int SRP_RSA = 23; + + /* + * RFC 5489 + */ + public const int ECDHE_PSK = 24; + } } diff --git a/crypto/src/crypto/tls/MacAlgorithm.cs b/crypto/src/crypto/tls/MacAlgorithm.cs new file mode 100644
index 000000000..e4aa88de6 --- /dev/null +++ b/crypto/src/crypto/tls/MacAlgorithm.cs
@@ -0,0 +1,25 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <summary>RFC 2246</summary> + /// <remarks> + /// Note that the values here are implementation-specific and arbitrary. It is recommended not to + /// depend on the particular values (e.g. serialization). + /// </remarks> + public abstract class MacAlgorithm + { + public const int cls_null = 0; + public const int md5 = 1; + public const int sha = 2; + + /* + * RFC 5246 + */ + public const int hmac_md5 = md5; + public const int hmac_sha1 = sha; + public const int hmac_sha256 = 3; + public const int hmac_sha384 = 4; + public const int hmac_sha512 = 5; + } +} diff --git a/crypto/src/crypto/tls/MaxFragmentLength.cs b/crypto/src/crypto/tls/MaxFragmentLength.cs new file mode 100644
index 000000000..5b10b35dd --- /dev/null +++ b/crypto/src/crypto/tls/MaxFragmentLength.cs
@@ -0,0 +1,20 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class MaxFragmentLength + { + /* + * RFC 3546 3.2. + */ + public const byte pow2_9 = 1; + public const byte pow2_10 = 2; + public const byte pow2_11 = 3; + public const byte pow2_12 = 4; + + public static bool IsValid(byte maxFragmentLength) + { + return maxFragmentLength >= pow2_9 && maxFragmentLength <= pow2_12; + } + } +} diff --git a/crypto/src/crypto/tls/NameType.cs b/crypto/src/crypto/tls/NameType.cs new file mode 100644
index 000000000..25f6046fc --- /dev/null +++ b/crypto/src/crypto/tls/NameType.cs
@@ -0,0 +1,12 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class NameType + { + /* + * RFC 3546 3.1. + */ + public const byte host_name = 0; + } +} diff --git a/crypto/src/crypto/tls/NamedCurve.cs b/crypto/src/crypto/tls/NamedCurve.cs
index c8ee189aa..b8aa0ecde 100644 --- a/crypto/src/crypto/tls/NamedCurve.cs +++ b/crypto/src/crypto/tls/NamedCurve.cs
@@ -6,67 +6,72 @@ 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, + /// <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 abstract class NamedCurve + { + public const int sect163k1 = 1; + public const int sect163r1 = 2; + public const int sect163r2 = 3; + public const int sect193r1 = 4; + public const int sect193r2 = 5; + public const int sect233k1 = 6; + public const int sect233r1 = 7; + public const int sect239k1 = 8; + public const int sect283k1 = 9; + public const int sect283r1 = 10; + public const int sect409k1 = 11; + public const int sect409r1 = 12; + public const int sect571k1 = 13; + public const int sect571r1 = 14; + public const int secp160k1 = 15; + public const int secp160r1 = 16; + public const int secp160r2 = 17; + public const int secp192k1 = 18; + public const int secp192r1 = 19; + public const int secp224k1 = 20; + public const int secp224r1 = 21; + public const int secp256k1 = 22; + public const int secp256r1 = 23; + public const int secp384r1 = 24; + public const int secp521r1 = 25; + + /* + * RFC 7027 + */ + public const int brainpoolP256r1 = 26; + public const int brainpoolP384r1 = 27; + public const int brainpoolP512r1 = 28; - /* - * reserved (0xFE00..0xFEFF) - */ + /* + * reserved (0xFE00..0xFEFF) + */ - arbitrary_explicit_prime_curves = 0xFF01, - arbitrary_explicit_char2_curves = 0xFF02, - } + public const int arbitrary_explicit_prime_curves = 0xFF01; + public const int arbitrary_explicit_char2_curves = 0xFF02; - internal class NamedCurveHelper - { - internal static ECDomainParameters GetECParameters(NamedCurve namedCurve) - { - if (!Enum.IsDefined(typeof(NamedCurve), namedCurve)) - return null; + public static bool IsValid(int namedCurve) + { + return (namedCurve >= sect163k1 && namedCurve <= brainpoolP512r1) + || (namedCurve >= arbitrary_explicit_prime_curves && namedCurve <= arbitrary_explicit_char2_curves); + } - 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()); - } - } + public static bool RefersToASpecificNamedCurve(int namedCurve) + { + switch (namedCurve) + { + case arbitrary_explicit_prime_curves: + case arbitrary_explicit_char2_curves: + return false; + default: + return true; + } + } + } } diff --git a/crypto/src/crypto/tls/NewSessionTicket.cs b/crypto/src/crypto/tls/NewSessionTicket.cs new file mode 100644
index 000000000..a84026b8c --- /dev/null +++ b/crypto/src/crypto/tls/NewSessionTicket.cs
@@ -0,0 +1,53 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class NewSessionTicket + { + protected readonly long mTicketLifetimeHint; + protected readonly byte[] mTicket; + + public NewSessionTicket(long ticketLifetimeHint, byte[] ticket) + { + this.mTicketLifetimeHint = ticketLifetimeHint; + this.mTicket = ticket; + } + + public virtual long TicketLifetimeHint + { + get { return mTicketLifetimeHint; } + } + + public virtual byte[] Ticket + { + get { return mTicket; } + } + + /** + * Encode this {@link NewSessionTicket} to a {@link Stream}. + * + * @param output the {@link Stream} to encode to. + * @throws IOException + */ + public virtual void Encode(Stream output) + { + TlsUtilities.WriteUint32(mTicketLifetimeHint, output); + TlsUtilities.WriteOpaque16(mTicket, output); + } + + /** + * Parse a {@link NewSessionTicket} from a {@link Stream}. + * + * @param input the {@link Stream} to parse from. + * @return a {@link NewSessionTicket} object. + * @throws IOException + */ + public static NewSessionTicket Parse(Stream input) + { + long ticketLifetimeHint = TlsUtilities.ReadUint32(input); + byte[] ticket = TlsUtilities.ReadOpaque16(input); + return new NewSessionTicket(ticketLifetimeHint, ticket); + } + } +} diff --git a/crypto/src/crypto/tls/OcspStatusRequest.cs b/crypto/src/crypto/tls/OcspStatusRequest.cs new file mode 100644
index 000000000..2dd8371e5 --- /dev/null +++ b/crypto/src/crypto/tls/OcspStatusRequest.cs
@@ -0,0 +1,130 @@ +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.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /** + * RFC 3546 3.6 + */ + public class OcspStatusRequest + { + protected readonly IList mResponderIDList; + protected readonly X509Extensions mRequestExtensions; + + /** + * @param responderIDList + * an {@link IList} of {@link ResponderID}, specifying the list of trusted OCSP + * responders. An empty list has the special meaning that the responders are + * implicitly known to the server - e.g., by prior arrangement. + * @param requestExtensions + * OCSP request extensions. A null value means that there are no extensions. + */ + public OcspStatusRequest(IList responderIDList, X509Extensions requestExtensions) + { + this.mResponderIDList = responderIDList; + this.mRequestExtensions = requestExtensions; + } + + /** + * @return an {@link IList} of {@link ResponderID} + */ + public virtual IList ResponderIDList + { + get { return mResponderIDList; } + } + + /** + * @return OCSP request extensions + */ + public virtual X509Extensions RequestExtensions + { + get { return mRequestExtensions; } + } + + /** + * Encode this {@link OcspStatusRequest} to a {@link Stream}. + * + * @param output + * the {@link Stream} to encode to. + * @throws IOException + */ + public virtual void Encode(Stream output) + { + if (mResponderIDList == null || mResponderIDList.Count < 1) + { + TlsUtilities.WriteUint16(0, output); + } + else + { + MemoryStream buf = new MemoryStream(); + for (int i = 0; i < mResponderIDList.Count; ++i) + { + ResponderID responderID = (ResponderID)mResponderIDList[i]; + byte[] derEncoding = responderID.GetEncoded(Asn1Encodable.Der); + TlsUtilities.WriteOpaque16(derEncoding, buf); + } + TlsUtilities.CheckUint16(buf.Length); + TlsUtilities.WriteUint16((int)buf.Length, output); + buf.WriteTo(output); + } + + if (mRequestExtensions == null) + { + TlsUtilities.WriteUint16(0, output); + } + else + { + byte[] derEncoding = mRequestExtensions.GetEncoded(Asn1Encodable.Der); + TlsUtilities.CheckUint16(derEncoding.Length); + TlsUtilities.WriteUint16(derEncoding.Length, output); + output.Write(derEncoding, 0, derEncoding.Length); + } + } + + /** + * Parse a {@link OcspStatusRequest} from a {@link Stream}. + * + * @param input + * the {@link Stream} to parse from. + * @return an {@link OcspStatusRequest} object. + * @throws IOException + */ + public static OcspStatusRequest Parse(Stream input) + { + IList responderIDList = Platform.CreateArrayList(); + { + int length = TlsUtilities.ReadUint16(input); + if (length > 0) + { + byte[] data = TlsUtilities.ReadFully(length, input); + MemoryStream buf = new MemoryStream(data, false); + do + { + byte[] derEncoding = TlsUtilities.ReadOpaque16(buf); + ResponderID responderID = ResponderID.GetInstance(TlsUtilities.ReadDerObject(derEncoding)); + responderIDList.Add(responderID); + } + while (buf.Position < buf.Length); + } + } + + X509Extensions requestExtensions = null; + { + int length = TlsUtilities.ReadUint16(input); + if (length > 0) + { + byte[] derEncoding = TlsUtilities.ReadFully(length, input); + requestExtensions = X509Extensions.GetInstance(TlsUtilities.ReadDerObject(derEncoding)); + } + } + + return new OcspStatusRequest(responderIDList, requestExtensions); + } + } +} diff --git a/crypto/src/crypto/tls/PrfAlgorithm.cs b/crypto/src/crypto/tls/PrfAlgorithm.cs new file mode 100644
index 000000000..871241bd2 --- /dev/null +++ b/crypto/src/crypto/tls/PrfAlgorithm.cs
@@ -0,0 +1,24 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <summary>RFC 5246</summary> + /// <remarks> + /// Note that the values here are implementation-specific and arbitrary. It is recommended not to + /// depend on the particular values (e.g. serialization). + /// </remarks> + public abstract class PrfAlgorithm + { + /* + * Placeholder to refer to the legacy TLS algorithm + */ + public const int tls_prf_legacy = 0; + + public const int tls_prf_sha256 = 1; + + /* + * Implied by RFC 5288 + */ + public const int tls_prf_sha384 = 2; + } +} diff --git a/crypto/src/crypto/tls/ProtocolVersion.cs b/crypto/src/crypto/tls/ProtocolVersion.cs new file mode 100644
index 000000000..b0d55183a --- /dev/null +++ b/crypto/src/crypto/tls/ProtocolVersion.cs
@@ -0,0 +1,159 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public sealed class ProtocolVersion + { + public static readonly ProtocolVersion SSLv3 = new ProtocolVersion(0x0300, "SSL 3.0"); + public static readonly ProtocolVersion TLSv10 = new ProtocolVersion(0x0301, "TLS 1.0"); + public static readonly ProtocolVersion TLSv11 = new ProtocolVersion(0x0302, "TLS 1.1"); + public static readonly ProtocolVersion TLSv12 = new ProtocolVersion(0x0303, "TLS 1.2"); + public static readonly ProtocolVersion DTLSv10 = new ProtocolVersion(0xFEFF, "DTLS 1.0"); + public static readonly ProtocolVersion DTLSv12 = new ProtocolVersion(0xFEFD, "DTLS 1.2"); + + private readonly int version; + private readonly String name; + + private ProtocolVersion(int v, String name) + { + this.version = v & 0xffff; + this.name = name; + } + + public int FullVersion + { + get { return version; } + } + + public int MajorVersion + { + get { return version >> 8; } + } + + public int MinorVersion + { + get { return version & 0xff; } + } + + public bool IsDtls + { + get { return MajorVersion == 0xFE; } + } + + public bool IsSsl + { + get { return this == SSLv3; } + } + + public bool IsTls + { + get { return MajorVersion == 0x03; } + } + + public ProtocolVersion GetEquivalentTLSVersion() + { + if (!IsDtls) + { + return this; + } + if (this == DTLSv10) + { + return TLSv11; + } + return TLSv12; + } + + public bool IsEqualOrEarlierVersionOf(ProtocolVersion version) + { + if (MajorVersion != version.MajorVersion) + { + return false; + } + int diffMinorVersion = version.MinorVersion - MinorVersion; + return IsDtls ? diffMinorVersion <= 0 : diffMinorVersion >= 0; + } + + public bool IsLaterVersionOf(ProtocolVersion version) + { + if (MajorVersion != version.MajorVersion) + { + return false; + } + int diffMinorVersion = version.MinorVersion - MinorVersion; + return IsDtls ? diffMinorVersion > 0 : diffMinorVersion < 0; + } + + public override bool Equals(object other) + { + return this == other || (other is ProtocolVersion && Equals((ProtocolVersion)other)); + } + + public bool Equals(ProtocolVersion other) + { + return other != null && this.version == other.version; + } + + public override int GetHashCode() + { + return version; + } + + /// <exception cref="IOException"/> + public static ProtocolVersion Get(int major, int minor) + { + switch (major) + { + case 0x03: + { + switch (minor) + { + case 0x00: + return SSLv3; + case 0x01: + return TLSv10; + case 0x02: + return TLSv11; + case 0x03: + return TLSv12; + } + return GetUnknownVersion(major, minor, "TLS"); + } + case 0xFE: + { + switch (minor) + { + case 0xFF: + return DTLSv10; + case 0xFE: + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + case 0xFD: + return DTLSv12; + } + return GetUnknownVersion(major, minor, "DTLS"); + } + default: + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + } + + public override string ToString() + { + return name; + } + + private static ProtocolVersion GetUnknownVersion(int major, int minor, string prefix) + { + TlsUtilities.CheckUint8(major); + TlsUtilities.CheckUint8(minor); + + int v = (major << 8) | minor; + String hex = Platform.ToUpperInvariant(Convert.ToString(0x10000 | v, 16).Substring(1)); + return new ProtocolVersion(v, prefix + " 0x" + hex); + } + } +} diff --git a/crypto/src/crypto/tls/PskTlsClient.cs b/crypto/src/crypto/tls/PskTlsClient.cs
index 16975e713..6063572a0 100644 --- a/crypto/src/crypto/tls/PskTlsClient.cs +++ b/crypto/src/crypto/tls/PskTlsClient.cs
@@ -3,180 +3,259 @@ 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); - } - } + public abstract class PskTlsClient + : AbstractTlsClient + { + protected TlsPskIdentity mPskIdentity; + + public PskTlsClient(TlsPskIdentity pskIdentity) + : this(new DefaultTlsCipherFactory(), pskIdentity) + { + } + + public PskTlsClient(TlsCipherFactory cipherFactory, TlsPskIdentity pskIdentity) + : base(cipherFactory) + { + this.mPskIdentity = pskIdentity; + } + + public override int[] GetCipherSuites() + { + return new int[] + { + CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, + CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256, + CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA + }; + } + + public override TlsKeyExchange GetKeyExchange() + { + switch (mSelectedCipherSuite) + { + 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_128_CBC_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1: + case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8: + case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8: + return CreatePskKeyExchange(KeyExchangeAlgorithm.DHE_PSK); + + case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1: + return CreatePskKeyExchange(KeyExchangeAlgorithm.ECDHE_PSK); + + case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_PSK_WITH_AES_128_CCM: + case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8: + case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_PSK_WITH_AES_256_CCM: + case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8: + case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_PSK_WITH_SALSA20_SHA1: + 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_128_CBC_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_SHA1: + return CreatePskKeyExchange(KeyExchangeAlgorithm.RSA_PSK); + + default: + /* + * Note: internal error here; the TlsProtocol implementation verifies that the + * server-selected cipher suite was in the list of client-offered cipher suites, so if + * we now can't produce an implementation, we shouldn't have offered it! + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public override TlsCipher GetCipher() + { + switch (mSelectedCipherSuite) + { + case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.cls_3DES_EDE_CBC, MacAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha256); + + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM: + case CipherSuite.TLS_PSK_WITH_AES_128_CCM: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CCM, MacAlgorithm.cls_null); + + case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8: + case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CCM_8, MacAlgorithm.cls_null); + + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_GCM, MacAlgorithm.cls_null); + + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha384); + + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM: + case CipherSuite.TLS_PSK_WITH_AES_256_CCM: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CCM, MacAlgorithm.cls_null); + + case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8: + case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CCM_8, MacAlgorithm.cls_null); + + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_GCM, MacAlgorithm.cls_null); + + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_CBC, MacAlgorithm.hmac_sha256); + + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_GCM, MacAlgorithm.cls_null); + + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha384); + + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_GCM, MacAlgorithm.cls_null); + + case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.ESTREAM_SALSA20, MacAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA256: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha256); + + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha384); + + case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.RC4_128, MacAlgorithm.hmac_sha1); + + case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1: + case CipherSuite.TLS_PSK_WITH_SALSA20_SHA1: + case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_SHA1: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.SALSA20, MacAlgorithm.hmac_sha1); + + default: + /* + * Note: internal error here; the TlsProtocol implementation verifies that the + * server-selected cipher suite was in the list of client-offered cipher suites, so if + * we now can't produce an implementation, we shouldn't have offered it! + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + protected virtual TlsKeyExchange CreatePskKeyExchange(int keyExchange) + { + return new TlsPskKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mPskIdentity, null, mNamedCurves, + mClientECPointFormats, mServerECPointFormats); + } + } } diff --git a/crypto/src/crypto/tls/RecordStream.cs b/crypto/src/crypto/tls/RecordStream.cs
index e18894b4e..db5b158bc 100644 --- a/crypto/src/crypto/tls/RecordStream.cs +++ b/crypto/src/crypto/tls/RecordStream.cs
@@ -3,164 +3,331 @@ 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; - } - } + /// <summary>An implementation of the TLS 1.0/1.1/1.2 record layer, allowing downgrade to SSLv3.</summary> + internal class RecordStream + { + private const int DEFAULT_PLAINTEXT_LIMIT = (1 << 14); + + private TlsProtocol mHandler; + private Stream mInput; + private Stream mOutput; + private TlsCompression mPendingCompression = null, mReadCompression = null, mWriteCompression = null; + private TlsCipher mPendingCipher = null, mReadCipher = null, mWriteCipher = null; + private long mReadSeqNo = 0, mWriteSeqNo = 0; + private MemoryStream mBuffer = new MemoryStream(); + + private TlsHandshakeHash mHandshakeHash = null; + + private ProtocolVersion mReadVersion = null, mWriteVersion = null; + private bool mRestrictReadVersion = true; + + private int mPlaintextLimit, mCompressedLimit, mCiphertextLimit; + + internal RecordStream(TlsProtocol handler, Stream input, Stream output) + { + this.mHandler = handler; + this.mInput = input; + this.mOutput = output; + this.mReadCompression = new TlsNullCompression(); + this.mWriteCompression = this.mReadCompression; + } + + internal virtual void Init(TlsContext context) + { + this.mReadCipher = new TlsNullCipher(context); + this.mWriteCipher = this.mReadCipher; + this.mHandshakeHash = new DeferredHash(); + this.mHandshakeHash.Init(context); + + SetPlaintextLimit(DEFAULT_PLAINTEXT_LIMIT); + } + + internal virtual int GetPlaintextLimit() + { + return mPlaintextLimit; + } + + internal virtual void SetPlaintextLimit(int plaintextLimit) + { + this.mPlaintextLimit = plaintextLimit; + this.mCompressedLimit = this.mPlaintextLimit + 1024; + this.mCiphertextLimit = this.mCompressedLimit + 1024; + } + + internal virtual ProtocolVersion ReadVersion + { + get { return mReadVersion; } + set { this.mReadVersion = value; } + } + + internal virtual void SetWriteVersion(ProtocolVersion writeVersion) + { + this.mWriteVersion = writeVersion; + } + + /** + * RFC 5246 E.1. "Earlier versions of the TLS specification were not fully clear on what the + * record layer version number (TLSPlaintext.version) should contain when sending ClientHello + * (i.e., before it is known which version of the protocol will be employed). Thus, TLS servers + * compliant with this specification MUST accept any value {03,XX} as the record layer version + * number for ClientHello." + */ + internal virtual void SetRestrictReadVersion(bool enabled) + { + this.mRestrictReadVersion = enabled; + } + + internal virtual void SetPendingConnectionState(TlsCompression tlsCompression, TlsCipher tlsCipher) + { + this.mPendingCompression = tlsCompression; + this.mPendingCipher = tlsCipher; + } + + internal virtual void SentWriteCipherSpec() + { + if (mPendingCompression == null || mPendingCipher == null) + throw new TlsFatalAlert(AlertDescription.handshake_failure); + + this.mWriteCompression = this.mPendingCompression; + this.mWriteCipher = this.mPendingCipher; + this.mWriteSeqNo = 0; + } + + internal virtual void ReceivedReadCipherSpec() + { + if (mPendingCompression == null || mPendingCipher == null) + throw new TlsFatalAlert(AlertDescription.handshake_failure); + + this.mReadCompression = this.mPendingCompression; + this.mReadCipher = this.mPendingCipher; + this.mReadSeqNo = 0; + } + + internal virtual void FinaliseHandshake() + { + if (mReadCompression != mPendingCompression || mWriteCompression != mPendingCompression + || mReadCipher != mPendingCipher || mWriteCipher != mPendingCipher) + { + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + this.mPendingCompression = null; + this.mPendingCipher = null; + } + + internal virtual bool ReadRecord() + { + byte[] recordHeader = TlsUtilities.ReadAllOrNothing(5, mInput); + if (recordHeader == null) + return false; + + byte type = TlsUtilities.ReadUint8(recordHeader, 0); + + /* + * RFC 5246 6. If a TLS implementation receives an unexpected record type, it MUST send an + * unexpected_message alert. + */ + CheckType(type, AlertDescription.unexpected_message); + + if (!mRestrictReadVersion) + { + int version = TlsUtilities.ReadVersionRaw(recordHeader, 1); + if ((version & 0xffffff00) != 0x0300) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + else + { + ProtocolVersion version = TlsUtilities.ReadVersion(recordHeader, 1); + if (mReadVersion == null) + { + mReadVersion = version; + } + else if (!version.Equals(mReadVersion)) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + + int length = TlsUtilities.ReadUint16(recordHeader, 3); + byte[] plaintext = DecodeAndVerify(type, mInput, length); + mHandler.ProcessRecord(type, plaintext, 0, plaintext.Length); + return true; + } + + internal virtual byte[] DecodeAndVerify(byte type, Stream input, int len) + { + CheckLength(len, mCiphertextLimit, AlertDescription.record_overflow); + + byte[] buf = TlsUtilities.ReadFully(len, input); + byte[] decoded = mReadCipher.DecodeCiphertext(mReadSeqNo++, type, buf, 0, buf.Length); + + CheckLength(decoded.Length, mCompressedLimit, AlertDescription.record_overflow); + + /* + * TODO RFC5264 6.2.2. Implementation note: Decompression functions are responsible for + * ensuring that messages cannot cause internal buffer overflows. + */ + Stream cOut = mReadCompression.Decompress(mBuffer); + if (cOut != mBuffer) + { + cOut.Write(decoded, 0, decoded.Length); + cOut.Flush(); + decoded = GetBufferContents(); + } + + /* + * RFC 5264 6.2.2. If the decompression function encounters a TLSCompressed.fragment that + * would decompress to a length in excess of 2^14 bytes, it should report a fatal + * decompression failure error. + */ + CheckLength(decoded.Length, mPlaintextLimit, AlertDescription.decompression_failure); + + /* + * RFC 5264 6.2.1 Implementations MUST NOT send zero-length fragments of Handshake, Alert, + * or ChangeCipherSpec content types. + */ + if (decoded.Length < 1 && type != ContentType.application_data) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + return decoded; + } + + internal virtual void WriteRecord(byte type, byte[] plaintext, int plaintextOffset, int plaintextLength) + { + // Never send anything until a valid ClientHello has been received + if (mWriteVersion == null) + return; + + /* + * RFC 5264 6. Implementations MUST NOT send record types not defined in this document + * unless negotiated by some extension. + */ + CheckType(type, AlertDescription.internal_error); + + /* + * RFC 5264 6.2.1 The length should not exceed 2^14. + */ + CheckLength(plaintextLength, mPlaintextLimit, AlertDescription.internal_error); + + /* + * RFC 5264 6.2.1 Implementations MUST NOT send zero-length fragments of Handshake, Alert, + * or ChangeCipherSpec content types. + */ + if (plaintextLength < 1 && type != ContentType.application_data) + throw new TlsFatalAlert(AlertDescription.internal_error); + + if (type == ContentType.handshake) + { + UpdateHandshakeData(plaintext, plaintextOffset, plaintextLength); + } + + Stream cOut = mWriteCompression.Compress(mBuffer); + + byte[] ciphertext; + if (cOut == mBuffer) + { + ciphertext = mWriteCipher.EncodePlaintext(mWriteSeqNo++, type, plaintext, plaintextOffset, plaintextLength); + } + else + { + cOut.Write(plaintext, plaintextOffset, plaintextLength); + cOut.Flush(); + byte[] compressed = GetBufferContents(); + + /* + * RFC5264 6.2.2. Compression must be lossless and may not increase the content length + * by more than 1024 bytes. + */ + CheckLength(compressed.Length, plaintextLength + 1024, AlertDescription.internal_error); + + ciphertext = mWriteCipher.EncodePlaintext(mWriteSeqNo++, type, compressed, 0, compressed.Length); + } + + /* + * RFC 5264 6.2.3. The length may not exceed 2^14 + 2048. + */ + CheckLength(ciphertext.Length, mCiphertextLimit, AlertDescription.internal_error); + + byte[] record = new byte[ciphertext.Length + 5]; + TlsUtilities.WriteUint8(type, record, 0); + TlsUtilities.WriteVersion(mWriteVersion, record, 1); + TlsUtilities.WriteUint16(ciphertext.Length, record, 3); + Array.Copy(ciphertext, 0, record, 5, ciphertext.Length); + mOutput.Write(record, 0, record.Length); + mOutput.Flush(); + } + + internal virtual void NotifyHelloComplete() + { + this.mHandshakeHash = mHandshakeHash.NotifyPrfDetermined(); + } + + internal virtual TlsHandshakeHash HandshakeHash + { + get { return mHandshakeHash; } + } + + internal virtual TlsHandshakeHash PrepareToFinish() + { + TlsHandshakeHash result = mHandshakeHash; + this.mHandshakeHash = mHandshakeHash.StopTracking(); + return result; + } + + internal virtual void UpdateHandshakeData(byte[] message, int offset, int len) + { + mHandshakeHash.BlockUpdate(message, offset, len); + } + + internal virtual void SafeClose() + { + try + { + mInput.Close(); + } + catch (IOException) + { + } + + try + { + mOutput.Close(); + } + catch (IOException) + { + } + } + + internal virtual void Flush() + { + mOutput.Flush(); + } + + private byte[] GetBufferContents() + { + byte[] contents = mBuffer.ToArray(); + mBuffer.SetLength(0); + return contents; + } + + private static void CheckType(byte type, byte alertDescription) + { + switch (type) + { + case ContentType.application_data: + case ContentType.alert: + case ContentType.change_cipher_spec: + case ContentType.handshake: + case ContentType.heartbeat: + break; + default: + throw new TlsFatalAlert(alertDescription); + } + } + + private static void CheckLength(int length, int limit, byte alertDescription) + { + if (length > limit) + throw new TlsFatalAlert(alertDescription); + } + } } diff --git a/crypto/src/crypto/tls/SecurityParameters.cs b/crypto/src/crypto/tls/SecurityParameters.cs
index 9ed3969eb..6a115a911 100644 --- a/crypto/src/crypto/tls/SecurityParameters.cs +++ b/crypto/src/crypto/tls/SecurityParameters.cs
@@ -1,26 +1,94 @@ using System; +using Org.BouncyCastle.Utilities; + 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; } - } - } + public class SecurityParameters + { + internal int entity = -1; + internal int cipherSuite = -1; + internal byte compressionAlgorithm = CompressionMethod.cls_null; + internal int prfAlgorithm = -1; + internal int verifyDataLength = -1; + internal byte[] masterSecret = null; + internal byte[] clientRandom = null; + internal byte[] serverRandom = null; + + // TODO Keep these internal, since it's maybe not the ideal place for them + internal short maxFragmentLength = -1; + internal bool truncatedHMac = false; + internal bool encryptThenMac = false; + + internal void CopySessionParametersFrom(SecurityParameters other) + { + this.entity = other.entity; + this.cipherSuite = other.cipherSuite; + this.compressionAlgorithm = other.compressionAlgorithm; + this.prfAlgorithm = other.prfAlgorithm; + this.verifyDataLength = other.verifyDataLength; + this.masterSecret = Arrays.Clone(other.masterSecret); + } + + internal virtual void Clear() + { + if (this.masterSecret != null) + { + Arrays.Fill(this.masterSecret, (byte)0); + this.masterSecret = null; + } + } + + /** + * @return {@link ConnectionEnd} + */ + public virtual int Entity + { + get { return entity; } + } + + /** + * @return {@link CipherSuite} + */ + public virtual int CipherSuite + { + get { return cipherSuite; } + } + + /** + * @return {@link CompressionMethod} + */ + public byte CompressionAlgorithm + { + get { return compressionAlgorithm; } + } + + /** + * @return {@link PRFAlgorithm} + */ + public virtual int PrfAlgorithm + { + get { return prfAlgorithm; } + } + + public virtual int VerifyDataLength + { + get { return verifyDataLength; } + } + + public virtual byte[] MasterSecret + { + get { return masterSecret; } + } + + public virtual byte[] ClientRandom + { + get { return clientRandom; } + } + + public virtual byte[] ServerRandom + { + get { return serverRandom; } + } + } } diff --git a/crypto/src/crypto/tls/ServerDHParams.cs b/crypto/src/crypto/tls/ServerDHParams.cs new file mode 100644
index 000000000..381858854 --- /dev/null +++ b/crypto/src/crypto/tls/ServerDHParams.cs
@@ -0,0 +1,60 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class ServerDHParams + { + protected readonly DHPublicKeyParameters mPublicKey; + + public ServerDHParams(DHPublicKeyParameters publicKey) + { + if (publicKey == null) + throw new ArgumentNullException("publicKey"); + + this.mPublicKey = publicKey; + } + + public virtual DHPublicKeyParameters PublicKey + { + get { return mPublicKey; } + } + + /** + * Encode this {@link ServerDHParams} to a {@link Stream}. + * + * @param output + * the {@link Stream} to encode to. + * @throws IOException + */ + public virtual void Encode(Stream output) + { + DHParameters dhParameters = mPublicKey.Parameters; + BigInteger Ys = mPublicKey.Y; + + TlsDHUtilities.WriteDHParameter(dhParameters.P, output); + TlsDHUtilities.WriteDHParameter(dhParameters.G, output); + TlsDHUtilities.WriteDHParameter(Ys, output); + } + + /** + * Parse a {@link ServerDHParams} from a {@link Stream}. + * + * @param input + * the {@link Stream} to parse from. + * @return a {@link ServerDHParams} object. + * @throws IOException + */ + public static ServerDHParams Parse(Stream input) + { + BigInteger p = TlsDHUtilities.ReadDHParameter(input); + BigInteger g = TlsDHUtilities.ReadDHParameter(input); + BigInteger Ys = TlsDHUtilities.ReadDHParameter(input); + + return new ServerDHParams(new DHPublicKeyParameters(Ys, new DHParameters(p, g))); + } + } +} diff --git a/crypto/src/crypto/tls/ServerName.cs b/crypto/src/crypto/tls/ServerName.cs new file mode 100644
index 000000000..3d1e8f844 --- /dev/null +++ b/crypto/src/crypto/tls/ServerName.cs
@@ -0,0 +1,105 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class ServerName + { + protected readonly byte mNameType; + protected readonly object mName; + + public ServerName(byte nameType, object name) + { + if (!IsCorrectType(nameType, name)) + throw new ArgumentException("not an instance of the correct type", "name"); + + this.mNameType = nameType; + this.mName = name; + } + + public virtual byte NameType + { + get { return mNameType; } + } + + public virtual object Name + { + get { return mName; } + } + + public virtual string GetHostName() + { + if (!IsCorrectType(Tls.NameType.host_name, mName)) + throw new InvalidOperationException("'name' is not a HostName string"); + + return (string)mName; + } + + /** + * Encode this {@link ServerName} to a {@link Stream}. + * + * @param output + * the {@link Stream} to encode to. + * @throws IOException + */ + public virtual void Encode(Stream output) + { + TlsUtilities.WriteUint8(mNameType, output); + + switch (mNameType) + { + case Tls.NameType.host_name: + byte[] utf8Encoding = Strings.ToUtf8ByteArray((string)mName); + if (utf8Encoding.Length < 1) + throw new TlsFatalAlert(AlertDescription.internal_error); + TlsUtilities.WriteOpaque16(utf8Encoding, output); + break; + default: + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + /** + * Parse a {@link ServerName} from a {@link Stream}. + * + * @param input + * the {@link Stream} to parse from. + * @return a {@link ServerName} object. + * @throws IOException + */ + public static ServerName Parse(Stream input) + { + byte name_type = TlsUtilities.ReadUint8(input); + object name; + + switch (name_type) + { + case Tls.NameType.host_name: + { + byte[] utf8Encoding = TlsUtilities.ReadOpaque16(input); + if (utf8Encoding.Length < 1) + throw new TlsFatalAlert(AlertDescription.decode_error); + name = Strings.FromUtf8ByteArray(utf8Encoding); + break; + } + default: + throw new TlsFatalAlert(AlertDescription.decode_error); + } + + return new ServerName(name_type, name); + } + + protected static bool IsCorrectType(byte nameType, object name) + { + switch (nameType) + { + case Tls.NameType.host_name: + return name is string; + default: + throw new ArgumentException("unsupported value", "name"); + } + } + } +} diff --git a/crypto/src/crypto/tls/ServerNameList.cs b/crypto/src/crypto/tls/ServerNameList.cs new file mode 100644
index 000000000..13da79bf6 --- /dev/null +++ b/crypto/src/crypto/tls/ServerNameList.cs
@@ -0,0 +1,81 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class ServerNameList + { + protected readonly IList mServerNameList; + + /** + * @param serverNameList an {@link IList} of {@link ServerName}. + */ + public ServerNameList(IList serverNameList) + { + if (serverNameList == null || serverNameList.Count < 1) + throw new ArgumentException("must not be null or empty", "serverNameList"); + + this.mServerNameList = serverNameList; + } + + /** + * @return an {@link IList} of {@link ServerName}. + */ + public virtual IList ServerNames + { + get { return mServerNameList; } + } + + /** + * Encode this {@link ServerNameList} to a {@link Stream}. + * + * @param output + * the {@link Stream} to encode to. + * @throws IOException + */ + public virtual void Encode(Stream output) + { + MemoryStream buf = new MemoryStream(); + + foreach (ServerName entry in ServerNames) + { + entry.Encode(buf); + } + + TlsUtilities.CheckUint16(buf.Length); + TlsUtilities.WriteUint16((int)buf.Length, output); + buf.WriteTo(output); + } + + /** + * Parse a {@link ServerNameList} from a {@link Stream}. + * + * @param input + * the {@link Stream} to parse from. + * @return a {@link ServerNameList} object. + * @throws IOException + */ + public static ServerNameList Parse(Stream input) + { + int length = TlsUtilities.ReadUint16(input); + if (length < 1) + throw new TlsFatalAlert(AlertDescription.decode_error); + + byte[] data = TlsUtilities.ReadFully(length, input); + + MemoryStream buf = new MemoryStream(data, false); + + IList server_name_list = Platform.CreateArrayList(); + while (buf.Position < buf.Length) + { + ServerName entry = ServerName.Parse(buf); + server_name_list.Add(entry); + } + + return new ServerNameList(server_name_list); + } + } +} diff --git a/crypto/src/crypto/tls/ServerOnlyTlsAuthentication.cs b/crypto/src/crypto/tls/ServerOnlyTlsAuthentication.cs new file mode 100644
index 000000000..485889709 --- /dev/null +++ b/crypto/src/crypto/tls/ServerOnlyTlsAuthentication.cs
@@ -0,0 +1,15 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class ServerOnlyTlsAuthentication + : TlsAuthentication + { + public abstract void NotifyServerCertificate(Certificate serverCertificate); + + public TlsCredentials GetClientCredentials(CertificateRequest certificateRequest) + { + return null; + } + } +} diff --git a/crypto/src/crypto/tls/SessionParameters.cs b/crypto/src/crypto/tls/SessionParameters.cs new file mode 100644
index 000000000..c4616ac71 --- /dev/null +++ b/crypto/src/crypto/tls/SessionParameters.cs
@@ -0,0 +1,137 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public sealed class SessionParameters + { + public sealed class Builder + { + private int mCipherSuite = -1; + private short mCompressionAlgorithm = -1; + private byte[] mMasterSecret = null; + private Certificate mPeerCertificate = null; + private byte[] mEncodedServerExtensions = null; + + public Builder() + { + } + + public SessionParameters Build() + { + Validate(this.mCipherSuite >= 0, "cipherSuite"); + Validate(this.mCompressionAlgorithm >= 0, "compressionAlgorithm"); + Validate(this.mMasterSecret != null, "masterSecret"); + return new SessionParameters(mCipherSuite, (byte)mCompressionAlgorithm, mMasterSecret, mPeerCertificate, + mEncodedServerExtensions); + } + + public Builder SetCipherSuite(int cipherSuite) + { + this.mCipherSuite = cipherSuite; + return this; + } + + public Builder SetCompressionAlgorithm(byte compressionAlgorithm) + { + this.mCompressionAlgorithm = compressionAlgorithm; + return this; + } + + public Builder SetMasterSecret(byte[] masterSecret) + { + this.mMasterSecret = masterSecret; + return this; + } + + public Builder SetPeerCertificate(Certificate peerCertificate) + { + this.mPeerCertificate = peerCertificate; + return this; + } + + public Builder SetServerExtensions(IDictionary serverExtensions) + { + if (serverExtensions == null) + { + mEncodedServerExtensions = null; + } + else + { + MemoryStream buf = new MemoryStream(); + TlsProtocol.WriteExtensions(buf, serverExtensions); + mEncodedServerExtensions = buf.ToArray(); + } + return this; + } + + private void Validate(bool condition, string parameter) + { + if (!condition) + throw new InvalidOperationException("Required session parameter '" + parameter + "' not configured"); + } + } + + private int mCipherSuite; + private byte mCompressionAlgorithm; + private byte[] mMasterSecret; + private Certificate mPeerCertificate; + private byte[] mEncodedServerExtensions; + + private SessionParameters(int cipherSuite, byte compressionAlgorithm, byte[] masterSecret, + Certificate peerCertificate, byte[] encodedServerExtensions) + { + this.mCipherSuite = cipherSuite; + this.mCompressionAlgorithm = compressionAlgorithm; + this.mMasterSecret = Arrays.Clone(masterSecret); + this.mPeerCertificate = peerCertificate; + this.mEncodedServerExtensions = encodedServerExtensions; + } + + public void Clear() + { + if (this.mMasterSecret != null) + { + Arrays.Fill(this.mMasterSecret, (byte)0); + } + } + + public SessionParameters Copy() + { + return new SessionParameters(mCipherSuite, mCompressionAlgorithm, mMasterSecret, mPeerCertificate, + mEncodedServerExtensions); + } + + public int CipherSuite + { + get { return mCipherSuite; } + } + + public byte CompressionAlgorithm + { + get { return mCompressionAlgorithm; } + } + + public byte[] MasterSecret + { + get { return mMasterSecret; } + } + + public Certificate PeerCertificate + { + get { return mPeerCertificate; } + } + + public IDictionary ReadServerExtensions() + { + if (mEncodedServerExtensions == null) + return null; + + MemoryStream buf = new MemoryStream(mEncodedServerExtensions, false); + return TlsProtocol.ReadExtensions(buf); + } + } +} diff --git a/crypto/src/crypto/tls/SignatureAlgorithm.cs b/crypto/src/crypto/tls/SignatureAlgorithm.cs new file mode 100644
index 000000000..35b961762 --- /dev/null +++ b/crypto/src/crypto/tls/SignatureAlgorithm.cs
@@ -0,0 +1,15 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /** + * RFC 5246 7.4.1.4.1 (in RFC 2246, there were no specific values assigned) + */ + public abstract class SignatureAlgorithm + { + public const byte anonymous = 0; + public const byte rsa = 1; + public const byte dsa = 2; + public const byte ecdsa = 3; + } +} diff --git a/crypto/src/crypto/tls/SignatureAndHashAlgorithm.cs b/crypto/src/crypto/tls/SignatureAndHashAlgorithm.cs new file mode 100644
index 000000000..f74205b62 --- /dev/null +++ b/crypto/src/crypto/tls/SignatureAndHashAlgorithm.cs
@@ -0,0 +1,94 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /** + * RFC 5246 7.4.1.4.1 + */ + public class SignatureAndHashAlgorithm + { + protected readonly byte mHash; + protected readonly byte mSignature; + + /** + * @param hash {@link HashAlgorithm} + * @param signature {@link SignatureAlgorithm} + */ + public SignatureAndHashAlgorithm(byte hash, byte signature) + { + if (!TlsUtilities.IsValidUint8(hash)) + { + throw new ArgumentException("should be a uint8", "hash"); + } + if (!TlsUtilities.IsValidUint8(signature)) + { + throw new ArgumentException("should be a uint8", "signature"); + } + if (signature == SignatureAlgorithm.anonymous) + { + throw new ArgumentException("MUST NOT be \"anonymous\"", "signature"); + } + + this.mHash = hash; + this.mSignature = signature; + } + + /** + * @return {@link HashAlgorithm} + */ + public virtual byte Hash + { + get { return mHash; } + } + + /** + * @return {@link SignatureAlgorithm} + */ + public virtual byte Signature + { + get { return mSignature; } + } + + public override bool Equals(object obj) + { + if (!(obj is SignatureAndHashAlgorithm)) + { + return false; + } + SignatureAndHashAlgorithm other = (SignatureAndHashAlgorithm)obj; + return other.Hash == Hash && other.Signature == Signature; + } + + public override int GetHashCode() + { + return ((int)Hash << 16) | (int)Signature; + } + + /** + * Encode this {@link SignatureAndHashAlgorithm} to a {@link Stream}. + * + * @param output the {@link Stream} to encode to. + * @throws IOException + */ + public virtual void Encode(Stream output) + { + TlsUtilities.WriteUint8(Hash, output); + TlsUtilities.WriteUint8(Signature, output); + } + + /** + * Parse a {@link SignatureAndHashAlgorithm} from a {@link Stream}. + * + * @param input the {@link Stream} to parse from. + * @return a {@link SignatureAndHashAlgorithm} object. + * @throws IOException + */ + public static SignatureAndHashAlgorithm Parse(Stream input) + { + byte hash = TlsUtilities.ReadUint8(input); + byte signature = TlsUtilities.ReadUint8(input); + return new SignatureAndHashAlgorithm(hash, signature); + } + } +} diff --git a/crypto/src/crypto/tls/SignerInputBuffer.cs b/crypto/src/crypto/tls/SignerInputBuffer.cs new file mode 100644
index 000000000..ef2827c4d --- /dev/null +++ b/crypto/src/crypto/tls/SignerInputBuffer.cs
@@ -0,0 +1,39 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + internal class SignerInputBuffer + : MemoryStream + { + internal void UpdateSigner(ISigner s) + { + WriteTo(new SigStream(s)); + } + + private class SigStream + : BaseOutputStream + { + private readonly ISigner s; + + internal SigStream(ISigner s) + { + this.s = s; + } + + public override void WriteByte(byte b) + { + s.Update(b); + } + + public override void Write(byte[] buf, int off, int len) + { + s.BlockUpdate(buf, off, len); + } + } + } +} diff --git a/crypto/src/crypto/tls/SrpTlsClient.cs b/crypto/src/crypto/tls/SrpTlsClient.cs
index 6c2638bb3..5d82ed470 100644 --- a/crypto/src/crypto/tls/SrpTlsClient.cs +++ b/crypto/src/crypto/tls/SrpTlsClient.cs
@@ -6,183 +6,118 @@ 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 abstract class SrpTlsClient + : AbstractTlsClient + { + protected byte[] mIdentity; + protected byte[] mPassword; + + public SrpTlsClient(byte[] identity, byte[] password) + : this(new DefaultTlsCipherFactory(), identity, password) + { } - 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); - } - } + public SrpTlsClient(TlsCipherFactory cipherFactory, byte[] identity, byte[] password) + : base(cipherFactory) + { + this.mIdentity = Arrays.Clone(identity); + this.mPassword = Arrays.Clone(password); + } + + protected virtual bool RequireSrpServerExtension + { + // No explicit guidance in RFC 5054; by default an (empty) extension from server is optional + get { return false; } + } + + public override int[] GetCipherSuites() + { + return new int[] + { + CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA + }; + } + + public override IDictionary GetClientExtensions() + { + IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(base.GetClientExtensions()); + TlsSrpUtilities.AddSrpExtension(clientExtensions, this.mIdentity); + return clientExtensions; + } + + public override void ProcessServerExtensions(IDictionary serverExtensions) + { + if (!TlsUtilities.HasExpectedEmptyExtensionData(serverExtensions, ExtensionType.srp, + AlertDescription.illegal_parameter)) + { + if (RequireSrpServerExtension) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + base.ProcessServerExtensions(serverExtensions); + } + + public override TlsKeyExchange GetKeyExchange() + { + switch (mSelectedCipherSuite) + { + case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA: + return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP); + + case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA: + return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP_RSA); + + case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA: + return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP_DSS); + + default: + /* + * Note: internal error here; the TlsProtocol implementation verifies that the + * server-selected cipher suite was in the list of client-offered cipher suites, so if + * we now can't produce an implementation, we shouldn't have offered it! + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public override TlsCipher GetCipher() + { + switch (mSelectedCipherSuite) + { + 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 mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.cls_3DES_EDE_CBC, MacAlgorithm.hmac_sha1); + + case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha1); + + case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA: + return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha1); + + default: + /* + * Note: internal error here; the TlsProtocol implementation verifies that the + * server-selected cipher suite was in the list of client-offered cipher suites, so if + * we now can't produce an implementation, we shouldn't have offered it! + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + protected virtual TlsKeyExchange CreateSrpKeyExchange(int keyExchange) + { + return new TlsSrpKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mIdentity, mPassword); + } + } } diff --git a/crypto/src/crypto/tls/SrtpProtectionProfile.cs b/crypto/src/crypto/tls/SrtpProtectionProfile.cs new file mode 100644
index 000000000..1ce89f85e --- /dev/null +++ b/crypto/src/crypto/tls/SrtpProtectionProfile.cs
@@ -0,0 +1,15 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class SrtpProtectionProfile + { + /* + * RFC 5764 4.1.2. + */ + public const int SRTP_AES128_CM_HMAC_SHA1_80 = 0x0001; + public const int SRTP_AES128_CM_HMAC_SHA1_32 = 0x0002; + public const int SRTP_NULL_HMAC_SHA1_80 = 0x0005; + public const int SRTP_NULL_HMAC_SHA1_32 = 0x0006; + } +} diff --git a/crypto/src/crypto/tls/Ssl3Mac.cs b/crypto/src/crypto/tls/Ssl3Mac.cs
index b2f3f309e..8bdb342dc 100644 --- a/crypto/src/crypto/tls/Ssl3Mac.cs +++ b/crypto/src/crypto/tls/Ssl3Mac.cs
@@ -6,109 +6,105 @@ 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; - } - } + /** + * 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_BYTE = 0x36; + private const byte OPAD_BYTE = 0x5C; + + internal static readonly byte[] IPAD = GenPad(IPAD_BYTE, 48); + internal static readonly byte[] OPAD = GenPad(OPAD_BYTE, 48); + + private readonly IDigest digest; + private readonly int padLength; + + private byte[] secret; + + /** + * 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.padLength = 40; + } + else + { + this.padLength = 48; + } + } + + 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, padLength); + 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, padLength); + } + + 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/SupplementalDataEntry.cs b/crypto/src/crypto/tls/SupplementalDataEntry.cs new file mode 100644
index 000000000..5adc4fa52 --- /dev/null +++ b/crypto/src/crypto/tls/SupplementalDataEntry.cs
@@ -0,0 +1,26 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class SupplementalDataEntry + { + protected readonly int mDataType; + protected readonly byte[] mData; + + public SupplementalDataEntry(int dataType, byte[] data) + { + this.mDataType = dataType; + this.mData = data; + } + + public virtual int DataType + { + get { return mDataType; } + } + + public virtual byte[] Data + { + get { return mData; } + } + } +} diff --git a/crypto/src/crypto/tls/SupplementalDataType.cs b/crypto/src/crypto/tls/SupplementalDataType.cs new file mode 100644
index 000000000..79511c50a --- /dev/null +++ b/crypto/src/crypto/tls/SupplementalDataType.cs
@@ -0,0 +1,13 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <summary>RFC 4680</summary> + public abstract class SupplementalDataType + { + /* + * RFC 4681 + */ + public const int user_mapping_data = 0; + } +} diff --git a/crypto/src/crypto/tls/TlsAeadCipher.cs b/crypto/src/crypto/tls/TlsAeadCipher.cs new file mode 100644
index 000000000..951e8663b --- /dev/null +++ b/crypto/src/crypto/tls/TlsAeadCipher.cs
@@ -0,0 +1,189 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class TlsAeadCipher + : TlsCipher + { + protected readonly TlsContext context; + protected readonly int macSize; + protected readonly int nonce_explicit_length; + + protected readonly IAeadBlockCipher encryptCipher; + protected readonly IAeadBlockCipher decryptCipher; + + protected readonly byte[] encryptImplicitNonce, decryptImplicitNonce; + + /// <exception cref="IOException"></exception> + public TlsAeadCipher(TlsContext context, IAeadBlockCipher clientWriteCipher, IAeadBlockCipher serverWriteCipher, + int cipherKeySize, int macSize) + { + if (!TlsUtilities.IsTlsV12(context)) + throw new TlsFatalAlert(AlertDescription.internal_error); + + this.context = context; + this.macSize = macSize; + + // NOTE: Valid for RFC 5288/6655 ciphers but may need review for other AEAD ciphers + this.nonce_explicit_length = 8; + + // TODO SecurityParameters.fixed_iv_length + int fixed_iv_length = 4; + + int key_block_size = (2 * cipherKeySize) + (2 * fixed_iv_length); + + byte[] key_block = TlsUtilities.CalculateKeyBlock(context, key_block_size); + + int offset = 0; + + KeyParameter client_write_key = new KeyParameter(key_block, offset, cipherKeySize); + offset += cipherKeySize; + KeyParameter server_write_key = new KeyParameter(key_block, offset, cipherKeySize); + offset += cipherKeySize; + byte[] client_write_IV = Arrays.CopyOfRange(key_block, offset, offset + fixed_iv_length); + offset += fixed_iv_length; + byte[] server_write_IV = Arrays.CopyOfRange(key_block, offset, offset + fixed_iv_length); + offset += fixed_iv_length; + + if (offset != key_block_size) + throw new TlsFatalAlert(AlertDescription.internal_error); + + KeyParameter encryptKey, decryptKey; + if (context.IsServer) + { + this.encryptCipher = serverWriteCipher; + this.decryptCipher = clientWriteCipher; + this.encryptImplicitNonce = server_write_IV; + this.decryptImplicitNonce = client_write_IV; + encryptKey = server_write_key; + decryptKey = client_write_key; + } + else + { + this.encryptCipher = clientWriteCipher; + this.decryptCipher = serverWriteCipher; + this.encryptImplicitNonce = client_write_IV; + this.decryptImplicitNonce = server_write_IV; + encryptKey = client_write_key; + decryptKey = server_write_key; + } + + byte[] dummyNonce = new byte[fixed_iv_length + nonce_explicit_length]; + + this.encryptCipher.Init(true, new AeadParameters(encryptKey, 8 * macSize, dummyNonce)); + this.decryptCipher.Init(false, new AeadParameters(decryptKey, 8 * macSize, dummyNonce)); + } + + public virtual int GetPlaintextLimit(int ciphertextLimit) + { + // TODO We ought to be able to ask the decryptCipher (independently of it's current state!) + return ciphertextLimit - macSize - nonce_explicit_length; + } + + /// <exception cref="IOException"></exception> + public virtual byte[] EncodePlaintext(long seqNo, byte type, byte[] plaintext, int offset, int len) + { + byte[] nonce = new byte[this.encryptImplicitNonce.Length + nonce_explicit_length]; + Array.Copy(encryptImplicitNonce, 0, nonce, 0, encryptImplicitNonce.Length); + + /* + * RFC 5288/6655 The nonce_explicit MAY be the 64-bit sequence number. + * + * (May need review for other AEAD ciphers). + */ + TlsUtilities.WriteUint64(seqNo, nonce, encryptImplicitNonce.Length); + + int plaintextOffset = offset; + int plaintextLength = len; + int ciphertextLength = encryptCipher.GetOutputSize(plaintextLength); + + byte[] output = new byte[nonce_explicit_length + ciphertextLength]; + Array.Copy(nonce, encryptImplicitNonce.Length, output, 0, nonce_explicit_length); + int outputPos = nonce_explicit_length; + + byte[] additionalData = GetAdditionalData(seqNo, type, plaintextLength); + AeadParameters parameters = new AeadParameters(null, 8 * macSize, nonce, additionalData); + + try + { + encryptCipher.Init(true, parameters); + outputPos += encryptCipher.ProcessBytes(plaintext, plaintextOffset, plaintextLength, output, outputPos); + outputPos += encryptCipher.DoFinal(output, outputPos); + } + catch (Exception e) + { + throw new TlsFatalAlert(AlertDescription.internal_error, e); + } + + if (outputPos != output.Length) + { + // NOTE: Existing AEAD cipher implementations all give exact output lengths + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + return output; + } + + /// <exception cref="IOException"></exception> + public virtual byte[] DecodeCiphertext(long seqNo, byte type, byte[] ciphertext, int offset, int len) + { + if (GetPlaintextLimit(len) < 0) + throw new TlsFatalAlert(AlertDescription.decode_error); + + byte[] nonce = new byte[this.decryptImplicitNonce.Length + nonce_explicit_length]; + Array.Copy(decryptImplicitNonce, 0, nonce, 0, decryptImplicitNonce.Length); + Array.Copy(ciphertext, offset, nonce, decryptImplicitNonce.Length, nonce_explicit_length); + + int ciphertextOffset = offset + nonce_explicit_length; + int ciphertextLength = len - nonce_explicit_length; + int plaintextLength = decryptCipher.GetOutputSize(ciphertextLength); + + byte[] output = new byte[plaintextLength]; + int outputPos = 0; + + byte[] additionalData = GetAdditionalData(seqNo, type, plaintextLength); + AeadParameters parameters = new AeadParameters(null, 8 * macSize, nonce, additionalData); + + try + { + decryptCipher.Init(false, parameters); + outputPos += decryptCipher.ProcessBytes(ciphertext, ciphertextOffset, ciphertextLength, output, outputPos); + outputPos += decryptCipher.DoFinal(output, outputPos); + } + catch (Exception e) + { + throw new TlsFatalAlert(AlertDescription.bad_record_mac, e); + } + + if (outputPos != output.Length) + { + // NOTE: Existing AEAD cipher implementations all give exact output lengths + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + return output; + } + + /// <exception cref="IOException"></exception> + protected virtual byte[] GetAdditionalData(long seqNo, byte type, int len) + { + /* + * additional_data = seq_num + TLSCompressed.type + TLSCompressed.version + + * TLSCompressed.length + */ + + byte[] additional_data = new byte[13]; + TlsUtilities.WriteUint64(seqNo, additional_data, 0); + TlsUtilities.WriteUint8(type, additional_data, 8); + TlsUtilities.WriteVersion(context.ServerVersion, additional_data, 9); + TlsUtilities.WriteUint16(len, additional_data, 11); + + return additional_data; + } + } +} diff --git a/crypto/src/crypto/tls/TlsAgreementCredentials.cs b/crypto/src/crypto/tls/TlsAgreementCredentials.cs
index 46ee4f90e..7c64072e8 100644 --- a/crypto/src/crypto/tls/TlsAgreementCredentials.cs +++ b/crypto/src/crypto/tls/TlsAgreementCredentials.cs
@@ -3,9 +3,10 @@ using System.IO; namespace Org.BouncyCastle.Crypto.Tls { - public interface TlsAgreementCredentials : TlsCredentials - { - /// <exception cref="IOException"></exception> - byte[] GenerateAgreement(AsymmetricKeyParameter serverPublicKey); - } + public interface TlsAgreementCredentials + : TlsCredentials + { + /// <exception cref="IOException"></exception> + byte[] GenerateAgreement(AsymmetricKeyParameter peerPublicKey); + } } 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
index ef7be1913..82c0318b2 100644 --- a/crypto/src/crypto/tls/TlsBlockCipher.cs +++ b/crypto/src/crypto/tls/TlsBlockCipher.cs
@@ -1,248 +1,378 @@ 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; - } - } + /// <summary> + /// A generic TLS 1.0-1.2 / SSLv3 block cipher. This can be used for AES or 3DES for example. + /// </summary> + public class TlsBlockCipher + : TlsCipher + { + protected readonly TlsContext context; + protected readonly byte[] randomData; + protected readonly bool useExplicitIV; + protected readonly bool encryptThenMac; + + protected readonly IBlockCipher encryptCipher; + protected readonly IBlockCipher decryptCipher; + + protected readonly TlsMac mWriteMac; + protected readonly TlsMac mReadMac; + + public virtual TlsMac WriteMac + { + get { return mWriteMac; } + } + + public virtual TlsMac ReadMac + { + get { return mReadMac; } + } + + /// <exception cref="IOException"></exception> + public TlsBlockCipher(TlsContext context, IBlockCipher clientWriteCipher, IBlockCipher serverWriteCipher, + IDigest clientWriteDigest, IDigest serverWriteDigest, int cipherKeySize) + { + this.context = context; + + this.randomData = new byte[256]; + context.NonceRandomGenerator.NextBytes(randomData); + + this.useExplicitIV = TlsUtilities.IsTlsV11(context); + this.encryptThenMac = context.SecurityParameters.encryptThenMac; + + int key_block_size = (2 * cipherKeySize) + clientWriteDigest.GetDigestSize() + + serverWriteDigest.GetDigestSize(); + + // From TLS 1.1 onwards, block ciphers don't need client_write_IV + if (!useExplicitIV) + { + key_block_size += clientWriteCipher.GetBlockSize() + serverWriteCipher.GetBlockSize(); + } + + byte[] key_block = TlsUtilities.CalculateKeyBlock(context, key_block_size); + + int offset = 0; + + TlsMac clientWriteMac = new TlsMac(context, clientWriteDigest, key_block, offset, + clientWriteDigest.GetDigestSize()); + offset += clientWriteDigest.GetDigestSize(); + TlsMac serverWriteMac = new TlsMac(context, serverWriteDigest, key_block, offset, + serverWriteDigest.GetDigestSize()); + offset += serverWriteDigest.GetDigestSize(); + + KeyParameter client_write_key = new KeyParameter(key_block, offset, cipherKeySize); + offset += cipherKeySize; + KeyParameter server_write_key = new KeyParameter(key_block, offset, cipherKeySize); + offset += cipherKeySize; + + byte[] client_write_IV, server_write_IV; + if (useExplicitIV) + { + client_write_IV = new byte[clientWriteCipher.GetBlockSize()]; + server_write_IV = new byte[serverWriteCipher.GetBlockSize()]; + } + else + { + client_write_IV = Arrays.CopyOfRange(key_block, offset, offset + clientWriteCipher.GetBlockSize()); + offset += clientWriteCipher.GetBlockSize(); + server_write_IV = Arrays.CopyOfRange(key_block, offset, offset + serverWriteCipher.GetBlockSize()); + offset += serverWriteCipher.GetBlockSize(); + } + + if (offset != key_block_size) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + ICipherParameters encryptParams, decryptParams; + if (context.IsServer) + { + this.mWriteMac = serverWriteMac; + this.mReadMac = clientWriteMac; + this.encryptCipher = serverWriteCipher; + this.decryptCipher = clientWriteCipher; + encryptParams = new ParametersWithIV(server_write_key, server_write_IV); + decryptParams = new ParametersWithIV(client_write_key, client_write_IV); + } + else + { + this.mWriteMac = clientWriteMac; + this.mReadMac = serverWriteMac; + this.encryptCipher = clientWriteCipher; + this.decryptCipher = serverWriteCipher; + encryptParams = new ParametersWithIV(client_write_key, client_write_IV); + decryptParams = new ParametersWithIV(server_write_key, server_write_IV); + } + + this.encryptCipher.Init(true, encryptParams); + this.decryptCipher.Init(false, decryptParams); + } + + public virtual int GetPlaintextLimit(int ciphertextLimit) + { + int blockSize = encryptCipher.GetBlockSize(); + int macSize = mWriteMac.Size; + + int plaintextLimit = ciphertextLimit; + + // An explicit IV consumes 1 block + if (useExplicitIV) + { + plaintextLimit -= blockSize; + } + + // Leave room for the MAC, and require block-alignment + if (encryptThenMac) + { + plaintextLimit -= macSize; + plaintextLimit -= plaintextLimit % blockSize; + } + else + { + plaintextLimit -= plaintextLimit % blockSize; + plaintextLimit -= macSize; + } + + // Minimum 1 byte of padding + --plaintextLimit; + + return plaintextLimit; + } + + public virtual byte[] EncodePlaintext(long seqNo, byte type, byte[] plaintext, int offset, int len) + { + int blockSize = encryptCipher.GetBlockSize(); + int macSize = mWriteMac.Size; + + ProtocolVersion version = context.ServerVersion; + + int enc_input_length = len; + if (!encryptThenMac) + { + enc_input_length += macSize; + } + + int padding_length = blockSize - 1 - (enc_input_length % blockSize); + + // TODO[DTLS] Consider supporting in DTLS (without exceeding send limit though) + if (!version.IsDtls && !version.IsSsl) + { + // Add a random number of extra blocks worth of padding + int maxExtraPadBlocks = (255 - padding_length) / blockSize; + int actualExtraPadBlocks = ChooseExtraPadBlocks(context.SecureRandom, maxExtraPadBlocks); + padding_length += actualExtraPadBlocks * blockSize; + } + + int totalSize = len + macSize + padding_length + 1; + if (useExplicitIV) + { + totalSize += blockSize; + } + + byte[] outBuf = new byte[totalSize]; + int outOff = 0; + + if (useExplicitIV) + { + byte[] explicitIV = new byte[blockSize]; + context.NonceRandomGenerator.NextBytes(explicitIV); + + encryptCipher.Init(true, new ParametersWithIV(null, explicitIV)); + + Array.Copy(explicitIV, 0, outBuf, outOff, blockSize); + outOff += blockSize; + } + + int blocks_start = outOff; + + Array.Copy(plaintext, offset, outBuf, outOff, len); + outOff += len; + + if (!encryptThenMac) + { + byte[] mac = mWriteMac.CalculateMac(seqNo, type, plaintext, offset, len); + Array.Copy(mac, 0, outBuf, outOff, mac.Length); + outOff += mac.Length; + } + + for (int i = 0; i <= padding_length; i++) + { + outBuf[outOff++] = (byte)padding_length; + } + + for (int i = blocks_start; i < outOff; i += blockSize) + { + encryptCipher.ProcessBlock(outBuf, i, outBuf, i); + } + + if (encryptThenMac) + { + byte[] mac = mWriteMac.CalculateMac(seqNo, type, outBuf, 0, outOff); + Array.Copy(mac, 0, outBuf, outOff, mac.Length); + outOff += mac.Length; + } + + // assert outBuf.length == outOff; + + return outBuf; + } + + /// <exception cref="IOException"></exception> + public virtual byte[] DecodeCiphertext(long seqNo, byte type, byte[] ciphertext, int offset, int len) + { + int blockSize = decryptCipher.GetBlockSize(); + int macSize = mReadMac.Size; + + int minLen = blockSize; + if (encryptThenMac) + { + minLen += macSize; + } + else + { + minLen = System.Math.Max(minLen, macSize + 1); + } + + if (useExplicitIV) + { + minLen += blockSize; + } + + if (len < minLen) + throw new TlsFatalAlert(AlertDescription.decode_error); + + int blocks_length = len; + if (encryptThenMac) + { + blocks_length -= macSize; + } + + if (blocks_length % blockSize != 0) + throw new TlsFatalAlert(AlertDescription.decryption_failed); + + if (encryptThenMac) + { + int end = offset + len; + byte[] receivedMac = Arrays.CopyOfRange(ciphertext, end - macSize, end); + byte[] calculatedMac = mReadMac.CalculateMac(seqNo, type, ciphertext, offset, len - macSize); + + bool badMac = !Arrays.ConstantTimeAreEqual(calculatedMac, receivedMac); + + if (badMac) + throw new TlsFatalAlert(AlertDescription.bad_record_mac); + } + + if (useExplicitIV) + { + decryptCipher.Init(false, new ParametersWithIV(null, ciphertext, offset, blockSize)); + + offset += blockSize; + blocks_length -= blockSize; + } + + for (int i = 0; i < blocks_length; i += blockSize) + { + decryptCipher.ProcessBlock(ciphertext, offset + i, ciphertext, offset + i); + } + + // If there's anything wrong with the padding, this will return zero + int totalPad = CheckPaddingConstantTime(ciphertext, offset, blocks_length, blockSize, encryptThenMac ? 0 : macSize); + + int dec_output_length = blocks_length - totalPad; + + if (!encryptThenMac) + { + dec_output_length -= macSize; + int macInputLen = dec_output_length; + int macOff = offset + macInputLen; + byte[] receivedMac = Arrays.CopyOfRange(ciphertext, macOff, macOff + macSize); + byte[] calculatedMac = mReadMac.CalculateMacConstantTime(seqNo, type, ciphertext, offset, macInputLen, + blocks_length - macSize, randomData); + + bool badMac = !Arrays.ConstantTimeAreEqual(calculatedMac, receivedMac); + + if (badMac || totalPad == 0) + { + throw new TlsFatalAlert(AlertDescription.bad_record_mac); + } + } + + return Arrays.CopyOfRange(ciphertext, offset, offset + dec_output_length); + } + + protected virtual int CheckPaddingConstantTime(byte[] buf, int off, int len, int blockSize, int macSize) + { + int end = off + len; + byte lastByte = buf[end - 1]; + int padlen = lastByte & 0xff; + int totalPad = padlen + 1; + + int dummyIndex = 0; + byte padDiff = 0; + + if ((TlsUtilities.IsSsl(context) && totalPad > blockSize) || (macSize + totalPad > len)) + { + totalPad = 0; + } + else + { + int padPos = end - totalPad; + do + { + padDiff |= (byte)(buf[padPos++] ^ lastByte); + } + while (padPos < end); + + dummyIndex = totalPad; + + if (padDiff != 0) + { + totalPad = 0; + } + } + + // Run some extra dummy checks so the number of checks is always constant + { + byte[] dummyPad = randomData; + while (dummyIndex < 256) + { + padDiff |= (byte)(dummyPad[dummyIndex++] ^ lastByte); + } + // Ensure the above loop is not eliminated + dummyPad[0] ^= padDiff; + } + + return totalPad; + } + + protected virtual int ChooseExtraPadBlocks(SecureRandom r, int max) + { + // return r.NextInt(max + 1); + + int x = r.NextInt(); + int n = LowestBitSet(x); + return System.Math.Min(n, max); + } + + protected virtual int LowestBitSet(int x) + { + if (x == 0) + return 32; + + uint ux = (uint)x; + int n = 0; + while ((ux & 1U) == 0) + { + ++n; + ux >>= 1; + } + return n; + } + } } diff --git a/crypto/src/crypto/tls/TlsCipher.cs b/crypto/src/crypto/tls/TlsCipher.cs
index 22c769d82..7bd8573ac 100644 --- a/crypto/src/crypto/tls/TlsCipher.cs +++ b/crypto/src/crypto/tls/TlsCipher.cs
@@ -3,12 +3,14 @@ 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); + public interface TlsCipher + { + int GetPlaintextLimit(int ciphertextLimit); - /// <exception cref="IOException"></exception> - byte[] DecodeCiphertext(ContentType type, byte[] ciphertext, int offset, int len); - } + /// <exception cref="IOException"></exception> + byte[] EncodePlaintext(long seqNo, byte type, byte[] plaintext, int offset, int len); + + /// <exception cref="IOException"></exception> + byte[] DecodeCiphertext(long seqNo, byte type, byte[] ciphertext, int offset, int len); + } } diff --git a/crypto/src/crypto/tls/TlsCipherFactory.cs b/crypto/src/crypto/tls/TlsCipherFactory.cs
index 0756603f4..4e1fe0eb9 100644 --- a/crypto/src/crypto/tls/TlsCipherFactory.cs +++ b/crypto/src/crypto/tls/TlsCipherFactory.cs
@@ -3,10 +3,9 @@ using System.IO; namespace Org.BouncyCastle.Crypto.Tls { - public interface TlsCipherFactory - { - /// <exception cref="IOException"></exception> - TlsCipher CreateCipher(TlsClientContext context, EncryptionAlgorithm encryptionAlgorithm, - DigestAlgorithm digestAlgorithm); - } + public interface TlsCipherFactory + { + /// <exception cref="IOException"></exception> + TlsCipher CreateCipher(TlsContext context, int encryptionAlgorithm, int macAlgorithm); + } } diff --git a/crypto/src/crypto/tls/TlsClient.cs b/crypto/src/crypto/tls/TlsClient.cs
index eceaa3cd3..cd5dfad13 100644 --- a/crypto/src/crypto/tls/TlsClient.cs +++ b/crypto/src/crypto/tls/TlsClient.cs
@@ -4,60 +4,74 @@ 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(); + public interface TlsClient + : TlsPeer + { + /// <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>Return the session this client wants to resume, if any.</summary> + /// <remarks>Note that the peer's certificate chain for the session (if any) may need to be periodically revalidated.</remarks> + /// <returns> + /// A <see cref="TlsSession"/> representing the resumable session to be used for this connection, + /// or null to use a new session. + /// </returns> + TlsSession GetSessionToResume(); + + ProtocolVersion ClientHelloRecordLayerVersion { get; } + + ProtocolVersion ClientVersion { get; } + + /// <summary> + /// Get the list of cipher suites that this client supports. + /// </summary> + /// <returns> + /// An array of <see cref="CipherSuite"/> values, each specifying a supported cipher suite. + /// </returns> + int[] 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. + /// An array of <see cref="CompressionMethod"/> values, each specifying a supported compression method. + /// </returns> + byte[] GetCompressionMethods(); + + /// <summary> + /// Get the (optional) table of client extensions to be included in (extended) client hello. + /// </summary> + /// <returns> + /// A <see cref="IDictionary"/> (Int32 -> byte[]). May be null. /// </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); + /// <exception cref="IOException"></exception> + IDictionary GetClientExtensions(); + + /// <exception cref="IOException"></exception> + void NotifyServerVersion(ProtocolVersion selectedVersion); + + /// <summary> + /// Notifies the client of the session_id sent in the ServerHello. + /// </summary> + /// <param name="sessionID">An array of <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(int selectedCipherSuite); /// <summary> /// Report the compression method that was selected by the server. @@ -69,61 +83,52 @@ namespace Org.BouncyCastle.Crypto.Tls /// <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(); - } + void NotifySelectedCompressionMethod(byte selectedCompressionMethod); + + /// <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"/> (Int32 -> byte[]) + /// </param> + void ProcessServerExtensions(IDictionary serverExtensions); + + /// <param name="serverSupplementalData">A <see cref="IList">list</see> of <see cref="SupplementalDataEntry"/></param> + /// <exception cref="IOException"/> + void ProcessServerSupplementalData(IList serverSupplementalData); + + /// <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(); + + /// <returns>A <see cref="IList">list</see> of <see cref="SupplementalDataEntry"/></returns> + /// <exception cref="IOException"/> + IList GetClientSupplementalData(); + + /// <summary>RFC 5077 3.3. NewSessionTicket Handshake Message</summary> + /// <remarks> + /// This method will be called (only) when a NewSessionTicket handshake message is received. The + /// ticket is opaque to the client and clients MUST NOT examine the ticket under the assumption + /// that it complies with e.g. <i>RFC 5077 4. Recommended Ticket Construction</i>. + /// </remarks> + /// <param name="newSessionTicket">The <see cref="NewSessionTicket">ticket</see></param> + /// <exception cref="IOException"/> + void NotifyNewSessionTicket(NewSessionTicket newSessionTicket); + } } diff --git a/crypto/src/crypto/tls/TlsClientContext.cs b/crypto/src/crypto/tls/TlsClientContext.cs
index dbb10aa76..b077d0aaf 100644 --- a/crypto/src/crypto/tls/TlsClientContext.cs +++ b/crypto/src/crypto/tls/TlsClientContext.cs
@@ -4,12 +4,8 @@ using Org.BouncyCastle.Security; namespace Org.BouncyCastle.Crypto.Tls { - public interface TlsClientContext - { - SecureRandom SecureRandom { get; } - - SecurityParameters SecurityParameters { get; } - - object UserObject { get; set; } - } + public interface TlsClientContext + : TlsContext + { + } } diff --git a/crypto/src/crypto/tls/TlsClientContextImpl.cs b/crypto/src/crypto/tls/TlsClientContextImpl.cs
index 9d5dee232..674d68937 100644 --- a/crypto/src/crypto/tls/TlsClientContextImpl.cs +++ b/crypto/src/crypto/tls/TlsClientContextImpl.cs
@@ -4,34 +4,17 @@ using Org.BouncyCastle.Security; namespace Org.BouncyCastle.Crypto.Tls { - internal class TlsClientContextImpl - : TlsClientContext - { - private readonly SecureRandom secureRandom; - private readonly SecurityParameters securityParameters; + internal class TlsClientContextImpl + : AbstractTlsContext, TlsClientContext + { + internal TlsClientContextImpl(SecureRandom secureRandom, SecurityParameters securityParameters) + : base(secureRandom, 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; } - } - } + public override bool IsServer + { + get { return false; } + } + } } diff --git a/crypto/src/crypto/tls/TlsClientProtocol.cs b/crypto/src/crypto/tls/TlsClientProtocol.cs new file mode 100644
index 000000000..e48c92d30 --- /dev/null +++ b/crypto/src/crypto/tls/TlsClientProtocol.cs
@@ -0,0 +1,861 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class TlsClientProtocol + : TlsProtocol + { + protected TlsClient mTlsClient = null; + internal TlsClientContextImpl mTlsClientContext = null; + + protected byte[] mSelectedSessionID = null; + + protected TlsKeyExchange mKeyExchange = null; + protected TlsAuthentication mAuthentication = null; + + protected CertificateStatus mCertificateStatus = null; + protected CertificateRequest mCertificateRequest = null; + + public TlsClientProtocol(Stream stream, SecureRandom secureRandom) + : base(stream, secureRandom) + { + } + + public TlsClientProtocol(Stream input, Stream output, SecureRandom secureRandom) + : base(input, output, secureRandom) + { + } + + /** + * Initiates a TLS handshake in the role of client + * + * @param tlsClient The {@link TlsClient} to use for the handshake. + * @throws IOException If handshake was not successful. + */ + public virtual void Connect(TlsClient tlsClient) + { + if (tlsClient == null) + throw new ArgumentNullException("tlsClient"); + if (this.mTlsClient != null) + throw new InvalidOperationException("'Connect' can only be called once"); + + this.mTlsClient = tlsClient; + + this.mSecurityParameters = new SecurityParameters(); + this.mSecurityParameters.entity = ConnectionEnd.client; + + this.mTlsClientContext = new TlsClientContextImpl(mSecureRandom, mSecurityParameters); + + this.mSecurityParameters.clientRandom = CreateRandomBlock(tlsClient.ShouldUseGmtUnixTime(), + mTlsClientContext.NonceRandomGenerator); + + this.mTlsClient.Init(mTlsClientContext); + this.mRecordStream.Init(mTlsClientContext); + + TlsSession sessionToResume = tlsClient.GetSessionToResume(); + if (sessionToResume != null) + { + SessionParameters sessionParameters = sessionToResume.ExportSessionParameters(); + if (sessionParameters != null) + { + this.mTlsSession = sessionToResume; + this.mSessionParameters = sessionParameters; + } + } + + SendClientHelloMessage(); + this.mConnectionState = CS_CLIENT_HELLO; + + CompleteHandshake(); + } + + protected override void CleanupHandshake() + { + base.CleanupHandshake(); + + this.mSelectedSessionID = null; + this.mKeyExchange = null; + this.mAuthentication = null; + this.mCertificateStatus = null; + this.mCertificateRequest = null; + } + + protected override TlsContext Context + { + get { return mTlsClientContext; } + } + + internal override AbstractTlsContext ContextAdmin + { + get { return mTlsClientContext; } + } + + protected override TlsPeer Peer + { + get { return mTlsClient; } + } + + protected override void HandleHandshakeMessage(byte type, byte[] data) + { + MemoryStream buf = new MemoryStream(data, false); + + if (this.mResumedSession) + { + if (type != HandshakeType.finished || this.mConnectionState != CS_SERVER_HELLO) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + + ProcessFinishedMessage(buf); + this.mConnectionState = CS_SERVER_FINISHED; + + SendFinishedMessage(); + this.mConnectionState = CS_CLIENT_FINISHED; + this.mConnectionState = CS_END; + + return; + } + + switch (type) + { + case HandshakeType.certificate: + { + switch (this.mConnectionState) + { + case CS_SERVER_HELLO: + case CS_SERVER_SUPPLEMENTAL_DATA: + { + if (this.mConnectionState == CS_SERVER_HELLO) + { + HandleSupplementalData(null); + } + + // Parse the Certificate message and Send to cipher suite + + this.mPeerCertificate = Certificate.Parse(buf); + + AssertEmpty(buf); + + // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus + if (this.mPeerCertificate == null || this.mPeerCertificate.IsEmpty) + { + this.mAllowCertificateStatus = false; + } + + this.mKeyExchange.ProcessServerCertificate(this.mPeerCertificate); + + this.mAuthentication = mTlsClient.GetAuthentication(); + this.mAuthentication.NotifyServerCertificate(this.mPeerCertificate); + + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + this.mConnectionState = CS_SERVER_CERTIFICATE; + break; + } + case HandshakeType.certificate_status: + { + switch (this.mConnectionState) + { + case CS_SERVER_CERTIFICATE: + { + if (!this.mAllowCertificateStatus) + { + /* + * RFC 3546 3.6. If a server returns a "CertificateStatus" message, then the + * server MUST have included an extension of type "status_request" with empty + * "extension_data" in the extended server hello.. + */ + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + this.mCertificateStatus = CertificateStatus.Parse(buf); + + AssertEmpty(buf); + + // TODO[RFC 3546] Figure out how to provide this to the client/authentication. + + this.mConnectionState = CS_CERTIFICATE_STATUS; + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.finished: + { + switch (this.mConnectionState) + { + case CS_CLIENT_FINISHED: + case CS_SERVER_SESSION_TICKET: + { + if (this.mConnectionState == CS_CLIENT_FINISHED && this.mExpectSessionTicket) + { + /* + * RFC 5077 3.3. This message MUST be sent if the server included a + * SessionTicket extension in the ServerHello. + */ + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + ProcessFinishedMessage(buf); + this.mConnectionState = CS_SERVER_FINISHED; + this.mConnectionState = CS_END; + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.server_hello: + { + switch (this.mConnectionState) + { + case CS_CLIENT_HELLO: + { + ReceiveServerHelloMessage(buf); + this.mConnectionState = CS_SERVER_HELLO; + + if (this.mSecurityParameters.maxFragmentLength >= 0) + { + int plainTextLimit = 1 << (8 + this.mSecurityParameters.maxFragmentLength); + mRecordStream.SetPlaintextLimit(plainTextLimit); + } + + this.mSecurityParameters.prfAlgorithm = GetPrfAlgorithm(Context, + this.mSecurityParameters.CipherSuite); + + /* + * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify + * verify_data_length has a verify_data_length equal to 12. This includes all + * existing cipher suites. + */ + this.mSecurityParameters.verifyDataLength = 12; + + this.mRecordStream.NotifyHelloComplete(); + + if (this.mResumedSession) + { + this.mSecurityParameters.masterSecret = Arrays.Clone(this.mSessionParameters.MasterSecret); + this.mRecordStream.SetPendingConnectionState(Peer.GetCompression(), Peer.GetCipher()); + + SendChangeCipherSpecMessage(); + } + else + { + InvalidateSession(); + + if (this.mSelectedSessionID.Length > 0) + { + this.mTlsSession = new TlsSessionImpl(this.mSelectedSessionID, null); + } + } + + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.supplemental_data: + { + switch (this.mConnectionState) + { + case CS_SERVER_HELLO: + { + HandleSupplementalData(ReadSupplementalDataMessage(buf)); + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.server_hello_done: + { + switch (this.mConnectionState) + { + case CS_SERVER_HELLO: + case CS_SERVER_SUPPLEMENTAL_DATA: + case CS_SERVER_CERTIFICATE: + case CS_CERTIFICATE_STATUS: + case CS_SERVER_KEY_EXCHANGE: + case CS_CERTIFICATE_REQUEST: + { + if (mConnectionState < CS_SERVER_SUPPLEMENTAL_DATA) + { + HandleSupplementalData(null); + } + + if (mConnectionState < CS_SERVER_CERTIFICATE) + { + // There was no server certificate message; check it's OK + this.mKeyExchange.SkipServerCredentials(); + this.mAuthentication = null; + } + + if (mConnectionState < CS_SERVER_KEY_EXCHANGE) + { + // There was no server key exchange message; check it's OK + this.mKeyExchange.SkipServerKeyExchange(); + } + + AssertEmpty(buf); + + this.mConnectionState = CS_SERVER_HELLO_DONE; + + this.mRecordStream.HandshakeHash.SealHashAlgorithms(); + + IList clientSupplementalData = mTlsClient.GetClientSupplementalData(); + if (clientSupplementalData != null) + { + SendSupplementalDataMessage(clientSupplementalData); + } + this.mConnectionState = CS_CLIENT_SUPPLEMENTAL_DATA; + + TlsCredentials clientCreds = null; + if (mCertificateRequest == null) + { + this.mKeyExchange.SkipClientCredentials(); + } + else + { + clientCreds = this.mAuthentication.GetClientCredentials(mCertificateRequest); + + if (clientCreds == null) + { + this.mKeyExchange.SkipClientCredentials(); + + /* + * RFC 5246 If no suitable certificate is available, the client MUST Send a + * certificate message containing no certificates. + * + * NOTE: In previous RFCs, this was SHOULD instead of MUST. + */ + SendCertificateMessage(Certificate.EmptyChain); + } + else + { + this.mKeyExchange.ProcessClientCredentials(clientCreds); + + SendCertificateMessage(clientCreds.Certificate); + } + } + + this.mConnectionState = CS_CLIENT_CERTIFICATE; + + /* + * Send the client key exchange message, depending on the key exchange we are using + * in our CipherSuite. + */ + SendClientKeyExchangeMessage(); + this.mConnectionState = CS_CLIENT_KEY_EXCHANGE; + + EstablishMasterSecret(Context, mKeyExchange); + mRecordStream.SetPendingConnectionState(Peer.GetCompression(), Peer.GetCipher()); + + TlsHandshakeHash prepareFinishHash = mRecordStream.PrepareToFinish(); + + if (clientCreds != null && clientCreds is TlsSignerCredentials) + { + TlsSignerCredentials signerCredentials = (TlsSignerCredentials)clientCreds; + + /* + * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2 + */ + SignatureAndHashAlgorithm signatureAndHashAlgorithm; + byte[] hash; + + if (TlsUtilities.IsTlsV12(Context)) + { + signatureAndHashAlgorithm = signerCredentials.SignatureAndHashAlgorithm; + if (signatureAndHashAlgorithm == null) + throw new TlsFatalAlert(AlertDescription.internal_error); + + hash = prepareFinishHash.GetFinalHash(signatureAndHashAlgorithm.Hash); + } + else + { + signatureAndHashAlgorithm = null; + hash = GetCurrentPrfHash(Context, prepareFinishHash, null); + } + + byte[] signature = signerCredentials.GenerateCertificateSignature(hash); + DigitallySigned certificateVerify = new DigitallySigned(signatureAndHashAlgorithm, signature); + SendCertificateVerifyMessage(certificateVerify); + + this.mConnectionState = CS_CERTIFICATE_VERIFY; + } + + SendChangeCipherSpecMessage(); + SendFinishedMessage(); + break; + } + default: + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + + this.mConnectionState = CS_CLIENT_FINISHED; + break; + } + case HandshakeType.server_key_exchange: + { + switch (this.mConnectionState) + { + case CS_SERVER_HELLO: + case CS_SERVER_SUPPLEMENTAL_DATA: + case CS_SERVER_CERTIFICATE: + case CS_CERTIFICATE_STATUS: + { + if (mConnectionState < CS_SERVER_SUPPLEMENTAL_DATA) + { + HandleSupplementalData(null); + } + + if (mConnectionState < CS_SERVER_CERTIFICATE) + { + // There was no server certificate message; check it's OK + this.mKeyExchange.SkipServerCredentials(); + this.mAuthentication = null; + } + + this.mKeyExchange.ProcessServerKeyExchange(buf); + + AssertEmpty(buf); + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + this.mConnectionState = CS_SERVER_KEY_EXCHANGE; + break; + } + case HandshakeType.certificate_request: + { + switch (this.mConnectionState) + { + case CS_SERVER_CERTIFICATE: + case CS_CERTIFICATE_STATUS: + case CS_SERVER_KEY_EXCHANGE: + { + if (this.mConnectionState != CS_SERVER_KEY_EXCHANGE) + { + // There was no server key exchange message; check it's OK + this.mKeyExchange.SkipServerKeyExchange(); + } + + if (this.mAuthentication == null) + { + /* + * RFC 2246 7.4.4. It is a fatal handshake_failure alert for an anonymous server + * to request client identification. + */ + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + + this.mCertificateRequest = CertificateRequest.Parse(Context, buf); + + AssertEmpty(buf); + + this.mKeyExchange.ValidateCertificateRequest(this.mCertificateRequest); + + /* + * TODO Give the client a chance to immediately select the CertificateVerify hash + * algorithm here to avoid tracking the other hash algorithms unnecessarily? + */ + TlsUtilities.TrackHashAlgorithms(this.mRecordStream.HandshakeHash, + this.mCertificateRequest.SupportedSignatureAlgorithms); + + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + this.mConnectionState = CS_CERTIFICATE_REQUEST; + break; + } + case HandshakeType.session_ticket: + { + switch (this.mConnectionState) + { + case CS_CLIENT_FINISHED: + { + if (!this.mExpectSessionTicket) + { + /* + * RFC 5077 3.3. This message MUST NOT be sent if the server did not include a + * SessionTicket extension in the ServerHello. + */ + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + /* + * RFC 5077 3.4. If the client receives a session ticket from the server, then it + * discards any Session ID that was sent in the ServerHello. + */ + InvalidateSession(); + + ReceiveNewSessionTicketMessage(buf); + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + this.mConnectionState = CS_SERVER_SESSION_TICKET; + break; + } + case HandshakeType.hello_request: + { + AssertEmpty(buf); + + /* + * RFC 2246 7.4.1.1 Hello request This message will be ignored by the client if the + * client is currently negotiating a session. This message may be ignored by the client + * if it does not wish to renegotiate a session, or the client may, if it wishes, + * respond with a no_renegotiation alert. + */ + if (this.mConnectionState == CS_END) + { + /* + * RFC 5746 4.5 SSLv3 clients that refuse renegotiation SHOULD use a fatal + * handshake_failure alert. + */ + if (TlsUtilities.IsSsl(Context)) + throw new TlsFatalAlert(AlertDescription.handshake_failure); + + string message = "Renegotiation not supported"; + RaiseWarning(AlertDescription.no_renegotiation, message); + } + break; + } + case HandshakeType.client_hello: + case HandshakeType.client_key_exchange: + case HandshakeType.certificate_verify: + case HandshakeType.hello_verify_request: + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + } + + protected virtual void HandleSupplementalData(IList serverSupplementalData) + { + this.mTlsClient.ProcessServerSupplementalData(serverSupplementalData); + this.mConnectionState = CS_SERVER_SUPPLEMENTAL_DATA; + + this.mKeyExchange = mTlsClient.GetKeyExchange(); + this.mKeyExchange.Init(Context); + } + + protected virtual void ReceiveNewSessionTicketMessage(MemoryStream buf) + { + NewSessionTicket newSessionTicket = NewSessionTicket.Parse(buf); + + TlsProtocol.AssertEmpty(buf); + + mTlsClient.NotifyNewSessionTicket(newSessionTicket); + } + + protected virtual void ReceiveServerHelloMessage(MemoryStream buf) + { + ProtocolVersion server_version = TlsUtilities.ReadVersion(buf); + if (server_version.IsDtls) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + // Check that this matches what the server is Sending in the record layer + if (!server_version.Equals(this.mRecordStream.ReadVersion)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + ProtocolVersion client_version = Context.ClientVersion; + if (!server_version.IsEqualOrEarlierVersionOf(client_version)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + this.mRecordStream.SetWriteVersion(server_version); + ContextAdmin.SetServerVersion(server_version); + this.mTlsClient.NotifyServerVersion(server_version); + + /* + * Read the server random + */ + this.mSecurityParameters.serverRandom = TlsUtilities.ReadFully(32, buf); + + this.mSelectedSessionID = TlsUtilities.ReadOpaque8(buf); + if (this.mSelectedSessionID.Length > 32) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + this.mTlsClient.NotifySessionID(this.mSelectedSessionID); + + this.mResumedSession = this.mSelectedSessionID.Length > 0 && this.mTlsSession != null + && Arrays.AreEqual(this.mSelectedSessionID, this.mTlsSession.SessionID); + + /* + * Find out which CipherSuite the server has chosen and check that it was one of the offered + * ones, and is a valid selection for the negotiated version. + */ + int selectedCipherSuite = TlsUtilities.ReadUint16(buf); + if (!Arrays.Contains(this.mOfferedCipherSuites, selectedCipherSuite) + || selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL + || selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV + || !TlsUtilities.IsValidCipherSuiteForVersion(selectedCipherSuite, server_version)) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + this.mTlsClient.NotifySelectedCipherSuite(selectedCipherSuite); + + /* + * Find out which CompressionMethod the server has chosen and check that it was one of the + * offered ones. + */ + byte selectedCompressionMethod = TlsUtilities.ReadUint8(buf); + if (!Arrays.Contains(this.mOfferedCompressionMethods, selectedCompressionMethod)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + this.mTlsClient.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. + */ + this.mServerExtensions = ReadExtensions(buf); + + /* + * RFC 3546 2.2 Note that the extended server hello message is only sent in response to an + * extended client hello message. + * + * However, see RFC 5746 exception below. We always include the SCSV, so an Extended Server + * Hello is always allowed. + */ + if (this.mServerExtensions != null) + { + foreach (int extType in this.mServerExtensions.Keys) + { + /* + * RFC 5746 3.6. Note that Sending a "renegotiation_info" extension in response to a + * ClientHello containing only the SCSV is an explicit exception to the prohibition + * in RFC 5246, Section 7.4.1.4, on the server Sending unsolicited extensions and is + * only allowed because the client is signaling its willingness to receive the + * extension via the TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. + */ + if (extType == ExtensionType.renegotiation_info) + continue; + + /* + * 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[.] + */ + if (this.mResumedSession) + { + // TODO[compat-gnutls] GnuTLS test server Sends server extensions e.g. ec_point_formats + // TODO[compat-openssl] OpenSSL test server Sends server extensions e.g. ec_point_formats + // TODO[compat-polarssl] PolarSSL test server Sends server extensions e.g. ec_point_formats + // throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + /* + * RFC 5246 7.4.1.4 An extension type MUST NOT appear in the ServerHello unless the + * same extension type appeared in the corresponding ClientHello. If a client + * receives an extension type in ServerHello that it did not request in the + * associated ClientHello, it MUST abort the handshake with an unsupported_extension + * fatal alert. + */ + if (null == TlsUtilities.GetExtensionData(this.mClientExtensions, extType)) + throw new TlsFatalAlert(AlertDescription.unsupported_extension); + } + } + + /* + * RFC 5746 3.4. Client Behavior: Initial Handshake + */ + { + /* + * When a ServerHello is received, the client MUST check if it includes the + * "renegotiation_info" extension: + */ + byte[] renegExtData = TlsUtilities.GetExtensionData(this.mServerExtensions, ExtensionType.renegotiation_info); + if (renegExtData != null) + { + /* + * If the extension is present, set the secure_renegotiation flag to TRUE. The + * client MUST then verify that the length of the "renegotiated_connection" + * field is zero, and if it is not, MUST abort the handshake (by Sending a fatal + * handshake_failure alert). + */ + this.mSecureRenegotiation = true; + + if (!Arrays.ConstantTimeAreEqual(renegExtData, CreateRenegotiationInfo(TlsUtilities.EmptyBytes))) + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + } + + // TODO[compat-gnutls] GnuTLS test server fails to Send renegotiation_info extension when resuming + this.mTlsClient.NotifySecureRenegotiation(this.mSecureRenegotiation); + + IDictionary sessionClientExtensions = mClientExtensions, sessionServerExtensions = mServerExtensions; + if (this.mResumedSession) + { + if (selectedCipherSuite != this.mSessionParameters.CipherSuite + || selectedCompressionMethod != this.mSessionParameters.CompressionAlgorithm) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + sessionClientExtensions = null; + sessionServerExtensions = this.mSessionParameters.ReadServerExtensions(); + } + + this.mSecurityParameters.cipherSuite = selectedCipherSuite; + this.mSecurityParameters.compressionAlgorithm = selectedCompressionMethod; + + if (sessionServerExtensions != null) + { + /* + * draft-ietf-tls-encrypt-then-mac-03 3. If a server receives an encrypt-then-MAC + * request extension from a client and then selects a stream or AEAD cipher suite, it + * MUST NOT Send an encrypt-then-MAC response extension back to the client. + */ + bool serverSentEncryptThenMAC = TlsExtensionsUtilities.HasEncryptThenMacExtension(sessionServerExtensions); + if (serverSentEncryptThenMAC && !TlsUtilities.IsBlockCipherSuite(selectedCipherSuite)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + this.mSecurityParameters.encryptThenMac = serverSentEncryptThenMAC; + + this.mSecurityParameters.maxFragmentLength = ProcessMaxFragmentLengthExtension(sessionClientExtensions, + sessionServerExtensions, AlertDescription.illegal_parameter); + + this.mSecurityParameters.truncatedHMac = TlsExtensionsUtilities.HasTruncatedHMacExtension(sessionServerExtensions); + + /* + * TODO It's surprising that there's no provision to allow a 'fresh' CertificateStatus to be sent in + * a session resumption handshake. + */ + this.mAllowCertificateStatus = !this.mResumedSession + && TlsUtilities.HasExpectedEmptyExtensionData(sessionServerExtensions, ExtensionType.status_request, + AlertDescription.illegal_parameter); + + this.mExpectSessionTicket = !this.mResumedSession + && TlsUtilities.HasExpectedEmptyExtensionData(sessionServerExtensions, ExtensionType.session_ticket, + AlertDescription.illegal_parameter); + } + + if (sessionClientExtensions != null) + { + this.mTlsClient.ProcessServerExtensions(sessionServerExtensions); + } + } + + protected virtual void SendCertificateVerifyMessage(DigitallySigned certificateVerify) + { + HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate_verify); + + certificateVerify.Encode(message); + + message.WriteToRecordStream(this); + } + + protected virtual void SendClientHelloMessage() + { + this.mRecordStream.SetWriteVersion(this.mTlsClient.ClientHelloRecordLayerVersion); + + ProtocolVersion client_version = this.mTlsClient.ClientVersion; + if (client_version.IsDtls) + throw new TlsFatalAlert(AlertDescription.internal_error); + + ContextAdmin.SetClientVersion(client_version); + + /* + * TODO RFC 5077 3.4. When presenting a ticket, the client MAY generate and include a + * Session ID in the TLS ClientHello. + */ + byte[] session_id = TlsUtilities.EmptyBytes; + if (this.mTlsSession != null) + { + session_id = this.mTlsSession.SessionID; + if (session_id == null || session_id.Length > 32) + { + session_id = TlsUtilities.EmptyBytes; + } + } + + this.mOfferedCipherSuites = this.mTlsClient.GetCipherSuites(); + + this.mOfferedCompressionMethods = this.mTlsClient.GetCompressionMethods(); + + if (session_id.Length > 0 && this.mSessionParameters != null) + { + if (!Arrays.Contains(this.mOfferedCipherSuites, mSessionParameters.CipherSuite) + || !Arrays.Contains(this.mOfferedCompressionMethods, mSessionParameters.CompressionAlgorithm)) + { + session_id = TlsUtilities.EmptyBytes; + } + } + + this.mClientExtensions = this.mTlsClient.GetClientExtensions(); + + HandshakeMessage message = new HandshakeMessage(HandshakeType.client_hello); + + TlsUtilities.WriteVersion(client_version, message); + + message.Write(this.mSecurityParameters.ClientRandom); + + TlsUtilities.WriteOpaque8(session_id, message); + + // 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. + */ + byte[] renegExtData = TlsUtilities.GetExtensionData(mClientExtensions, ExtensionType.renegotiation_info); + bool noRenegExt = (null == renegExtData); + + bool noSCSV = !Arrays.Contains(mOfferedCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV); + + if (noRenegExt && noSCSV) + { + // TODO Consider whether to default to a client extension instead + // this.mClientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(this.mClientExtensions); + // this.mClientExtensions[ExtensionType.renegotiation_info] = CreateRenegotiationInfo(TlsUtilities.EmptyBytes); + this.mOfferedCipherSuites = Arrays.Append(mOfferedCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV); + } + + TlsUtilities.WriteUint16ArrayWithUint16Length(mOfferedCipherSuites, message); + } + + TlsUtilities.WriteUint8ArrayWithUint8Length(mOfferedCompressionMethods, message); + + if (mClientExtensions != null) + { + WriteExtensions(message, mClientExtensions); + } + + message.WriteToRecordStream(this); + } + + protected virtual void SendClientKeyExchangeMessage() + { + HandshakeMessage message = new HandshakeMessage(HandshakeType.client_key_exchange); + + this.mKeyExchange.GenerateClientKeyExchange(message); + + message.WriteToRecordStream(this); + } + } +} 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/TlsContext.cs b/crypto/src/crypto/tls/TlsContext.cs new file mode 100644
index 000000000..d066723fc --- /dev/null +++ b/crypto/src/crypto/tls/TlsContext.cs
@@ -0,0 +1,45 @@ +using System; + +using Org.BouncyCastle.Crypto.Prng; +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsContext + { + IRandomGenerator NonceRandomGenerator { get; } + + SecureRandom SecureRandom { get; } + + SecurityParameters SecurityParameters { get; } + + bool IsServer { get; } + + ProtocolVersion ClientVersion { get; } + + ProtocolVersion ServerVersion { get; } + + /** + * Used to get the resumable session, if any, used by this connection. Only available after the + * handshake has successfully completed. + * + * @return A {@link TlsSession} representing the resumable session used by this connection, or + * null if no resumable session available. + * @see TlsPeer#NotifyHandshakeComplete() + */ + TlsSession ResumableSession { get; } + + object UserObject { get; set; } + + /** + * Export keying material according to RFC 5705: "Keying Material Exporters for TLS". + * + * @param asciiLabel indicates which application will use the exported keys. + * @param context_value allows the application using the exporter to mix its own data with the TLS PRF for + * the exporter output. + * @param length the number of bytes to generate + * @return a pseudorandom bit string of 'length' bytes generated from the master_secret. + */ + byte[] ExportKeyingMaterial(string asciiLabel, byte[] context_value, int length); + } +} diff --git a/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
index 40ac416e0..b831249a6 100644 --- a/crypto/src/crypto/tls/TlsDHKeyExchange.cs +++ b/crypto/src/crypto/tls/TlsDHKeyExchange.cs
@@ -1,201 +1,193 @@ 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.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); - } - } + /// <summary>(D)TLS DH key exchange.</summary> + public class TlsDHKeyExchange + : AbstractTlsKeyExchange + { + protected TlsSigner mTlsSigner; + protected DHParameters mDHParameters; + + protected AsymmetricKeyParameter mServerPublicKey; + protected DHPublicKeyParameters mDHAgreeServerPublicKey; + protected TlsAgreementCredentials mAgreementCredentials; + protected DHPrivateKeyParameters mDHAgreeClientPrivateKey; + + protected DHPrivateKeyParameters mDHAgreeServerPrivateKey; + protected DHPublicKeyParameters mDHAgreeClientPublicKey; + + public TlsDHKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, DHParameters dhParameters) + : base(keyExchange, supportedSignatureAlgorithms) + { + switch (keyExchange) + { + case KeyExchangeAlgorithm.DH_RSA: + case KeyExchangeAlgorithm.DH_DSS: + this.mTlsSigner = null; + break; + case KeyExchangeAlgorithm.DHE_RSA: + this.mTlsSigner = new TlsRsaSigner(); + break; + case KeyExchangeAlgorithm.DHE_DSS: + this.mTlsSigner = new TlsDssSigner(); + break; + default: + throw new InvalidOperationException("unsupported key exchange algorithm"); + } + + this.mDHParameters = dhParameters; + } + + public override void Init(TlsContext context) + { + base.Init(context); + + if (this.mTlsSigner != null) + { + this.mTlsSigner.Init(context); + } + } + + public override void SkipServerCredentials() + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + public override void ProcessServerCertificate(Certificate serverCertificate) + { + if (serverCertificate.IsEmpty) + throw new TlsFatalAlert(AlertDescription.bad_certificate); + + X509CertificateStructure x509Cert = serverCertificate.GetCertificateAt(0); + + SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo; + try + { + this.mServerPublicKey = PublicKeyFactory.CreateKey(keyInfo); + } + catch (Exception e) + { + throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e); + } + + if (mTlsSigner == null) + { + try + { + this.mDHAgreeServerPublicKey = TlsDHUtilities.ValidateDHPublicKey((DHPublicKeyParameters)this.mServerPublicKey); + } + catch (InvalidCastException e) + { + throw new TlsFatalAlert(AlertDescription.certificate_unknown, e); + } + + TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.KeyAgreement); + } + else + { + if (!mTlsSigner.IsValidPublicKey(this.mServerPublicKey)) + { + throw new TlsFatalAlert(AlertDescription.certificate_unknown); + } + + TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature); + } + + base.ProcessServerCertificate(serverCertificate); + } + + public override bool RequiresServerKeyExchange + { + get + { + switch (mKeyExchange) + { + case KeyExchangeAlgorithm.DHE_DSS: + case KeyExchangeAlgorithm.DHE_RSA: + case KeyExchangeAlgorithm.DH_anon: + return true; + default: + return false; + } + } + } + + public override void ValidateCertificateRequest(CertificateRequest certificateRequest) + { + byte[] types = certificateRequest.CertificateTypes; + for (int i = 0; i < types.Length; ++i) + { + switch (types[i]) + { + case ClientCertificateType.rsa_sign: + case ClientCertificateType.dss_sign: + case ClientCertificateType.rsa_fixed_dh: + case ClientCertificateType.dss_fixed_dh: + case ClientCertificateType.ecdsa_sign: + break; + default: + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + } + + public override void ProcessClientCredentials(TlsCredentials clientCredentials) + { + if (clientCredentials is TlsAgreementCredentials) + { + // TODO Validate client cert has matching parameters (see 'areCompatibleParameters')? + + this.mAgreementCredentials = (TlsAgreementCredentials)clientCredentials; + } + else if (clientCredentials is TlsSignerCredentials) + { + // OK + } + else + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public override 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 (mAgreementCredentials == null) + { + this.mDHAgreeClientPrivateKey = TlsDHUtilities.GenerateEphemeralClientKeyExchange(context.SecureRandom, + mDHAgreeServerPublicKey.Parameters, output); + } + } + + public override byte[] GeneratePremasterSecret() + { + if (mAgreementCredentials != null) + { + return mAgreementCredentials.GenerateAgreement(mDHAgreeServerPublicKey); + } + + if (mDHAgreeServerPrivateKey != null) + { + return TlsDHUtilities.CalculateDHBasicAgreement(mDHAgreeClientPublicKey, mDHAgreeServerPrivateKey); + } + + if (mDHAgreeClientPrivateKey != null) + { + return TlsDHUtilities.CalculateDHBasicAgreement(mDHAgreeServerPublicKey, mDHAgreeClientPrivateKey); + } + + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } } diff --git a/crypto/src/crypto/tls/TlsDHUtilities.cs b/crypto/src/crypto/tls/TlsDHUtilities.cs
index 733749ea1..477c3ebac 100644 --- a/crypto/src/crypto/tls/TlsDHUtilities.cs +++ b/crypto/src/crypto/tls/TlsDHUtilities.cs
@@ -10,61 +10,92 @@ 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 + public abstract class TlsDHUtilities + { + internal static readonly BigInteger One = BigInteger.One; + internal static readonly BigInteger Two = BigInteger.Two; + + public static bool AreCompatibleParameters(DHParameters a, DHParameters b) + { + return a.P.Equals(b.P) && a.G.Equals(b.G); + } + + public static byte[] CalculateDHBasicAgreement(DHPublicKeyParameters publicKey, + DHPrivateKeyParameters privateKey) + { + DHBasicAgreement basicAgreement = new DHBasicAgreement(); + basicAgreement.Init(privateKey); + BigInteger agreementValue = basicAgreement.CalculateAgreement(publicKey); + + /* + * RFC 5246 8.1.2. Leading bytes of Z that contain all zero bits are stripped before it is + * used as the pre_master_secret. + */ + return BigIntegers.AsUnsignedByteArray(agreementValue); + } + + public static AsymmetricCipherKeyPair GenerateDHKeyPair(SecureRandom random, DHParameters dhParams) + { + DHBasicKeyPairGenerator dhGen = new DHBasicKeyPairGenerator(); + dhGen.Init(new DHKeyGenerationParameters(random, dhParams)); + return dhGen.GenerateKeyPair(); + } + + public static DHPrivateKeyParameters GenerateEphemeralClientKeyExchange(SecureRandom random, + DHParameters dhParams, Stream output) + { + AsymmetricCipherKeyPair kp = GenerateDHKeyPair(random, dhParams); + + DHPublicKeyParameters dhPublic = (DHPublicKeyParameters)kp.Public; + WriteDHParameter(dhPublic.Y, output); + + return (DHPrivateKeyParameters)kp.Private; + } + + public static DHPrivateKeyParameters GenerateEphemeralServerKeyExchange(SecureRandom random, + DHParameters dhParams, Stream output) + { + AsymmetricCipherKeyPair kp = GenerateDHKeyPair(random, dhParams); + + DHPublicKeyParameters dhPublic = (DHPublicKeyParameters)kp.Public; + new ServerDHParams(dhPublic).Encode(output); + + return (DHPrivateKeyParameters)kp.Private; + } + + 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(Two) < 0 || g.CompareTo(p.Subtract(Two)) > 0) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + if (Y.CompareTo(Two) < 0 || Y.CompareTo(p.Subtract(One)) > 0) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + // TODO See RFC 2631 for more discussion of Diffie-Hellman validation + + return key; + } + + public static BigInteger ReadDHParameter(Stream input) + { + return new BigInteger(1, TlsUtilities.ReadOpaque16(input)); + } + + public static void WriteDHParameter(BigInteger x, Stream output) + { + TlsUtilities.WriteOpaque16(BigIntegers.AsUnsignedByteArray(x), output); + } + } +} diff --git a/crypto/src/crypto/tls/TlsDeflateCompression.cs b/crypto/src/crypto/tls/TlsDeflateCompression.cs
index 146c961c7..9e1152908 100644 --- a/crypto/src/crypto/tls/TlsDeflateCompression.cs +++ b/crypto/src/crypto/tls/TlsDeflateCompression.cs
@@ -5,41 +5,64 @@ 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; - } - } - } + public class TlsDeflateCompression : TlsCompression + { + public const int LEVEL_NONE = JZlib.Z_NO_COMPRESSION; + public const int LEVEL_FASTEST = JZlib.Z_BEST_SPEED; + public const int LEVEL_SMALLEST = JZlib.Z_BEST_COMPRESSION; + public const int LEVEL_DEFAULT = JZlib.Z_DEFAULT_COMPRESSION; + + protected readonly ZStream zIn, zOut; + + public TlsDeflateCompression() + : this(LEVEL_DEFAULT) + { + } + + public TlsDeflateCompression(int level) + { + this.zIn = new ZStream(); + this.zIn.inflateInit(); + + this.zOut = new ZStream(); + this.zOut.deflateInit(level); + } + + 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, z) + { + this.compress = compress; + + /* + * See discussion at http://www.bolet.org/~pornin/deflate-flush.html . + */ + this.FlushMode = JZlib.Z_SYNC_FLUSH; + } + + public override void Flush() + { + /* + * TODO The inflateSyncPoint doesn't appear to work the way I hoped at the moment. + * In any case, we may like to accept PARTIAL_FLUSH input, not just SYNC_FLUSH. + * It's not clear how to check this in the Inflater. + */ + //if (!this.compress && (z == null || z.istate == null || z.istate.inflateSyncPoint(z) <= 0)) + //{ + // throw new TlsFatalAlert(AlertDescription.decompression_failure); + //} + } + } + } } diff --git a/crypto/src/crypto/tls/TlsDheKeyExchange.cs b/crypto/src/crypto/tls/TlsDheKeyExchange.cs
index edadaeb38..3c05bb6f0 100644 --- a/crypto/src/crypto/tls/TlsDheKeyExchange.cs +++ b/crypto/src/crypto/tls/TlsDheKeyExchange.cs
@@ -1,56 +1,105 @@ using System; +using System.Collections; using System.IO; -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.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; - } - } + public class TlsDheKeyExchange + : TlsDHKeyExchange + { + protected TlsSignerCredentials mServerCredentials = null; + + public TlsDheKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, DHParameters dhParameters) + : base(keyExchange, supportedSignatureAlgorithms, dhParameters) + { + } + + public override void ProcessServerCredentials(TlsCredentials serverCredentials) + { + if (!(serverCredentials is TlsSignerCredentials)) + throw new TlsFatalAlert(AlertDescription.internal_error); + + ProcessServerCertificate(serverCredentials.Certificate); + + this.mServerCredentials = (TlsSignerCredentials)serverCredentials; + } + + public override byte[] GenerateServerKeyExchange() + { + if (this.mDHParameters == null) + throw new TlsFatalAlert(AlertDescription.internal_error); + + DigestInputBuffer buf = new DigestInputBuffer(); + + this.mDHAgreeServerPrivateKey = TlsDHUtilities.GenerateEphemeralServerKeyExchange(context.SecureRandom, + this.mDHParameters, buf); + + /* + * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2 + */ + SignatureAndHashAlgorithm signatureAndHashAlgorithm; + IDigest d; + + if (TlsUtilities.IsTlsV12(context)) + { + signatureAndHashAlgorithm = mServerCredentials.SignatureAndHashAlgorithm; + if (signatureAndHashAlgorithm == null) + throw new TlsFatalAlert(AlertDescription.internal_error); + + d = TlsUtilities.CreateHash(signatureAndHashAlgorithm.Hash); + } + else + { + signatureAndHashAlgorithm = null; + d = new CombinedHash(); + } + + SecurityParameters securityParameters = context.SecurityParameters; + d.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length); + d.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length); + buf.UpdateDigest(d); + + byte[] hash = DigestUtilities.DoFinal(d); + + byte[] signature = mServerCredentials.GenerateCertificateSignature(hash); + + DigitallySigned signed_params = new DigitallySigned(signatureAndHashAlgorithm, signature); + signed_params.Encode(buf); + + return buf.ToArray(); + } + + public override void ProcessServerKeyExchange(Stream input) + { + SecurityParameters securityParameters = context.SecurityParameters; + + SignerInputBuffer buf = new SignerInputBuffer(); + Stream teeIn = new TeeInputStream(input, buf); + + ServerDHParams dhParams = ServerDHParams.Parse(teeIn); + + DigitallySigned signed_params = DigitallySigned.Parse(context, input); + + ISigner signer = InitVerifyer(mTlsSigner, signed_params.Algorithm, securityParameters); + buf.UpdateSigner(signer); + if (!signer.VerifySignature(signed_params.Signature)) + throw new TlsFatalAlert(AlertDescription.decrypt_error); + + this.mDHAgreeServerPublicKey = TlsDHUtilities.ValidateDHPublicKey(dhParams.PublicKey); + } + + protected virtual ISigner InitVerifyer(TlsSigner tlsSigner, SignatureAndHashAlgorithm algorithm, + SecurityParameters securityParameters) + { + ISigner signer = tlsSigner.CreateVerifyer(algorithm, this.mServerPublicKey); + 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
index 27d7b1f91..a5ac55974 100644 --- a/crypto/src/crypto/tls/TlsDsaSigner.cs +++ b/crypto/src/crypto/tls/TlsDsaSigner.cs
@@ -7,45 +7,77 @@ 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(); - } + public abstract class TlsDsaSigner + : AbstractTlsSigner + { + public override byte[] GenerateRawSignature(SignatureAndHashAlgorithm algorithm, + AsymmetricKeyParameter privateKey, byte[] hash) + { + ISigner signer = MakeSigner(algorithm, true, true, + new ParametersWithRandom(privateKey, this.mContext.SecureRandom)); + if (algorithm == null) + { + // Note: Only use the SHA1 part of the (MD5/SHA1) hash + signer.BlockUpdate(hash, 16, 20); + } + else + { + signer.BlockUpdate(hash, 0, hash.Length); + } + return signer.GenerateSignature(); + } + + public override bool VerifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes, + AsymmetricKeyParameter publicKey, byte[] hash) + { + ISigner signer = MakeSigner(algorithm, true, false, publicKey); + if (algorithm == null) + { + // Note: Only use the SHA1 part of the (MD5/SHA1) hash + signer.BlockUpdate(hash, 16, 20); + } + else + { + signer.BlockUpdate(hash, 0, hash.Length); + } + return signer.VerifySignature(sigBytes); + } + + public override ISigner CreateSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey) + { + return MakeSigner(algorithm, false, true, privateKey); + } + + public override ISigner CreateVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey) + { + return MakeSigner(algorithm, false, false, publicKey); + } + + protected virtual ICipherParameters MakeInitParameters(bool forSigning, ICipherParameters cp) + { + return cp; + } + + protected virtual ISigner MakeSigner(SignatureAndHashAlgorithm algorithm, bool raw, bool forSigning, + ICipherParameters cp) + { + if ((algorithm != null) != TlsUtilities.IsTlsV12(mContext)) + throw new InvalidOperationException(); + + // TODO For TLS 1.2+, lift the SHA-1 restriction here + if (algorithm != null && (algorithm.Hash != HashAlgorithm.sha1 || algorithm.Signature != SignatureAlgorithm)) + throw new InvalidOperationException(); + + byte hashAlgorithm = algorithm == null ? HashAlgorithm.sha1 : algorithm.Hash; + IDigest d = raw ? new NullDigest() : TlsUtilities.CreateHash(hashAlgorithm); + + ISigner s = new DsaDigestSigner(CreateDsaImpl(hashAlgorithm), d); + s.Init(forSigning, MakeInitParameters(forSigning, cp)); + return s; + } + + protected abstract byte SignatureAlgorithm { get; } + + protected abstract IDsa CreateDsaImpl(byte hashAlgorithm); + } } diff --git a/crypto/src/crypto/tls/TlsDssSigner.cs b/crypto/src/crypto/tls/TlsDssSigner.cs
index c6f1abcec..707ef3853 100644 --- a/crypto/src/crypto/tls/TlsDssSigner.cs +++ b/crypto/src/crypto/tls/TlsDssSigner.cs
@@ -5,17 +5,22 @@ using Org.BouncyCastle.Crypto.Signers; namespace Org.BouncyCastle.Crypto.Tls { - internal class TlsDssSigner - : TlsDsaSigner - { - public override bool IsValidPublicKey(AsymmetricKeyParameter publicKey) - { - return publicKey is DsaPublicKeyParameters; - } + public class TlsDssSigner + : TlsDsaSigner + { + public override bool IsValidPublicKey(AsymmetricKeyParameter publicKey) + { + return publicKey is DsaPublicKeyParameters; + } - protected override IDsa CreateDsaImpl() - { - return new DsaSigner(); - } - } + protected override IDsa CreateDsaImpl(byte hashAlgorithm) + { + return new DsaSigner(new HMacDsaKCalculator(TlsUtilities.CreateHash(hashAlgorithm))); + } + + protected override byte SignatureAlgorithm + { + get { return Tls.SignatureAlgorithm.dsa; } + } + } } diff --git a/crypto/src/crypto/tls/TlsECDHKeyExchange.cs b/crypto/src/crypto/tls/TlsECDHKeyExchange.cs
index 83983ba47..42dc2f2ef 100644 --- a/crypto/src/crypto/tls/TlsECDHKeyExchange.cs +++ b/crypto/src/crypto/tls/TlsECDHKeyExchange.cs
@@ -3,228 +3,209 @@ 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 + /// <summary>(D)TLS ECDH key exchange (see RFC 4492).</summary> + public class TlsECDHKeyExchange + : AbstractTlsKeyExchange { - protected TlsClientContext context; - protected KeyExchangeAlgorithm keyExchange; - protected TlsSigner tlsSigner; + protected TlsSigner mTlsSigner; + protected int[] mNamedCurves; + protected byte[] mClientECPointFormats, mServerECPointFormats; - protected AsymmetricKeyParameter serverPublicKey; - protected ECPublicKeyParameters ecAgreeServerPublicKey; - protected TlsAgreementCredentials agreementCredentials; - protected ECPrivateKeyParameters ecAgreeClientPrivateKey = null; + protected AsymmetricKeyParameter mServerPublicKey; + protected TlsAgreementCredentials mAgreementCredentials; - internal TlsECDHKeyExchange(TlsClientContext context, KeyExchangeAlgorithm keyExchange) + protected ECPrivateKeyParameters mECAgreePrivateKey; + protected ECPublicKeyParameters mECAgreePublicKey; + + public TlsECDHKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, int[] namedCurves, + byte[] clientECPointFormats, byte[] serverECPointFormats) + : base(keyExchange, supportedSignatureAlgorithms) { - switch (keyExchange) - { - case KeyExchangeAlgorithm.ECDHE_RSA: - this.tlsSigner = new TlsRsaSigner(); - break; - case KeyExchangeAlgorithm.ECDHE_ECDSA: - this.tlsSigner = new TlsECDsaSigner(); - break; - case KeyExchangeAlgorithm.ECDH_RSA: - case KeyExchangeAlgorithm.ECDH_ECDSA: - this.tlsSigner = null; - break; - default: - throw new ArgumentException("unsupported key exchange algorithm", "keyExchange"); - } - - this.context = context; - this.keyExchange = keyExchange; + switch (keyExchange) + { + case KeyExchangeAlgorithm.ECDHE_RSA: + this.mTlsSigner = new TlsRsaSigner(); + break; + case KeyExchangeAlgorithm.ECDHE_ECDSA: + this.mTlsSigner = new TlsECDsaSigner(); + break; + case KeyExchangeAlgorithm.ECDH_RSA: + case KeyExchangeAlgorithm.ECDH_ECDSA: + this.mTlsSigner = null; + break; + default: + throw new InvalidOperationException("unsupported key exchange algorithm"); + } + + this.mNamedCurves = namedCurves; + this.mClientECPointFormats = clientECPointFormats; + this.mServerECPointFormats = serverECPointFormats; } - public virtual void SkipServerCertificate() + public override void Init(TlsContext context) + { + base.Init(context); + + if (this.mTlsSigner != null) + { + this.mTlsSigner.Init(context); + } + } + + public override void SkipServerCredentials() { throw new TlsFatalAlert(AlertDescription.unexpected_message); } - public virtual void ProcessServerCertificate(Certificate serverCertificate) + public override void ProcessServerCertificate(Certificate serverCertificate) { - X509CertificateStructure x509Cert = serverCertificate.certs[0]; - SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo; + if (serverCertificate.IsEmpty) + throw new TlsFatalAlert(AlertDescription.bad_certificate); + + X509CertificateStructure x509Cert = serverCertificate.GetCertificateAt(0); + SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo; try { - this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo); + this.mServerPublicKey = PublicKeyFactory.CreateKey(keyInfo); } - catch (Exception) + catch (Exception e) { - throw new TlsFatalAlert(AlertDescription.unsupported_certificate); + throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e); } - 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 + if (mTlsSigner == null) + { + try + { + this.mECAgreePublicKey = TlsEccUtilities.ValidateECPublicKey((ECPublicKeyParameters) this.mServerPublicKey); + } + catch (InvalidCastException e) + { + throw new TlsFatalAlert(AlertDescription.certificate_unknown, e); + } + + TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.KeyAgreement); + } + else + { + if (!mTlsSigner.IsValidPublicKey(this.mServerPublicKey)) + throw new TlsFatalAlert(AlertDescription.certificate_unknown); + + TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature); + } + + base.ProcessServerCertificate(serverCertificate); + } + + public override bool RequiresServerKeyExchange + { + get + { + switch (mKeyExchange) + { + case KeyExchangeAlgorithm.ECDHE_ECDSA: + case KeyExchangeAlgorithm.ECDHE_RSA: + case KeyExchangeAlgorithm.ECDH_anon: + return true; + default: + return false; + } + } + } + + public override void ValidateCertificateRequest(CertificateRequest certificateRequest) + { /* - * 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." - */ + * 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. + */ + byte[] types = certificateRequest.CertificateTypes; + for (int i = 0; i < types.Length; ++i) + { + switch (types[i]) + { + case ClientCertificateType.rsa_sign: + case ClientCertificateType.dss_sign: + case ClientCertificateType.ecdsa_sign: + case ClientCertificateType.rsa_fixed_ecdh: + case ClientCertificateType.ecdsa_fixed_ecdh: + break; + default: + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } } - - public virtual void SkipServerKeyExchange() + + public override void ProcessClientCredentials(TlsCredentials clientCredentials) { - // do nothing + if (clientCredentials is TlsAgreementCredentials) + { + // TODO Validate client cert has matching parameters (see 'TlsEccUtilities.AreOnSameCurve')? + + this.mAgreementCredentials = (TlsAgreementCredentials)clientCredentials; + } + else if (clientCredentials is TlsSignerCredentials) + { + // OK + } + else + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } } - public virtual void ProcessServerKeyExchange(Stream input) + public override void GenerateClientKeyExchange(Stream output) { - throw new TlsFatalAlert(AlertDescription.unexpected_message); + if (mAgreementCredentials == null) + { + this.mECAgreePrivateKey = TlsEccUtilities.GenerateEphemeralClientKeyExchange(context.SecureRandom, + mServerECPointFormats, mECAgreePublicKey.Parameters, output); + } } - 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) + public override void ProcessClientCertificate(Certificate clientCertificate) { - if (agreementCredentials == null) - { - GenerateEphemeralClientKeyExchange(ecAgreeServerPublicKey.Parameters, output); - } + // TODO Extract the public key + // TODO If the certificate is 'fixed', take the public key as mECAgreeClientPublicKey } - public virtual byte[] GeneratePremasterSecret() + public override void ProcessClientKeyExchange(Stream input) { - if (agreementCredentials != null) - { - return agreementCredentials.GenerateAgreement(ecAgreeServerPublicKey); - } + if (mECAgreePublicKey != null) + { + // For ecdsa_fixed_ecdh and rsa_fixed_ecdh, the key arrived in the client certificate + return; + } + + byte[] point = TlsUtilities.ReadOpaque8(input); - return CalculateECDHBasicAgreement(ecAgreeServerPublicKey, ecAgreeClientPrivateKey); + ECDomainParameters curve_params = this.mECAgreePrivateKey.Parameters; + + this.mECAgreePublicKey = TlsEccUtilities.ValidateECPublicKey(TlsEccUtilities.DeserializeECPublicKey( + mServerECPointFormats, curve_params, point)); } - 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; - } + public override byte[] GeneratePremasterSecret() + { + if (mAgreementCredentials != null) + { + return mAgreementCredentials.GenerateAgreement(mECAgreePublicKey); + } + + if (mECAgreePrivateKey != null) + { + return TlsEccUtilities.CalculateECDHBasicAgreement(mECAgreePublicKey, mECAgreePrivateKey); + } + + throw new TlsFatalAlert(AlertDescription.internal_error); + } } } diff --git a/crypto/src/crypto/tls/TlsECDheKeyExchange.cs b/crypto/src/crypto/tls/TlsECDheKeyExchange.cs
index 5516154ce..0644bd44d 100644 --- a/crypto/src/crypto/tls/TlsECDheKeyExchange.cs +++ b/crypto/src/crypto/tls/TlsECDheKeyExchange.cs
@@ -2,106 +2,202 @@ using System; using System.Collections; using System.IO; -using Org.BouncyCastle.Crypto.IO; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.IO; namespace Org.BouncyCastle.Crypto.Tls { - /** - * ECDHE key exchange (see RFC 4492) - */ - internal class TlsECDheKeyExchange : TlsECDHKeyExchange + /// <summary>(D)TLS ECDHE key exchange (see RFC 4492).</summary> + public class TlsECDheKeyExchange + : TlsECDHKeyExchange { - internal TlsECDheKeyExchange(TlsClientContext context, KeyExchangeAlgorithm keyExchange) - : base(context, keyExchange) + protected TlsSignerCredentials mServerCredentials = null; + + public TlsECDheKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, int[] namedCurves, + byte[] clientECPointFormats, byte[] serverECPointFormats) + : base(keyExchange, supportedSignatureAlgorithms, namedCurves, clientECPointFormats, serverECPointFormats) { } - public override void SkipServerKeyExchange() + public override void ProcessServerCredentials(TlsCredentials serverCredentials) { - throw new TlsFatalAlert(AlertDescription.unexpected_message); + if (!(serverCredentials is TlsSignerCredentials)) + throw new TlsFatalAlert(AlertDescription.internal_error); + + ProcessServerCertificate(serverCredentials.Certificate); + + this.mServerCredentials = (TlsSignerCredentials)serverCredentials; } - public override void ProcessServerKeyExchange(Stream input) + public override byte[] GenerateServerKeyExchange() { - SecurityParameters securityParameters = context.SecurityParameters; - - ISigner signer = InitSigner(tlsSigner, securityParameters); - Stream sigIn = new SignerStream(input, signer, null); + /* + * First we try to find a supported named curve from the client's list. + */ + int namedCurve = -1; + if (mNamedCurves == null) + { + // TODO Let the peer choose the default named curve + namedCurve = NamedCurve.secp256r1; + } + else + { + for (int i = 0; i < mNamedCurves.Length; ++i) + { + int entry = mNamedCurves[i]; + if (NamedCurve.IsValid(entry) && TlsEccUtilities.IsSupportedNamedCurve(entry)) + { + namedCurve = entry; + break; + } + } + } - ECCurveType curveType = (ECCurveType)TlsUtilities.ReadUint8(sigIn); - ECDomainParameters curve_params; + ECDomainParameters curve_params = null; + if (namedCurve >= 0) + { + curve_params = TlsEccUtilities.GetParametersForNamedCurve(namedCurve); + } + else + { + /* + * If no named curves are suitable, check if the client supports explicit curves. + */ + if (Arrays.Contains(mNamedCurves, NamedCurve.arbitrary_explicit_prime_curves)) + { + curve_params = TlsEccUtilities.GetParametersForNamedCurve(NamedCurve.secp256r1); + } + else if (Arrays.Contains(mNamedCurves, NamedCurve.arbitrary_explicit_char2_curves)) + { + curve_params = TlsEccUtilities.GetParametersForNamedCurve(NamedCurve.sect283r1); + } + } - // Currently, we only support named curves - if (curveType == ECCurveType.named_curve) + if (curve_params == null) { - NamedCurve namedCurve = (NamedCurve)TlsUtilities.ReadUint16(sigIn); + /* + * NOTE: We shouldn't have negotiated ECDHE key exchange since we apparently can't find + * a suitable curve. + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } - // TODO Check namedCurve is one we offered? + AsymmetricCipherKeyPair kp = TlsEccUtilities.GenerateECKeyPair(context.SecureRandom, curve_params); + this.mECAgreePrivateKey = (ECPrivateKeyParameters)kp.Private; - curve_params = NamedCurveHelper.GetECParameters(namedCurve); + DigestInputBuffer buf = new DigestInputBuffer(); + + if (namedCurve < 0) + { + TlsEccUtilities.WriteExplicitECParameters(mClientECPointFormats, curve_params, buf); } else { - // TODO Add support for explicit curve parameters (read from sigIn) - - throw new TlsFatalAlert(AlertDescription.handshake_failure); + TlsEccUtilities.WriteNamedECParameters(namedCurve, buf); } - byte[] publicBytes = TlsUtilities.ReadOpaque8(sigIn); + ECPublicKeyParameters ecPublicKey = (ECPublicKeyParameters)kp.Public; + TlsEccUtilities.WriteECPoint(mClientECPointFormats, ecPublicKey.Q, buf); - byte[] sigByte = TlsUtilities.ReadOpaque16(input); - if (!signer.VerifySignature(sigByte)) + /* + * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2 + */ + SignatureAndHashAlgorithm signatureAndHashAlgorithm; + IDigest d; + + if (TlsUtilities.IsTlsV12(context)) + { + signatureAndHashAlgorithm = mServerCredentials.SignatureAndHashAlgorithm; + if (signatureAndHashAlgorithm == null) + throw new TlsFatalAlert(AlertDescription.internal_error); + + d = TlsUtilities.CreateHash(signatureAndHashAlgorithm.Hash); + } + else { - throw new TlsFatalAlert(AlertDescription.bad_certificate); + signatureAndHashAlgorithm = null; + d = new CombinedHash(); } - // TODO Check curve_params not null + SecurityParameters securityParameters = context.SecurityParameters; + d.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length); + d.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length); + buf.UpdateDigest(d); + + byte[] hash = DigestUtilities.DoFinal(d); - ECPoint Q = curve_params.Curve.DecodePoint(publicBytes); + byte[] signature = mServerCredentials.GenerateCertificateSignature(hash); - this.ecAgreeServerPublicKey = ValidateECPublicKey(new ECPublicKeyParameters(Q, curve_params)); + DigitallySigned signed_params = new DigitallySigned(signatureAndHashAlgorithm, signature); + signed_params.Encode(buf); + + return buf.ToArray(); } - - 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) + + public override void ProcessServerKeyExchange(Stream input) + { + SecurityParameters securityParameters = context.SecurityParameters; + + SignerInputBuffer buf = new SignerInputBuffer(); + Stream teeIn = new TeeInputStream(input, buf); + + ECDomainParameters curve_params = TlsEccUtilities.ReadECParameters(mNamedCurves, mClientECPointFormats, teeIn); + + byte[] point = TlsUtilities.ReadOpaque8(teeIn); + + DigitallySigned signed_params = DigitallySigned.Parse(context, input); + + ISigner signer = InitVerifyer(mTlsSigner, signed_params.Algorithm, securityParameters); + buf.UpdateSigner(signer); + if (!signer.VerifySignature(signed_params.Signature)) + throw new TlsFatalAlert(AlertDescription.decrypt_error); + + this.mECAgreePublicKey = TlsEccUtilities.ValidateECPublicKey(TlsEccUtilities.DeserializeECPublicKey( + mClientECPointFormats, curve_params, point)); + } + + 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. + */ + byte[] types = certificateRequest.CertificateTypes; + for (int i = 0; i < types.Length; ++i) + { + switch (types[i]) + { + case ClientCertificateType.rsa_sign: + case ClientCertificateType.dss_sign: + case ClientCertificateType.ecdsa_sign: + break; + default: + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + } + + public override void ProcessClientCredentials(TlsCredentials clientCredentials) + { + if (clientCredentials is TlsSignerCredentials) + { + // OK + } + else + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + protected virtual ISigner InitVerifyer(TlsSigner tlsSigner, SignatureAndHashAlgorithm algorithm, + SecurityParameters securityParameters) { - ISigner signer = tlsSigner.CreateVerifyer(this.serverPublicKey); + ISigner signer = tlsSigner.CreateVerifyer(algorithm, this.mServerPublicKey); 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
index 3c30fdc0c..fa9d0b714 100644 --- a/crypto/src/crypto/tls/TlsECDsaSigner.cs +++ b/crypto/src/crypto/tls/TlsECDsaSigner.cs
@@ -5,17 +5,22 @@ using Org.BouncyCastle.Crypto.Signers; namespace Org.BouncyCastle.Crypto.Tls { - internal class TlsECDsaSigner - : TlsDsaSigner - { - public override bool IsValidPublicKey(AsymmetricKeyParameter publicKey) - { - return publicKey is ECPublicKeyParameters; - } + public class TlsECDsaSigner + : TlsDsaSigner + { + public override bool IsValidPublicKey(AsymmetricKeyParameter publicKey) + { + return publicKey is ECPublicKeyParameters; + } - protected override IDsa CreateDsaImpl() - { - return new ECDsaSigner(); - } - } + protected override IDsa CreateDsaImpl(byte hashAlgorithm) + { + return new ECDsaSigner(new HMacDsaKCalculator(TlsUtilities.CreateHash(hashAlgorithm))); + } + + protected override byte SignatureAlgorithm + { + get { return Tls.SignatureAlgorithm.ecdsa; } + } + } } diff --git a/crypto/src/crypto/tls/TlsEccUtilities.cs b/crypto/src/crypto/tls/TlsEccUtilities.cs new file mode 100644
index 000000000..64c3c1593 --- /dev/null +++ b/crypto/src/crypto/tls/TlsEccUtilities.cs
@@ -0,0 +1,647 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Agreement; +using Org.BouncyCastle.Crypto.EC; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Math.Field; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class TlsEccUtilities + { + private static readonly string[] CurveNames = new string[] { "sect163k1", "sect163r1", "sect163r2", "sect193r1", + "sect193r2", "sect233k1", "sect233r1", "sect239k1", "sect283k1", "sect283r1", "sect409k1", "sect409r1", + "sect571k1", "sect571r1", "secp160k1", "secp160r1", "secp160r2", "secp192k1", "secp192r1", "secp224k1", + "secp224r1", "secp256k1", "secp256r1", "secp384r1", "secp521r1", + "brainpoolP256r1", "brainpoolP384r1", "brainpoolP512r1"}; + + public static void AddSupportedEllipticCurvesExtension(IDictionary extensions, int[] namedCurves) + { + extensions[ExtensionType.elliptic_curves] = CreateSupportedEllipticCurvesExtension(namedCurves); + } + + public static void AddSupportedPointFormatsExtension(IDictionary extensions, byte[] ecPointFormats) + { + extensions[ExtensionType.ec_point_formats] = CreateSupportedPointFormatsExtension(ecPointFormats); + } + + public static int[] GetSupportedEllipticCurvesExtension(IDictionary extensions) + { + byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.elliptic_curves); + return extensionData == null ? null : ReadSupportedEllipticCurvesExtension(extensionData); + } + + public static byte[] GetSupportedPointFormatsExtension(IDictionary extensions) + { + byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.ec_point_formats); + return extensionData == null ? null : ReadSupportedPointFormatsExtension(extensionData); + } + + public static byte[] CreateSupportedEllipticCurvesExtension(int[] namedCurves) + { + if (namedCurves == null || namedCurves.Length < 1) + throw new TlsFatalAlert(AlertDescription.internal_error); + + return TlsUtilities.EncodeUint16ArrayWithUint16Length(namedCurves); + } + + public static byte[] CreateSupportedPointFormatsExtension(byte[] ecPointFormats) + { + if (ecPointFormats == null || !Arrays.Contains(ecPointFormats, ECPointFormat.uncompressed)) + { + /* + * RFC 4492 5.1. If the Supported Point Formats Extension is indeed sent, it MUST + * contain the value 0 (uncompressed) as one of the items in the list of point formats. + */ + + // NOTE: We add it at the end (lowest preference) + ecPointFormats = Arrays.Append(ecPointFormats, ECPointFormat.uncompressed); + } + + return TlsUtilities.EncodeUint8ArrayWithUint8Length(ecPointFormats); + } + + public static int[] ReadSupportedEllipticCurvesExtension(byte[] extensionData) + { + if (extensionData == null) + throw new ArgumentNullException("extensionData"); + + MemoryStream buf = new MemoryStream(extensionData, false); + + int length = TlsUtilities.ReadUint16(buf); + if (length < 2 || (length & 1) != 0) + throw new TlsFatalAlert(AlertDescription.decode_error); + + int[] namedCurves = TlsUtilities.ReadUint16Array(length / 2, buf); + + TlsProtocol.AssertEmpty(buf); + + return namedCurves; + } + + public static byte[] ReadSupportedPointFormatsExtension(byte[] extensionData) + { + if (extensionData == null) + throw new ArgumentNullException("extensionData"); + + MemoryStream buf = new MemoryStream(extensionData, false); + + byte length = TlsUtilities.ReadUint8(buf); + if (length < 1) + throw new TlsFatalAlert(AlertDescription.decode_error); + + byte[] ecPointFormats = TlsUtilities.ReadUint8Array(length, buf); + + TlsProtocol.AssertEmpty(buf); + + if (!Arrays.Contains(ecPointFormats, ECPointFormat.uncompressed)) + { + /* + * RFC 4492 5.1. If the Supported Point Formats Extension is indeed sent, it MUST + * contain the value 0 (uncompressed) as one of the items in the list of point formats. + */ + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + return ecPointFormats; + } + + public static string GetNameOfNamedCurve(int namedCurve) + { + return IsSupportedNamedCurve(namedCurve) ? CurveNames[namedCurve - 1] : null; + } + + public static ECDomainParameters GetParametersForNamedCurve(int namedCurve) + { + string curveName = GetNameOfNamedCurve(namedCurve); + if (curveName == null) + return null; + + // Parameters are lazily created the first time a particular curve is accessed + + X9ECParameters ecP = CustomNamedCurves.GetByName(curveName); + if (ecP == null) + { + ecP = ECNamedCurveTable.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()); + } + + public static bool HasAnySupportedNamedCurves() + { + return CurveNames.Length > 0; + } + + public static bool ContainsEccCipherSuites(int[] cipherSuites) + { + for (int i = 0; i < cipherSuites.Length; ++i) + { + if (IsEccCipherSuite(cipherSuites[i])) + return true; + } + return false; + } + + public static bool IsEccCipherSuite(int cipherSuite) + { + switch (cipherSuite) + { + /* + * RFC 4492 + */ + case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_anon_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_anon_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_anon_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_anon_WITH_AES_256_CBC_SHA: + + /* + * RFC 5289 + */ + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384: + + /* + * RFC 5489 + */ + case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA: + + /* + * RFC 6367 + */ + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384: + + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384: + + case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384: + + /* + * draft-agl-tls-chacha20poly1305-04 + */ + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + + /* + * draft-josefsson-salsa20-tls-04 + */ + case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1: + + return true; + + default: + return false; + } + } + + public static 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); + } + + public static bool IsSupportedNamedCurve(int namedCurve) + { + return (namedCurve > 0 && namedCurve <= CurveNames.Length); + } + + public static bool IsCompressionPreferred(byte[] ecPointFormats, byte compressionFormat) + { + if (ecPointFormats == null) + return false; + + for (int i = 0; i < ecPointFormats.Length; ++i) + { + byte ecPointFormat = ecPointFormats[i]; + if (ecPointFormat == ECPointFormat.uncompressed) + return false; + if (ecPointFormat == compressionFormat) + return true; + } + return false; + } + + public static byte[] SerializeECFieldElement(int fieldSize, BigInteger x) + { + return BigIntegers.AsUnsignedByteArray((fieldSize + 7) / 8, x); + } + + public static byte[] SerializeECPoint(byte[] ecPointFormats, ECPoint point) + { + ECCurve curve = point.Curve; + + /* + * 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. + */ + bool compressed = false; + if (ECAlgorithms.IsFpCurve(curve)) + { + compressed = IsCompressionPreferred(ecPointFormats, ECPointFormat.ansiX962_compressed_prime); + } + else if (ECAlgorithms.IsF2mCurve(curve)) + { + compressed = IsCompressionPreferred(ecPointFormats, ECPointFormat.ansiX962_compressed_char2); + } + return point.GetEncoded(compressed); + } + + public static byte[] SerializeECPublicKey(byte[] ecPointFormats, ECPublicKeyParameters keyParameters) + { + return SerializeECPoint(ecPointFormats, keyParameters.Q); + } + + public static BigInteger DeserializeECFieldElement(int fieldSize, byte[] encoding) + { + int requiredLength = (fieldSize + 7) / 8; + if (encoding.Length != requiredLength) + throw new TlsFatalAlert(AlertDescription.decode_error); + return new BigInteger(1, encoding); + } + + public static ECPoint DeserializeECPoint(byte[] ecPointFormats, ECCurve curve, byte[] encoding) + { + if (encoding == null || encoding.Length < 1) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + byte actualFormat; + switch (encoding[0]) + { + case 0x02: // compressed + case 0x03: // compressed + { + if (ECAlgorithms.IsF2mCurve(curve)) + { + actualFormat = ECPointFormat.ansiX962_compressed_char2; + } + else if (ECAlgorithms.IsFpCurve(curve)) + { + actualFormat = ECPointFormat.ansiX962_compressed_prime; + } + else + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + break; + } + case 0x04: // uncompressed + { + actualFormat = ECPointFormat.uncompressed; + break; + } + case 0x00: // infinity + case 0x06: // hybrid + case 0x07: // hybrid + default: + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + if (actualFormat != ECPointFormat.uncompressed + && (ecPointFormats == null || !Arrays.Contains(ecPointFormats, actualFormat))) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + return curve.DecodePoint(encoding); + } + + public static ECPublicKeyParameters DeserializeECPublicKey(byte[] ecPointFormats, ECDomainParameters curve_params, + byte[] encoding) + { + try + { + ECPoint Y = DeserializeECPoint(ecPointFormats, curve_params.Curve, encoding); + return new ECPublicKeyParameters(Y, curve_params); + } + catch (Exception e) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter, e); + } + } + + public static byte[] CalculateECDHBasicAgreement(ECPublicKeyParameters publicKey, ECPrivateKeyParameters privateKey) + { + ECDHBasicAgreement basicAgreement = new ECDHBasicAgreement(); + basicAgreement.Init(privateKey); + BigInteger agreementValue = basicAgreement.CalculateAgreement(publicKey); + + /* + * RFC 4492 5.10. Note that this octet string (Z in IEEE 1363 terminology) as output by + * FE2OSP, the Field Element to Octet String Conversion Primitive, has constant length for + * any given field; leading zeros found in this octet string MUST NOT be truncated. + */ + return BigIntegers.AsUnsignedByteArray(basicAgreement.GetFieldSize(), agreementValue); + } + + public static AsymmetricCipherKeyPair GenerateECKeyPair(SecureRandom random, ECDomainParameters ecParams) + { + ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator(); + keyPairGenerator.Init(new ECKeyGenerationParameters(ecParams, random)); + return keyPairGenerator.GenerateKeyPair(); + } + + public static ECPrivateKeyParameters GenerateEphemeralClientKeyExchange(SecureRandom random, byte[] ecPointFormats, + ECDomainParameters ecParams, Stream output) + { + AsymmetricCipherKeyPair kp = TlsEccUtilities.GenerateECKeyPair(random, ecParams); + + ECPublicKeyParameters ecPublicKey = (ECPublicKeyParameters)kp.Public; + WriteECPoint(ecPointFormats, ecPublicKey.Q, output); + + return (ECPrivateKeyParameters)kp.Private; + } + + public static ECPublicKeyParameters ValidateECPublicKey(ECPublicKeyParameters key) + { + // TODO Check RFC 4492 for validation + return key; + } + + public static int ReadECExponent(int fieldSize, Stream input) + { + BigInteger K = ReadECParameter(input); + if (K.BitLength < 32) + { + int k = K.IntValue; + if (k > 0 && k < fieldSize) + { + return k; + } + } + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + public static BigInteger ReadECFieldElement(int fieldSize, Stream input) + { + return DeserializeECFieldElement(fieldSize, TlsUtilities.ReadOpaque8(input)); + } + + public static BigInteger ReadECParameter(Stream input) + { + // TODO Are leading zeroes okay here? + return new BigInteger(1, TlsUtilities.ReadOpaque8(input)); + } + + public static ECDomainParameters ReadECParameters(int[] namedCurves, byte[] ecPointFormats, Stream input) + { + try + { + byte curveType = TlsUtilities.ReadUint8(input); + + switch (curveType) + { + case ECCurveType.explicit_prime: + { + CheckNamedCurve(namedCurves, NamedCurve.arbitrary_explicit_prime_curves); + + BigInteger prime_p = ReadECParameter(input); + BigInteger a = ReadECFieldElement(prime_p.BitLength, input); + BigInteger b = ReadECFieldElement(prime_p.BitLength, input); + byte[] baseEncoding = TlsUtilities.ReadOpaque8(input); + BigInteger order = ReadECParameter(input); + BigInteger cofactor = ReadECParameter(input); + ECCurve curve = new FpCurve(prime_p, a, b, order, cofactor); + ECPoint basePoint = DeserializeECPoint(ecPointFormats, curve, baseEncoding); + return new ECDomainParameters(curve, basePoint, order, cofactor); + } + case ECCurveType.explicit_char2: + { + CheckNamedCurve(namedCurves, NamedCurve.arbitrary_explicit_char2_curves); + + int m = TlsUtilities.ReadUint16(input); + byte basis = TlsUtilities.ReadUint8(input); + if (!ECBasisType.IsValid(basis)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + int k1 = ReadECExponent(m, input), k2 = -1, k3 = -1; + if (basis == ECBasisType.ec_basis_pentanomial) + { + k2 = ReadECExponent(m, input); + k3 = ReadECExponent(m, input); + } + + BigInteger a = ReadECFieldElement(m, input); + BigInteger b = ReadECFieldElement(m, input); + byte[] baseEncoding = TlsUtilities.ReadOpaque8(input); + BigInteger order = ReadECParameter(input); + BigInteger cofactor = ReadECParameter(input); + + ECCurve curve = (basis == ECBasisType.ec_basis_pentanomial) + ? new F2mCurve(m, k1, k2, k3, a, b, order, cofactor) + : new F2mCurve(m, k1, a, b, order, cofactor); + + ECPoint basePoint = DeserializeECPoint(ecPointFormats, curve, baseEncoding); + + return new ECDomainParameters(curve, basePoint, order, cofactor); + } + case ECCurveType.named_curve: + { + int namedCurve = TlsUtilities.ReadUint16(input); + if (!NamedCurve.RefersToASpecificNamedCurve(namedCurve)) + { + /* + * RFC 4492 5.4. All those values of NamedCurve are allowed that refer to a + * specific curve. Values of NamedCurve that indicate support for a class of + * explicitly defined curves are not allowed here [...]. + */ + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + CheckNamedCurve(namedCurves, namedCurve); + + return GetParametersForNamedCurve(namedCurve); + } + default: + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + catch (Exception e) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter, e); + } + } + + private static void CheckNamedCurve(int[] namedCurves, int namedCurve) + { + if (namedCurves != null && !Arrays.Contains(namedCurves, namedCurve)) + { + /* + * RFC 4492 4. [...] servers MUST NOT negotiate the use of an ECC cipher suite + * unless they can complete the handshake while respecting the choice of curves + * and compression techniques specified by the client. + */ + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + + public static void WriteECExponent(int k, Stream output) + { + BigInteger K = BigInteger.ValueOf(k); + WriteECParameter(K, output); + } + + public static void WriteECFieldElement(ECFieldElement x, Stream output) + { + TlsUtilities.WriteOpaque8(x.GetEncoded(), output); + } + + public static void WriteECFieldElement(int fieldSize, BigInteger x, Stream output) + { + TlsUtilities.WriteOpaque8(SerializeECFieldElement(fieldSize, x), output); + } + + public static void WriteECParameter(BigInteger x, Stream output) + { + TlsUtilities.WriteOpaque8(BigIntegers.AsUnsignedByteArray(x), output); + } + + public static void WriteExplicitECParameters(byte[] ecPointFormats, ECDomainParameters ecParameters, + Stream output) + { + ECCurve curve = ecParameters.Curve; + + if (ECAlgorithms.IsFpCurve(curve)) + { + TlsUtilities.WriteUint8(ECCurveType.explicit_prime, output); + + WriteECParameter(curve.Field.Characteristic, output); + } + else if (ECAlgorithms.IsF2mCurve(curve)) + { + IPolynomialExtensionField field = (IPolynomialExtensionField)curve.Field; + int[] exponents = field.MinimalPolynomial.GetExponentsPresent(); + + TlsUtilities.WriteUint8(ECCurveType.explicit_char2, output); + + int m = exponents[exponents.Length - 1]; + TlsUtilities.CheckUint16(m); + TlsUtilities.WriteUint16(m, output); + + if (exponents.Length == 3) + { + TlsUtilities.WriteUint8(ECBasisType.ec_basis_trinomial, output); + WriteECExponent(exponents[1], output); + } + else if (exponents.Length == 5) + { + TlsUtilities.WriteUint8(ECBasisType.ec_basis_pentanomial, output); + WriteECExponent(exponents[1], output); + WriteECExponent(exponents[2], output); + WriteECExponent(exponents[3], output); + } + else + { + throw new ArgumentException("Only trinomial and pentomial curves are supported"); + } + } + else + { + throw new ArgumentException("'ecParameters' not a known curve type"); + } + + WriteECFieldElement(curve.A, output); + WriteECFieldElement(curve.B, output); + TlsUtilities.WriteOpaque8(SerializeECPoint(ecPointFormats, ecParameters.G), output); + WriteECParameter(ecParameters.N, output); + WriteECParameter(ecParameters.H, output); + } + + public static void WriteECPoint(byte[] ecPointFormats, ECPoint point, Stream output) + { + TlsUtilities.WriteOpaque8(SerializeECPoint(ecPointFormats, point), output); + } + + public static void WriteNamedECParameters(int namedCurve, Stream output) + { + if (!NamedCurve.RefersToASpecificNamedCurve(namedCurve)) + { + /* + * RFC 4492 5.4. All those values of NamedCurve are allowed that refer to a specific + * curve. Values of NamedCurve that indicate support for a class of explicitly defined + * curves are not allowed here [...]. + */ + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + TlsUtilities.WriteUint8(ECCurveType.named_curve, output); + TlsUtilities.CheckUint16(namedCurve); + TlsUtilities.WriteUint16(namedCurve, output); + } + } +} diff --git a/crypto/src/crypto/tls/TlsEncryptionCredentials.cs b/crypto/src/crypto/tls/TlsEncryptionCredentials.cs new file mode 100644
index 000000000..52f007006 --- /dev/null +++ b/crypto/src/crypto/tls/TlsEncryptionCredentials.cs
@@ -0,0 +1,12 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsEncryptionCredentials + : TlsCredentials + { + /// <exception cref="IOException"></exception> + byte[] DecryptPreMasterSecret(byte[] encryptedPreMasterSecret); + } +} diff --git a/crypto/src/crypto/tls/TlsExtensionsUtilities.cs b/crypto/src/crypto/tls/TlsExtensionsUtilities.cs new file mode 100644
index 000000000..8876911e6 --- /dev/null +++ b/crypto/src/crypto/tls/TlsExtensionsUtilities.cs
@@ -0,0 +1,243 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class TlsExtensionsUtilities + { + public static IDictionary EnsureExtensionsInitialised(IDictionary extensions) + { + return extensions == null ? Platform.CreateHashtable() : extensions; + } + + public static void AddEncryptThenMacExtension(IDictionary extensions) + { + extensions[ExtensionType.encrypt_then_mac] = CreateEncryptThenMacExtension(); + } + + /// <exception cref="IOException"></exception> + public static void AddHeartbeatExtension(IDictionary extensions, HeartbeatExtension heartbeatExtension) + { + extensions[ExtensionType.heartbeat] = CreateHeartbeatExtension(heartbeatExtension); + } + + /// <exception cref="IOException"></exception> + public static void AddMaxFragmentLengthExtension(IDictionary extensions, byte maxFragmentLength) + { + extensions[ExtensionType.max_fragment_length] = CreateMaxFragmentLengthExtension(maxFragmentLength); + } + + /// <exception cref="IOException"></exception> + public static void AddServerNameExtension(IDictionary extensions, ServerNameList serverNameList) + { + extensions[ExtensionType.server_name] = CreateServerNameExtension(serverNameList); + } + + /// <exception cref="IOException"></exception> + public static void AddStatusRequestExtension(IDictionary extensions, CertificateStatusRequest statusRequest) + { + extensions[ExtensionType.status_request] = CreateStatusRequestExtension(statusRequest); + } + + public static void AddTruncatedHMacExtension(IDictionary extensions) + { + extensions[ExtensionType.truncated_hmac] = CreateTruncatedHMacExtension(); + } + + /// <exception cref="IOException"></exception> + public static HeartbeatExtension GetHeartbeatExtension(IDictionary extensions) + { + byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.heartbeat); + return extensionData == null ? null : ReadHeartbeatExtension(extensionData); + } + + /// <exception cref="IOException"></exception> + public static short GetMaxFragmentLengthExtension(IDictionary extensions) + { + byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.max_fragment_length); + return extensionData == null ? (short)-1 : (short)ReadMaxFragmentLengthExtension(extensionData); + } + + /// <exception cref="IOException"></exception> + public static ServerNameList GetServerNameExtension(IDictionary extensions) + { + byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.server_name); + return extensionData == null ? null : ReadServerNameExtension(extensionData); + } + + /// <exception cref="IOException"></exception> + public static CertificateStatusRequest GetStatusRequestExtension(IDictionary extensions) + { + byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.status_request); + return extensionData == null ? null : ReadStatusRequestExtension(extensionData); + } + + /// <exception cref="IOException"></exception> + public static bool HasEncryptThenMacExtension(IDictionary extensions) + { + byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.encrypt_then_mac); + return extensionData == null ? false : ReadEncryptThenMacExtension(extensionData); + } + + /// <exception cref="IOException"></exception> + public static bool HasTruncatedHMacExtension(IDictionary extensions) + { + byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.truncated_hmac); + return extensionData == null ? false : ReadTruncatedHMacExtension(extensionData); + } + + public static byte[] CreateEmptyExtensionData() + { + return TlsUtilities.EmptyBytes; + } + + public static byte[] CreateEncryptThenMacExtension() + { + return CreateEmptyExtensionData(); + } + + /// <exception cref="IOException"></exception> + public static byte[] CreateHeartbeatExtension(HeartbeatExtension heartbeatExtension) + { + if (heartbeatExtension == null) + throw new TlsFatalAlert(AlertDescription.internal_error); + + MemoryStream buf = new MemoryStream(); + + heartbeatExtension.Encode(buf); + + return buf.ToArray(); + } + + /// <exception cref="IOException"></exception> + public static byte[] CreateMaxFragmentLengthExtension(byte maxFragmentLength) + { + if (!MaxFragmentLength.IsValid(maxFragmentLength)) + throw new TlsFatalAlert(AlertDescription.internal_error); + + return new byte[]{ maxFragmentLength }; + } + + /// <exception cref="IOException"></exception> + public static byte[] CreateServerNameExtension(ServerNameList serverNameList) + { + if (serverNameList == null) + throw new TlsFatalAlert(AlertDescription.internal_error); + + MemoryStream buf = new MemoryStream(); + + serverNameList.Encode(buf); + + return buf.ToArray(); + } + + /// <exception cref="IOException"></exception> + public static byte[] CreateStatusRequestExtension(CertificateStatusRequest statusRequest) + { + if (statusRequest == null) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + MemoryStream buf = new MemoryStream(); + + statusRequest.Encode(buf); + + return buf.ToArray(); + } + + public static byte[] CreateTruncatedHMacExtension() + { + return CreateEmptyExtensionData(); + } + + /// <exception cref="IOException"></exception> + private static bool ReadEmptyExtensionData(byte[] extensionData) + { + if (extensionData == null) + throw new ArgumentNullException("extensionData"); + + if (extensionData.Length != 0) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + return true; + } + + /// <exception cref="IOException"></exception> + public static bool ReadEncryptThenMacExtension(byte[] extensionData) + { + return ReadEmptyExtensionData(extensionData); + } + + /// <exception cref="IOException"></exception> + public static HeartbeatExtension ReadHeartbeatExtension(byte[] extensionData) + { + if (extensionData == null) + throw new ArgumentNullException("extensionData"); + + MemoryStream buf = new MemoryStream(extensionData, false); + + HeartbeatExtension heartbeatExtension = HeartbeatExtension.Parse(buf); + + TlsProtocol.AssertEmpty(buf); + + return heartbeatExtension; + } + + /// <exception cref="IOException"></exception> + public static short ReadMaxFragmentLengthExtension(byte[] extensionData) + { + if (extensionData == null) + throw new ArgumentNullException("extensionData"); + + if (extensionData.Length != 1) + throw new TlsFatalAlert(AlertDescription.decode_error); + + byte maxFragmentLength = extensionData[0]; + + if (!MaxFragmentLength.IsValid(maxFragmentLength)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + return maxFragmentLength; + } + + /// <exception cref="IOException"></exception> + public static ServerNameList ReadServerNameExtension(byte[] extensionData) + { + if (extensionData == null) + throw new ArgumentNullException("extensionData"); + + MemoryStream buf = new MemoryStream(extensionData, false); + + ServerNameList serverNameList = ServerNameList.Parse(buf); + + TlsProtocol.AssertEmpty(buf); + + return serverNameList; + } + + /// <exception cref="IOException"></exception> + public static CertificateStatusRequest ReadStatusRequestExtension(byte[] extensionData) + { + if (extensionData == null) + throw new ArgumentNullException("extensionData"); + + MemoryStream buf = new MemoryStream(extensionData, false); + + CertificateStatusRequest statusRequest = CertificateStatusRequest.Parse(buf); + + TlsProtocol.AssertEmpty(buf); + + return statusRequest; + } + + /// <exception cref="IOException"></exception> + public static bool ReadTruncatedHMacExtension(byte[] extensionData) + { + return ReadEmptyExtensionData(extensionData); + } + } +} diff --git a/crypto/src/crypto/tls/TlsFatalAlert.cs b/crypto/src/crypto/tls/TlsFatalAlert.cs
index 0a9cc6f3a..0c7ed88d9 100644 --- a/crypto/src/crypto/tls/TlsFatalAlert.cs +++ b/crypto/src/crypto/tls/TlsFatalAlert.cs
@@ -3,19 +3,25 @@ using System.IO; namespace Org.BouncyCastle.Crypto.Tls { - public class TlsFatalAlert - : IOException - { - private readonly AlertDescription alertDescription; + public class TlsFatalAlert + : IOException + { + private readonly byte alertDescription; - public TlsFatalAlert(AlertDescription alertDescription) - { - this.alertDescription = alertDescription; - } + public TlsFatalAlert(byte alertDescription) + : this(alertDescription, null) + { + } - public AlertDescription AlertDescription - { - get { return alertDescription; } - } - } + public TlsFatalAlert(byte alertDescription, Exception alertCause) + : base("Fatal alert: " + alertDescription, alertCause) + { + this.alertDescription = alertDescription; + } + + public virtual byte AlertDescription + { + get { return alertDescription; } + } + } } diff --git a/crypto/src/crypto/tls/TlsHandshakeHash.cs b/crypto/src/crypto/tls/TlsHandshakeHash.cs new file mode 100644
index 000000000..7118d9769 --- /dev/null +++ b/crypto/src/crypto/tls/TlsHandshakeHash.cs
@@ -0,0 +1,22 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsHandshakeHash + : IDigest + { + void Init(TlsContext context); + + TlsHandshakeHash NotifyPrfDetermined(); + + void TrackHashAlgorithm(byte hashAlgorithm); + + void SealHashAlgorithms(); + + TlsHandshakeHash StopTracking(); + + IDigest ForkPrfHash(); + + byte[] GetFinalHash(byte hashAlgorithm); + } +} diff --git a/crypto/src/crypto/tls/TlsKeyExchange.cs b/crypto/src/crypto/tls/TlsKeyExchange.cs
index 5102edbec..6731f6f63 100644 --- a/crypto/src/crypto/tls/TlsKeyExchange.cs +++ b/crypto/src/crypto/tls/TlsKeyExchange.cs
@@ -3,36 +3,52 @@ 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(); - } + /// <summary> + /// A generic interface for key exchange implementations in (D)TLS. + /// </summary> + public interface TlsKeyExchange + { + void Init(TlsContext context); + + /// <exception cref="IOException"/> + void SkipServerCredentials(); + + /// <exception cref="IOException"/> + void ProcessServerCredentials(TlsCredentials serverCredentials); + + /// <exception cref="IOException"/> + void ProcessServerCertificate(Certificate serverCertificate); + + bool RequiresServerKeyExchange { get; } + + /// <exception cref="IOException"/> + byte[] GenerateServerKeyExchange(); + + /// <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 ProcessClientCertificate(Certificate clientCertificate); + + /// <exception cref="IOException"/> + void GenerateClientKeyExchange(Stream output); + + /// <exception cref="IOException"/> + void ProcessClientKeyExchange(Stream input); + + /// <exception cref="IOException"/> + byte[] GeneratePremasterSecret(); + } } diff --git a/crypto/src/crypto/tls/TlsMac.cs b/crypto/src/crypto/tls/TlsMac.cs
index 0e58b89dc..a80319a17 100644 --- a/crypto/src/crypto/tls/TlsMac.cs +++ b/crypto/src/crypto/tls/TlsMac.cs
@@ -9,98 +9,165 @@ 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); - } - } + /// <summary> + /// A generic TLS MAC implementation, acting as an HMAC based on some underlying Digest. + /// </summary> + public class TlsMac + { + protected readonly TlsContext context; + protected readonly byte[] secret; + protected readonly IMac mac; + protected readonly int digestBlockSize; + protected readonly int digestOverhead; + protected readonly int macLength; + + /** + * Generate a new instance of an TlsMac. + * + * @param context the TLS client context + * @param digest The digest to use. + * @param key A byte-array where the key for this MAC is located. + * @param keyOff The number of bytes to skip, before the key starts in the buffer. + * @param keyLen The length of the key. + */ + public TlsMac(TlsContext context, IDigest digest, byte[] key, int keyOff, int keyLen) + { + this.context = context; + + KeyParameter keyParameter = new KeyParameter(key, keyOff, keyLen); + + this.secret = Arrays.Clone(keyParameter.GetKey()); + + // TODO This should check the actual algorithm, not rely on the engine type + if (digest is LongDigest) + { + this.digestBlockSize = 128; + this.digestOverhead = 16; + } + else + { + this.digestBlockSize = 64; + this.digestOverhead = 8; + } + + if (TlsUtilities.IsSsl(context)) + { + this.mac = new Ssl3Mac(digest); + + // TODO This should check the actual algorithm, not assume based on the digest size + if (digest.GetDigestSize() == 20) + { + /* + * NOTE: When SHA-1 is used with the SSL 3.0 MAC, the secret + input pad is not + * digest block-aligned. + */ + this.digestOverhead = 4; + } + } + else + { + this.mac = new HMac(digest); + + // NOTE: The input pad for HMAC is always a full digest block + } + + this.mac.Init(keyParameter); + + this.macLength = mac.GetMacSize(); + if (context.SecurityParameters.truncatedHMac) + { + this.macLength = System.Math.Min(this.macLength, 10); + } + } + + /** + * @return the MAC write secret + */ + public virtual byte[] MacSecret + { + get { return this.secret; } + } + + /** + * @return The output length of this MAC. + */ + public virtual int Size + { + get { return macLength; } + } + + /** + * Calculate the MAC for some given data. + * + * @param type The message type of the message. + * @param message A byte-buffer containing the message. + * @param offset The number of bytes to skip, before the message starts. + * @param length The length of the message. + * @return A new byte-buffer containing the MAC value. + */ + public virtual byte[] CalculateMac(long seqNo, byte type, byte[] message, int offset, int length) + { + ProtocolVersion serverVersion = context.ServerVersion; + bool isSsl = serverVersion.IsSsl; + + byte[] macHeader = new byte[isSsl ? 11 : 13]; + TlsUtilities.WriteUint64(seqNo, macHeader, 0); + TlsUtilities.WriteUint8(type, macHeader, 8); + if (!isSsl) + { + TlsUtilities.WriteVersion(serverVersion, macHeader, 9); + } + TlsUtilities.WriteUint16(length, macHeader, macHeader.Length - 2); + + mac.BlockUpdate(macHeader, 0, macHeader.Length); + mac.BlockUpdate(message, offset, length); + + return Truncate(MacUtilities.DoFinal(mac)); + } + + public virtual byte[] CalculateMacConstantTime(long seqNo, byte type, byte[] message, int offset, int length, + int fullLength, byte[] dummyData) + { + /* + * Actual MAC only calculated on 'length' bytes... + */ + byte[] result = CalculateMac(seqNo, type, message, offset, length); + + /* + * ...but ensure a constant number of complete digest blocks are processed (as many as would + * be needed for 'fullLength' bytes of input). + */ + int headerLength = TlsUtilities.IsSsl(context) ? 11 : 13; + + // How many extra full blocks do we need to calculate? + int extra = GetDigestBlockCount(headerLength + fullLength) - GetDigestBlockCount(headerLength + length); + + while (--extra >= 0) + { + mac.BlockUpdate(dummyData, 0, digestBlockSize); + } + + // One more byte in case the implementation is "lazy" about processing blocks + mac.Update(dummyData[0]); + mac.Reset(); + + return result; + } + + protected virtual int GetDigestBlockCount(int inputLength) + { + // NOTE: This calculation assumes a minimum of 1 pad byte + return (inputLength + digestOverhead) / digestBlockSize; + } + + protected virtual byte[] Truncate(byte[] bs) + { + if (bs.Length <= macLength) + { + return bs; + } + + return Arrays.CopyOf(bs, macLength); + } + } } diff --git a/crypto/src/crypto/tls/TlsNullCipher.cs b/crypto/src/crypto/tls/TlsNullCipher.cs
index b76f76d9c..f30ace24f 100644 --- a/crypto/src/crypto/tls/TlsNullCipher.cs +++ b/crypto/src/crypto/tls/TlsNullCipher.cs
@@ -1,28 +1,118 @@ using System; +using System.IO; + +using Org.BouncyCastle.Utilities; 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; - } - } + /// <summary> + /// A NULL CipherSuite, with optional MAC. + /// </summary> + public class TlsNullCipher + : TlsCipher + { + protected readonly TlsContext context; + + protected readonly TlsMac writeMac; + protected readonly TlsMac readMac; + + public TlsNullCipher(TlsContext context) + { + this.context = context; + this.writeMac = null; + this.readMac = null; + } + + /// <exception cref="IOException"></exception> + public TlsNullCipher(TlsContext context, IDigest clientWriteDigest, IDigest serverWriteDigest) + { + if ((clientWriteDigest == null) != (serverWriteDigest == null)) + throw new TlsFatalAlert(AlertDescription.internal_error); + + this.context = context; + + TlsMac clientWriteMac = null, serverWriteMac = null; + + if (clientWriteDigest != null) + { + int key_block_size = clientWriteDigest.GetDigestSize() + + serverWriteDigest.GetDigestSize(); + byte[] key_block = TlsUtilities.CalculateKeyBlock(context, key_block_size); + + int offset = 0; + + clientWriteMac = new TlsMac(context, clientWriteDigest, key_block, offset, + clientWriteDigest.GetDigestSize()); + offset += clientWriteDigest.GetDigestSize(); + + serverWriteMac = new TlsMac(context, serverWriteDigest, key_block, offset, + serverWriteDigest.GetDigestSize()); + offset += serverWriteDigest.GetDigestSize(); + + if (offset != key_block_size) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + if (context.IsServer) + { + writeMac = serverWriteMac; + readMac = clientWriteMac; + } + else + { + writeMac = clientWriteMac; + readMac = serverWriteMac; + } + } + + public virtual int GetPlaintextLimit(int ciphertextLimit) + { + int result = ciphertextLimit; + if (writeMac != null) + { + result -= writeMac.Size; + } + return result; + } + + /// <exception cref="IOException"></exception> + public virtual byte[] EncodePlaintext(long seqNo, byte type, byte[] plaintext, int offset, int len) + { + if (writeMac == null) + { + return Arrays.CopyOfRange(plaintext, offset, offset + len); + } + + byte[] mac = writeMac.CalculateMac(seqNo, type, plaintext, offset, len); + byte[] ciphertext = new byte[len + mac.Length]; + Array.Copy(plaintext, offset, ciphertext, 0, len); + Array.Copy(mac, 0, ciphertext, len, mac.Length); + return ciphertext; + } + + /// <exception cref="IOException"></exception> + public virtual byte[] DecodeCiphertext(long seqNo, byte type, byte[] ciphertext, int offset, int len) + { + if (readMac == null) + { + return Arrays.CopyOfRange(ciphertext, offset, offset + len); + } + + int macSize = readMac.Size; + if (len < macSize) + throw new TlsFatalAlert(AlertDescription.decode_error); + + int macInputLen = len - macSize; + + byte[] receivedMac = Arrays.CopyOfRange(ciphertext, offset + macInputLen, offset + len); + byte[] computedMac = readMac.CalculateMac(seqNo, type, ciphertext, offset, macInputLen); + + if (!Arrays.ConstantTimeAreEqual(receivedMac, computedMac)) + throw new TlsFatalAlert(AlertDescription.bad_record_mac); + + return Arrays.CopyOfRange(ciphertext, offset, offset + macInputLen); + } + } } diff --git a/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/TlsPeer.cs b/crypto/src/crypto/tls/TlsPeer.cs new file mode 100644
index 000000000..1ae41a41a --- /dev/null +++ b/crypto/src/crypto/tls/TlsPeer.cs
@@ -0,0 +1,62 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsPeer + { + /// <summary> + /// draft-mathewson-no-gmtunixtime-00 2. "If existing users of a TLS implementation may rely on + /// gmt_unix_time containing the current time, we recommend that implementors MAY provide the + /// ability to set gmt_unix_time as an option only, off by default." + /// </summary> + /// <returns> + /// <code>true</code> if the current time should be used in the gmt_unix_time field of + /// Random, or <code>false</code> if gmt_unix_time should contain a cryptographically + /// random value. + /// </returns> + bool ShouldUseGmtUnixTime(); + + /// <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> + /// Return an implementation of <see cref="TlsCompression"/> to handle record compression. + /// </summary> + /// <returns>A <see cref="TlsCompression"/></returns> + /// <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(); + + /// <summary>This method will be called when an alert is raised by the protocol.</summary> + /// <param name="alertLevel"><see cref="AlertLevel"/></param> + /// <param name="alertDescription"><see cref="AlertDescription"/></param> + /// <param name="message">A human-readable message explaining what caused this alert. May be null.</param> + /// <param name="cause">The <c>Exception</c> that caused this alert to be raised. May be null.</param> + void NotifyAlertRaised(byte alertLevel, byte alertDescription, string message, Exception cause); + + /// <summary>This method will be called when an alert is received from the remote peer.</summary> + /// <param name="alertLevel"><see cref="AlertLevel"/></param> + /// <param name="alertDescription"><see cref="AlertDescription"/></param> + void NotifyAlertReceived(byte alertLevel, byte alertDescription); + + /// <summary>Notifies the peer that the handshake has been successfully completed.</summary> + /// <exception cref="IOException"></exception> + void NotifyHandshakeComplete(); + } +} diff --git a/crypto/src/crypto/tls/TlsProtocol.cs b/crypto/src/crypto/tls/TlsProtocol.cs new file mode 100644
index 000000000..8ba156952 --- /dev/null +++ b/crypto/src/crypto/tls/TlsProtocol.cs
@@ -0,0 +1,1102 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Crypto.Prng; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class TlsProtocol + { + private static readonly string TLS_ERROR_MESSAGE = "Internal TLS error, this could be an attack"; + + /* + * Our Connection states + */ + protected const short CS_START = 0; + protected const short CS_CLIENT_HELLO = 1; + protected const short CS_SERVER_HELLO = 2; + protected const short CS_SERVER_SUPPLEMENTAL_DATA = 3; + protected const short CS_SERVER_CERTIFICATE = 4; + protected const short CS_CERTIFICATE_STATUS = 5; + protected const short CS_SERVER_KEY_EXCHANGE = 6; + protected const short CS_CERTIFICATE_REQUEST = 7; + protected const short CS_SERVER_HELLO_DONE = 8; + protected const short CS_CLIENT_SUPPLEMENTAL_DATA = 9; + protected const short CS_CLIENT_CERTIFICATE = 10; + protected const short CS_CLIENT_KEY_EXCHANGE = 11; + protected const short CS_CERTIFICATE_VERIFY = 12; + protected const short CS_CLIENT_FINISHED = 13; + protected const short CS_SERVER_SESSION_TICKET = 14; + protected const short CS_SERVER_FINISHED = 15; + protected const short CS_END = 16; + + /* + * Queues for data from some protocols. + */ + private ByteQueue mApplicationDataQueue = new ByteQueue(); + private ByteQueue mAlertQueue = new ByteQueue(2); + private ByteQueue mHandshakeQueue = new ByteQueue(); + // private ByteQueue mHeartbeatQueue = new ByteQueue(); + + /* + * The Record Stream we use + */ + internal RecordStream mRecordStream; + protected SecureRandom mSecureRandom; + + private TlsStream mTlsStream = null; + + private volatile bool mClosed = false; + private volatile bool mFailedWithError = false; + private volatile bool mAppDataReady = false; + private volatile bool mSplitApplicationDataRecords = true; + private byte[] mExpectedVerifyData = null; + + protected TlsSession mTlsSession = null; + protected SessionParameters mSessionParameters = null; + protected SecurityParameters mSecurityParameters = null; + protected Certificate mPeerCertificate = null; + + protected int[] mOfferedCipherSuites = null; + protected byte[] mOfferedCompressionMethods = null; + protected IDictionary mClientExtensions = null; + protected IDictionary mServerExtensions = null; + + protected short mConnectionState = CS_START; + protected bool mResumedSession = false; + protected bool mReceivedChangeCipherSpec = false; + protected bool mSecureRenegotiation = false; + protected bool mAllowCertificateStatus = false; + protected bool mExpectSessionTicket = false; + + public TlsProtocol(Stream stream, SecureRandom secureRandom) + : this(stream, stream, secureRandom) + { + } + + public TlsProtocol(Stream input, Stream output, SecureRandom secureRandom) + { + this.mRecordStream = new RecordStream(this, input, output); + this.mSecureRandom = secureRandom; + } + + protected abstract TlsContext Context { get; } + + internal abstract AbstractTlsContext ContextAdmin { get; } + + protected abstract TlsPeer Peer { get; } + + protected virtual void HandleChangeCipherSpecMessage() + { + } + + protected abstract void HandleHandshakeMessage(byte type, byte[] buf); + + protected virtual void HandleWarningMessage(byte description) + { + } + + protected virtual void CleanupHandshake() + { + if (this.mExpectedVerifyData != null) + { + Arrays.Fill(this.mExpectedVerifyData, (byte)0); + this.mExpectedVerifyData = null; + } + + this.mSecurityParameters.Clear(); + this.mPeerCertificate = null; + + this.mOfferedCipherSuites = null; + this.mOfferedCompressionMethods = null; + this.mClientExtensions = null; + this.mServerExtensions = null; + + this.mResumedSession = false; + this.mReceivedChangeCipherSpec = false; + this.mSecureRenegotiation = false; + this.mAllowCertificateStatus = false; + this.mExpectSessionTicket = false; + } + + protected virtual void CompleteHandshake() + { + try + { + /* + * We will now read data, until we have completed the handshake. + */ + while (this.mConnectionState != CS_END) + { + if (this.mClosed) + { + // TODO What kind of exception/alert? + } + + SafeReadRecord(); + } + + this.mRecordStream.FinaliseHandshake(); + + this.mSplitApplicationDataRecords = !TlsUtilities.IsTlsV11(Context); + + /* + * If this was an initial handshake, we are now ready to send and receive application data. + */ + if (!mAppDataReady) + { + this.mAppDataReady = true; + + this.mTlsStream = new TlsStream(this); + } + + if (this.mTlsSession != null) + { + if (this.mSessionParameters == null) + { + this.mSessionParameters = new SessionParameters.Builder() + .SetCipherSuite(this.mSecurityParameters.cipherSuite) + .SetCompressionAlgorithm(this.mSecurityParameters.compressionAlgorithm) + .SetMasterSecret(this.mSecurityParameters.masterSecret) + .SetPeerCertificate(this.mPeerCertificate) + // TODO Consider filtering extensions that aren't relevant to resumed sessions + .SetServerExtensions(this.mServerExtensions) + .Build(); + + this.mTlsSession = new TlsSessionImpl(this.mTlsSession.SessionID, this.mSessionParameters); + } + + ContextAdmin.SetResumableSession(this.mTlsSession); + } + + Peer.NotifyHandshakeComplete(); + } + finally + { + CleanupHandshake(); + } + } + + protected internal void ProcessRecord(byte 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.alert: + { + mAlertQueue.AddData(buf, offset, len); + ProcessAlert(); + break; + } + case ContentType.application_data: + { + if (!mAppDataReady) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + + mApplicationDataQueue.AddData(buf, offset, len); + ProcessApplicationData(); + break; + } + case ContentType.change_cipher_spec: + { + ProcessChangeCipherSpec(buf, offset, len); + break; + } + case ContentType.handshake: + { + mHandshakeQueue.AddData(buf, offset, len); + ProcessHandshake(); + break; + } + case ContentType.heartbeat: + { + if (!mAppDataReady) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + + // TODO[RFC 6520] + // mHeartbeatQueue.AddData(buf, offset, len); + // ProcessHeartbeat(); + 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 (mHandshakeQueue.Available >= 4) + { + byte[] beginning = new byte[4]; + mHandshakeQueue.Read(beginning, 0, 4, 0); + byte type = TlsUtilities.ReadUint8(beginning, 0); + int len = TlsUtilities.ReadUint24(beginning, 1); + + /* + * Check if we have enough bytes in the buffer to read the full message. + */ + if (mHandshakeQueue.Available >= (len + 4)) + { + /* + * Read the message. + */ + byte[] buf = mHandshakeQueue.RemoveData(len, 4); + + /* + * RFC 2246 7.4.9. The value handshake_messages includes all handshake messages + * starting at client hello up to, but not including, this finished message. + * [..] Note: [Also,] Hello Request messages are omitted from handshake hashes. + */ + switch (type) + { + case HandshakeType.hello_request: + break; + case HandshakeType.finished: + default: + if (type == HandshakeType.finished && this.mExpectedVerifyData == null) + { + this.mExpectedVerifyData = CreateVerifyData(!Context.IsServer); + } + + mRecordStream.UpdateHandshakeData(beginning, 0, 4); + mRecordStream.UpdateHandshakeData(buf, 0, len); + break; + } + + /* + * Now, parse the message. + */ + HandleHandshakeMessage(type, buf); + read = true; + } + } + } + while (read); + } + + private void ProcessApplicationData() + { + /* + * There is nothing we need to do here. + * + * This function could be used for callbacks when application data arrives in the future. + */ + } + + private void ProcessAlert() + { + while (mAlertQueue.Available >= 2) + { + /* + * An alert is always 2 bytes. Read the alert. + */ + byte[] tmp = mAlertQueue.RemoveData(2, 0); + byte level = tmp[0]; + byte description = tmp[1]; + + Peer.NotifyAlertReceived(level, description); + + if (level == AlertLevel.fatal) + { + /* + * RFC 2246 7.2.1. The session becomes unresumable if any connection is terminated + * without proper close_notify messages with level equal to warning. + */ + InvalidateSession(); + + this.mFailedWithError = true; + this.mClosed = true; + + mRecordStream.SafeClose(); + + throw new IOException(TLS_ERROR_MESSAGE); + } + else + { + + /* + * RFC 5246 7.2.1. The other party MUST respond with a close_notify alert of its own + * and close down the connection immediately, discarding any pending writes. + */ + // TODO Can close_notify be a fatal alert? + if (description == AlertDescription.close_notify) + { + HandleClose(false); + } + + /* + * If it is just a warning, we continue. + */ + HandleWarningMessage(description); + } + } + } + + /** + * This method is called, when a change cipher spec message is received. + * + * @throws IOException If the message has an invalid content or the handshake is not in the correct + * state. + */ + private void ProcessChangeCipherSpec(byte[] buf, int off, int len) + { + for (int i = 0; i < len; ++i) + { + byte message = TlsUtilities.ReadUint8(buf, off + i); + + if (message != ChangeCipherSpec.change_cipher_spec) + throw new TlsFatalAlert(AlertDescription.decode_error); + + if (this.mReceivedChangeCipherSpec + || mAlertQueue.Available > 0 + || mHandshakeQueue.Available > 0) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + mRecordStream.ReceivedReadCipherSpec(); + + this.mReceivedChangeCipherSpec = true; + + HandleChangeCipherSpecMessage(); + } + } + + protected internal virtual int ApplicationDataAvailable() + { + return mApplicationDataQueue.Available; + } + + /** + * Read data from the network. The method will return immediately, if there is still some data + * left in the buffer, or block until some application data has been read from the network. + * + * @param buf The buffer where the data will be copied to. + * @param offset The position where the data will be placed in the buffer. + * @param len The maximum number of bytes to read. + * @return The number of bytes read. + * @throws IOException If something goes wrong during reading data. + */ + protected internal virtual int ReadApplicationData(byte[] buf, int offset, int len) + { + if (len < 1) + return 0; + + while (mApplicationDataQueue.Available == 0) + { + /* + * We need to read some data. + */ + if (this.mClosed) + { + if (this.mFailedWithError) + { + /* + * 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; + } + + SafeReadRecord(); + } + + len = System.Math.Min(len, mApplicationDataQueue.Available); + mApplicationDataQueue.RemoveData(buf, offset, len, 0); + return len; + } + + protected virtual void SafeReadRecord() + { + try + { + if (!mRecordStream.ReadRecord()) + { + // TODO It would be nicer to allow graceful connection close if between records + // this.FailWithError(AlertLevel.warning, AlertDescription.close_notify); + throw new EndOfStreamException(); + } + } + catch (TlsFatalAlert e) + { + if (!mClosed) + { + this.FailWithError(AlertLevel.fatal, e.AlertDescription, "Failed to read record", e); + } + throw e; + } + catch (Exception e) + { + if (!mClosed) + { + this.FailWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to read record", e); + } + throw e; + } + } + + protected virtual void SafeWriteRecord(byte type, byte[] buf, int offset, int len) + { + try + { + mRecordStream.WriteRecord(type, buf, offset, len); + } + catch (TlsFatalAlert e) + { + if (!mClosed) + { + this.FailWithError(AlertLevel.fatal, e.AlertDescription, "Failed to write record", e); + } + throw e; + } + catch (Exception e) + { + if (!mClosed) + { + this.FailWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to write record", 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. + */ + protected internal virtual void WriteData(byte[] buf, int offset, int len) + { + if (this.mClosed) + { + if (this.mFailedWithError) + throw new IOException(TLS_ERROR_MESSAGE); + + throw new IOException("Sorry, connection has been closed, you cannot write more data"); + } + + while (len > 0) + { + /* + * RFC 5246 6.2.1. Zero-length fragments of Application data MAY be sent as they are + * potentially useful as a traffic analysis countermeasure. + * + * NOTE: Actually, implementations appear to have settled on 1/n-1 record splitting. + */ + + if (this.mSplitApplicationDataRecords) + { + /* + * Protect against known IV attack! + * + * DO NOT REMOVE THIS CODE, EXCEPT YOU KNOW EXACTLY WHAT YOU ARE DOING HERE. + */ + SafeWriteRecord(ContentType.application_data, buf, offset, 1); + ++offset; + --len; + } + + if (len > 0) + { + // Fragment data according to the current fragment limit. + int toWrite = System.Math.Min(len, mRecordStream.GetPlaintextLimit()); + SafeWriteRecord(ContentType.application_data, buf, offset, toWrite); + offset += toWrite; + len -= toWrite; + } + } + } + + protected virtual void WriteHandshakeMessage(byte[] buf, int off, int len) + { + while (len > 0) + { + // Fragment data according to the current fragment limit. + int toWrite = System.Math.Min(len, mRecordStream.GetPlaintextLimit()); + SafeWriteRecord(ContentType.handshake, buf, off, toWrite); + off += toWrite; + len -= toWrite; + } + } + + /// <summary>The secure bidirectional stream for this connection</summary> + public virtual Stream Stream + { + get { return this.mTlsStream; } + } + + /** + * Terminate this connection with an alert. Can be used for normal closure too. + * + * @param alertLevel + * See {@link AlertLevel} for values. + * @param alertDescription + * See {@link AlertDescription} for values. + * @throws IOException + * If alert was fatal. + */ + protected virtual void FailWithError(byte alertLevel, byte alertDescription, string message, Exception cause) + { + /* + * Check if the connection is still open. + */ + if (!mClosed) + { + /* + * Prepare the message + */ + this.mClosed = true; + + if (alertLevel == AlertLevel.fatal) + { + /* + * RFC 2246 7.2.1. The session becomes unresumable if any connection is terminated + * without proper close_notify messages with level equal to warning. + */ + // TODO This isn't quite in the right place. Also, as of TLS 1.1 the above is obsolete. + InvalidateSession(); + + this.mFailedWithError = true; + } + RaiseAlert(alertLevel, alertDescription, message, cause); + mRecordStream.SafeClose(); + if (alertLevel != AlertLevel.fatal) + { + return; + } + } + + throw new IOException(TLS_ERROR_MESSAGE); + } + + protected virtual void InvalidateSession() + { + if (this.mSessionParameters != null) + { + this.mSessionParameters.Clear(); + this.mSessionParameters = null; + } + + if (this.mTlsSession != null) + { + this.mTlsSession.Invalidate(); + this.mTlsSession = null; + } + } + + protected virtual void ProcessFinishedMessage(MemoryStream buf) + { + byte[] verify_data = TlsUtilities.ReadFully(mExpectedVerifyData.Length, buf); + + AssertEmpty(buf); + + /* + * Compare both checksums. + */ + if (!Arrays.ConstantTimeAreEqual(mExpectedVerifyData, verify_data)) + { + /* + * Wrong checksum in the finished message. + */ + throw new TlsFatalAlert(AlertDescription.decrypt_error); + } + } + + protected virtual void RaiseAlert(byte alertLevel, byte alertDescription, string message, Exception cause) + { + Peer.NotifyAlertRaised(alertLevel, alertDescription, message, cause); + + byte[] error = new byte[]{ alertLevel, alertDescription }; + + SafeWriteRecord(ContentType.alert, error, 0, 2); + } + + protected virtual void RaiseWarning(byte alertDescription, string message) + { + RaiseAlert(AlertLevel.warning, alertDescription, message, null); + } + + protected virtual void SendCertificateMessage(Certificate certificate) + { + if (certificate == null) + { + certificate = Certificate.EmptyChain; + } + + if (certificate.IsEmpty) + { + TlsContext context = Context; + if (!context.IsServer) + { + ProtocolVersion serverVersion = Context.ServerVersion; + if (serverVersion.IsSsl) + { + string errorMessage = serverVersion.ToString() + " client didn't provide credentials"; + RaiseWarning(AlertDescription.no_certificate, errorMessage); + return; + } + } + } + + HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate); + + certificate.Encode(message); + + message.WriteToRecordStream(this); + } + + protected virtual void SendChangeCipherSpecMessage() + { + byte[] message = new byte[]{ 1 }; + SafeWriteRecord(ContentType.change_cipher_spec, message, 0, message.Length); + mRecordStream.SentWriteCipherSpec(); + } + + protected virtual void SendFinishedMessage() + { + byte[] verify_data = CreateVerifyData(Context.IsServer); + + HandshakeMessage message = new HandshakeMessage(HandshakeType.finished, verify_data.Length); + + message.Write(verify_data, 0, verify_data.Length); + + message.WriteToRecordStream(this); + } + + protected virtual void SendSupplementalDataMessage(IList supplementalData) + { + HandshakeMessage message = new HandshakeMessage(HandshakeType.supplemental_data); + + WriteSupplementalData(message, supplementalData); + + message.WriteToRecordStream(this); + } + + protected virtual byte[] CreateVerifyData(bool isServer) + { + TlsContext context = Context; + string asciiLabel = isServer ? ExporterLabel.server_finished : ExporterLabel.client_finished; + byte[] sslSender = isServer ? TlsUtilities.SSL_SERVER : TlsUtilities.SSL_CLIENT; + byte[] hash = GetCurrentPrfHash(context, mRecordStream.HandshakeHash, sslSender); + return TlsUtilities.CalculateVerifyData(context, asciiLabel, hash); + } + + /** + * Closes this connection. + * + * @throws IOException If something goes wrong during closing. + */ + public virtual void Close() + { + HandleClose(true); + } + + protected virtual void HandleClose(bool user_canceled) + { + if (!mClosed) + { + if (user_canceled && !mAppDataReady) + { + RaiseWarning(AlertDescription.user_canceled, "User canceled handshake"); + } + this.FailWithError(AlertLevel.warning, AlertDescription.close_notify, "Connection closed", null); + } + } + + protected internal virtual void Flush() + { + mRecordStream.Flush(); + } + + protected internal virtual bool IsClosed + { + get { return mClosed; } + } + + protected virtual short ProcessMaxFragmentLengthExtension(IDictionary clientExtensions, IDictionary serverExtensions, + byte alertDescription) + { + short maxFragmentLength = TlsExtensionsUtilities.GetMaxFragmentLengthExtension(serverExtensions); + if (maxFragmentLength >= 0 && !this.mResumedSession) + { + if (maxFragmentLength != TlsExtensionsUtilities.GetMaxFragmentLengthExtension(clientExtensions)) + throw new TlsFatalAlert(alertDescription); + } + return maxFragmentLength; + } + + /** + * Make sure the InputStream 'buf' now empty. Fail otherwise. + * + * @param buf The InputStream to check. + * @throws IOException If 'buf' is not empty. + */ + protected internal static void AssertEmpty(MemoryStream buf) + { + if (buf.Position < buf.Length) + throw new TlsFatalAlert(AlertDescription.decode_error); + } + + protected internal static byte[] CreateRandomBlock(bool useGmtUnixTime, IRandomGenerator randomGenerator) + { + byte[] result = new byte[32]; + randomGenerator.NextBytes(result); + + if (useGmtUnixTime) + { + TlsUtilities.WriteGmtUnixTime(result, 0); + } + + return result; + } + + protected internal static byte[] CreateRenegotiationInfo(byte[] renegotiated_connection) + { + return TlsUtilities.EncodeOpaque8(renegotiated_connection); + } + + protected internal static void EstablishMasterSecret(TlsContext context, TlsKeyExchange keyExchange) + { + byte[] pre_master_secret = keyExchange.GeneratePremasterSecret(); + + try + { + context.SecurityParameters.masterSecret = TlsUtilities.CalculateMasterSecret(context, pre_master_secret); + } + finally + { + // TODO Is there a way to ensure the data is really overwritten? + /* + * RFC 2246 8.1. The pre_master_secret should be deleted from memory once the + * master_secret has been computed. + */ + if (pre_master_secret != null) + { + Arrays.Fill(pre_master_secret, (byte)0); + } + } + } + + /** + * 'sender' only relevant to SSLv3 + */ + protected internal static byte[] GetCurrentPrfHash(TlsContext context, TlsHandshakeHash handshakeHash, byte[] sslSender) + { + IDigest d = handshakeHash.ForkPrfHash(); + + if (sslSender != null && TlsUtilities.IsSsl(context)) + { + d.BlockUpdate(sslSender, 0, sslSender.Length); + } + + return DigestUtilities.DoFinal(d); + } + + protected internal static IDictionary ReadExtensions(MemoryStream input) + { + if (input.Position >= input.Length) + return null; + + byte[] extBytes = TlsUtilities.ReadOpaque16(input); + + AssertEmpty(input); + + MemoryStream buf = new MemoryStream(extBytes, false); + + // Integer -> byte[] + IDictionary extensions = Platform.CreateHashtable(); + + while (buf.Position < buf.Length) + { + int extension_type = TlsUtilities.ReadUint16(buf); + byte[] extension_data = TlsUtilities.ReadOpaque16(buf); + + /* + * RFC 3546 2.3 There MUST NOT be more than one extension of the same type. + */ + if (extensions.Contains(extension_type)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + extensions.Add(extension_type, extension_data); + } + + return extensions; + } + + protected internal static IList ReadSupplementalDataMessage(MemoryStream input) + { + byte[] supp_data = TlsUtilities.ReadOpaque24(input); + + AssertEmpty(input); + + MemoryStream buf = new MemoryStream(supp_data, false); + + IList supplementalData = Platform.CreateArrayList(); + + while (buf.Position < buf.Length) + { + int supp_data_type = TlsUtilities.ReadUint16(buf); + byte[] data = TlsUtilities.ReadOpaque16(buf); + + supplementalData.Add(new SupplementalDataEntry(supp_data_type, data)); + } + + return supplementalData; + } + + protected internal static void WriteExtensions(Stream output, IDictionary extensions) + { + MemoryStream buf = new MemoryStream(); + + foreach (int extension_type in extensions.Keys) + { + byte[] extension_data = (byte[])extensions[extension_type]; + + TlsUtilities.CheckUint16(extension_type); + TlsUtilities.WriteUint16(extension_type, buf); + TlsUtilities.WriteOpaque16(extension_data, buf); + } + + byte[] extBytes = buf.ToArray(); + + TlsUtilities.WriteOpaque16(extBytes, output); + } + + protected internal static void WriteSupplementalData(Stream output, IList supplementalData) + { + MemoryStream buf = new MemoryStream(); + + foreach (SupplementalDataEntry entry in supplementalData) + { + int supp_data_type = entry.DataType; + TlsUtilities.CheckUint16(supp_data_type); + TlsUtilities.WriteUint16(supp_data_type, buf); + TlsUtilities.WriteOpaque16(entry.Data, buf); + } + + byte[] supp_data = buf.ToArray(); + + TlsUtilities.WriteOpaque24(supp_data, output); + } + + protected internal static int GetPrfAlgorithm(TlsContext context, int ciphersuite) + { + bool isTLSv12 = TlsUtilities.IsTlsV12(context); + + switch (ciphersuite) + { + case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8: + case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8: + case CipherSuite.TLS_PSK_WITH_AES_128_CCM: + case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8: + case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_PSK_WITH_AES_256_CCM: + case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_NULL_SHA256: + { + if (isTLSv12) + { + return PrfAlgorithm.tls_prf_sha256; + } + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384: + { + if (isTLSv12) + { + return PrfAlgorithm.tls_prf_sha384; + } + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384: + { + if (isTLSv12) + { + return PrfAlgorithm.tls_prf_sha384; + } + return PrfAlgorithm.tls_prf_legacy; + } + + default: + { + if (isTLSv12) + { + return PrfAlgorithm.tls_prf_sha256; + } + return PrfAlgorithm.tls_prf_legacy; + } + } + } + + internal class HandshakeMessage + : MemoryStream + { + internal HandshakeMessage(byte handshakeType) + : this(handshakeType, 60) + { + } + + internal HandshakeMessage(byte handshakeType, int length) + : base(length + 4) + { + TlsUtilities.WriteUint8(handshakeType, this); + // Reserve space for length + TlsUtilities.WriteUint24(0, this); + } + + internal void Write(byte[] data) + { + Write(data, 0, data.Length); + } + + internal void WriteToRecordStream(TlsProtocol protocol) + { + // Patch actual length back in + long length = Length - 4; + TlsUtilities.CheckUint24(length); + this.Position = 1; + TlsUtilities.WriteUint24((int)length, this); + protocol.WriteHandshakeMessage(GetBuffer(), 0, (int)Length); + this.Close(); + } + } + } +} diff --git a/crypto/src/crypto/tls/TlsProtocolHandler.cs b/crypto/src/crypto/tls/TlsProtocolHandler.cs
index 6d2b0b144..6f223467f 100644 --- a/crypto/src/crypto/tls/TlsProtocolHandler.cs +++ b/crypto/src/crypto/tls/TlsProtocolHandler.cs
@@ -21,1239 +21,19 @@ 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) + [Obsolete("Use 'TlsClientProtocol' instead")] + public class TlsProtocolHandler + : TlsClientProtocol + { + public TlsProtocolHandler(Stream stream, SecureRandom secureRandom) + : base(stream, stream, secureRandom) { - 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) + /// <remarks>Both streams can be the same object</remarks> + public TlsProtocolHandler(Stream input, Stream output, SecureRandom secureRandom) + : base(input, output, secureRandom) { - 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
index 226153a97..cd13e3438 100644 --- a/crypto/src/crypto/tls/TlsPskKeyExchange.cs +++ b/crypto/src/crypto/tls/TlsPskKeyExchange.cs
@@ -1,149 +1,254 @@ using System; +using System.Collections; using System.IO; +using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Crypto.Parameters; -using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; 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]; - } - } + /// <summary>(D)TLS PSK key exchange (RFC 4279).</summary> + public class TlsPskKeyExchange + : AbstractTlsKeyExchange + { + protected TlsPskIdentity mPskIdentity; + protected DHParameters mDHParameters; + protected int[] mNamedCurves; + protected byte[] mClientECPointFormats, mServerECPointFormats; + + protected byte[] mPskIdentityHint = null; + + protected DHPrivateKeyParameters mDHAgreePrivateKey = null; + protected DHPublicKeyParameters mDHAgreePublicKey = null; + + protected AsymmetricKeyParameter mServerPublicKey = null; + protected RsaKeyParameters mRsaServerPublicKey = null; + protected TlsEncryptionCredentials mServerCredentials = null; + protected byte[] mPremasterSecret; + + public TlsPskKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, TlsPskIdentity pskIdentity, + DHParameters dhParameters, int[] namedCurves, byte[] clientECPointFormats, byte[] serverECPointFormats) + : base(keyExchange, supportedSignatureAlgorithms) + { + switch (keyExchange) + { + case KeyExchangeAlgorithm.DHE_PSK: + case KeyExchangeAlgorithm.ECDHE_PSK: + case KeyExchangeAlgorithm.PSK: + case KeyExchangeAlgorithm.RSA_PSK: + break; + default: + throw new InvalidOperationException("unsupported key exchange algorithm"); + } + + this.mPskIdentity = pskIdentity; + this.mDHParameters = dhParameters; + this.mNamedCurves = namedCurves; + this.mClientECPointFormats = clientECPointFormats; + this.mServerECPointFormats = serverECPointFormats; + } + + public override void SkipServerCredentials() + { + if (mKeyExchange == KeyExchangeAlgorithm.RSA_PSK) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + public override void ProcessServerCredentials(TlsCredentials serverCredentials) + { + if (!(serverCredentials is TlsEncryptionCredentials)) + throw new TlsFatalAlert(AlertDescription.internal_error); + + ProcessServerCertificate(serverCredentials.Certificate); + + this.mServerCredentials = (TlsEncryptionCredentials)serverCredentials; + } + + public override byte[] GenerateServerKeyExchange() + { + // TODO[RFC 4279] Need a server-side PSK API to determine hint and resolve identities to keys + this.mPskIdentityHint = null; + + if (this.mPskIdentityHint == null && !RequiresServerKeyExchange) + return null; + + MemoryStream buf = new MemoryStream(); + + if (this.mPskIdentityHint == null) + { + TlsUtilities.WriteOpaque16(TlsUtilities.EmptyBytes, buf); + } + else + { + TlsUtilities.WriteOpaque16(this.mPskIdentityHint, buf); + } + + if (this.mKeyExchange == KeyExchangeAlgorithm.DHE_PSK) + { + if (this.mDHParameters == null) + throw new TlsFatalAlert(AlertDescription.internal_error); + + this.mDHAgreePrivateKey = TlsDHUtilities.GenerateEphemeralServerKeyExchange(context.SecureRandom, + this.mDHParameters, buf); + } + else if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK) + { + // TODO[RFC 5489] + } + + return buf.ToArray(); + } + + public override void ProcessServerCertificate(Certificate serverCertificate) + { + if (mKeyExchange != KeyExchangeAlgorithm.RSA_PSK) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + if (serverCertificate.IsEmpty) + throw new TlsFatalAlert(AlertDescription.bad_certificate); + + X509CertificateStructure x509Cert = serverCertificate.GetCertificateAt(0); + + SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo; + try + { + this.mServerPublicKey = PublicKeyFactory.CreateKey(keyInfo); + } + catch (Exception e) + { + throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e); + } + + // Sanity check the PublicKeyFactory + if (this.mServerPublicKey.IsPrivate) + throw new TlsFatalAlert(AlertDescription.internal_error); + + this.mRsaServerPublicKey = ValidateRsaPublicKey((RsaKeyParameters)this.mServerPublicKey); + + TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.KeyEncipherment); + + base.ProcessServerCertificate(serverCertificate); + } + + public override bool RequiresServerKeyExchange + { + get + { + switch (mKeyExchange) + { + case KeyExchangeAlgorithm.DHE_PSK: + case KeyExchangeAlgorithm.ECDHE_PSK: + return true; + default: + return false; + } + } + } + + public override void ProcessServerKeyExchange(Stream input) + { + this.mPskIdentityHint = TlsUtilities.ReadOpaque16(input); + + if (this.mKeyExchange == KeyExchangeAlgorithm.DHE_PSK) + { + ServerDHParams serverDHParams = ServerDHParams.Parse(input); + + this.mDHAgreePublicKey = TlsDHUtilities.ValidateDHPublicKey(serverDHParams.PublicKey); + } + else if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK) + { + // TODO[RFC 5489] + } + } + + public override void ValidateCertificateRequest(CertificateRequest certificateRequest) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + public override void ProcessClientCredentials(TlsCredentials clientCredentials) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + public override void GenerateClientKeyExchange(Stream output) + { + if (mPskIdentityHint == null) + { + mPskIdentity.SkipIdentityHint(); + } + else + { + mPskIdentity.NotifyIdentityHint(mPskIdentityHint); + } + + byte[] psk_identity = mPskIdentity.GetPskIdentity(); + + TlsUtilities.WriteOpaque16(psk_identity, output); + + if (this.mKeyExchange == KeyExchangeAlgorithm.DHE_PSK) + { + this.mDHAgreePrivateKey = TlsDHUtilities.GenerateEphemeralClientKeyExchange(context.SecureRandom, + mDHAgreePublicKey.Parameters, output); + } + else if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK) + { + // TODO[RFC 5489] + throw new TlsFatalAlert(AlertDescription.internal_error); + } + else if (this.mKeyExchange == KeyExchangeAlgorithm.RSA_PSK) + { + this.mPremasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret(context, + this.mRsaServerPublicKey, output); + } + } + + public override byte[] GeneratePremasterSecret() + { + byte[] psk = mPskIdentity.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.mKeyExchange == KeyExchangeAlgorithm.DHE_PSK) + { + if (mDHAgreePrivateKey != null) + { + return TlsDHUtilities.CalculateDHBasicAgreement(mDHAgreePublicKey, mDHAgreePrivateKey); + } + + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK) + { + // TODO[RFC 5489] + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + if (this.mKeyExchange == KeyExchangeAlgorithm.RSA_PSK) + { + return this.mPremasterSecret; + } + + return new byte[pskLength]; + } + + 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/TlsRsaKeyExchange.cs b/crypto/src/crypto/tls/TlsRsaKeyExchange.cs
index 4538a2a81..3a0a49154 100644 --- a/crypto/src/crypto/tls/TlsRsaKeyExchange.cs +++ b/crypto/src/crypto/tls/TlsRsaKeyExchange.cs
@@ -1,165 +1,140 @@ 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.Encodings; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities.IO; namespace Org.BouncyCastle.Crypto.Tls { - /// <summary> - /// TLS 1.0 RSA key exchange. - /// </summary> - internal class TlsRsaKeyExchange - : TlsKeyExchange - { - protected TlsClientContext context; - - protected AsymmetricKeyParameter serverPublicKey = null; + /// <summary>(D)TLS and SSLv3 RSA key exchange.</summary> + public class TlsRsaKeyExchange + : AbstractTlsKeyExchange + { + protected AsymmetricKeyParameter serverPublicKey = null; protected RsaKeyParameters rsaServerPublicKey = null; + protected TlsEncryptionCredentials serverCredentials = 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)); -// } + public TlsRsaKeyExchange(IList supportedSignatureAlgorithms) + : base(KeyExchangeAlgorithm.RSA, supportedSignatureAlgorithms) + { + } + + public override void SkipServerCredentials() + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + public override void ProcessServerCredentials(TlsCredentials serverCredentials) + { + if (!(serverCredentials is TlsEncryptionCredentials)) + throw new TlsFatalAlert(AlertDescription.internal_error); + + ProcessServerCertificate(serverCredentials.Certificate); + + this.serverCredentials = (TlsEncryptionCredentials)serverCredentials; + } + + public override void ProcessServerCertificate(Certificate serverCertificate) + { + if (serverCertificate.IsEmpty) + throw new TlsFatalAlert(AlertDescription.bad_certificate); + + X509CertificateStructure x509Cert = serverCertificate.GetCertificateAt(0); + + SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo; + try + { + this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo); + } + catch (Exception e) + { + throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e); + } + + // 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); + + base.ProcessServerCertificate(serverCertificate); + } + + public override void ValidateCertificateRequest(CertificateRequest certificateRequest) + { + byte[] types = certificateRequest.CertificateTypes; + for (int i = 0; i < types.Length; ++i) + { + switch (types[i]) + { + case ClientCertificateType.rsa_sign: + case ClientCertificateType.dss_sign: + case ClientCertificateType.ecdsa_sign: + break; + default: + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + } + } + + public override void ProcessClientCredentials(TlsCredentials clientCredentials) + { + if (!(clientCredentials is TlsSignerCredentials)) + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + public override void GenerateClientKeyExchange(Stream output) + { + this.premasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret(context, rsaServerPublicKey, output); + } + + public override void ProcessClientKeyExchange(Stream input) + { + byte[] encryptedPreMasterSecret; + if (TlsUtilities.IsSsl(context)) + { + // TODO Do any SSLv3 clients actually include the length? + encryptedPreMasterSecret = Streams.ReadAll(input); + } + else + { + encryptedPreMasterSecret = TlsUtilities.ReadOpaque16(input); + } + + this.premasterSecret = serverCredentials.DecryptPreMasterSecret(encryptedPreMasterSecret); + } + + public override byte[] GeneratePremasterSecret() + { + if (this.premasterSecret == null) + throw new TlsFatalAlert(AlertDescription.internal_error); + + byte[] tmp = this.premasterSecret; + this.premasterSecret = null; + return tmp; + } 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; - } - } + { + // 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
index a50ff9558..6da1c5e9b 100644 --- a/crypto/src/crypto/tls/TlsRsaSigner.cs +++ b/crypto/src/crypto/tls/TlsRsaSigner.cs
@@ -6,48 +6,97 @@ using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Signers; using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; 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; - } - } + public class TlsRsaSigner + : AbstractTlsSigner + { + public override byte[] GenerateRawSignature(SignatureAndHashAlgorithm algorithm, + AsymmetricKeyParameter privateKey, byte[] hash) + { + ISigner signer = MakeSigner(algorithm, true, true, + new ParametersWithRandom(privateKey, this.mContext.SecureRandom)); + signer.BlockUpdate(hash, 0, hash.Length); + return signer.GenerateSignature(); + } + + public override bool VerifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes, + AsymmetricKeyParameter publicKey, byte[] hash) + { + ISigner signer = MakeSigner(algorithm, true, false, publicKey); + signer.BlockUpdate(hash, 0, hash.Length); + return signer.VerifySignature(sigBytes); + } + + public override ISigner CreateSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey) + { + return MakeSigner(algorithm, false, true, new ParametersWithRandom(privateKey, this.mContext.SecureRandom)); + } + + public override ISigner CreateVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey) + { + return MakeSigner(algorithm, false, false, publicKey); + } + + public override bool IsValidPublicKey(AsymmetricKeyParameter publicKey) + { + return publicKey is RsaKeyParameters && !publicKey.IsPrivate; + } + + protected virtual ISigner MakeSigner(SignatureAndHashAlgorithm algorithm, bool raw, bool forSigning, + ICipherParameters cp) + { + if ((algorithm != null) != TlsUtilities.IsTlsV12(mContext)) + throw new InvalidOperationException(); + if (algorithm != null && algorithm.Signature != SignatureAlgorithm.rsa) + throw new InvalidOperationException(); + + IDigest d; + if (raw) + { + d = new NullDigest(); + } + else if (algorithm == null) + { + d = new CombinedHash(); + } + else + { + d = TlsUtilities.CreateHash(algorithm.Hash); + } + + ISigner s; + if (algorithm != null) + { + /* + * RFC 5246 4.7. In RSA signing, the opaque vector contains the signature generated + * using the RSASSA-PKCS1-v1_5 signature scheme defined in [PKCS1]. + */ + s = new RsaDigestSigner(d, TlsUtilities.GetOidForHashAlgorithm(algorithm.Hash)); + } + else + { + /* + * RFC 5246 4.7. Note that earlier versions of TLS used a different RSA signature scheme + * that did not include a DigestInfo encoding. + */ + s = new GenericSigner(CreateRsaImpl(), d); + } + s.Init(forSigning, cp); + return s; + } + + protected virtual IAsymmetricBlockCipher CreateRsaImpl() + { + /* + * RFC 5264 7.4.7.1. Implementation note: It is now known that remote timing-based attacks + * on TLS are possible, at least when the client and server are on the same LAN. + * Accordingly, implementations that use static RSA keys MUST use RSA blinding or some other + * anti-timing technique, as described in [TIMING]. + */ + return new Pkcs1Encoding(new RsaBlindedEngine()); + } + } } diff --git a/crypto/src/crypto/tls/TlsRsaUtilities.cs b/crypto/src/crypto/tls/TlsRsaUtilities.cs
index 4450ba452..0e42c1733 100644 --- a/crypto/src/crypto/tls/TlsRsaUtilities.cs +++ b/crypto/src/crypto/tls/TlsRsaUtilities.cs
@@ -5,38 +5,128 @@ using Org.BouncyCastle.Crypto.Encodings; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; 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; - } - } + public abstract class TlsRsaUtilities + { + /// <exception cref="IOException"></exception> + public static byte[] GenerateEncryptedPreMasterSecret(TlsContext context, RsaKeyParameters rsaServerPublicKey, + Stream output) + { + /* + * Choose a PremasterSecret and send it encrypted to the server + */ + byte[] premasterSecret = new byte[48]; + context.SecureRandom.NextBytes(premasterSecret); + TlsUtilities.WriteVersion(context.ClientVersion, premasterSecret, 0); + + Pkcs1Encoding encoding = new Pkcs1Encoding(new RsaBlindedEngine()); + encoding.Init(true, new ParametersWithRandom(rsaServerPublicKey, context.SecureRandom)); + + try + { + byte[] encryptedPreMasterSecret = encoding.ProcessBlock(premasterSecret, 0, premasterSecret.Length); + + if (TlsUtilities.IsSsl(context)) + { + // TODO Do any SSLv3 servers actually expect the length? + output.Write(encryptedPreMasterSecret, 0, encryptedPreMasterSecret.Length); + } + else + { + TlsUtilities.WriteOpaque16(encryptedPreMasterSecret, output); + } + } + catch (InvalidCipherTextException e) + { + /* + * This should never happen, only during decryption. + */ + throw new TlsFatalAlert(AlertDescription.internal_error, e); + } + + return premasterSecret; + } + + public static byte[] SafeDecryptPreMasterSecret(TlsContext context, RsaKeyParameters rsaServerPrivateKey, + byte[] encryptedPreMasterSecret) + { + /* + * RFC 5246 7.4.7.1. + */ + ProtocolVersion clientVersion = context.ClientVersion; + + // TODO Provide as configuration option? + bool versionNumberCheckDisabled = false; + + /* + * Generate 48 random bytes we can use as a Pre-Master-Secret, if the + * PKCS1 padding check should fail. + */ + byte[] fallback = new byte[48]; + context.SecureRandom.NextBytes(fallback); + + byte[] M = Arrays.Clone(fallback); + try + { + Pkcs1Encoding encoding = new Pkcs1Encoding(new RsaBlindedEngine(), fallback); + encoding.Init(false, + new ParametersWithRandom(rsaServerPrivateKey, context.SecureRandom)); + + M = encoding.ProcessBlock(encryptedPreMasterSecret, 0, encryptedPreMasterSecret.Length); + } + catch (Exception) + { + /* + * This should never happen since the decryption should never throw an exception + * and return a random value instead. + * + * In any case, a TLS server MUST NOT generate an alert if processing an + * RSA-encrypted premaster secret message fails, or the version number is not as + * expected. Instead, it MUST continue the handshake with a randomly generated + * premaster secret. + */ + } + + /* + * If ClientHello.client_version is TLS 1.1 or higher, server implementations MUST + * check the version number [..]. + */ + if (versionNumberCheckDisabled && clientVersion.IsEqualOrEarlierVersionOf(ProtocolVersion.TLSv10)) + { + /* + * If the version number is TLS 1.0 or earlier, server + * implementations SHOULD check the version number, but MAY have a + * configuration option to disable the check. + * + * So there is nothing to do here. + */ + } + else + { + /* + * OK, we need to compare the version number in the decrypted Pre-Master-Secret with the + * clientVersion received during the handshake. If they don't match, we replace the + * decrypted Pre-Master-Secret with a random one. + */ + int correct = (clientVersion.MajorVersion ^ (M[0] & 0xff)) + | (clientVersion.MinorVersion ^ (M[1] & 0xff)); + correct |= correct >> 1; + correct |= correct >> 2; + correct |= correct >> 4; + int mask = ~((correct & 1) - 1); + + /* + * mask will be all bits set to 0xff if the version number differed. + */ + for (int i = 0; i < 48; i++) + { + M[i] = (byte)((M[i] & (~mask)) | (fallback[i] & mask)); + } + } + return M; + } + } } diff --git a/crypto/src/crypto/tls/TlsServer.cs b/crypto/src/crypto/tls/TlsServer.cs new file mode 100644
index 000000000..93e62b9ac --- /dev/null +++ b/crypto/src/crypto/tls/TlsServer.cs
@@ -0,0 +1,90 @@ +using System; +using System.Collections; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsServer + : TlsPeer + { + void Init(TlsServerContext context); + + /// <exception cref="IOException"></exception> + void NotifyClientVersion(ProtocolVersion clientVersion); + + /// <exception cref="IOException"></exception> + void NotifyOfferedCipherSuites(int[] offeredCipherSuites); + + /// <exception cref="IOException"></exception> + void NotifyOfferedCompressionMethods(byte[] offeredCompressionMethods); + + /// <param name="clientExtensions">A <see cref="IDictionary"/> (Int32 -> byte[]). Will never be null.</param> + /// <exception cref="IOException"></exception> + void ProcessClientExtensions(IDictionary clientExtensions); + + /// <exception cref="IOException"></exception> + ProtocolVersion GetServerVersion(); + + /// <exception cref="IOException"></exception> + int GetSelectedCipherSuite(); + + /// <exception cref="IOException"></exception> + byte GetSelectedCompressionMethod(); + + /// <summary> + /// Get the (optional) table of server extensions to be included in (extended) server hello. + /// </summary> + /// <returns> + /// A <see cref="IDictionary"/> (Int32 -> byte[]). May be null. + /// </returns> + /// <exception cref="IOException"></exception> + IDictionary GetServerExtensions(); + + /// <returns> + /// A <see cref="IList"/> (<see cref="SupplementalDataEntry"/>). May be null. + /// </returns> + /// <exception cref="IOException"></exception> + IList GetServerSupplementalData(); + + /// <exception cref="IOException"></exception> + TlsCredentials GetCredentials(); + + /// <remarks> + /// This method will be called (only) if the server included an extension of type + /// "status_request" with empty "extension_data" in the extended server hello. See <i>RFC 3546 + /// 3.6. Certificate Status Request</i>. If a non-null <see cref="CertificateStatus"/> is returned, it + /// is sent to the client as a handshake message of type "certificate_status". + /// </remarks> + /// <returns>A <see cref="CertificateStatus"/> to be sent to the client (or null for none).</returns> + /// <exception cref="IOException"></exception> + CertificateStatus GetCertificateStatus(); + + /// <exception cref="IOException"></exception> + TlsKeyExchange GetKeyExchange(); + + /// <exception cref="IOException"></exception> + CertificateRequest GetCertificateRequest(); + + /// <param name="clientSupplementalData"><see cref="IList"/> (<see cref="SupplementalDataEntry"/>)</param> + /// <exception cref="IOException"></exception> + void ProcessClientSupplementalData(IList clientSupplementalData); + + /// <summary> + /// Called by the protocol handler to report the client certificate, only if <c>GetCertificateRequest</c> + /// returned non-null. + /// </summary> + /// <remarks>Note: this method is responsible for certificate verification and validation.</remarks> + /// <param name="clientCertificate">the effective client certificate (may be an empty chain).</param> + /// <exception cref="IOException"></exception> + void NotifyClientCertificate(Certificate clientCertificate); + + /// <summary>RFC 5077 3.3. NewSessionTicket Handshake Message.</summary> + /// <remarks> + /// This method will be called (only) if a NewSessionTicket extension was sent by the server. See + /// <i>RFC 5077 4. Recommended Ticket Construction</i> for recommended format and protection. + /// </remarks> + /// <returns>The <see cref="NewSessionTicket">ticket</see>)</returns> + /// <exception cref="IOException"></exception> + NewSessionTicket GetNewSessionTicket(); + } +} diff --git a/crypto/src/crypto/tls/TlsServerContext.cs b/crypto/src/crypto/tls/TlsServerContext.cs new file mode 100644
index 000000000..4021571aa --- /dev/null +++ b/crypto/src/crypto/tls/TlsServerContext.cs
@@ -0,0 +1,11 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsServerContext + : TlsContext + { + } +} diff --git a/crypto/src/crypto/tls/TlsServerContextImpl.cs b/crypto/src/crypto/tls/TlsServerContextImpl.cs new file mode 100644
index 000000000..d56566ffc --- /dev/null +++ b/crypto/src/crypto/tls/TlsServerContextImpl.cs
@@ -0,0 +1,20 @@ +using System; + +using Org.BouncyCastle.Security; + +namespace Org.BouncyCastle.Crypto.Tls +{ + internal class TlsServerContextImpl + : AbstractTlsContext, TlsServerContext + { + internal TlsServerContextImpl(SecureRandom secureRandom, SecurityParameters securityParameters) + : base(secureRandom, securityParameters) + { + } + + public override bool IsServer + { + get { return true; } + } + } +} diff --git a/crypto/src/crypto/tls/TlsServerProtocol.cs b/crypto/src/crypto/tls/TlsServerProtocol.cs new file mode 100644
index 000000000..589ede802 --- /dev/null +++ b/crypto/src/crypto/tls/TlsServerProtocol.cs
@@ -0,0 +1,744 @@ +using System; +using System.Collections; +using System.IO; + +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class TlsServerProtocol + : TlsProtocol + { + protected TlsServer mTlsServer = null; + internal TlsServerContextImpl mTlsServerContext = null; + + protected TlsKeyExchange mKeyExchange = null; + protected TlsCredentials mServerCredentials = null; + protected CertificateRequest mCertificateRequest = null; + + protected short mClientCertificateType = -1; + protected TlsHandshakeHash mPrepareFinishHash = null; + + public TlsServerProtocol(Stream stream, SecureRandom secureRandom) + : base(stream, secureRandom) + { + } + + public TlsServerProtocol(Stream input, Stream output, SecureRandom secureRandom) + : base(input, output, secureRandom) + { + } + + /** + * Receives a TLS handshake in the role of server + * + * @param mTlsServer + * @throws IOException If handshake was not successful. + */ + public virtual void Accept(TlsServer tlsServer) + { + if (tlsServer == null) + throw new ArgumentNullException("tlsServer"); + if (this.mTlsServer != null) + throw new InvalidOperationException("'Accept' can only be called once"); + + this.mTlsServer = tlsServer; + + this.mSecurityParameters = new SecurityParameters(); + this.mSecurityParameters.entity = ConnectionEnd.server; + + this.mTlsServerContext = new TlsServerContextImpl(mSecureRandom, mSecurityParameters); + + this.mSecurityParameters.serverRandom = CreateRandomBlock(tlsServer.ShouldUseGmtUnixTime(), + mTlsServerContext.NonceRandomGenerator); + + this.mTlsServer.Init(mTlsServerContext); + this.mRecordStream.Init(mTlsServerContext); + + this.mRecordStream.SetRestrictReadVersion(false); + + CompleteHandshake(); + } + + protected override void CleanupHandshake() + { + base.CleanupHandshake(); + + this.mKeyExchange = null; + this.mServerCredentials = null; + this.mCertificateRequest = null; + this.mPrepareFinishHash = null; + } + + protected override TlsContext Context + { + get { return mTlsServerContext; } + } + + internal override AbstractTlsContext ContextAdmin + { + get { return mTlsServerContext; } + } + + protected override TlsPeer Peer + { + get { return mTlsServer; } + } + + protected override void HandleHandshakeMessage(byte type, byte[] data) + { + MemoryStream buf = new MemoryStream(data); + + switch (type) + { + case HandshakeType.client_hello: + { + switch (this.mConnectionState) + { + case CS_START: + { + ReceiveClientHelloMessage(buf); + this.mConnectionState = CS_CLIENT_HELLO; + + SendServerHelloMessage(); + this.mConnectionState = CS_SERVER_HELLO; + + IList serverSupplementalData = mTlsServer.GetServerSupplementalData(); + if (serverSupplementalData != null) + { + SendSupplementalDataMessage(serverSupplementalData); + } + this.mConnectionState = CS_SERVER_SUPPLEMENTAL_DATA; + + this.mKeyExchange = mTlsServer.GetKeyExchange(); + this.mKeyExchange.Init(Context); + + this.mServerCredentials = mTlsServer.GetCredentials(); + + Certificate serverCertificate = null; + + if (this.mServerCredentials == null) + { + this.mKeyExchange.SkipServerCredentials(); + } + else + { + this.mKeyExchange.ProcessServerCredentials(this.mServerCredentials); + + serverCertificate = this.mServerCredentials.Certificate; + SendCertificateMessage(serverCertificate); + } + this.mConnectionState = CS_SERVER_CERTIFICATE; + + // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus + if (serverCertificate == null || serverCertificate.IsEmpty) + { + this.mAllowCertificateStatus = false; + } + + if (this.mAllowCertificateStatus) + { + CertificateStatus certificateStatus = mTlsServer.GetCertificateStatus(); + if (certificateStatus != null) + { + SendCertificateStatusMessage(certificateStatus); + } + } + + this.mConnectionState = CS_CERTIFICATE_STATUS; + + byte[] serverKeyExchange = this.mKeyExchange.GenerateServerKeyExchange(); + if (serverKeyExchange != null) + { + SendServerKeyExchangeMessage(serverKeyExchange); + } + this.mConnectionState = CS_SERVER_KEY_EXCHANGE; + + if (this.mServerCredentials != null) + { + this.mCertificateRequest = mTlsServer.GetCertificateRequest(); + if (this.mCertificateRequest != null) + { + this.mKeyExchange.ValidateCertificateRequest(mCertificateRequest); + + SendCertificateRequestMessage(mCertificateRequest); + + TlsUtilities.TrackHashAlgorithms(this.mRecordStream.HandshakeHash, + this.mCertificateRequest.SupportedSignatureAlgorithms); + } + } + this.mConnectionState = CS_CERTIFICATE_REQUEST; + + SendServerHelloDoneMessage(); + this.mConnectionState = CS_SERVER_HELLO_DONE; + + this.mRecordStream.HandshakeHash.SealHashAlgorithms(); + + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.supplemental_data: + { + switch (this.mConnectionState) + { + case CS_SERVER_HELLO_DONE: + { + mTlsServer.ProcessClientSupplementalData(ReadSupplementalDataMessage(buf)); + this.mConnectionState = CS_CLIENT_SUPPLEMENTAL_DATA; + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.certificate: + { + switch (this.mConnectionState) + { + case CS_SERVER_HELLO_DONE: + case CS_CLIENT_SUPPLEMENTAL_DATA: + { + if (mConnectionState < CS_CLIENT_SUPPLEMENTAL_DATA) + { + mTlsServer.ProcessClientSupplementalData(null); + } + + if (this.mCertificateRequest == null) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + + ReceiveCertificateMessage(buf); + this.mConnectionState = CS_CLIENT_CERTIFICATE; + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.client_key_exchange: + { + switch (this.mConnectionState) + { + case CS_SERVER_HELLO_DONE: + case CS_CLIENT_SUPPLEMENTAL_DATA: + case CS_CLIENT_CERTIFICATE: + { + if (mConnectionState < CS_CLIENT_SUPPLEMENTAL_DATA) + { + mTlsServer.ProcessClientSupplementalData(null); + } + + if (mConnectionState < CS_CLIENT_CERTIFICATE) + { + if (this.mCertificateRequest == null) + { + this.mKeyExchange.SkipClientCredentials(); + } + else + { + if (TlsUtilities.IsTlsV12(Context)) + { + /* + * RFC 5246 If no suitable certificate is available, the client MUST Send a + * certificate message containing no certificates. + * + * NOTE: In previous RFCs, this was SHOULD instead of MUST. + */ + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + else if (TlsUtilities.IsSsl(Context)) + { + if (this.mPeerCertificate == null) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + else + { + NotifyClientCertificate(Certificate.EmptyChain); + } + } + } + + ReceiveClientKeyExchangeMessage(buf); + this.mConnectionState = CS_CLIENT_KEY_EXCHANGE; + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.certificate_verify: + { + switch (this.mConnectionState) + { + case CS_CLIENT_KEY_EXCHANGE: + { + /* + * RFC 5246 7.4.8 This message is only sent following a client certificate that has + * signing capability (i.e., all certificates except those containing fixed + * Diffie-Hellman parameters). + */ + if (!ExpectCertificateVerifyMessage()) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + + ReceiveCertificateVerifyMessage(buf); + this.mConnectionState = CS_CERTIFICATE_VERIFY; + + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.finished: + { + switch (this.mConnectionState) + { + case CS_CLIENT_KEY_EXCHANGE: + case CS_CERTIFICATE_VERIFY: + { + if (mConnectionState < CS_CERTIFICATE_VERIFY && ExpectCertificateVerifyMessage()) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + + ProcessFinishedMessage(buf); + this.mConnectionState = CS_CLIENT_FINISHED; + + if (this.mExpectSessionTicket) + { + SendNewSessionTicketMessage(mTlsServer.GetNewSessionTicket()); + SendChangeCipherSpecMessage(); + } + this.mConnectionState = CS_SERVER_SESSION_TICKET; + + SendFinishedMessage(); + this.mConnectionState = CS_SERVER_FINISHED; + this.mConnectionState = CS_END; + break; + } + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + break; + } + case HandshakeType.hello_request: + case HandshakeType.hello_verify_request: + case HandshakeType.server_hello: + case HandshakeType.server_key_exchange: + case HandshakeType.certificate_request: + case HandshakeType.server_hello_done: + case HandshakeType.session_ticket: + default: + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + } + + protected override void HandleWarningMessage(byte description) + { + switch (description) + { + case AlertDescription.no_certificate: + { + /* + * SSL 3.0 If the server has sent a certificate request Message, the client must Send + * either the certificate message or a no_certificate alert. + */ + if (TlsUtilities.IsSsl(Context) && mCertificateRequest != null) + { + NotifyClientCertificate(Certificate.EmptyChain); + } + break; + } + default: + { + base.HandleWarningMessage(description); + break; + } + } + } + + protected virtual void NotifyClientCertificate(Certificate clientCertificate) + { + if (mCertificateRequest == null) + throw new InvalidOperationException(); + if (mPeerCertificate != null) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + + this.mPeerCertificate = clientCertificate; + + if (clientCertificate.IsEmpty) + { + this.mKeyExchange.SkipClientCredentials(); + } + else + { + + /* + * TODO RFC 5246 7.4.6. If the certificate_authorities list in the certificate request + * message was non-empty, one of the certificates in the certificate chain SHOULD be + * issued by one of the listed CAs. + */ + + this.mClientCertificateType = TlsUtilities.GetClientCertificateType(clientCertificate, + this.mServerCredentials.Certificate); + + this.mKeyExchange.ProcessClientCertificate(clientCertificate); + } + + /* + * RFC 5246 7.4.6. If the client does not Send any certificates, the server MAY at its + * discretion either continue the handshake without client authentication, or respond with a + * fatal handshake_failure alert. Also, if some aspect of the certificate chain was + * unacceptable (e.g., it was not signed by a known, trusted CA), the server MAY at its + * discretion either continue the handshake (considering the client unauthenticated) or Send + * a fatal alert. + */ + this.mTlsServer.NotifyClientCertificate(clientCertificate); + } + + protected virtual void ReceiveCertificateMessage(MemoryStream buf) + { + Certificate clientCertificate = Certificate.Parse(buf); + + AssertEmpty(buf); + + NotifyClientCertificate(clientCertificate); + } + + protected virtual void ReceiveCertificateVerifyMessage(MemoryStream buf) + { + DigitallySigned clientCertificateVerify = DigitallySigned.Parse(Context, buf); + + AssertEmpty(buf); + + // Verify the CertificateVerify message contains a correct signature. + try + { + byte[] certificateVerifyHash; + if (TlsUtilities.IsTlsV12(Context)) + { + certificateVerifyHash = mPrepareFinishHash.GetFinalHash(clientCertificateVerify.Algorithm.Hash); + } + else + { + certificateVerifyHash = TlsProtocol.GetCurrentPrfHash(Context, mPrepareFinishHash, null); + } + + X509CertificateStructure x509Cert = mPeerCertificate.GetCertificateAt(0); + SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo; + AsymmetricKeyParameter publicKey = PublicKeyFactory.CreateKey(keyInfo); + + TlsSigner tlsSigner = TlsUtilities.CreateTlsSigner((byte)mClientCertificateType); + tlsSigner.Init(Context); + if (!tlsSigner.VerifyRawSignature(clientCertificateVerify.Algorithm, + clientCertificateVerify.Signature, publicKey, certificateVerifyHash)) + { + throw new TlsFatalAlert(AlertDescription.decrypt_error); + } + } + catch (Exception e) + { + throw new TlsFatalAlert(AlertDescription.decrypt_error, e); + } + } + + protected virtual void ReceiveClientHelloMessage(MemoryStream buf) + { + ProtocolVersion client_version = TlsUtilities.ReadVersion(buf); + if (client_version.IsDtls) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + byte[] client_random = TlsUtilities.ReadFully(32, buf); + + /* + * TODO RFC 5077 3.4. If a ticket is presented by the client, the server MUST NOT attempt to + * use the Session ID in the ClientHello for stateful session resumption. + */ + byte[] sessionID = TlsUtilities.ReadOpaque8(buf); + if (sessionID.Length > 32) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + /* + * TODO RFC 5246 7.4.1.2. If the session_id field is not empty (implying a session + * resumption request), this vector MUST include at least the cipher_suite from that + * session. + */ + int cipher_suites_length = TlsUtilities.ReadUint16(buf); + if (cipher_suites_length < 2 || (cipher_suites_length & 1) != 0) + throw new TlsFatalAlert(AlertDescription.decode_error); + + this.mOfferedCipherSuites = TlsUtilities.ReadUint16Array(cipher_suites_length / 2, buf); + + /* + * TODO RFC 5246 7.4.1.2. If the session_id field is not empty (implying a session + * resumption request), it MUST include the compression_method from that session. + */ + int compression_methods_length = TlsUtilities.ReadUint8(buf); + if (compression_methods_length < 1) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + + this.mOfferedCompressionMethods = TlsUtilities.ReadUint8Array(compression_methods_length, buf); + + /* + * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore + * extensions appearing in the client hello, and Send a server hello containing no + * extensions. + */ + this.mClientExtensions = ReadExtensions(buf); + + ContextAdmin.SetClientVersion(client_version); + + mTlsServer.NotifyClientVersion(client_version); + + mSecurityParameters.clientRandom = client_random; + + mTlsServer.NotifyOfferedCipherSuites(mOfferedCipherSuites); + mTlsServer.NotifyOfferedCompressionMethods(mOfferedCompressionMethods); + + /* + * RFC 5746 3.6. Server Behavior: Initial Handshake + */ + { + /* + * RFC 5746 3.4. The client MUST include either an empty "renegotiation_info" extension, + * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the + * ClientHello. Including both is NOT RECOMMENDED. + */ + + /* + * When a ClientHello is received, the server MUST check if it includes the + * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. If it does, set the secure_renegotiation flag + * to TRUE. + */ + if (Arrays.Contains(mOfferedCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) + { + this.mSecureRenegotiation = true; + } + + /* + * The server MUST check if the "renegotiation_info" extension is included in the + * ClientHello. + */ + byte[] renegExtData = TlsUtilities.GetExtensionData(mClientExtensions, ExtensionType.renegotiation_info); + if (renegExtData != null) + { + /* + * If the extension is present, set secure_renegotiation flag to TRUE. The + * server MUST then verify that the length of the "renegotiated_connection" + * field is zero, and if it is not, MUST abort the handshake. + */ + this.mSecureRenegotiation = true; + + if (!Arrays.ConstantTimeAreEqual(renegExtData, CreateRenegotiationInfo(TlsUtilities.EmptyBytes))) + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + } + + mTlsServer.NotifySecureRenegotiation(this.mSecureRenegotiation); + + if (mClientExtensions != null) + { + mTlsServer.ProcessClientExtensions(mClientExtensions); + } + } + + protected virtual void ReceiveClientKeyExchangeMessage(MemoryStream buf) + { + mKeyExchange.ProcessClientKeyExchange(buf); + + AssertEmpty(buf); + + EstablishMasterSecret(Context, mKeyExchange); + mRecordStream.SetPendingConnectionState(Peer.GetCompression(), Peer.GetCipher()); + + this.mPrepareFinishHash = mRecordStream.PrepareToFinish(); + + if (!mExpectSessionTicket) + { + SendChangeCipherSpecMessage(); + } + } + + protected virtual void SendCertificateRequestMessage(CertificateRequest certificateRequest) + { + HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate_request); + + certificateRequest.Encode(message); + + message.WriteToRecordStream(this); + } + + protected virtual void SendCertificateStatusMessage(CertificateStatus certificateStatus) + { + HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate_status); + + certificateStatus.Encode(message); + + message.WriteToRecordStream(this); + } + + protected virtual void SendNewSessionTicketMessage(NewSessionTicket newSessionTicket) + { + if (newSessionTicket == null) + throw new TlsFatalAlert(AlertDescription.internal_error); + + HandshakeMessage message = new HandshakeMessage(HandshakeType.session_ticket); + + newSessionTicket.Encode(message); + + message.WriteToRecordStream(this); + } + + protected virtual void SendServerHelloMessage() + { + HandshakeMessage message = new HandshakeMessage(HandshakeType.server_hello); + + ProtocolVersion server_version = mTlsServer.GetServerVersion(); + if (!server_version.IsEqualOrEarlierVersionOf(Context.ClientVersion)) + throw new TlsFatalAlert(AlertDescription.internal_error); + + mRecordStream.ReadVersion = server_version; + mRecordStream.SetWriteVersion(server_version); + mRecordStream.SetRestrictReadVersion(true); + ContextAdmin.SetServerVersion(server_version); + + TlsUtilities.WriteVersion(server_version, message); + + message.Write(this.mSecurityParameters.serverRandom); + + /* + * The server may return an empty session_id to indicate that the session will not be cached + * and therefore cannot be resumed. + */ + TlsUtilities.WriteOpaque8(TlsUtilities.EmptyBytes, message); + + int selectedCipherSuite = mTlsServer.GetSelectedCipherSuite(); + if (!Arrays.Contains(mOfferedCipherSuites, selectedCipherSuite) + || selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL + || selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV + || !TlsUtilities.IsValidCipherSuiteForVersion(selectedCipherSuite, server_version)) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + mSecurityParameters.cipherSuite = selectedCipherSuite; + + byte selectedCompressionMethod = mTlsServer.GetSelectedCompressionMethod(); + if (!Arrays.Contains(mOfferedCompressionMethods, selectedCompressionMethod)) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + mSecurityParameters.compressionAlgorithm = selectedCompressionMethod; + + TlsUtilities.WriteUint16(selectedCipherSuite, message); + TlsUtilities.WriteUint8(selectedCompressionMethod, message); + + this.mServerExtensions = mTlsServer.GetServerExtensions(); + + /* + * RFC 5746 3.6. Server Behavior: Initial Handshake + */ + if (this.mSecureRenegotiation) + { + byte[] renegExtData = TlsUtilities.GetExtensionData(this.mServerExtensions, ExtensionType.renegotiation_info); + bool noRenegExt = (null == renegExtData); + + if (noRenegExt) + { + /* + * Note that Sending a "renegotiation_info" extension in response to a ClientHello + * containing only the SCSV is an explicit exception to the prohibition in RFC 5246, + * Section 7.4.1.4, on the server Sending unsolicited extensions and is only allowed + * because the client is signaling its willingness to receive the extension via the + * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. + */ + + /* + * If the secure_renegotiation flag is set to TRUE, the server MUST include an empty + * "renegotiation_info" extension in the ServerHello message. + */ + this.mServerExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(mServerExtensions); + this.mServerExtensions[ExtensionType.renegotiation_info] = CreateRenegotiationInfo(TlsUtilities.EmptyBytes); + } + } + + /* + * 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. + */ + + if (this.mServerExtensions != null) + { + this.mSecurityParameters.encryptThenMac = TlsExtensionsUtilities.HasEncryptThenMacExtension(mServerExtensions); + + this.mSecurityParameters.maxFragmentLength = ProcessMaxFragmentLengthExtension(mClientExtensions, + mServerExtensions, AlertDescription.internal_error); + + this.mSecurityParameters.truncatedHMac = TlsExtensionsUtilities.HasTruncatedHMacExtension(mServerExtensions); + + /* + * TODO It's surprising that there's no provision to allow a 'fresh' CertificateStatus to be sent in + * a session resumption handshake. + */ + this.mAllowCertificateStatus = !mResumedSession + && TlsUtilities.HasExpectedEmptyExtensionData(mServerExtensions, ExtensionType.status_request, + AlertDescription.internal_error); + + this.mExpectSessionTicket = !mResumedSession + && TlsUtilities.HasExpectedEmptyExtensionData(mServerExtensions, ExtensionType.session_ticket, + AlertDescription.internal_error); + + WriteExtensions(message, this.mServerExtensions); + } + + if (mSecurityParameters.maxFragmentLength >= 0) + { + int plainTextLimit = 1 << (8 + mSecurityParameters.maxFragmentLength); + mRecordStream.SetPlaintextLimit(plainTextLimit); + } + + mSecurityParameters.prfAlgorithm = GetPrfAlgorithm(Context, mSecurityParameters.CipherSuite); + + /* + * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify verify_data_length has + * a verify_data_length equal to 12. This includes all existing cipher suites. + */ + mSecurityParameters.verifyDataLength = 12; + + message.WriteToRecordStream(this); + + this.mRecordStream.NotifyHelloComplete(); + } + + protected virtual void SendServerHelloDoneMessage() + { + byte[] message = new byte[4]; + TlsUtilities.WriteUint8(HandshakeType.server_hello_done, message, 0); + TlsUtilities.WriteUint24(0, message, 1); + + WriteHandshakeMessage(message, 0, message.Length); + } + + protected virtual void SendServerKeyExchangeMessage(byte[] serverKeyExchange) + { + HandshakeMessage message = new HandshakeMessage(HandshakeType.server_key_exchange, serverKeyExchange.Length); + + message.Write(serverKeyExchange); + + message.WriteToRecordStream(this); + } + + protected virtual bool ExpectCertificateVerifyMessage() + { + return mClientCertificateType >= 0 && TlsUtilities.HasSigningCapability((byte)mClientCertificateType); + } + } +} diff --git a/crypto/src/crypto/tls/TlsSession.cs b/crypto/src/crypto/tls/TlsSession.cs new file mode 100644
index 000000000..6c229913b --- /dev/null +++ b/crypto/src/crypto/tls/TlsSession.cs
@@ -0,0 +1,15 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public interface TlsSession + { + SessionParameters ExportSessionParameters(); + + byte[] SessionID { get; } + + void Invalidate(); + + bool IsResumable { get; } + } +} diff --git a/crypto/src/crypto/tls/TlsSessionImpl.cs b/crypto/src/crypto/tls/TlsSessionImpl.cs new file mode 100644
index 000000000..866392623 --- /dev/null +++ b/crypto/src/crypto/tls/TlsSessionImpl.cs
@@ -0,0 +1,54 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + internal class TlsSessionImpl + : TlsSession + { + internal readonly byte[] mSessionID; + internal SessionParameters mSessionParameters; + + internal TlsSessionImpl(byte[] sessionID, SessionParameters sessionParameters) + { + if (sessionID == null) + throw new ArgumentNullException("sessionID"); + if (sessionID.Length < 1 || sessionID.Length > 32) + throw new ArgumentException("must have length between 1 and 32 bytes, inclusive", "sessionID"); + + this.mSessionID = Arrays.Clone(sessionID); + this.mSessionParameters = sessionParameters; + } + + public virtual SessionParameters ExportSessionParameters() + { + lock (this) + { + return this.mSessionParameters == null ? null : this.mSessionParameters.Copy(); + } + } + + public virtual byte[] SessionID + { + get { lock (this) return mSessionID; } + } + + public virtual void Invalidate() + { + lock (this) + { + if (this.mSessionParameters != null) + { + this.mSessionParameters.Clear(); + this.mSessionParameters = null; + } + } + } + + public virtual bool IsResumable + { + get { lock (this) return this.mSessionParameters != null; } + } + } +} diff --git a/crypto/src/crypto/tls/TlsSigner.cs b/crypto/src/crypto/tls/TlsSigner.cs
index e59b90705..ffdd4c9a1 100644 --- a/crypto/src/crypto/tls/TlsSigner.cs +++ b/crypto/src/crypto/tls/TlsSigner.cs
@@ -1,18 +1,29 @@ 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); + public interface TlsSigner + { + void Init(TlsContext context); + + byte[] GenerateRawSignature(AsymmetricKeyParameter privateKey, byte[] md5AndSha1); + + byte[] GenerateRawSignature(SignatureAndHashAlgorithm algorithm, + AsymmetricKeyParameter privateKey, byte[] hash); + + bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5AndSha1); + + bool VerifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes, + AsymmetricKeyParameter publicKey, byte[] hash); + + ISigner CreateSigner(AsymmetricKeyParameter privateKey); + + ISigner CreateSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey); + + ISigner CreateVerifyer(AsymmetricKeyParameter publicKey); - ISigner CreateSigner(SecureRandom random, AsymmetricKeyParameter privateKey); - ISigner CreateVerifyer(AsymmetricKeyParameter publicKey); + ISigner CreateVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey); - bool IsValidPublicKey(AsymmetricKeyParameter publicKey); - } + bool IsValidPublicKey(AsymmetricKeyParameter publicKey); + } } diff --git a/crypto/src/crypto/tls/TlsSignerCredentials.cs b/crypto/src/crypto/tls/TlsSignerCredentials.cs
index 2adb06c26..92ed7cc19 100644 --- a/crypto/src/crypto/tls/TlsSignerCredentials.cs +++ b/crypto/src/crypto/tls/TlsSignerCredentials.cs
@@ -3,9 +3,12 @@ using System.IO; namespace Org.BouncyCastle.Crypto.Tls { - public interface TlsSignerCredentials : TlsCredentials - { - /// <exception cref="IOException"></exception> - byte[] GenerateCertificateSignature(byte[] md5andsha1); - } + public interface TlsSignerCredentials + : TlsCredentials + { + /// <exception cref="IOException"></exception> + byte[] GenerateCertificateSignature(byte[] hash); + + SignatureAndHashAlgorithm SignatureAndHashAlgorithm { get; } + } } diff --git a/crypto/src/crypto/tls/TlsSrpKeyExchange.cs b/crypto/src/crypto/tls/TlsSrpKeyExchange.cs
index 852aace41..f42f7456d 100644 --- a/crypto/src/crypto/tls/TlsSrpKeyExchange.cs +++ b/crypto/src/crypto/tls/TlsSrpKeyExchange.cs
@@ -1,203 +1,189 @@ 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.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; +using Org.BouncyCastle.Utilities.IO; 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; - } - } + /// <summary>(D)TLS SRP key exchange (RFC 5054).</summary> + public class TlsSrpKeyExchange + : AbstractTlsKeyExchange + { + protected TlsSigner mTlsSigner; + protected byte[] mIdentity; + protected byte[] mPassword; + + protected AsymmetricKeyParameter mServerPublicKey = null; + + protected byte[] mS = null; + protected BigInteger mB = null; + protected Srp6Client mSrpClient = new Srp6Client(); + + public TlsSrpKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, byte[] identity, byte[] password) + : base(keyExchange, supportedSignatureAlgorithms) + { + switch (keyExchange) + { + case KeyExchangeAlgorithm.SRP: + this.mTlsSigner = null; + break; + case KeyExchangeAlgorithm.SRP_RSA: + this.mTlsSigner = new TlsRsaSigner(); + break; + case KeyExchangeAlgorithm.SRP_DSS: + this.mTlsSigner = new TlsDssSigner(); + break; + default: + throw new InvalidOperationException("unsupported key exchange algorithm"); + } + + this.mIdentity = identity; + this.mPassword = password; + } + + public override void Init(TlsContext context) + { + base.Init(context); + + if (this.mTlsSigner != null) + { + this.mTlsSigner.Init(context); + } + } + + public override void SkipServerCredentials() + { + if (mTlsSigner != null) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + public override void ProcessServerCertificate(Certificate serverCertificate) + { + if (mTlsSigner == null) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + if (serverCertificate.IsEmpty) + throw new TlsFatalAlert(AlertDescription.bad_certificate); + + X509CertificateStructure x509Cert = serverCertificate.GetCertificateAt(0); + + SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo; + try + { + this.mServerPublicKey = PublicKeyFactory.CreateKey(keyInfo); + } + catch (Exception e) + { + throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e); + } + + if (!mTlsSigner.IsValidPublicKey(this.mServerPublicKey)) + throw new TlsFatalAlert(AlertDescription.certificate_unknown); + + TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature); + + base.ProcessServerCertificate(serverCertificate); + } + + public override bool RequiresServerKeyExchange + { + get { return true; } + } + + public override void ProcessServerKeyExchange(Stream input) + { + SecurityParameters securityParameters = context.SecurityParameters; + + SignerInputBuffer buf = null; + Stream teeIn = input; + + if (mTlsSigner != null) + { + buf = new SignerInputBuffer(); + teeIn = new TeeInputStream(input, buf); + } + + byte[] NBytes = TlsUtilities.ReadOpaque16(teeIn); + byte[] gBytes = TlsUtilities.ReadOpaque16(teeIn); + byte[] sBytes = TlsUtilities.ReadOpaque8(teeIn); + byte[] BBytes = TlsUtilities.ReadOpaque16(teeIn); + + if (buf != null) + { + DigitallySigned signed_params = DigitallySigned.Parse(context, input); + + ISigner signer = InitVerifyer(mTlsSigner, signed_params.Algorithm, securityParameters); + buf.UpdateSigner(signer); + if (!signer.VerifySignature(signed_params.Signature)) + throw new TlsFatalAlert(AlertDescription.decrypt_error); + } + + BigInteger N = new BigInteger(1, NBytes); + BigInteger g = new BigInteger(1, gBytes); + + // TODO Validate group parameters (see RFC 5054) + // throw new TlsFatalAlert(AlertDescription.insufficient_security); + + this.mS = sBytes; + + /* + * RFC 5054 2.5.3: The client MUST abort the handshake with an "illegal_parameter" alert if + * B % N = 0. + */ + try + { + this.mB = Srp6Utilities.ValidatePublicValue(N, new BigInteger(1, BBytes)); + } + catch (CryptoException e) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter, e); + } + + this.mSrpClient.Init(N, g, TlsUtilities.CreateHash(HashAlgorithm.sha1), context.SecureRandom); + } + + public override void ValidateCertificateRequest(CertificateRequest certificateRequest) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + + public override void ProcessClientCredentials(TlsCredentials clientCredentials) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + public override void GenerateClientKeyExchange(Stream output) + { + BigInteger A = mSrpClient.GenerateClientCredentials(mS, this.mIdentity, this.mPassword); + TlsUtilities.WriteOpaque16(BigIntegers.AsUnsignedByteArray(A), output); + } + + public override byte[] GeneratePremasterSecret() + { + try + { + // TODO Check if this needs to be a fixed size + return BigIntegers.AsUnsignedByteArray(mSrpClient.CalculateSecret(mB)); + } + catch (CryptoException e) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter, e); + } + } + + protected virtual ISigner InitVerifyer(TlsSigner tlsSigner, SignatureAndHashAlgorithm algorithm, + SecurityParameters securityParameters) + { + ISigner signer = tlsSigner.CreateVerifyer(algorithm, this.mServerPublicKey); + 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/TlsSrpUtilities.cs b/crypto/src/crypto/tls/TlsSrpUtilities.cs new file mode 100644
index 000000000..bbb6ac280 --- /dev/null +++ b/crypto/src/crypto/tls/TlsSrpUtilities.cs
@@ -0,0 +1,41 @@ +using System; +using System.Collections; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public abstract class TlsSrpUtilities + { + public static void AddSrpExtension(IDictionary extensions, byte[] identity) + { + extensions[ExtensionType.srp] = CreateSrpExtension(identity); + } + + public static byte[] GetSrpExtension(IDictionary extensions) + { + byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.srp); + return extensionData == null ? null : ReadSrpExtension(extensionData); + } + + public static byte[] CreateSrpExtension(byte[] identity) + { + if (identity == null) + throw new TlsFatalAlert(AlertDescription.internal_error); + + return TlsUtilities.EncodeOpaque8(identity); + } + + public static byte[] ReadSrpExtension(byte[] extensionData) + { + if (extensionData == null) + throw new ArgumentNullException("extensionData"); + + MemoryStream buf = new MemoryStream(extensionData, false); + byte[] identity = TlsUtilities.ReadOpaque8(buf); + + TlsProtocol.AssertEmpty(buf); + + return identity; + } + } +} diff --git a/crypto/src/crypto/tls/TlsSrtpUtilities.cs b/crypto/src/crypto/tls/TlsSrtpUtilities.cs new file mode 100644
index 000000000..626c0e3a4 --- /dev/null +++ b/crypto/src/crypto/tls/TlsSrtpUtilities.cs
@@ -0,0 +1,62 @@ +using System; +using System.Collections; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /** + * RFC 5764 DTLS Extension to Establish Keys for SRTP. + */ + public abstract class TlsSRTPUtils + { + public static void AddUseSrtpExtension(IDictionary extensions, UseSrtpData useSRTPData) + { + extensions[ExtensionType.use_srtp] = CreateUseSrtpExtension(useSRTPData); + } + + public static UseSrtpData GetUseSrtpExtension(IDictionary extensions) + { + byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.use_srtp); + return extensionData == null ? null : ReadUseSrtpExtension(extensionData); + } + + public static byte[] CreateUseSrtpExtension(UseSrtpData useSrtpData) + { + if (useSrtpData == null) + throw new ArgumentNullException("useSrtpData"); + + MemoryStream buf = new MemoryStream(); + + // SRTPProtectionProfiles + TlsUtilities.WriteUint16ArrayWithUint16Length(useSrtpData.ProtectionProfiles, buf); + + // srtp_mki + TlsUtilities.WriteOpaque8(useSrtpData.Mki, buf); + + return buf.ToArray(); + } + + public static UseSrtpData ReadUseSrtpExtension(byte[] extensionData) + { + if (extensionData == null) + throw new ArgumentNullException("extensionData"); + + MemoryStream buf = new MemoryStream(extensionData, true); + + // SRTPProtectionProfiles + int length = TlsUtilities.ReadUint16(buf); + if (length < 2 || (length & 1) != 0) + { + throw new TlsFatalAlert(AlertDescription.decode_error); + } + int[] protectionProfiles = TlsUtilities.ReadUint16Array(length / 2, buf); + + // srtp_mki + byte[] mki = TlsUtilities.ReadOpaque8(buf); + + TlsProtocol.AssertEmpty(buf); + + return new UseSrtpData(protectionProfiles, mki); + } + } +} diff --git a/crypto/src/crypto/tls/TlsStream.cs b/crypto/src/crypto/tls/TlsStream.cs
index e3d05686b..7ff7184e3 100644 --- a/crypto/src/crypto/tls/TlsStream.cs +++ b/crypto/src/crypto/tls/TlsStream.cs
@@ -3,87 +3,83 @@ using System.IO; namespace Org.BouncyCastle.Crypto.Tls { - internal class TlsStream - : Stream - { - private readonly TlsProtocolHandler handler; + internal class TlsStream + : Stream + { + private readonly TlsProtocol handler; - internal TlsStream( - TlsProtocolHandler handler) - { - this.handler = handler; - } + internal TlsStream(TlsProtocol handler) + { + this.handler = handler; + } - public override bool CanRead - { - get { return !handler.IsClosed; } - } + public override bool CanRead + { + get { return !handler.IsClosed; } + } - public override bool CanSeek - { - get { return false; } - } + public override bool CanSeek + { + get { return false; } + } - public override bool CanWrite - { - get { return !handler.IsClosed; } - } + public override bool CanWrite + { + get { return !handler.IsClosed; } + } - protected override void Dispose(bool disposing) + public override void Close() { - if (disposing) - { - handler.Close(); - } + handler.Close(); } - public override void Flush() - { - handler.Flush(); - } + public override void Flush() + { + handler.Flush(); + } public override long Length - { - get { throw new NotSupportedException(); } - } + { + get { throw new NotSupportedException(); } + } - public override long Position + 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 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 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 long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } public override void SetLength(long value) - { - throw new NotSupportedException(); - } + { + throw new NotSupportedException(); + } - public override void Write(byte[] buf, int off, int len) - { - this.handler.WriteData(buf, off, len); - } + 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); - } - } + public override void WriteByte(byte b) + { + this.handler.WriteData(new byte[] { b }, 0, 1); + } + } } diff --git a/crypto/src/crypto/tls/TlsStreamCipher.cs b/crypto/src/crypto/tls/TlsStreamCipher.cs new file mode 100644
index 000000000..45f2a9a70 --- /dev/null +++ b/crypto/src/crypto/tls/TlsStreamCipher.cs
@@ -0,0 +1,164 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Tls; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + public class TlsStreamCipher + : TlsCipher + { + protected readonly TlsContext context; + + protected readonly IStreamCipher encryptCipher; + protected readonly IStreamCipher decryptCipher; + + protected readonly TlsMac writeMac; + protected readonly TlsMac readMac; + + protected readonly bool usesNonce; + + /// <exception cref="IOException"></exception> + public TlsStreamCipher(TlsContext context, IStreamCipher clientWriteCipher, + IStreamCipher serverWriteCipher, IDigest clientWriteDigest, IDigest serverWriteDigest, + int cipherKeySize, bool usesNonce) + { + bool isServer = context.IsServer; + + this.context = context; + this.usesNonce = usesNonce; + + this.encryptCipher = clientWriteCipher; + this.decryptCipher = serverWriteCipher; + + int key_block_size = (2 * cipherKeySize) + clientWriteDigest.GetDigestSize() + + serverWriteDigest.GetDigestSize(); + + byte[] key_block = TlsUtilities.CalculateKeyBlock(context, key_block_size); + + int offset = 0; + + // Init MACs + TlsMac clientWriteMac = new TlsMac(context, clientWriteDigest, key_block, offset, + clientWriteDigest.GetDigestSize()); + offset += clientWriteDigest.GetDigestSize(); + TlsMac serverWriteMac = new TlsMac(context, serverWriteDigest, key_block, offset, + serverWriteDigest.GetDigestSize()); + offset += serverWriteDigest.GetDigestSize(); + + // Build keys + KeyParameter clientWriteKey = new KeyParameter(key_block, offset, cipherKeySize); + offset += cipherKeySize; + KeyParameter serverWriteKey = new KeyParameter(key_block, offset, cipherKeySize); + offset += cipherKeySize; + + if (offset != key_block_size) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + ICipherParameters encryptParams, decryptParams; + if (isServer) + { + this.writeMac = serverWriteMac; + this.readMac = clientWriteMac; + this.encryptCipher = serverWriteCipher; + this.decryptCipher = clientWriteCipher; + encryptParams = serverWriteKey; + decryptParams = clientWriteKey; + } + else + { + this.writeMac = clientWriteMac; + this.readMac = serverWriteMac; + this.encryptCipher = clientWriteCipher; + this.decryptCipher = serverWriteCipher; + encryptParams = clientWriteKey; + decryptParams = serverWriteKey; + } + + if (usesNonce) + { + byte[] dummyNonce = new byte[8]; + encryptParams = new ParametersWithIV(encryptParams, dummyNonce); + decryptParams = new ParametersWithIV(decryptParams, dummyNonce); + } + + this.encryptCipher.Init(true, encryptParams); + this.decryptCipher.Init(false, decryptParams); + } + + public virtual int GetPlaintextLimit(int ciphertextLimit) + { + return ciphertextLimit - writeMac.Size; + } + + public virtual byte[] EncodePlaintext(long seqNo, byte type, byte[] plaintext, int offset, int len) + { + /* + * draft-josefsson-salsa20-tls-04 2.1 Note that Salsa20 requires a 64-bit nonce. That + * nonce is updated on the encryption of every TLS record, and is set to be the 64-bit TLS + * record sequence number. In case of DTLS the 64-bit nonce is formed as the concatenation + * of the 16-bit epoch with the 48-bit sequence number. + */ + if (usesNonce) + { + UpdateIV(encryptCipher, true, seqNo); + } + + byte[] outBuf = new byte[len + writeMac.Size]; + + encryptCipher.ProcessBytes(plaintext, offset, len, outBuf, 0); + + byte[] mac = writeMac.CalculateMac(seqNo, type, plaintext, offset, len); + encryptCipher.ProcessBytes(mac, 0, mac.Length, outBuf, len); + + return outBuf; + } + + /// <exception cref="IOException"></exception> + public virtual byte[] DecodeCiphertext(long seqNo, byte type, byte[] ciphertext, int offset, int len) + { + /* + * draft-josefsson-salsa20-tls-04 2.1 Note that Salsa20 requires a 64-bit nonce. That + * nonce is updated on the encryption of every TLS record, and is set to be the 64-bit TLS + * record sequence number. In case of DTLS the 64-bit nonce is formed as the concatenation + * of the 16-bit epoch with the 48-bit sequence number. + */ + if (usesNonce) + { + UpdateIV(decryptCipher, false, seqNo); + } + + int macSize = readMac.Size; + if (len < macSize) + throw new TlsFatalAlert(AlertDescription.decode_error); + + int plaintextLength = len - macSize; + + byte[] deciphered = new byte[len]; + decryptCipher.ProcessBytes(ciphertext, offset, len, deciphered, 0); + CheckMac(seqNo, type, deciphered, plaintextLength, len, deciphered, 0, plaintextLength); + return Arrays.CopyOfRange(deciphered, 0, plaintextLength); + } + + /// <exception cref="IOException"></exception> + protected virtual void CheckMac(long seqNo, byte type, byte[] recBuf, int recStart, int recEnd, byte[] calcBuf, int calcOff, int calcLen) + { + byte[] receivedMac = Arrays.CopyOfRange(recBuf, recStart, recEnd); + byte[] computedMac = readMac.CalculateMac(seqNo, type, calcBuf, calcOff, calcLen); + + if (!Arrays.ConstantTimeAreEqual(receivedMac, computedMac)) + throw new TlsFatalAlert(AlertDescription.bad_record_mac); + } + + protected virtual void UpdateIV(IStreamCipher cipher, bool forEncryption, long seqNo) + { + byte[] nonce = new byte[8]; + TlsUtilities.WriteUint64(seqNo, nonce, 0); + cipher.Init(forEncryption, new ParametersWithIV(null, nonce)); + } + } +} diff --git a/crypto/src/crypto/tls/TlsUtilities.cs b/crypto/src/crypto/tls/TlsUtilities.cs
index 0e2452689..f1ea0996d 100644 --- a/crypto/src/crypto/tls/TlsUtilities.cs +++ b/crypto/src/crypto/tls/TlsUtilities.cs
@@ -1,286 +1,1631 @@ 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.X509; using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Macs; using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; 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); - //} - } - } - } - } + /// <remarks>Some helper functions for MicroTLS.</remarks> + public abstract class TlsUtilities + { + public static readonly byte[] EmptyBytes = new byte[0]; + + public static void CheckUint8(int i) + { + if (!IsValidUint8(i)) + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + public static void CheckUint8(long i) + { + if (!IsValidUint8(i)) + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + public static void CheckUint16(int i) + { + if (!IsValidUint16(i)) + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + public static void CheckUint16(long i) + { + if (!IsValidUint16(i)) + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + public static void CheckUint24(int i) + { + if (!IsValidUint24(i)) + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + public static void CheckUint24(long i) + { + if (!IsValidUint24(i)) + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + public static void CheckUint32(long i) + { + if (!IsValidUint32(i)) + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + public static void CheckUint48(long i) + { + if (!IsValidUint48(i)) + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + public static void CheckUint64(long i) + { + if (!IsValidUint64(i)) + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + public static bool IsValidUint8(int i) + { + return (i & 0xFF) == i; + } + + public static bool IsValidUint8(long i) + { + return (i & 0xFFL) == i; + } + + public static bool IsValidUint16(int i) + { + return (i & 0xFFFF) == i; + } + + public static bool IsValidUint16(long i) + { + return (i & 0xFFFFL) == i; + } + + public static bool IsValidUint24(int i) + { + return (i & 0xFFFFFF) == i; + } + + public static bool IsValidUint24(long i) + { + return (i & 0xFFFFFFL) == i; + } + + public static bool IsValidUint32(long i) + { + return (i & 0xFFFFFFFFL) == i; + } + + public static bool IsValidUint48(long i) + { + return (i & 0xFFFFFFFFFFFFL) == i; + } + + public static bool IsValidUint64(long i) + { + return true; + } + + public static bool IsSsl(TlsContext context) + { + return context.ServerVersion.IsSsl; + } + + public static bool IsTlsV11(TlsContext context) + { + return ProtocolVersion.TLSv11.IsEqualOrEarlierVersionOf(context.ServerVersion.GetEquivalentTLSVersion()); + } + + public static bool IsTlsV12(TlsContext context) + { + return ProtocolVersion.TLSv12.IsEqualOrEarlierVersionOf(context.ServerVersion.GetEquivalentTLSVersion()); + } + + public static void WriteUint8(byte i, Stream output) + { + output.WriteByte(i); + } + + public static void WriteUint8(byte i, byte[] buf, int offset) + { + buf[offset] = i; + } + + public static void WriteUint16(int i, Stream output) + { + output.WriteByte((byte)(i >> 8)); + output.WriteByte((byte)i); + } + + public static void WriteUint16(int i, byte[] buf, int offset) + { + buf[offset] = (byte)(i >> 8); + buf[offset + 1] = (byte)i; + } + + public static void WriteUint24(int i, Stream output) + { + output.WriteByte((byte)(i >> 16)); + output.WriteByte((byte)(i >> 8)); + output.WriteByte((byte)i); + } + + public static void WriteUint24(int i, byte[] buf, int offset) + { + buf[offset] = (byte)(i >> 16); + buf[offset + 1] = (byte)(i >> 8); + buf[offset + 2] = (byte)i; + } + + public static void WriteUint32(long i, Stream output) + { + output.WriteByte((byte)(i >> 24)); + output.WriteByte((byte)(i >> 16)); + output.WriteByte((byte)(i >> 8)); + output.WriteByte((byte)i); + } + + public static void WriteUint32(long i, byte[] buf, int offset) + { + buf[offset] = (byte)(i >> 24); + buf[offset + 1] = (byte)(i >> 16); + buf[offset + 2] = (byte)(i >> 8); + buf[offset + 3] = (byte)i; + } + + public static void WriteUint48(long i, Stream output) + { + output.WriteByte((byte)(i >> 40)); + output.WriteByte((byte)(i >> 32)); + output.WriteByte((byte)(i >> 24)); + output.WriteByte((byte)(i >> 16)); + output.WriteByte((byte)(i >> 8)); + output.WriteByte((byte)i); + } + + public static void WriteUint48(long i, byte[] buf, int offset) + { + buf[offset] = (byte)(i >> 40); + buf[offset + 1] = (byte)(i >> 32); + buf[offset + 2] = (byte)(i >> 24); + buf[offset + 3] = (byte)(i >> 16); + buf[offset + 4] = (byte)(i >> 8); + buf[offset + 5] = (byte)i; + } + + public static void WriteUint64(long i, Stream output) + { + output.WriteByte((byte)(i >> 56)); + output.WriteByte((byte)(i >> 48)); + output.WriteByte((byte)(i >> 40)); + output.WriteByte((byte)(i >> 32)); + output.WriteByte((byte)(i >> 24)); + output.WriteByte((byte)(i >> 16)); + output.WriteByte((byte)(i >> 8)); + output.WriteByte((byte)i); + } + + public static void WriteUint64(long i, byte[] buf, int offset) + { + buf[offset] = (byte)(i >> 56); + buf[offset + 1] = (byte)(i >> 48); + buf[offset + 2] = (byte)(i >> 40); + buf[offset + 3] = (byte)(i >> 32); + buf[offset + 4] = (byte)(i >> 24); + buf[offset + 5] = (byte)(i >> 16); + buf[offset + 6] = (byte)(i >> 8); + buf[offset + 7] = (byte)i; + } + + public static void WriteOpaque8(byte[] buf, Stream output) + { + WriteUint8((byte)buf.Length, output); + output.Write(buf, 0, buf.Length); + } + + public static void WriteOpaque16(byte[] buf, Stream output) + { + WriteUint16(buf.Length, output); + output.Write(buf, 0, buf.Length); + } + + public static void WriteOpaque24(byte[] buf, Stream output) + { + WriteUint24(buf.Length, output); + output.Write(buf, 0, buf.Length); + } + + public static void WriteUint8Array(byte[] uints, Stream output) + { + output.Write(uints, 0, uints.Length); + } + + public static void WriteUint8Array(byte[] uints, byte[] buf, int offset) + { + for (int i = 0; i < uints.Length; ++i) + { + WriteUint8(uints[i], buf, offset); + ++offset; + } + } + + public static void WriteUint8ArrayWithUint8Length(byte[] uints, Stream output) + { + CheckUint8(uints.Length); + WriteUint8((byte)uints.Length, output); + WriteUint8Array(uints, output); + } + + public static void WriteUint8ArrayWithUint8Length(byte[] uints, byte[] buf, int offset) + { + CheckUint8(uints.Length); + WriteUint8((byte)uints.Length, buf, offset); + WriteUint8Array(uints, buf, offset + 1); + } + + public static void WriteUint16Array(int[] uints, Stream output) + { + for (int i = 0; i < uints.Length; ++i) + { + WriteUint16(uints[i], output); + } + } + + public static void WriteUint16Array(int[] uints, byte[] buf, int offset) + { + for (int i = 0; i < uints.Length; ++i) + { + WriteUint16(uints[i], buf, offset); + offset += 2; + } + } + + public static void WriteUint16ArrayWithUint16Length(int[] uints, Stream output) + { + int length = 2 * uints.Length; + CheckUint16(length); + WriteUint16(length, output); + WriteUint16Array(uints, output); + } + + public static void WriteUint16ArrayWithUint16Length(int[] uints, byte[] buf, int offset) + { + int length = 2 * uints.Length; + CheckUint16(length); + WriteUint16(length, buf, offset); + WriteUint16Array(uints, buf, offset + 2); + } + + public static byte[] EncodeOpaque8(byte[] buf) + { + CheckUint8(buf.Length); + return Arrays.Prepend(buf, (byte)buf.Length); + } + + public static byte[] EncodeUint8ArrayWithUint8Length(byte[] uints) + { + byte[] result = new byte[1 + uints.Length]; + WriteUint8ArrayWithUint8Length(uints, result, 0); + return result; + } + + public static byte[] EncodeUint16ArrayWithUint16Length(int[] uints) + { + int length = 2 * uints.Length; + byte[] result = new byte[2 + length]; + WriteUint16ArrayWithUint16Length(uints, result, 0); + return result; + } + + public static byte ReadUint8(Stream input) + { + int i = input.ReadByte(); + if (i < 0) + { + throw new EndOfStreamException(); + } + return (byte)i; + } + + public static byte ReadUint8(byte[] buf, int offset) + { + return buf[offset]; + } + + public static int ReadUint16(Stream input) + { + int i1 = input.ReadByte(); + int i2 = input.ReadByte(); + if ((i1 | i2) < 0) + { + throw new EndOfStreamException(); + } + return i1 << 8 | i2; + } + + public static int ReadUint16(byte[] buf, int offset) + { + uint n = (uint)buf[offset] << 8; + n |= (uint)buf[++offset]; + return (int)n; + } + + public static int ReadUint24(Stream input) + { + int i1 = input.ReadByte(); + int i2 = input.ReadByte(); + int i3 = input.ReadByte(); + if ((i1 | i2 | i3) < 0) + { + throw new EndOfStreamException(); + } + return (i1 << 16) | (i2 << 8) | i3; + } + + public static int ReadUint24(byte[] buf, int offset) + { + uint n = (uint)buf[offset] << 16; + n |= (uint)buf[++offset] << 8; + n |= (uint)buf[++offset]; + return (int)n; + } + + public static long ReadUint32(Stream input) + { + int i1 = input.ReadByte(); + int i2 = input.ReadByte(); + int i3 = input.ReadByte(); + int i4 = input.ReadByte(); + if (i4 < 0) + { + throw new EndOfStreamException(); + } + return (long)(uint)((i1 << 24) | (i2 << 16) | (i3 << 8) | i4); + } + + public static long ReadUint32(byte[] buf, int offset) + { + uint n = (uint)buf[offset] << 24; + n |= (uint)buf[++offset] << 16; + n |= (uint)buf[++offset] << 8; + n |= (uint)buf[++offset]; + return (long)n; + } + + public static long ReadUint48(Stream input) + { + int hi = ReadUint24(input); + int lo = ReadUint24(input); + return ((long)(hi & 0xffffffffL) << 24) | (long)(lo & 0xffffffffL); + } + + public static long ReadUint48(byte[] buf, int offset) + { + int hi = ReadUint24(buf, offset); + int lo = ReadUint24(buf, offset + 3); + return ((long)(hi & 0xffffffffL) << 24) | (long)(lo & 0xffffffffL); + } + + public static byte[] ReadAllOrNothing(int length, Stream input) + { + if (length < 1) + return EmptyBytes; + byte[] buf = new byte[length]; + int read = Streams.ReadFully(input, buf); + if (read == 0) + return null; + if (read != length) + throw new EndOfStreamException(); + return buf; + } + + public static byte[] ReadFully(int length, Stream input) + { + if (length < 1) + return EmptyBytes; + byte[] buf = new byte[length]; + if (length != Streams.ReadFully(input, buf)) + throw new EndOfStreamException(); + return buf; + } + + public static void ReadFully(byte[] buf, Stream input) + { + if (Streams.ReadFully(input, buf, 0, buf.Length) < buf.Length) + throw new EndOfStreamException(); + } + + public static byte[] ReadOpaque8(Stream input) + { + byte length = ReadUint8(input); + byte[] bytes = new byte[length]; + ReadFully(bytes, input); + return bytes; + } + + public static byte[] ReadOpaque16(Stream input) + { + int length = ReadUint16(input); + byte[] bytes = new byte[length]; + ReadFully(bytes, input); + return bytes; + } + + public static byte[] ReadOpaque24(Stream input) + { + int length = ReadUint24(input); + return ReadFully(length, input); + } + + public static byte[] ReadUint8Array(int count, Stream input) + { + byte[] uints = new byte[count]; + for (int i = 0; i < count; ++i) + { + uints[i] = ReadUint8(input); + } + return uints; + } + + public static int[] ReadUint16Array(int count, Stream input) + { + int[] uints = new int[count]; + for (int i = 0; i < count; ++i) + { + uints[i] = ReadUint16(input); + } + return uints; + } + + public static ProtocolVersion ReadVersion(byte[] buf, int offset) + { + return ProtocolVersion.Get(buf[offset], buf[offset + 1]); + } + + public static ProtocolVersion ReadVersion(Stream input) + { + int i1 = input.ReadByte(); + int i2 = input.ReadByte(); + if (i2 < 0) + { + throw new EndOfStreamException(); + } + return ProtocolVersion.Get(i1, i2); + } + + public static int ReadVersionRaw(byte[] buf, int offset) + { + return (buf[offset] << 8) | buf[offset + 1]; + } + + public static int ReadVersionRaw(Stream input) + { + int i1 = input.ReadByte(); + int i2 = input.ReadByte(); + if (i2 < 0) + { + throw new EndOfStreamException(); + } + return (i1 << 8) | i2; + } + + public static Asn1Object ReadAsn1Object(byte[] encoding) + { + Asn1InputStream asn1 = new Asn1InputStream(encoding); + Asn1Object result = asn1.ReadObject(); + if (null == result) + throw new TlsFatalAlert(AlertDescription.decode_error); + if (null != asn1.ReadObject()) + throw new TlsFatalAlert(AlertDescription.decode_error); + return result; + } + + public static Asn1Object ReadDerObject(byte[] encoding) + { + /* + * NOTE: The current ASN.1 parsing code can't enforce DER-only parsing, but since DER is + * canonical, we can check it by re-encoding the result and comparing to the original. + */ + Asn1Object result = ReadAsn1Object(encoding); + byte[] check = result.GetEncoded(Asn1Encodable.Der); + if (!Arrays.AreEqual(check, encoding)) + throw new TlsFatalAlert(AlertDescription.decode_error); + return result; + } + + public 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; + } + + public static void WriteVersion(ProtocolVersion version, Stream output) + { + output.WriteByte((byte)version.MajorVersion); + output.WriteByte((byte)version.MinorVersion); + } + + public static void WriteVersion(ProtocolVersion version, byte[] buf, int offset) + { + buf[offset] = (byte)version.MajorVersion; + buf[offset + 1] = (byte)version.MinorVersion; + } + + public static IList GetDefaultDssSignatureAlgorithms() + { + return VectorOfOne(new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.dsa)); + } + + public static IList GetDefaultECDsaSignatureAlgorithms() + { + return VectorOfOne(new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.ecdsa)); + } + + public static IList GetDefaultRsaSignatureAlgorithms() + { + return VectorOfOne(new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.rsa)); + } + + public static byte[] GetExtensionData(IDictionary extensions, int extensionType) + { + return extensions == null ? null : (byte[])extensions[extensionType]; + } + + public static bool HasExpectedEmptyExtensionData(IDictionary extensions, int extensionType, + byte alertDescription) + { + byte[] extension_data = GetExtensionData(extensions, extensionType); + if (extension_data == null) + return false; + if (extension_data.Length != 0) + throw new TlsFatalAlert(alertDescription); + return true; + } + + public static TlsSession ImportSession(byte[] sessionID, SessionParameters sessionParameters) + { + return new TlsSessionImpl(sessionID, sessionParameters); + } + + public static bool IsSignatureAlgorithmsExtensionAllowed(ProtocolVersion clientVersion) + { + return ProtocolVersion.TLSv12.IsEqualOrEarlierVersionOf(clientVersion.GetEquivalentTLSVersion()); + } + + /** + * Add a 'signature_algorithms' extension to existing extensions. + * + * @param extensions A {@link Hashtable} to add the extension to. + * @param supportedSignatureAlgorithms {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}. + * @throws IOException + */ + public static void AddSignatureAlgorithmsExtension(IDictionary extensions, IList supportedSignatureAlgorithms) + { + extensions[ExtensionType.signature_algorithms] = CreateSignatureAlgorithmsExtension(supportedSignatureAlgorithms); + } + + /** + * Get a 'signature_algorithms' extension from extensions. + * + * @param extensions A {@link Hashtable} to get the extension from, if it is present. + * @return A {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}, or null. + * @throws IOException + */ + public static IList GetSignatureAlgorithmsExtension(IDictionary extensions) + { + byte[] extensionData = GetExtensionData(extensions, ExtensionType.signature_algorithms); + return extensionData == null ? null : ReadSignatureAlgorithmsExtension(extensionData); + } + + /** + * Create a 'signature_algorithms' extension value. + * + * @param supportedSignatureAlgorithms A {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}. + * @return A byte array suitable for use as an extension value. + * @throws IOException + */ + public static byte[] CreateSignatureAlgorithmsExtension(IList supportedSignatureAlgorithms) + { + MemoryStream buf = new MemoryStream(); + + // supported_signature_algorithms + EncodeSupportedSignatureAlgorithms(supportedSignatureAlgorithms, false, buf); + + return buf.ToArray(); + } + + /** + * Read 'signature_algorithms' extension data. + * + * @param extensionData The extension data. + * @return A {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}. + * @throws IOException + */ + public static IList ReadSignatureAlgorithmsExtension(byte[] extensionData) + { + if (extensionData == null) + throw new ArgumentNullException("extensionData"); + + MemoryStream buf = new MemoryStream(extensionData, false); + + // supported_signature_algorithms + IList supported_signature_algorithms = ParseSupportedSignatureAlgorithms(false, buf); + + TlsProtocol.AssertEmpty(buf); + + return supported_signature_algorithms; + } + + public static void EncodeSupportedSignatureAlgorithms(IList supportedSignatureAlgorithms, bool allowAnonymous, + Stream output) + { + if (supportedSignatureAlgorithms == null || supportedSignatureAlgorithms.Count < 1 + || supportedSignatureAlgorithms.Count >= (1 << 15)) + { + throw new ArgumentException("must have length from 1 to (2^15 - 1)", "supportedSignatureAlgorithms"); + } + + // supported_signature_algorithms + int length = 2 * supportedSignatureAlgorithms.Count; + CheckUint16(length); + WriteUint16(length, output); + + foreach (SignatureAndHashAlgorithm entry in supportedSignatureAlgorithms) + { + if (!allowAnonymous && entry.Signature == SignatureAlgorithm.anonymous) + { + /* + * RFC 5246 7.4.1.4.1 The "anonymous" value is meaningless in this context but used + * in Section 7.4.3. It MUST NOT appear in this extension. + */ + throw new ArgumentException( + "SignatureAlgorithm.anonymous MUST NOT appear in the signature_algorithms extension"); + } + entry.Encode(output); + } + } + + public static IList ParseSupportedSignatureAlgorithms(bool allowAnonymous, Stream input) + { + // supported_signature_algorithms + int length = ReadUint16(input); + if (length < 2 || (length & 1) != 0) + throw new TlsFatalAlert(AlertDescription.decode_error); + int count = length / 2; + IList supportedSignatureAlgorithms = Platform.CreateArrayList(count); + for (int i = 0; i < count; ++i) + { + SignatureAndHashAlgorithm entry = SignatureAndHashAlgorithm.Parse(input); + if (!allowAnonymous && entry.Signature == SignatureAlgorithm.anonymous) + { + /* + * RFC 5246 7.4.1.4.1 The "anonymous" value is meaningless in this context but used + * in Section 7.4.3. It MUST NOT appear in this extension. + */ + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + supportedSignatureAlgorithms.Add(entry); + } + return supportedSignatureAlgorithms; + } + + public static byte[] PRF(TlsContext context, byte[] secret, string asciiLabel, byte[] seed, int size) + { + ProtocolVersion version = context.ServerVersion; + + if (version.IsSsl) + throw new InvalidOperationException("No PRF available for SSLv3 session"); + + byte[] label = Strings.ToByteArray(asciiLabel); + byte[] labelSeed = Concat(label, seed); + + int prfAlgorithm = context.SecurityParameters.PrfAlgorithm; + + if (prfAlgorithm == PrfAlgorithm.tls_prf_legacy) + { + return PRF_legacy(secret, label, labelSeed, size); + } + + IDigest prfDigest = CreatePrfHash(prfAlgorithm); + byte[] buf = new byte[size]; + HMacHash(prfDigest, secret, labelSeed, buf); + return buf; + } + + public static byte[] PRF_legacy(byte[] secret, string asciiLabel, byte[] seed, int size) + { + byte[] label = Strings.ToByteArray(asciiLabel); + byte[] labelSeed = Concat(label, seed); + + return PRF_legacy(secret, label, labelSeed, size); + } + + internal static byte[] PRF_legacy(byte[] secret, byte[] label, byte[] labelSeed, int size) + { + int s_half = (secret.Length + 1) / 2; + byte[] s1 = new byte[s_half]; + byte[] s2 = new byte[s_half]; + Array.Copy(secret, 0, s1, 0, s_half); + Array.Copy(secret, secret.Length - s_half, s2, 0, s_half); + + byte[] b1 = new byte[size]; + byte[] b2 = new byte[size]; + HMacHash(CreateHash(HashAlgorithm.md5), s1, labelSeed, b1); + HMacHash(CreateHash(HashAlgorithm.sha1), s2, labelSeed, b2); + for (int i = 0; i < size; i++) + { + b1[i] ^= b2[i]; + } + return b1; + } + + 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 HMacHash(IDigest digest, byte[] secret, byte[] seed, byte[] output) + { + HMac mac = new HMac(digest); + mac.Init(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.BlockUpdate(a, 0, a.Length); + mac.DoFinal(buf, 0); + a = buf; + 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 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); + } + } + } + } + + internal static byte[] CalculateKeyBlock(TlsContext context, int size) + { + SecurityParameters securityParameters = context.SecurityParameters; + byte[] master_secret = securityParameters.MasterSecret; + byte[] seed = Concat(securityParameters.ServerRandom, securityParameters.ClientRandom); + + if (IsSsl(context)) + { + return CalculateKeyBlock_Ssl(master_secret, seed, size); + } + + return PRF(context, master_secret, ExporterLabel.key_expansion, seed, size); + } + + internal static byte[] CalculateKeyBlock_Ssl(byte[] master_secret, byte[] random, int size) + { + IDigest md5 = CreateHash(HashAlgorithm.md5); + IDigest sha1 = CreateHash(HashAlgorithm.sha1); + int md5Size = md5.GetDigestSize(); + byte[] shatmp = new byte[sha1.GetDigestSize()]; + byte[] tmp = new byte[size + md5Size]; + + int i = 0, pos = 0; + while (pos < size) + { + byte[] ssl3Const = SSL3_CONST[i]; + + sha1.BlockUpdate(ssl3Const, 0, ssl3Const.Length); + sha1.BlockUpdate(master_secret, 0, master_secret.Length); + sha1.BlockUpdate(random, 0, random.Length); + sha1.DoFinal(shatmp, 0); + + md5.BlockUpdate(master_secret, 0, master_secret.Length); + md5.BlockUpdate(shatmp, 0, shatmp.Length); + md5.DoFinal(tmp, pos); + + pos += md5Size; + ++i; + } + + return Arrays.CopyOfRange(tmp, 0, size); + } + + internal static byte[] CalculateMasterSecret(TlsContext context, byte[] pre_master_secret) + { + SecurityParameters securityParameters = context.SecurityParameters; + byte[] seed = Concat(securityParameters.ClientRandom, securityParameters.ServerRandom); + + if (IsSsl(context)) + { + return CalculateMasterSecret_Ssl(pre_master_secret, seed); + } + + return PRF(context, pre_master_secret, ExporterLabel.master_secret, seed, 48); + } + + internal static byte[] CalculateMasterSecret_Ssl(byte[] pre_master_secret, byte[] random) + { + IDigest md5 = CreateHash(HashAlgorithm.md5); + IDigest sha1 = CreateHash(HashAlgorithm.sha1); + int md5Size = md5.GetDigestSize(); + byte[] shatmp = new byte[sha1.GetDigestSize()]; + + byte[] rval = new byte[md5Size * 3]; + int pos = 0; + + for (int i = 0; i < 3; ++i) + { + byte[] ssl3Const = SSL3_CONST[i]; + + sha1.BlockUpdate(ssl3Const, 0, ssl3Const.Length); + sha1.BlockUpdate(pre_master_secret, 0, pre_master_secret.Length); + sha1.BlockUpdate(random, 0, random.Length); + sha1.DoFinal(shatmp, 0); + + md5.BlockUpdate(pre_master_secret, 0, pre_master_secret.Length); + md5.BlockUpdate(shatmp, 0, shatmp.Length); + md5.DoFinal(rval, pos); + + pos += md5Size; + } + + return rval; + } + + internal static byte[] CalculateVerifyData(TlsContext context, string asciiLabel, byte[] handshakeHash) + { + if (IsSsl(context)) + { + return handshakeHash; + } + + SecurityParameters securityParameters = context.SecurityParameters; + byte[] master_secret = securityParameters.MasterSecret; + int verify_data_length = securityParameters.VerifyDataLength; + + return PRF(context, master_secret, asciiLabel, handshakeHash, verify_data_length); + } + + public static IDigest CreateHash(byte hashAlgorithm) + { + switch (hashAlgorithm) + { + case HashAlgorithm.md5: + return new MD5Digest(); + case HashAlgorithm.sha1: + return new Sha1Digest(); + case HashAlgorithm.sha224: + return new Sha224Digest(); + case HashAlgorithm.sha256: + return new Sha256Digest(); + case HashAlgorithm.sha384: + return new Sha384Digest(); + case HashAlgorithm.sha512: + return new Sha512Digest(); + default: + throw new ArgumentException("unknown HashAlgorithm", "hashAlgorithm"); + } + } + + public static IDigest CloneHash(byte hashAlgorithm, IDigest hash) + { + switch (hashAlgorithm) + { + case HashAlgorithm.md5: + return new MD5Digest((MD5Digest)hash); + case HashAlgorithm.sha1: + return new Sha1Digest((Sha1Digest)hash); + case HashAlgorithm.sha224: + return new Sha224Digest((Sha224Digest)hash); + case HashAlgorithm.sha256: + return new Sha256Digest((Sha256Digest)hash); + case HashAlgorithm.sha384: + return new Sha384Digest((Sha384Digest)hash); + case HashAlgorithm.sha512: + return new Sha512Digest((Sha512Digest)hash); + default: + throw new ArgumentException("unknown HashAlgorithm", "hashAlgorithm"); + } + } + + public static IDigest CreatePrfHash(int prfAlgorithm) + { + switch (prfAlgorithm) + { + case PrfAlgorithm.tls_prf_legacy: + return new CombinedHash(); + default: + return CreateHash(GetHashAlgorithmForPrfAlgorithm(prfAlgorithm)); + } + } + + public static IDigest ClonePrfHash(int prfAlgorithm, IDigest hash) + { + switch (prfAlgorithm) + { + case PrfAlgorithm.tls_prf_legacy: + return new CombinedHash((CombinedHash)hash); + default: + return CloneHash(GetHashAlgorithmForPrfAlgorithm(prfAlgorithm), hash); + } + } + + public static byte GetHashAlgorithmForPrfAlgorithm(int prfAlgorithm) + { + switch (prfAlgorithm) + { + case PrfAlgorithm.tls_prf_legacy: + throw new ArgumentException("legacy PRF not a valid algorithm", "prfAlgorithm"); + case PrfAlgorithm.tls_prf_sha256: + return HashAlgorithm.sha256; + case PrfAlgorithm.tls_prf_sha384: + return HashAlgorithm.sha384; + default: + throw new ArgumentException("unknown PrfAlgorithm", "prfAlgorithm"); + } + } + + public static DerObjectIdentifier GetOidForHashAlgorithm(byte hashAlgorithm) + { + switch (hashAlgorithm) + { + case HashAlgorithm.md5: + return PkcsObjectIdentifiers.MD5; + case HashAlgorithm.sha1: + return X509ObjectIdentifiers.IdSha1; + case HashAlgorithm.sha224: + return NistObjectIdentifiers.IdSha224; + case HashAlgorithm.sha256: + return NistObjectIdentifiers.IdSha256; + case HashAlgorithm.sha384: + return NistObjectIdentifiers.IdSha384; + case HashAlgorithm.sha512: + return NistObjectIdentifiers.IdSha512; + default: + throw new ArgumentException("unknown HashAlgorithm", "hashAlgorithm"); + } + } + + internal static short GetClientCertificateType(Certificate clientCertificate, Certificate serverCertificate) + { + if (clientCertificate.IsEmpty) + return -1; + + X509CertificateStructure x509Cert = clientCertificate.GetCertificateAt(0); + SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo; + try + { + AsymmetricKeyParameter publicKey = PublicKeyFactory.CreateKey(keyInfo); + if (publicKey.IsPrivate) + throw new TlsFatalAlert(AlertDescription.internal_error); + + /* + * TODO RFC 5246 7.4.6. The certificates MUST be signed using an acceptable hash/ + * signature algorithm pair, as described in Section 7.4.4. Note that this relaxes the + * constraints on certificate-signing algorithms found in prior versions of TLS. + */ + + /* + * RFC 5246 7.4.6. Client Certificate + */ + + /* + * RSA public key; the certificate MUST allow the key to be used for signing with the + * signature scheme and hash algorithm that will be employed in the certificate verify + * message. + */ + if (publicKey is RsaKeyParameters) + { + ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature); + return ClientCertificateType.rsa_sign; + } + + /* + * DSA public key; the certificate MUST allow the key to be used for signing with the + * hash algorithm that will be employed in the certificate verify message. + */ + if (publicKey is DsaPublicKeyParameters) + { + ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature); + return ClientCertificateType.dss_sign; + } + + /* + * ECDSA-capable public key; the certificate MUST allow the key to be used for signing + * with the hash algorithm that will be employed in the certificate verify message; the + * public key MUST use a curve and point format supported by the server. + */ + if (publicKey is ECPublicKeyParameters) + { + ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature); + // TODO Check the curve and point format + return ClientCertificateType.ecdsa_sign; + } + + // TODO Add support for ClientCertificateType.*_fixed_* + + throw new TlsFatalAlert(AlertDescription.unsupported_certificate); + } + catch (Exception e) + { + throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e); + } + } + + internal static void TrackHashAlgorithms(TlsHandshakeHash handshakeHash, IList supportedSignatureAlgorithms) + { + if (supportedSignatureAlgorithms != null) + { + foreach (SignatureAndHashAlgorithm signatureAndHashAlgorithm in supportedSignatureAlgorithms) + { + byte hashAlgorithm = signatureAndHashAlgorithm.Hash; + handshakeHash.TrackHashAlgorithm(hashAlgorithm); + } + } + } + + public static bool HasSigningCapability(byte clientCertificateType) + { + switch (clientCertificateType) + { + case ClientCertificateType.dss_sign: + case ClientCertificateType.ecdsa_sign: + case ClientCertificateType.rsa_sign: + return true; + default: + return false; + } + } + + public static TlsSigner CreateTlsSigner(byte clientCertificateType) + { + switch (clientCertificateType) + { + case ClientCertificateType.dss_sign: + return new TlsDssSigner(); + case ClientCertificateType.ecdsa_sign: + return new TlsECDsaSigner(); + case ClientCertificateType.rsa_sign: + return new TlsRsaSigner(); + default: + throw new ArgumentException("not a type with signing capability", "clientCertificateType"); + } + } + + internal static readonly byte[] SSL_CLIENT = {0x43, 0x4C, 0x4E, 0x54}; + internal static readonly byte[] SSL_SERVER = {0x53, 0x52, 0x56, 0x52}; + + // SSL3 magic mix constants ("A", "BB", "CCC", ...) + internal static readonly byte[][] SSL3_CONST = GenSsl3Const(); + + private static byte[][] GenSsl3Const() + { + int n = 10; + byte[][] arr = new byte[n][]; + for (int i = 0; i < n; i++) + { + byte[] b = new byte[i + 1]; + Arrays.Fill(b, (byte)('A' + i)); + arr[i] = b; + } + return arr; + } + + private static IList VectorOfOne(object obj) + { + IList v = Platform.CreateArrayList(1); + v.Add(obj); + return v; + } + + public static int GetCipherType(int ciphersuite) + { + switch (GetEncryptionAlgorithm(ciphersuite)) + { + case EncryptionAlgorithm.AES_128_GCM: + case EncryptionAlgorithm.AES_256_GCM: + case EncryptionAlgorithm.AES_128_CCM: + case EncryptionAlgorithm.AES_128_CCM_8: + case EncryptionAlgorithm.AES_256_CCM: + case EncryptionAlgorithm.AES_256_CCM_8: + case EncryptionAlgorithm.CAMELLIA_128_GCM: + case EncryptionAlgorithm.CAMELLIA_256_GCM: + case EncryptionAlgorithm.AEAD_CHACHA20_POLY1305: + return CipherType.aead; + + case EncryptionAlgorithm.RC2_CBC_40: + case EncryptionAlgorithm.IDEA_CBC: + case EncryptionAlgorithm.DES40_CBC: + case EncryptionAlgorithm.DES_CBC: + case EncryptionAlgorithm.cls_3DES_EDE_CBC: + case EncryptionAlgorithm.AES_128_CBC: + case EncryptionAlgorithm.AES_256_CBC: + case EncryptionAlgorithm.CAMELLIA_128_CBC: + case EncryptionAlgorithm.CAMELLIA_256_CBC: + case EncryptionAlgorithm.SEED_CBC: + return CipherType.block; + + case EncryptionAlgorithm.RC4_40: + case EncryptionAlgorithm.RC4_128: + case EncryptionAlgorithm.ESTREAM_SALSA20: + case EncryptionAlgorithm.SALSA20: + return CipherType.stream; + + default: + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public static int GetEncryptionAlgorithm(int ciphersuite) + { + switch (ciphersuite) + { + 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_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA: + return EncryptionAlgorithm.cls_3DES_EDE_CBC; + + case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + return EncryptionAlgorithm.AEAD_CHACHA20_POLY1305; + + 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_PSK_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_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA: + return EncryptionAlgorithm.AES_128_CBC; + + case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256: + return EncryptionAlgorithm.AES_128_CBC; + + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_PSK_WITH_AES_128_CCM: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM: + return EncryptionAlgorithm.AES_128_CCM; + + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8: + case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8: + return EncryptionAlgorithm.AES_128_CCM_8; + + case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256: + return EncryptionAlgorithm.AES_128_GCM; + + 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_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA: + case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA: + return EncryptionAlgorithm.AES_256_CBC; + + case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256: + return EncryptionAlgorithm.AES_256_CBC; + + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384: + return EncryptionAlgorithm.AES_256_CBC; + + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_PSK_WITH_AES_256_CCM: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM: + return EncryptionAlgorithm.AES_256_CCM; + + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8: + case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8: + return EncryptionAlgorithm.AES_256_CCM_8; + + case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384: + return EncryptionAlgorithm.AES_256_GCM; + + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA: + return EncryptionAlgorithm.CAMELLIA_128_CBC; + + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256: + return EncryptionAlgorithm.CAMELLIA_128_CBC; + + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256: + return EncryptionAlgorithm.CAMELLIA_128_GCM; + + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA: + return EncryptionAlgorithm.CAMELLIA_256_CBC; + + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256: + return EncryptionAlgorithm.CAMELLIA_256_CBC; + + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384: + return EncryptionAlgorithm.CAMELLIA_256_CBC; + + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384: + return EncryptionAlgorithm.CAMELLIA_256_GCM; + + case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1: + case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1: + return EncryptionAlgorithm.ESTREAM_SALSA20; + + case CipherSuite.TLS_RSA_WITH_NULL_MD5: + return EncryptionAlgorithm.NULL; + + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA: + case CipherSuite.TLS_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA: + case CipherSuite.TLS_RSA_WITH_NULL_SHA: + return EncryptionAlgorithm.NULL; + + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA256: + case CipherSuite.TLS_RSA_WITH_NULL_SHA256: + return EncryptionAlgorithm.NULL; + + case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_PSK_WITH_NULL_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384: + return EncryptionAlgorithm.NULL; + + case CipherSuite.TLS_DH_anon_WITH_RC4_128_MD5: + case CipherSuite.TLS_RSA_WITH_RC4_128_MD5: + return EncryptionAlgorithm.RC4_128; + + case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDH_anon_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_PSK_WITH_RC4_128_SHA: + case CipherSuite.TLS_RSA_WITH_RC4_128_SHA: + case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA: + return EncryptionAlgorithm.RC4_128; + + case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1: + case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1: + case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1: + case CipherSuite.TLS_PSK_WITH_SALSA20_SHA1: + case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_SHA1: + case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1: + return EncryptionAlgorithm.SALSA20; + + case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA: + case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA: + return EncryptionAlgorithm.SEED_CBC; + + default: + throw new TlsFatalAlert(AlertDescription.internal_error); + } + } + + public static ProtocolVersion GetMinimumVersion(int ciphersuite) + { + switch (ciphersuite) + { + case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM: + case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM: + case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: + case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8: + case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8: + case CipherSuite.TLS_PSK_WITH_AES_128_CCM: + case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8: + case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_PSK_WITH_AES_256_CCM: + case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8: + case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM: + case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8: + case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256: + case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384: + case CipherSuite.TLS_RSA_WITH_NULL_SHA256: + return ProtocolVersion.TLSv12; + + default: + return ProtocolVersion.SSLv3; + } + } + + public static bool IsAeadCipherSuite(int ciphersuite) + { + return CipherType.aead == GetCipherType(ciphersuite); + } + + public static bool IsBlockCipherSuite(int ciphersuite) + { + return CipherType.block == GetCipherType(ciphersuite); + } + + public static bool IsStreamCipherSuite(int ciphersuite) + { + return CipherType.stream == GetCipherType(ciphersuite); + } + + public static bool IsValidCipherSuiteForVersion(int cipherSuite, ProtocolVersion serverVersion) + { + return GetMinimumVersion(cipherSuite).IsEqualOrEarlierVersionOf(serverVersion.GetEquivalentTLSVersion()); + } + } } diff --git a/crypto/src/crypto/tls/UrlAndHash.cs b/crypto/src/crypto/tls/UrlAndHash.cs new file mode 100644
index 000000000..9ffd2cbf8 --- /dev/null +++ b/crypto/src/crypto/tls/UrlAndHash.cs
@@ -0,0 +1,94 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /** + * RFC 6066 5. + */ + public class UrlAndHash + { + protected readonly string mUrl; + protected readonly byte[] mSha1Hash; + + public UrlAndHash(string url, byte[] sha1Hash) + { + if (url == null || url.Length < 1 || url.Length >= (1 << 16)) + throw new ArgumentException("must have length from 1 to (2^16 - 1)", "url"); + if (sha1Hash != null && sha1Hash.Length != 20) + throw new ArgumentException("must have length == 20, if present", "sha1Hash"); + + this.mUrl = url; + this.mSha1Hash = sha1Hash; + } + + public virtual string Url + { + get { return mUrl; } + } + + public virtual byte[] Sha1Hash + { + get { return mSha1Hash; } + } + + /** + * Encode this {@link UrlAndHash} to a {@link Stream}. + * + * @param output the {@link Stream} to encode to. + * @throws IOException + */ + public virtual void Encode(Stream output) + { + byte[] urlEncoding = Strings.ToByteArray(this.mUrl); + TlsUtilities.WriteOpaque16(urlEncoding, output); + + if (this.mSha1Hash == null) + { + TlsUtilities.WriteUint8(0, output); + } + else + { + TlsUtilities.WriteUint8(1, output); + output.Write(this.mSha1Hash, 0, this.mSha1Hash.Length); + } + } + + /** + * Parse a {@link UrlAndHash} from a {@link Stream}. + * + * @param context + * the {@link TlsContext} of the current connection. + * @param input + * the {@link Stream} to parse from. + * @return a {@link UrlAndHash} object. + * @throws IOException + */ + public static UrlAndHash Parse(TlsContext context, Stream input) + { + byte[] urlEncoding = TlsUtilities.ReadOpaque16(input); + if (urlEncoding.Length < 1) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + string url = Strings.FromByteArray(urlEncoding); + + byte[] sha1Hash = null; + byte padding = TlsUtilities.ReadUint8(input); + switch (padding) + { + case 0: + if (TlsUtilities.IsTlsV12(context)) + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + break; + case 1: + sha1Hash = TlsUtilities.ReadFully(20, input); + break; + default: + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } + + return new UrlAndHash(url, sha1Hash); + } + } +} diff --git a/crypto/src/crypto/tls/UseSrtpData.cs b/crypto/src/crypto/tls/UseSrtpData.cs new file mode 100644
index 000000000..fe8f8accb --- /dev/null +++ b/crypto/src/crypto/tls/UseSrtpData.cs
@@ -0,0 +1,56 @@ +using System; +using System.Collections; +using System.IO; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /** + * RFC 5764 4.1.1 + */ + public class UseSrtpData + { + protected readonly int[] mProtectionProfiles; + protected readonly byte[] mMki; + + /** + * @param protectionProfiles see {@link SrtpProtectionProfile} for valid constants. + * @param mki valid lengths from 0 to 255. + */ + public UseSrtpData(int[] protectionProfiles, byte[] mki) + { + if (protectionProfiles == null || protectionProfiles.Length < 1 + || protectionProfiles.Length >= (1 << 15)) + { + throw new ArgumentException("must have length from 1 to (2^15 - 1)", "protectionProfiles"); + } + + if (mki == null) + { + mki = TlsUtilities.EmptyBytes; + } + else if (mki.Length > 255) + { + throw new ArgumentException("cannot be longer than 255 bytes", "mki"); + } + + this.mProtectionProfiles = protectionProfiles; + this.mMki = mki; + } + + /** + * @return see {@link SrtpProtectionProfile} for valid constants. + */ + public virtual int[] ProtectionProfiles + { + get { return mProtectionProfiles; } + } + + /** + * @return valid lengths from 0 to 255. + */ + public virtual byte[] Mki + { + get { return mMki; } + } + } +} diff --git a/crypto/src/crypto/tls/UserMappingType.cs b/crypto/src/crypto/tls/UserMappingType.cs new file mode 100644
index 000000000..6cff51736 --- /dev/null +++ b/crypto/src/crypto/tls/UserMappingType.cs
@@ -0,0 +1,13 @@ +using System; + +namespace Org.BouncyCastle.Crypto.Tls +{ + /// <remarks>RFC 4681</remarks> + public abstract class UserMappingType + { + /* + * RFC 4681 + */ + public const byte upn_domain_hint = 64; + } +} diff --git a/crypto/src/crypto/util/Pack.cs b/crypto/src/crypto/util/Pack.cs
index 67c939ad5..087cb7cea 100644 --- a/crypto/src/crypto/util/Pack.cs +++ b/crypto/src/crypto/util/Pack.cs
@@ -2,218 +2,265 @@ 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); - } - } + 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 + 1] = (byte)(n); + } + + internal static ushort BE_To_UInt16(byte[] bs) + { + uint n = (uint)bs[0] << 8 + | (uint)bs[1]; + return (ushort)n; + } + + internal static ushort BE_To_UInt16(byte[] bs, int off) + { + uint n = (uint)bs[off] << 8 + | (uint)bs[off + 1]; + return (ushort)n; + } + + internal static byte[] UInt32_To_BE(uint n) + { + byte[] bs = new byte[4]; + UInt32_To_BE(n, bs, 0); + return bs; + } + + 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 + 1] = (byte)(n >> 16); + bs[off + 2] = (byte)(n >> 8); + bs[off + 3] = (byte)(n); + } + + internal static byte[] UInt32_To_BE(uint[] ns) + { + byte[] bs = new byte[4 * ns.Length]; + UInt32_To_BE(ns, bs, 0); + return bs; + } + + 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) + { + return (uint)bs[0] << 24 + | (uint)bs[1] << 16 + | (uint)bs[2] << 8 + | (uint)bs[3]; + } + + internal static uint BE_To_UInt32(byte[] bs, int off) + { + return (uint)bs[off] << 24 + | (uint)bs[off + 1] << 16 + | (uint)bs[off + 2] << 8 + | (uint)bs[off + 3]; + } + + 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 byte[] UInt64_To_BE(ulong n) + { + byte[] bs = new byte[8]; + UInt64_To_BE(n, bs, 0); + return bs; + } + + 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 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 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 + 1] = (byte)(n >> 8); + } + + internal static ushort LE_To_UInt16(byte[] bs) + { + uint n = (uint)bs[0] + | (uint)bs[1] << 8; + return (ushort)n; + } + + internal static ushort LE_To_UInt16(byte[] bs, int off) + { + uint n = (uint)bs[off] + | (uint)bs[off + 1] << 8; + return (ushort)n; + } + + internal static byte[] UInt32_To_LE(uint n) + { + byte[] bs = new byte[4]; + UInt32_To_LE(n, bs, 0); + return bs; + } + + 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 + 1] = (byte)(n >> 8); + bs[off + 2] = (byte)(n >> 16); + bs[off + 3] = (byte)(n >> 24); + } + + internal static byte[] UInt32_To_LE(uint[] ns) + { + byte[] bs = new byte[4 * ns.Length]; + UInt32_To_LE(ns, bs, 0); + return bs; + } + + 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) + { + return (uint)bs[0] + | (uint)bs[1] << 8 + | (uint)bs[2] << 16 + | (uint)bs[3] << 24; + } + + internal static uint LE_To_UInt32(byte[] bs, int off) + { + return (uint)bs[off] + | (uint)bs[off + 1] << 8 + | (uint)bs[off + 2] << 16 + | (uint)bs[off + 3] << 24; + } + + 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 void LE_To_UInt32(byte[] bs, int bOff, uint[] ns, int nOff, int count) + { + for (int i = 0; i < count; ++i) + { + ns[nOff + i] = LE_To_UInt32(bs, bOff); + bOff += 4; + } + } + + internal static byte[] UInt64_To_LE(ulong n) + { + byte[] bs = new byte[8]; + UInt64_To_LE(n, bs, 0); + return bs; + } + + 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); + } + + 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; + } + } } diff --git a/crypto/src/math/BigInteger.cs b/crypto/src/math/BigInteger.cs
index d52c0f83c..f302f077e 100644 --- a/crypto/src/math/BigInteger.cs +++ b/crypto/src/math/BigInteger.cs
@@ -8,1300 +8,1388 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Math { -#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT || PORTABLE) - [Serializable] +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [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 + { + // The first few odd primes + /* + 3 5 7 11 13 17 19 23 29 + 31 37 41 43 47 53 59 61 67 71 + 73 79 83 89 97 101 103 107 109 113 + 127 131 137 139 149 151 157 163 167 173 + 179 181 191 193 197 199 211 223 227 229 + 233 239 241 251 257 263 269 271 277 281 + 283 293 307 311 313 317 331 337 347 349 + 353 359 367 373 379 383 389 397 401 409 + 419 421 431 433 439 443 449 457 461 463 + 467 479 487 491 499 503 509 521 523 541 + 547 557 563 569 571 577 587 593 599 601 + 607 613 617 619 631 641 643 647 653 659 + 661 673 677 683 691 701 709 719 727 733 + 739 743 751 757 761 769 773 787 797 809 + 811 821 823 827 829 839 853 857 859 863 + 877 881 883 887 907 911 919 929 937 941 + 947 953 967 971 977 983 991 997 1009 + 1013 1019 1021 1031 1033 1039 1049 1051 + 1061 1063 1069 1087 1091 1093 1097 1103 + 1109 1117 1123 1129 1151 1153 1163 1171 + 1181 1187 1193 1201 1213 1217 1223 1229 + 1231 1237 1249 1259 1277 1279 1283 1289 + */ + + // Each list has a product < 2^31 + internal 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 }, + new int[]{ 1033, 1039, 1049 }, + new int[]{ 1051, 1061, 1063 }, + new int[]{ 1069, 1087, 1091 }, + + new int[]{ 1093, 1097, 1103 }, + new int[]{ 1109, 1117, 1123 }, + new int[]{ 1129, 1151, 1153 }, + new int[]{ 1163, 1171, 1181 }, + new int[]{ 1187, 1193, 1201 }, + + new int[]{ 1213, 1217, 1223 }, + new int[]{ 1229, 1231, 1237 }, + new int[]{ 1249, 1259, 1277 }, + new int[]{ 1279, 1283, 1289 }, + }; + + internal static readonly int[] primeProducts; + + private const long IMASK = 0xFFFFFFFFL; + private const ulong UIMASK = 0xFFFFFFFFUL; + + private static readonly int[] ZeroMagnitude = new int[0]; + private static readonly byte[] ZeroEncoding = new byte[0]; + + private static readonly BigInteger[] SMALL_CONSTANTS = new BigInteger[17]; + public static readonly BigInteger Zero; + public static readonly BigInteger One; + public static readonly BigInteger Two; + public static readonly BigInteger Three; + public static readonly BigInteger Ten; + + //private readonly static byte[] BitCountTable = + //{ + // 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 readonly static byte[] BitLengthTable = + { + 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 + }; + + // TODO Parse radix-2 64 bits at a time and radix-8 63 bits at a time + private const int chunk2 = 1, chunk8 = 1, chunk10 = 19, chunk16 = 16; + private static readonly BigInteger radix2, radix2E, radix8, radix8E, radix10, radix10E, radix16, radix16E; + + private static readonly Random RandomSource = new Random(); + + /* + * These are the threshold bit-lengths (of an exponent) where we increase the window size. + * They are calculated according to the expected savings in multiplications. + * Some squares will also be saved on average, but we offset these against the extra storage costs. + */ + private static readonly int[] ExpWindowThresholds = { 7, 25, 81, 241, 673, 1793, 4609, Int32.MaxValue }; + + private const int BitsPerByte = 8; + private const int BitsPerInt = 32; + private const int BytesPerInt = 4; + + static BigInteger() + { + Zero = new BigInteger(0, ZeroMagnitude, false); + Zero.nBits = 0; Zero.nBitLength = 0; + + SMALL_CONSTANTS[0] = Zero; + for (uint i = 1; i < SMALL_CONSTANTS.Length; ++i) + { + SMALL_CONSTANTS[i] = CreateUValueOf(i); + } + + One = SMALL_CONSTANTS[1]; + Two = SMALL_CONSTANTS[2]; + Three = SMALL_CONSTANTS[3]; + Ten = SMALL_CONSTANTS[10]; + + radix2 = ValueOf(2); + radix2E = radix2.Pow(chunk2); + + radix8 = ValueOf(8); + radix8E = radix8.Pow(chunk8); + + radix10 = ValueOf(10); + radix10E = radix10.Pow(chunk10); + + radix16 = ValueOf(16); + radix16E = radix16.Pow(chunk16); + + 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[] magnitude; // array of ints with [0] being the most significant + private int sign; // -1 means -ve; +1 means +ve; 0 means 0; + private int nBits = -1; // cache BitCount() value + private int nBitLength = -1; // cache BitLength() value + private int mQuote = 0; // -m^(-1) mod b, b = 2^32 (see Montgomery mult.), 0 when uninitialised + + 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 8: + // Is there anyway to restrict to octal digits? + style = NumberStyles.Integer; + chunk = chunk8; + r = radix8; + rE = radix8E; + 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, 8, 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 >= 2) + throw new FormatException("Bad character in radix 2 string: " + s); + + // TODO Parse 64 bits at a time + b = b.ShiftLeft(1); + break; + case 8: + // TODO Need this because we are parsing in radix 10 above + if (i >= 8) + throw new FormatException("Bad character in radix 8 string: " + s); + + // TODO Parse 63 bits at a time + b = b.ShiftLeft(3); + 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) - { + } + else if (radix == 8) + { + // 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 * 3); + } + 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 + int xBits = BitsPerByte * nBytes - sizeInBits; + b[0] &= (byte)(255U >> xBits); + + this.magnitude = MakeMagnitude(b, 0, b.Length); + this.sign = this.magnitude.Length < 1 ? 0 : 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 = (byte)(255U >> 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 = 0; + + 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 = 0; + + 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 += BitCnt(magnitude[i]); + } + nBits = sum; + } + } + + return nBits; + } + } + + public static int BitCnt(int i) + { + uint u = (uint)i; + u = u - ((u >> 1) & 0x55555555); + u = (u & 0x33333333) + ((u >> 2) & 0x33333333); + u = (u + (u >> 4)) & 0x0f0f0f0f; + u += (u >> 8); + u += (u >> 16); + u &= 0x3f; + return (int)u; + } + + private static int CalcBitLength(int sign, 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(sign, 0, magnitude); + } + + return nBitLength; + } + } + + // + // BitLen(value) is the number of bits in value. + // + private static int BitLen(int w) + { + uint v = (uint)w; + uint t = v >> 24; + if (t != 0) + return 24 + BitLengthTable[t]; + t = v >> 16; + if (t != 0) + return 16 + BitLengthTable[t]; + t = v >> 8; + if (t != 0) + return 8 + BitLengthTable[t]; + return BitLengthTable[v]; + } + + 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(1, yStart, y); + int xBitLength = CalcBitLength(1, 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) + 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; + + return sign == biggie.sign && IsEqualMagnitude(biggie); + } + + private bool IsEqualMagnitude(BigInteger x) + { + int[] xMag = x.magnitude; + if (magnitude.Length != x.magnitude.Length) + return false; + for (int i = 0; i < magnitude.Length; i++) + { + if (magnitude[i] != x.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 + { + if (sign == 0) + return 0; + + int n = magnitude.Length; + + int v = magnitude[n - 1]; + + return sign < 0 ? -v : v; + } + } + + /** + * 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); + // 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); @@ -1310,58 +1398,58 @@ namespace Org.BouncyCastle.Math // 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; - } + } + + public 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; + int s = n.GetLowestSetBitMaskFirst(-1 << 1); + Debug.Assert(s >= 1); + BigInteger r = n.ShiftRight(s); + + // NOTE: Avoid conversion to/from Montgomery form and check for R/-R as result instead + + BigInteger montRadix = One.ShiftLeft(32 * n.magnitude.Length).Remainder(n); + BigInteger minusMontRadix = n.Subtract(montRadix); + + do + { + BigInteger a; + do + { + a = new BigInteger(n.BitLength, random); + } + while (a.sign == 0 || a.CompareTo(n) >= 0 + || a.IsEqualMagnitude(montRadix) || a.IsEqualMagnitude(minusMontRadix)); + + BigInteger y = ModPowMonty(a, r, n, false); + + if (!y.Equals(montRadix)) + { + int j = 0; + while (!y.Equals(minusMontRadix)) + { + if (++j == s) + return false; + + y = ModPowMonty(y, Two, n, false); + + if (y.Equals(montRadix)) + return false; + } + } + + certainty -= 2; // composites pass for only 1/4 possible 'a' + } + while (certainty > 0); + + return true; + } // private bool SolovayStrassenTest( // int certainty, @@ -1452,58 +1540,55 @@ namespace Org.BouncyCastle.Math // 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 + public long LongValue + { + get + { + if (sign == 0) + return 0; + + int n = magnitude.Length; + + long v = magnitude[n - 1] & IMASK; + if (n > 1) + { + v |= (magnitude[n - 2] & IMASK) << 32; + } + + 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)) // { @@ -1541,992 +1626,1309 @@ namespace Org.BouncyCastle.Math // } // } + if (m.QuickPow2Check()) + { + return ModInversePow2(m); + } + + BigInteger d = this.Remainder(m); BigInteger x; - BigInteger gcd = ExtEuclid(this.Mod(m), m, out x); + BigInteger gcd = ExtEuclid(d, m, out x); + + if (!gcd.Equals(One)) + throw new ArithmeticException("Numbers not relatively prime."); - if (!gcd.Equals(One)) - throw new ArithmeticException("Numbers not relatively prime."); + if (x.sign < 0) + { + x = x.Add(m); + } + + return x; + } + + private BigInteger ModInversePow2(BigInteger m) + { + Debug.Assert(m.SignValue > 0); + Debug.Assert(m.BitCount == 1); + + if (!TestBit(0)) + { + throw new ArithmeticException("Numbers not relatively prime."); + } + + int pow = m.BitLength - 1; + + long inv64 = ModInverse64(LongValue); + if (pow < 64) + { + inv64 &= ((1L << pow) - 1); + } - if (x.sign < 0) - { + BigInteger x = BigInteger.ValueOf(inv64); + + if (pow > 64) + { + BigInteger d = this.Remainder(m); + int bitsCorrect = 64; + + do + { + BigInteger t = x.Multiply(d).Remainder(m); + x = x.Multiply(Two.Subtract(t)).Remainder(m); + bitsCorrect <<= 1; + } + while (bitsCorrect < pow); + } + + 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; - //} + } + + return x; + } + + private static int ModInverse32(int d) + { + // Newton's method with initial estimate "correct to 4 bits" + Debug.Assert((d & 1) != 0); + int x = d + (((d + 1) & 4) << 1); // d.x == 1 mod 2**4 + Debug.Assert(((d * x) & 15) == 1); + x *= 2 - d * x; // d.x == 1 mod 2**8 + x *= 2 - d * x; // d.x == 1 mod 2**16 + x *= 2 - d * x; // d.x == 1 mod 2**32 + Debug.Assert(d * x == 1); + return x; + } + + private static long ModInverse64(long d) + { + // Newton's method with initial estimate "correct to 4 bits" + Debug.Assert((d & 1L) != 0); + long x = d + (((d + 1L) & 4L) << 1); // d.x == 1 mod 2**4 + Debug.Assert(((d * x) & 15L) == 1L); + x *= 2 - d * x; // d.x == 1 mod 2**8 + x *= 2 - d * x; // d.x == 1 mod 2**16 + x *= 2 - d * x; // d.x == 1 mod 2**32 + x *= 2 - d * x; // d.x == 1 mod 2**64 + Debug.Assert(d * x == 1L); + 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 + * @return The greatest common divisor of a and b + */ + private static BigInteger ExtEuclid(BigInteger a, BigInteger b, out BigInteger u1Out) + { + BigInteger u1 = One, v1 = Zero; + BigInteger u3 = a, v3 = b; + + if (v3.sign > 0) + { + for (;;) + { + BigInteger[] q = u3.DivideAndRemainder(v3); + u3 = v3; + v3 = q[1]; + + BigInteger oldU1 = u1; + u1 = v1; + + if (v3.sign <= 0) + break; + + v1 = oldU1.Subtract(v1.Multiply(q[0])); + } + } + 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 + return u3; + } + + private static void ZeroOut( + int[] x) + { + Array.Clear(x, 0, x.Length); + } + + public BigInteger ModPow(BigInteger e, BigInteger m) + { + if (m.sign < 1) + throw new ArithmeticException("Modulus must be positive"); + + if (m.Equals(One)) + return Zero; + + if (e.sign == 0) + return One; + + if (sign == 0) + return Zero; + + bool negExp = e.sign < 0; + if (negExp) + e = e.Negate(); + + BigInteger result = this.Mod(m); + if (!e.Equals(One)) + { + if ((m.magnitude[m.magnitude.Length - 1] & 1) == 0) + { + result = ModPowBarrett(result, e, m); + } + else + { + result = ModPowMonty(result, e, m, true); + } + } + + if (negExp) + result = result.ModInverse(m); + + return result; + } + + private static BigInteger ModPowBarrett(BigInteger b, BigInteger e, BigInteger m) + { + int k = m.magnitude.Length; + BigInteger mr = One.ShiftLeft((k + 1) << 5); + BigInteger yu = One.ShiftLeft(k << 6).Divide(m); + + // Sliding window from MSW to LSW + int extraBits = 0, expLength = e.BitLength; + while (expLength > ExpWindowThresholds[extraBits]) + { + ++extraBits; + } + + int numPowers = 1 << extraBits; + BigInteger[] oddPowers = new BigInteger[numPowers]; + oddPowers[0] = b; + + BigInteger b2 = ReduceBarrett(b.Square(), m, mr, yu); + + for (int i = 1; i < numPowers; ++i) + { + oddPowers[i] = ReduceBarrett(oddPowers[i - 1].Multiply(b2), m, mr, yu); + } + + int[] windowList = GetWindowList(e.magnitude, extraBits); + Debug.Assert(windowList.Length > 0); + + int window = windowList[0]; + int mult = window & 0xFF, lastZeroes = window >> 8; + + BigInteger y; + if (mult == 1) + { + y = b2; + --lastZeroes; + } + else + { + y = oddPowers[mult >> 1]; + } + + int windowPos = 1; + while ((window = windowList[windowPos++]) != -1) + { + mult = window & 0xFF; + + int bits = lastZeroes + BitLengthTable[mult]; + for (int j = 0; j < bits; ++j) + { + y = ReduceBarrett(y.Square(), m, mr, yu); + } + + y = ReduceBarrett(y.Multiply(oddPowers[mult >> 1]), m, mr, yu); + + lastZeroes = window >> 8; + } + + for (int i = 0; i < lastZeroes; ++i) + { + y = ReduceBarrett(y.Square(), m, mr, yu); + } + + return y; + } + + private static BigInteger ReduceBarrett(BigInteger x, BigInteger m, BigInteger mr, BigInteger yu) + { + int xLen = x.BitLength, mLen = m.BitLength; + if (xLen < mLen) + return x; + + if (xLen - mLen > 1) + { + int k = m.magnitude.Length; + + BigInteger q1 = x.DivideWords(k - 1); + BigInteger q2 = q1.Multiply(yu); // TODO Only need partial multiplication here + BigInteger q3 = q2.DivideWords(k + 1); + + BigInteger r1 = x.RemainderWords(k + 1); + BigInteger r2 = q3.Multiply(m); // TODO Only need partial multiplication here + BigInteger r3 = r2.RemainderWords(k + 1); + + x = r1.Subtract(r3); + if (x.sign < 0) + { + x = x.Add(mr); + } + } + + while (x.CompareTo(m) >= 0) + { + x = x.Subtract(m); + } + + return x; + } + + private static BigInteger ModPowMonty(BigInteger b, BigInteger e, BigInteger m, bool convert) + { + int n = m.magnitude.Length; + int powR = 32 * n; + bool smallMontyModulus = m.BitLength + 2 <= powR; + uint mDash = (uint)m.GetMQuote(); + + // tmp = this * R mod m + if (convert) + { + b = b.ShiftLeft(powR).Remainder(m); + } + + int[] yAccum = new int[n + 1]; + + int[] zVal = b.magnitude; + Debug.Assert(zVal.Length <= n); + if (zVal.Length < n) + { + int[] tmp = new int[n]; + zVal.CopyTo(tmp, n - zVal.Length); + zVal = tmp; + } + + // Sliding window from MSW to LSW + + int extraBits = 0; + + // Filter the common case of small RSA exponents with few bits set + if (e.magnitude.Length > 1 || e.BitCount > 2) + { + int expLength = e.BitLength; + while (expLength > ExpWindowThresholds[extraBits]) + { + ++extraBits; + } + } + + int numPowers = 1 << extraBits; + int[][] oddPowers = new int[numPowers][]; + oddPowers[0] = zVal; + + int[] zSquared = Arrays.Clone(zVal); + SquareMonty(yAccum, zSquared, m.magnitude, mDash, smallMontyModulus); + + for (int i = 1; i < numPowers; ++i) + { + oddPowers[i] = Arrays.Clone(oddPowers[i - 1]); + MultiplyMonty(yAccum, oddPowers[i], zSquared, m.magnitude, mDash, smallMontyModulus); + } + + int[] windowList = GetWindowList(e.magnitude, extraBits); + Debug.Assert(windowList.Length > 1); + + int window = windowList[0]; + int mult = window & 0xFF, lastZeroes = window >> 8; + + int[] yVal; + if (mult == 1) + { + yVal = zSquared; + --lastZeroes; + } + else + { + yVal = Arrays.Clone(oddPowers[mult >> 1]); + } + + int windowPos = 1; + while ((window = windowList[windowPos++]) != -1) + { + mult = window & 0xFF; + + int bits = lastZeroes + BitLengthTable[mult]; + for (int j = 0; j < bits; ++j) + { + SquareMonty(yAccum, yVal, m.magnitude, mDash, smallMontyModulus); + } + + MultiplyMonty(yAccum, yVal, oddPowers[mult >> 1], m.magnitude, mDash, smallMontyModulus); + + lastZeroes = window >> 8; + } + + for (int i = 0; i < lastZeroes; ++i) + { + SquareMonty(yAccum, yVal, m.magnitude, mDash, smallMontyModulus); + } + + if (convert) + { + // Return y * R^(-1) mod m + MontgomeryReduce(yVal, m.magnitude, mDash); + } + else if (smallMontyModulus && CompareTo(0, yVal, 0, m.magnitude) >= 0) + { + Subtract(0, yVal, 0, m.magnitude); + } + + return new BigInteger(1, yVal, true); + } + + private static int[] GetWindowList(int[] mag, int extraBits) + { + int v = mag[0]; + Debug.Assert(v != 0); + + int leadingBits = BitLen(v); + + int resultSize = (((mag.Length - 1) << 5) + leadingBits) / (1 + extraBits) + 2; + int[] result = new int[resultSize]; + int resultPos = 0; + + int bitPos = 33 - leadingBits; + v <<= bitPos; + + int mult = 1, multLimit = 1 << extraBits; + int zeroes = 0; + + int i = 0; + for (; ; ) + { + for (; bitPos < 32; ++bitPos) + { + if (mult < multLimit) + { + mult = (mult << 1) | (int)((uint)v >> 31); + } + else if (v < 0) + { + result[resultPos++] = CreateWindowEntry(mult, zeroes); + mult = 1; + zeroes = 0; + } + else + { + ++zeroes; + } + + v <<= 1; + } + + if (++i == mag.Length) + { + result[resultPos++] = CreateWindowEntry(mult, zeroes); + break; + } + + v = mag[i]; + bitPos = 0; + } + + result[resultPos] = -1; + return result; + } + + private static int CreateWindowEntry(int mult, int zeroes) + { + while ((mult & 1) == 0) + { + mult >>= 1; + ++zeroes; + } + + return mult | (zeroes << 8); + } + + /** + * 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); - } + ulong c; + + int wBase = w.Length - 1; + + for (int i = x.Length - 1; i > 0; --i) + { + ulong v = (uint)x[i]; + + c = v * v + (uint)w[wBase]; + w[wBase] = (int)c; + c >>= 32; + + for (int j = i - 1; j >= 0; --j) + { + ulong prod = v * (uint)x[j]; + + c += ((uint)w[--wBase] & UIMASK) + ((uint)prod << 1); + w[wBase] = (int)c; + c = (c >> 32) + (prod >> 31); + } + + c += (uint)w[--wBase]; + w[wBase] = (int)c; + + if (--wBase >= 0) + { + w[wBase] = (int)(c >> 32); + } + else + { + Debug.Assert((c >> 32) == 0); + } + + wBase += i; + } + + c = (uint)x[0]; + + c = c * c + (uint)w[wBase]; + w[wBase] = (int)c; + + if (--wBase >= 0) + { + w[wBase] += (int)(c >> 32); + } + else + { + Debug.Assert((c >> 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; + } + + /** + * Calculate mQuote = -m^(-1) mod b with b = 2^32 (32 = word size) + */ + private int GetMQuote() + { + if (mQuote != 0) + { + return mQuote; // already calculated + } + + Debug.Assert(this.sign > 0); + + int d = -magnitude[magnitude.Length - 1]; + + Debug.Assert((d & 1) != 0); + + return mQuote = ModInverse32(d); + } + + private static void MontgomeryReduce(int[] x, int[] m, uint mDash) // mDash = -m^(-1) mod b + { + // NOTE: Not a general purpose reduction (which would allow x up to twice the bitlength of m) + Debug.Assert(x.Length == m.Length); + + int n = m.Length; + + for (int i = n - 1; i >= 0; --i) + { + uint x0 = (uint)x[n - 1]; + ulong t = x0 * mDash; + + ulong carry = t * (uint)m[n - 1] + x0; + Debug.Assert((uint)carry == 0); + carry >>= 32; + + for (int j = n - 2; j >= 0; --j) + { + carry += t * (uint)m[j] + (uint)x[j]; + x[j + 1] = (int)carry; + carry >>= 32; + } + + x[0] = (int)carry; + Debug.Assert(carry >> 32 == 0); + } + + if (CompareTo(0, x, 0, m) >= 0) + { + Subtract(0, x, 0, m); + } + } + + /** + * 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, uint mDash, bool smallMontyModulus) + // mDash = -m^(-1) mod b + { + int n = m.Length; + + if (n == 1) + { + x[0] = (int)MultiplyMontyNIsOne((uint)x[0], (uint)y[0], (uint)m[0], mDash); + return; + } + + uint y0 = (uint)y[n - 1]; + int aMax; + + { + ulong xi = (uint)x[n - 1]; + + ulong carry = xi * y0; + ulong t = (uint)carry * mDash; + + ulong prod2 = t * (uint)m[n - 1]; + carry += (uint)prod2; + Debug.Assert((uint)carry == 0); + carry = (carry >> 32) + (prod2 >> 32); + + for (int j = n - 2; j >= 0; --j) + { + ulong prod1 = xi * (uint)y[j]; + prod2 = t * (uint)m[j]; + + carry += (prod1 & UIMASK) + (uint)prod2; + a[j + 2] = (int)carry; + carry = (carry >> 32) + (prod1 >> 32) + (prod2 >> 32); + } + + a[1] = (int)carry; + aMax = (int)(carry >> 32); + } + + for (int i = n - 2; i >= 0; --i) + { + uint a0 = (uint)a[n]; + ulong xi = (uint)x[i]; + + ulong prod1 = xi * y0; + ulong carry = (prod1 & UIMASK) + a0; + ulong t = (uint)carry * mDash; + + ulong prod2 = t * (uint)m[n - 1]; + carry += (uint)prod2; + Debug.Assert((uint)carry == 0); + carry = (carry >> 32) + (prod1 >> 32) + (prod2 >> 32); + + for (int j = n - 2; j >= 0; --j) + { + prod1 = xi * (uint)y[j]; + prod2 = t * (uint)m[j]; + + carry += (prod1 & UIMASK) + (uint)prod2 + (uint)a[j + 1]; + a[j + 2] = (int)carry; + carry = (carry >> 32) + (prod1 >> 32) + (prod2 >> 32); + } + + carry += (uint)aMax; + a[1] = (int)carry; + aMax = (int)(carry >> 32); + } + + a[0] = aMax; + + if (!smallMontyModulus && CompareTo(0, a, 0, m) >= 0) + { + Subtract(0, a, 0, m); + } + + Array.Copy(a, 1, x, 0, n); + } + + private static void SquareMonty(int[] a, int[] x, int[] m, uint mDash, bool smallMontyModulus) + // mDash = -m^(-1) mod b + { + int n = m.Length; + + if (n == 1) + { + uint xVal = (uint)x[0]; + x[0] = (int)MultiplyMontyNIsOne(xVal, xVal, (uint)m[0], mDash); + return; + } + + ulong x0 = (uint)x[n - 1]; + int aMax; + + { + ulong carry = x0 * x0; + ulong t = (uint)carry * mDash; + + ulong prod2 = t * (uint)m[n - 1]; + carry += (uint)prod2; + Debug.Assert((uint)carry == 0); + carry = (carry >> 32) + (prod2 >> 32); + + for (int j = n - 2; j >= 0; --j) + { + ulong prod1 = x0 * (uint)x[j]; + prod2 = t * (uint)m[j]; + + carry += (prod2 & UIMASK) + ((uint)prod1 << 1); + a[j + 2] = (int)carry; + carry = (carry >> 32) + (prod1 >> 31) + (prod2 >> 32); + } + + a[1] = (int)carry; + aMax = (int)(carry >> 32); + } + + for (int i = n - 2; i >= 0; --i) + { + uint a0 = (uint)a[n]; + ulong t = a0 * mDash; + + ulong carry = t * (uint)m[n - 1] + a0; + Debug.Assert((uint)carry == 0); + carry >>= 32; + + for (int j = n - 2; j > i; --j) + { + carry += t * (uint)m[j] + (uint)a[j + 1]; + a[j + 2] = (int)carry; + carry >>= 32; + } + + ulong xi = (uint)x[i]; + + { + ulong prod1 = xi * xi; + ulong prod2 = t * (uint)m[i]; + + carry += (prod1 & UIMASK) + (uint)prod2 + (uint)a[i + 1]; + a[i + 2] = (int)carry; + carry = (carry >> 32) + (prod1 >> 32) + (prod2 >> 32); + } + + for (int j = i - 1; j >= 0; --j) + { + ulong prod1 = xi * (uint)x[j]; + ulong prod2 = t * (uint)m[j]; + + carry += (prod2 & UIMASK) + ((uint)prod1 << 1) + (uint)a[j + 1]; + a[j + 2] = (int)carry; + carry = (carry >> 32) + (prod1 >> 31) + (prod2 >> 32); + } + + carry += (uint)aMax; + a[1] = (int)carry; + aMax = (int)(carry >> 32); + } + + a[0] = aMax; + + if (!smallMontyModulus && CompareTo(0, a, 0, m) >= 0) + { + Subtract(0, a, 0, m); + } + + Array.Copy(a, 1, x, 0, n); + } + + private static uint MultiplyMontyNIsOne(uint x, uint y, uint m, uint mDash) + { + ulong carry = (ulong)x * y; + uint t = (uint)carry * mDash; + ulong um = m; + ulong prod2 = um * t; + carry += (uint)prod2; + Debug.Assert((uint)carry == 0); + carry = (carry >> 32) + (prod2 >> 32); + if (carry > um) + { + carry -= um; + } + Debug.Assert(carry < um); + return (uint)carry; + } + + public BigInteger Multiply( + BigInteger val) + { + if (val == this) + return Square(); + + if ((sign & 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 = magnitude.Length + val.magnitude.Length; + int[] res = new int[resLength]; + + Multiply(res, this.magnitude, val.magnitude); + + int resSign = sign ^ val.sign ^ 1; + return new BigInteger(resSign, res, true); + } + + public BigInteger Square() + { + if (sign == 0) + return Zero; + if (this.QuickPow2Check()) + return ShiftLeft(Abs().BitLength - 1); + int resLength = magnitude.Length << 1; + if ((uint)magnitude[0] >> 16 == 0) + --resLength; + int[] res = new int[resLength]; + Square(res, magnitude); + return new BigInteger(1, res, false); + } + + 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) + { + if (exp < 0) + throw new ArithmeticException("Negative exponent"); + + return One; + } + + if (sign == 0) + { + return this; + } + + if (QuickPow2Check()) + { + long powOf2 = (long)exp * (BitLength - 1); + if (powOf2 > Int32.MaxValue) + { + throw new ArithmeticException("Result too large"); + } + return One.ShiftLeft((int)powOf2); + } + + 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 static 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(1, yStart, y); + int xBitLength = CalcBitLength(1, 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 excessBits = (numWords << 5) - n; + if (excessBits > 0) + { + result[0] &= (int)(UInt32.MaxValue >> excessBits); + } + + return result; + } + + private BigInteger DivideWords(int w) + { + Debug.Assert(w >= 0); + int n = magnitude.Length; + if (w >= n) + return Zero; + int[] mag = new int[n - w]; + Array.Copy(magnitude, 0, mag, 0, n - w); + return new BigInteger(sign, mag, false); + } + + private BigInteger RemainderWords(int w) + { + Debug.Assert(w >= 0); + int n = magnitude.Length; + if (w >= n) + return this; + int[] mag = new int[w]; + Array.Copy(magnitude, n - w, mag, 0, w); + return new BigInteger(sign, mag, false); + } + + /** + * 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; + } + + private static int ShiftLeftOneInPlace(int[] x, int carry) + { + Debug.Assert(carry == 0 || carry == 1); + int pos = x.Length; + while (--pos >= 0) + { + uint val = (uint)x[pos]; + x[pos] = (int)(val << 1) | carry; + carry = (int)(val >> 31); + } + return carry; + } + + 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; + int n) + { + if (n == 0) + return this; - if (n < 0) - return ShiftLeft(-n); + if (n < 0) + return ShiftLeft(-n); - if (n >= BitLength) - return (this.sign < 0 ? One.Negate() : Zero); + if (n >= BitLength) + return (this.sign < 0 ? One.Negate() : Zero); // int[] res = (int[]) this.magnitude.Clone(); // @@ -2534,608 +2936,630 @@ namespace Org.BouncyCastle.Math // // 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; + 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 + 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 <= 36 as in Java) + + switch (radix) + { + case 2: + case 8: + case 10: + case 16: + break; + default: + throw new FormatException("Only bases 2, 8, 10, 16 are allowed"); + } + + // NB: Can only happen to internally managed instances + if (magnitude == null) + return "null"; + + if (sign == 0) + return "0"; + + + // NOTE: This *should* be unnecessary, since the magnitude *should* never have leading zero digits + int firstNonZero = 0; + while (firstNonZero < magnitude.Length) + { + if (magnitude[firstNonZero] != 0) + { + break; + } + ++firstNonZero; + } + + if (firstNonZero == magnitude.Length) + { + return "0"; + } + + + StringBuilder sb = new StringBuilder(); + if (sign == -1) + { + sb.Append('-'); + } + + switch (radix) + { + case 2: + { + int pos = firstNonZero; + sb.Append(Convert.ToString(magnitude[pos], 2)); + while (++pos < magnitude.Length) + { + AppendZeroExtendedString(sb, Convert.ToString(magnitude[pos], 2), 32); + } + break; + } + case 8: + { + int mask = (1 << 30) - 1; + BigInteger u = this.Abs(); + int bits = u.BitLength; + IList S = Platform.CreateArrayList(); + while (bits > 30) + { + S.Add(Convert.ToString(u.IntValue & mask, 8)); + u = u.ShiftRight(30); + bits -= 30; + } + sb.Append(Convert.ToString(u.IntValue, 8)); for (int i = S.Count - 1; i >= 0; --i) { - sb.Append((string)S[i]); + AppendZeroExtendedString(sb, (string)S[i], 10); + } + break; + } + case 16: + { + int pos = firstNonZero; + sb.Append(Convert.ToString(magnitude[pos], 16)); + while (++pos < magnitude.Length) + { + AppendZeroExtendedString(sb, Convert.ToString(magnitude[pos], 16), 8); + } + break; + } + // TODO This could work for other radices if there is an alternative to Convert.ToString method + //default: + case 10: + { + BigInteger q = this.Abs(); + if (q.BitLength < 64) + { + sb.Append(Convert.ToString(q.LongValue, radix)); + break; } - } - string s = sb.ToString(); + // Based on algorithm 1a from chapter 4.4 in Seminumerical Algorithms (Knuth) - Debug.Assert(s.Length > 0); + // Work out the largest power of 'rdx' that is a positive 64-bit integer + // TODO possibly cache power/exponent against radix? + long limit = Int64.MaxValue / radix; + long power = radix; + int exponent = 1; + while (power <= limit) + { + power *= radix; + ++exponent; + } - // Strip leading zeros. (We know this number is not all zeroes though) - if (s[0] == '0') - { - int nonZeroPos = 0; - while (s[++nonZeroPos] == '0') {} + BigInteger bigPower = BigInteger.ValueOf(power); - s = s.Substring(nonZeroPos); - } + IList S = Platform.CreateArrayList(); + while (q.CompareTo(bigPower) >= 0) + { + BigInteger[] qr = q.DivideAndRemainder(bigPower); + S.Add(Convert.ToString(qr[1].LongValue, radix)); + q = qr[0]; + } - if (sign == -1) - { - s = "-" + s; - } + sb.Append(Convert.ToString(q.LongValue, radix)); + for (int i = S.Count - 1; i >= 0; --i) + { + AppendZeroExtendedString(sb, (string)S[i], exponent); + } + break; + } + } + + return sb.ToString(); + } + + private static void AppendZeroExtendedString(StringBuilder sb, string s, int minLength) + { + for (int len = s.Length; len < minLength; ++len) + { + sb.Append('0'); + } + sb.Append(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); + } + + public static BigInteger ValueOf( + long value) + { + if (value >= 0 && value < SMALL_CONSTANTS.Length) + { + return SMALL_CONSTANTS[value]; + } + + return CreateValueOf(value); + } + + public int GetLowestSetBit() + { + if (this.sign == 0) + return -1; + + return GetLowestSetBitMaskFirst(-1); + } + + private int GetLowestSetBitMaskFirst(int firstWordMask) + { + int w = magnitude.Length, offset = 0; + + uint word = (uint)(magnitude[--w] & firstWordMask); + Debug.Assert(magnitude[0] != 0); + + while (word == 0) + { + word = (uint)magnitude[--w]; + offset += 32; + } + + while ((word & 0xFF) == 0) + { + word >>= 8; + offset += 8; + } + + while ((word & 1) == 0) + { + word >>= 1; + ++offset; + } + + return offset; + } + + 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; - return s; - } + if (resultNeg) + { + resultMag[i] = ~resultMag[i]; + } + } - private static BigInteger createUValueOf( - ulong value) - { - int msw = (int)(value >> 32); - int lsw = (int)value; + BigInteger result = new BigInteger(1, resultMag, true); - if (msw != 0) - return new BigInteger(1, new int[] { msw, lsw }, false); + // TODO Optimise this case + if (resultNeg) + { + result = result.Not(); + } - 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 result; + } - return Zero; - } + public BigInteger Xor( + BigInteger value) + { + if (this.sign == 0) + return value; - private static BigInteger createValueOf( - long value) - { - if (value < 0) - { - if (value == long.MinValue) - return createValueOf(~value).Not(); + if (value.sign == 0) + return this; - return createValueOf(-value).Negate(); - } + int[] aMag = this.sign > 0 + ? this.magnitude + : Add(One).magnitude; - return createUValueOf((ulong)value); + int[] bMag = value.sign > 0 + ? value.magnitude + : value.Add(One).magnitude; -// // 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); - } - } + // 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
index be4fd1b14..3c911b173 100644 --- a/crypto/src/math/ec/ECAlgorithms.cs +++ b/crypto/src/math/ec/ECAlgorithms.cs
@@ -1,93 +1,459 @@ using System; -using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC.Endo; +using Org.BouncyCastle.Math.EC.Multiplier; +using Org.BouncyCastle.Math.Field; 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; - } - } + public class ECAlgorithms + { + public static bool IsF2mCurve(ECCurve c) + { + IFiniteField field = c.Field; + return field.Dimension > 1 && field.Characteristic.Equals(BigInteger.Two) + && field is IPolynomialExtensionField; + } + + public static bool IsFpCurve(ECCurve c) + { + return c.Field.Dimension == 1; + } + + public static ECPoint SumOfMultiplies(ECPoint[] ps, BigInteger[] ks) + { + if (ps == null || ks == null || ps.Length != ks.Length || ps.Length < 1) + throw new ArgumentException("point and scalar arrays should be non-null, and of equal, non-zero, length"); + + int count = ps.Length; + switch (count) + { + case 1: + return ps[0].Multiply(ks[0]); + case 2: + return SumOfTwoMultiplies(ps[0], ks[0], ps[1], ks[1]); + default: + break; + } + + ECPoint p = ps[0]; + ECCurve c = p.Curve; + + ECPoint[] imported = new ECPoint[count]; + imported[0] = p; + for (int i = 1; i < count; ++i) + { + imported[i] = ImportPoint(c, ps[i]); + } + + GlvEndomorphism glvEndomorphism = c.GetEndomorphism() as GlvEndomorphism; + if (glvEndomorphism != null) + { + return ValidatePoint(ImplSumOfMultipliesGlv(imported, ks, glvEndomorphism)); + } + + return ValidatePoint(ImplSumOfMultiplies(imported, ks)); + } + + public static ECPoint SumOfTwoMultiplies(ECPoint P, BigInteger a, ECPoint Q, BigInteger b) + { + ECCurve cp = P.Curve; + Q = ImportPoint(cp, Q); + + // Point multiplication for Koblitz curves (using WTNAF) beats Shamir's trick + if (cp is F2mCurve) + { + F2mCurve f2mCurve = (F2mCurve) cp; + if (f2mCurve.IsKoblitz) + { + return ValidatePoint(P.Multiply(a).Add(Q.Multiply(b))); + } + } + + GlvEndomorphism glvEndomorphism = cp.GetEndomorphism() as GlvEndomorphism; + if (glvEndomorphism != null) + { + return ValidatePoint( + ImplSumOfMultipliesGlv(new ECPoint[] { P, Q }, new BigInteger[] { a, b }, glvEndomorphism)); + } + + return ValidatePoint(ImplShamirsTrickWNaf(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) + { + ECCurve cp = P.Curve; + Q = ImportPoint(cp, Q); + + return ValidatePoint(ImplShamirsTrickJsf(P, k, Q, l)); + } + + public static ECPoint ImportPoint(ECCurve c, ECPoint p) + { + ECCurve cp = p.Curve; + if (!c.Equals(cp)) + throw new ArgumentException("Point must be on the same curve"); + + return c.ImportPoint(p); + } + + public static void MontgomeryTrick(ECFieldElement[] zs, int off, int len) + { + /* + * Uses the "Montgomery Trick" to invert many field elements, with only a single actual + * field inversion. See e.g. the paper: + * "Fast Multi-scalar Multiplication Methods on Elliptic Curves with Precomputation Strategy Using Montgomery Trick" + * by Katsuyuki Okeya, Kouichi Sakurai. + */ + + ECFieldElement[] c = new ECFieldElement[len]; + c[0] = zs[off]; + + int i = 0; + while (++i < len) + { + c[i] = c[i - 1].Multiply(zs[off + i]); + } + + ECFieldElement u = c[--i].Invert(); + + while (i > 0) + { + int j = off + i--; + ECFieldElement tmp = zs[j]; + zs[j] = c[i].Multiply(u); + u = u.Multiply(tmp); + } + + zs[off] = u; + } + + /** + * Simple shift-and-add multiplication. Serves as reference implementation + * to verify (possibly faster) implementations, and for very small scalars. + * + * @param p + * The point to multiply. + * @param k + * The multiplier. + * @return The result of the point multiplication <code>kP</code>. + */ + public static ECPoint ReferenceMultiply(ECPoint p, BigInteger k) + { + BigInteger x = k.Abs(); + ECPoint q = p.Curve.Infinity; + int t = x.BitLength; + if (t > 0) + { + if (x.TestBit(0)) + { + q = p; + } + for (int i = 1; i < t; i++) + { + p = p.Twice(); + if (x.TestBit(i)) + { + q = q.Add(p); + } + } + } + return k.SignValue < 0 ? q.Negate() : q; + } + + public static ECPoint ValidatePoint(ECPoint p) + { + if (!p.IsValid()) + throw new ArgumentException("Invalid point", "p"); + + return p; + } + + internal static ECPoint ImplShamirsTrickJsf(ECPoint P, BigInteger k, ECPoint Q, BigInteger l) + { + ECCurve curve = P.Curve; + ECPoint infinity = curve.Infinity; + + // TODO conjugate co-Z addition (ZADDC) can return both of these + ECPoint PaddQ = P.Add(Q); + ECPoint PsubQ = P.Subtract(Q); + + ECPoint[] points = new ECPoint[] { Q, PsubQ, P, PaddQ }; + curve.NormalizeAll(points); + + ECPoint[] table = new ECPoint[] { + points[3].Negate(), points[2].Negate(), points[1].Negate(), + points[0].Negate(), infinity, points[0], + points[1], points[2], points[3] }; + + byte[] jsf = WNafUtilities.GenerateJsf(k, l); + + ECPoint R = infinity; + + int i = jsf.Length; + while (--i >= 0) + { + int jsfi = jsf[i]; + + // NOTE: The shifting ensures the sign is extended correctly + int kDigit = ((jsfi << 24) >> 28), lDigit = ((jsfi << 28) >> 28); + + int index = 4 + (kDigit * 3) + lDigit; + R = R.TwicePlus(table[index]); + } + + return R; + } + + internal static ECPoint ImplShamirsTrickWNaf(ECPoint P, BigInteger k, + ECPoint Q, BigInteger l) + { + bool negK = k.SignValue < 0, negL = l.SignValue < 0; + + k = k.Abs(); + l = l.Abs(); + + int widthP = System.Math.Max(2, System.Math.Min(16, WNafUtilities.GetWindowSize(k.BitLength))); + int widthQ = System.Math.Max(2, System.Math.Min(16, WNafUtilities.GetWindowSize(l.BitLength))); + + WNafPreCompInfo infoP = WNafUtilities.Precompute(P, widthP, true); + WNafPreCompInfo infoQ = WNafUtilities.Precompute(Q, widthQ, true); + + ECPoint[] preCompP = negK ? infoP.PreCompNeg : infoP.PreComp; + ECPoint[] preCompQ = negL ? infoQ.PreCompNeg : infoQ.PreComp; + ECPoint[] preCompNegP = negK ? infoP.PreComp : infoP.PreCompNeg; + ECPoint[] preCompNegQ = negL ? infoQ.PreComp : infoQ.PreCompNeg; + + byte[] wnafP = WNafUtilities.GenerateWindowNaf(widthP, k); + byte[] wnafQ = WNafUtilities.GenerateWindowNaf(widthQ, l); + + return ImplShamirsTrickWNaf(preCompP, preCompNegP, wnafP, preCompQ, preCompNegQ, wnafQ); + } + + internal static ECPoint ImplShamirsTrickWNaf(ECPoint P, BigInteger k, ECPointMap pointMapQ, BigInteger l) + { + bool negK = k.SignValue < 0, negL = l.SignValue < 0; + + k = k.Abs(); + l = l.Abs(); + + int width = System.Math.Max(2, System.Math.Min(16, WNafUtilities.GetWindowSize(System.Math.Max(k.BitLength, l.BitLength)))); + + ECPoint Q = WNafUtilities.MapPointWithPrecomp(P, width, true, pointMapQ); + WNafPreCompInfo infoP = WNafUtilities.GetWNafPreCompInfo(P); + WNafPreCompInfo infoQ = WNafUtilities.GetWNafPreCompInfo(Q); + + ECPoint[] preCompP = negK ? infoP.PreCompNeg : infoP.PreComp; + ECPoint[] preCompQ = negL ? infoQ.PreCompNeg : infoQ.PreComp; + ECPoint[] preCompNegP = negK ? infoP.PreComp : infoP.PreCompNeg; + ECPoint[] preCompNegQ = negL ? infoQ.PreComp : infoQ.PreCompNeg; + + byte[] wnafP = WNafUtilities.GenerateWindowNaf(width, k); + byte[] wnafQ = WNafUtilities.GenerateWindowNaf(width, l); + + return ImplShamirsTrickWNaf(preCompP, preCompNegP, wnafP, preCompQ, preCompNegQ, wnafQ); + } + + private static ECPoint ImplShamirsTrickWNaf(ECPoint[] preCompP, ECPoint[] preCompNegP, byte[] wnafP, + ECPoint[] preCompQ, ECPoint[] preCompNegQ, byte[] wnafQ) + { + int len = System.Math.Max(wnafP.Length, wnafQ.Length); + + ECCurve curve = preCompP[0].Curve; + ECPoint infinity = curve.Infinity; + + ECPoint R = infinity; + int zeroes = 0; + + for (int i = len - 1; i >= 0; --i) + { + int wiP = i < wnafP.Length ? (int)(sbyte)wnafP[i] : 0; + int wiQ = i < wnafQ.Length ? (int)(sbyte)wnafQ[i] : 0; + + if ((wiP | wiQ) == 0) + { + ++zeroes; + continue; + } + + ECPoint r = infinity; + if (wiP != 0) + { + int nP = System.Math.Abs(wiP); + ECPoint[] tableP = wiP < 0 ? preCompNegP : preCompP; + r = r.Add(tableP[nP >> 1]); + } + if (wiQ != 0) + { + int nQ = System.Math.Abs(wiQ); + ECPoint[] tableQ = wiQ < 0 ? preCompNegQ : preCompQ; + r = r.Add(tableQ[nQ >> 1]); + } + + if (zeroes > 0) + { + R = R.TimesPow2(zeroes); + zeroes = 0; + } + + R = R.TwicePlus(r); + } + + if (zeroes > 0) + { + R = R.TimesPow2(zeroes); + } + + return R; + } + + internal static ECPoint ImplSumOfMultiplies(ECPoint[] ps, BigInteger[] ks) + { + int count = ps.Length; + bool[] negs = new bool[count]; + WNafPreCompInfo[] infos = new WNafPreCompInfo[count]; + byte[][] wnafs = new byte[count][]; + + for (int i = 0; i < count; ++i) + { + BigInteger ki = ks[i]; negs[i] = ki.SignValue < 0; ki = ki.Abs(); + + int width = System.Math.Max(2, System.Math.Min(16, WNafUtilities.GetWindowSize(ki.BitLength))); + infos[i] = WNafUtilities.Precompute(ps[i], width, true); + wnafs[i] = WNafUtilities.GenerateWindowNaf(width, ki); + } + + return ImplSumOfMultiplies(negs, infos, wnafs); + } + + internal static ECPoint ImplSumOfMultipliesGlv(ECPoint[] ps, BigInteger[] ks, GlvEndomorphism glvEndomorphism) + { + BigInteger n = ps[0].Curve.Order; + + int len = ps.Length; + + BigInteger[] abs = new BigInteger[len << 1]; + for (int i = 0, j = 0; i < len; ++i) + { + BigInteger[] ab = glvEndomorphism.DecomposeScalar(ks[i].Mod(n)); + abs[j++] = ab[0]; + abs[j++] = ab[1]; + } + + ECPointMap pointMap = glvEndomorphism.PointMap; + if (glvEndomorphism.HasEfficientPointMap) + { + return ECAlgorithms.ImplSumOfMultiplies(ps, pointMap, abs); + } + + ECPoint[] pqs = new ECPoint[len << 1]; + for (int i = 0, j = 0; i < len; ++i) + { + ECPoint p = ps[i], q = pointMap.Map(p); + pqs[j++] = p; + pqs[j++] = q; + } + + return ECAlgorithms.ImplSumOfMultiplies(pqs, abs); + } + + internal static ECPoint ImplSumOfMultiplies(ECPoint[] ps, ECPointMap pointMap, BigInteger[] ks) + { + int halfCount = ps.Length, fullCount = halfCount << 1; + + bool[] negs = new bool[fullCount]; + WNafPreCompInfo[] infos = new WNafPreCompInfo[fullCount]; + byte[][] wnafs = new byte[fullCount][]; + + for (int i = 0; i < halfCount; ++i) + { + int j0 = i << 1, j1 = j0 + 1; + + BigInteger kj0 = ks[j0]; negs[j0] = kj0.SignValue < 0; kj0 = kj0.Abs(); + BigInteger kj1 = ks[j1]; negs[j1] = kj1.SignValue < 0; kj1 = kj1.Abs(); + + int width = System.Math.Max(2, System.Math.Min(16, WNafUtilities.GetWindowSize(System.Math.Max(kj0.BitLength, kj1.BitLength)))); + + ECPoint P = ps[i], Q = WNafUtilities.MapPointWithPrecomp(P, width, true, pointMap); + infos[j0] = WNafUtilities.GetWNafPreCompInfo(P); + infos[j1] = WNafUtilities.GetWNafPreCompInfo(Q); + wnafs[j0] = WNafUtilities.GenerateWindowNaf(width, kj0); + wnafs[j1] = WNafUtilities.GenerateWindowNaf(width, kj1); + } + + return ImplSumOfMultiplies(negs, infos, wnafs); + } + + private static ECPoint ImplSumOfMultiplies(bool[] negs, WNafPreCompInfo[] infos, byte[][] wnafs) + { + int len = 0, count = wnafs.Length; + for (int i = 0; i < count; ++i) + { + len = System.Math.Max(len, wnafs[i].Length); + } + + ECCurve curve = infos[0].PreComp[0].Curve; + ECPoint infinity = curve.Infinity; + + ECPoint R = infinity; + int zeroes = 0; + + for (int i = len - 1; i >= 0; --i) + { + ECPoint r = infinity; + + for (int j = 0; j < count; ++j) + { + byte[] wnaf = wnafs[j]; + int wi = i < wnaf.Length ? (int)(sbyte)wnaf[i] : 0; + if (wi != 0) + { + int n = System.Math.Abs(wi); + WNafPreCompInfo info = infos[j]; + ECPoint[] table = (wi < 0 == negs[j]) ? info.PreComp : info.PreCompNeg; + r = r.Add(table[n >> 1]); + } + } + + if (r == infinity) + { + ++zeroes; + continue; + } + + if (zeroes > 0) + { + R = R.TimesPow2(zeroes); + zeroes = 0; + } + + R = R.TwicePlus(r); + } + + if (zeroes > 0) + { + R = R.TimesPow2(zeroes); + } + + return R; + } + } } diff --git a/crypto/src/math/ec/ECCurve.cs b/crypto/src/math/ec/ECCurve.cs
index 4dd5e74e2..eaa3e0c3d 100644 --- a/crypto/src/math/ec/ECCurve.cs +++ b/crypto/src/math/ec/ECCurve.cs
@@ -2,230 +2,635 @@ using System; using System.Collections; using Org.BouncyCastle.Math.EC.Abc; +using Org.BouncyCastle.Math.EC.Endo; +using Org.BouncyCastle.Math.EC.Multiplier; +using Org.BouncyCastle.Math.Field; +using Org.BouncyCastle.Utilities; 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 + /// <remarks>Base class for an elliptic curve.</remarks> + public abstract class ECCurve { - private readonly BigInteger q; - private readonly FpPoint infinity; + public const int COORD_AFFINE = 0; + public const int COORD_HOMOGENEOUS = 1; + public const int COORD_JACOBIAN = 2; + public const int COORD_JACOBIAN_CHUDNOVSKY = 3; + public const int COORD_JACOBIAN_MODIFIED = 4; + public const int COORD_LAMBDA_AFFINE = 5; + public const int COORD_LAMBDA_PROJECTIVE = 6; + public const int COORD_SKEWED = 7; + + public static int[] GetAllCoordinateSystems() + { + return new int[]{ COORD_AFFINE, COORD_HOMOGENEOUS, COORD_JACOBIAN, COORD_JACOBIAN_CHUDNOVSKY, + COORD_JACOBIAN_MODIFIED, COORD_LAMBDA_AFFINE, COORD_LAMBDA_PROJECTIVE, COORD_SKEWED }; + } - public FpCurve(BigInteger q, BigInteger a, BigInteger b) + public class Config { - this.q = q; - this.a = FromBigInteger(a); - this.b = FromBigInteger(b); - this.infinity = new FpPoint(this, null, null); + protected ECCurve outer; + protected int coord; + protected ECEndomorphism endomorphism; + protected ECMultiplier multiplier; + + internal Config(ECCurve outer, int coord, ECEndomorphism endomorphism, ECMultiplier multiplier) + { + this.outer = outer; + this.coord = coord; + this.endomorphism = endomorphism; + this.multiplier = multiplier; + } + + public Config SetCoordinateSystem(int coord) + { + this.coord = coord; + return this; + } + + public Config SetEndomorphism(ECEndomorphism endomorphism) + { + this.endomorphism = endomorphism; + return this; + } + + public Config SetMultiplier(ECMultiplier multiplier) + { + this.multiplier = multiplier; + return this; + } + + public ECCurve Create() + { + if (!outer.SupportsCoordinateSystem(coord)) + { + throw new InvalidOperationException("unsupported coordinate system"); + } + + ECCurve c = outer.CloneCurve(); + if (c == outer) + { + throw new InvalidOperationException("implementation returned current curve"); + } + + c.m_coord = coord; + c.m_endomorphism = endomorphism; + c.m_multiplier = multiplier; + + return c; + } + } + + protected readonly IFiniteField m_field; + protected ECFieldElement m_a, m_b; + protected BigInteger m_order, m_cofactor; + + protected int m_coord = COORD_AFFINE; + protected ECEndomorphism m_endomorphism = null; + protected ECMultiplier m_multiplier = null; + + protected ECCurve(IFiniteField field) + { + this.m_field = field; } - public BigInteger Q + public abstract int FieldSize { get; } + public abstract ECFieldElement FromBigInteger(BigInteger x); + + public virtual Config Configure() { - get { return q; } + return new Config(this, this.m_coord, this.m_endomorphism, this.m_multiplier); } - public override ECPoint Infinity - { - get { return infinity; } - } + public virtual ECPoint ValidatePoint(BigInteger x, BigInteger y) + { + ECPoint p = CreatePoint(x, y); + if (!p.IsValid()) + { + throw new ArgumentException("Invalid point coordinates"); + } + return p; + } - public override int FieldSize - { - get { return q.BitLength; } - } + [Obsolete("Per-point compression property will be removed")] + public virtual ECPoint ValidatePoint(BigInteger x, BigInteger y, bool withCompression) + { + ECPoint p = CreatePoint(x, y, withCompression); + if (!p.IsValid()) + { + throw new ArgumentException("Invalid point coordinates"); + } + return p; + } - public override ECFieldElement FromBigInteger(BigInteger x) + public virtual ECPoint CreatePoint(BigInteger x, BigInteger y) { - return new FpFieldElement(this.q, x); + return CreatePoint(x, y, false); } - public override ECPoint CreatePoint( - BigInteger X1, - BigInteger Y1, - bool withCompression) - { - // TODO Validation of X1, Y1? - return new FpPoint( - this, - FromBigInteger(X1), - FromBigInteger(Y1), - withCompression); - } + [Obsolete("Per-point compression property will be removed")] + public virtual ECPoint CreatePoint(BigInteger x, BigInteger y, bool withCompression) + { + return CreateRawPoint(FromBigInteger(x), FromBigInteger(y), 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(); + protected abstract ECCurve CloneCurve(); - // - // 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"); + protected internal abstract ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression); - BigInteger betaValue = beta.ToBigInteger(); - int bit0 = betaValue.TestBit(0) ? 1 : 0; + protected internal abstract ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression); - if (bit0 != yTilde) - { - // Use the other root - beta = FromBigInteger(q.Subtract(betaValue)); - } + protected virtual ECMultiplier CreateDefaultMultiplier() + { + GlvEndomorphism glvEndomorphism = m_endomorphism as GlvEndomorphism; + if (glvEndomorphism != null) + { + return new GlvMultiplier(this, glvEndomorphism); + } - return new FpPoint(this, x, beta, true); - } + return new WNafL2RMultiplier(); + } - public override bool Equals( - object obj) + public virtual bool SupportsCoordinateSystem(int coord) { - if (obj == this) - return true; + return coord == COORD_AFFINE; + } - FpCurve other = obj as FpCurve; + public virtual PreCompInfo GetPreCompInfo(ECPoint point, string name) + { + CheckPoint(point); + lock (point) + { + IDictionary table = point.m_preCompTable; + return table == null ? null : (PreCompInfo)table[name]; + } + } + + /** + * Adds <code>PreCompInfo</code> for a point on this curve, under a given name. Used by + * <code>ECMultiplier</code>s to save the precomputation for this <code>ECPoint</code> for use + * by subsequent multiplication. + * + * @param point + * The <code>ECPoint</code> to store precomputations for. + * @param name + * A <code>String</code> used to index precomputations of different types. + * @param preCompInfo + * The values precomputed by the <code>ECMultiplier</code>. + */ + public virtual void SetPreCompInfo(ECPoint point, string name, PreCompInfo preCompInfo) + { + CheckPoint(point); + lock (point) + { + IDictionary table = point.m_preCompTable; + if (null == table) + { + point.m_preCompTable = table = Platform.CreateHashtable(4); + } + table[name] = preCompInfo; + } + } + + public virtual ECPoint ImportPoint(ECPoint p) + { + if (this == p.Curve) + { + return p; + } + if (p.IsInfinity) + { + return Infinity; + } + + // TODO Default behaviour could be improved if the two curves have the same coordinate system by copying any Z coordinates. + p = p.Normalize(); + + return ValidatePoint(p.XCoord.ToBigInteger(), p.YCoord.ToBigInteger(), p.IsCompressed); + } + + /** + * Normalization ensures that any projective coordinate is 1, and therefore that the x, y + * coordinates reflect those of the equivalent point in an affine coordinate system. Where more + * than one point is to be normalized, this method will generally be more efficient than + * normalizing each point separately. + * + * @param points + * An array of points that will be updated in place with their normalized versions, + * where necessary + */ + public virtual void NormalizeAll(ECPoint[] points) + { + CheckPoints(points); + + if (this.CoordinateSystem == ECCurve.COORD_AFFINE) + { + return; + } + + /* + * Figure out which of the points actually need to be normalized + */ + ECFieldElement[] zs = new ECFieldElement[points.Length]; + int[] indices = new int[points.Length]; + int count = 0; + for (int i = 0; i < points.Length; ++i) + { + ECPoint p = points[i]; + if (null != p && !p.IsNormalized()) + { + zs[count] = p.GetZCoord(0); + indices[count++] = i; + } + } + + if (count == 0) + { + return; + } + + ECAlgorithms.MontgomeryTrick(zs, 0, count); + + for (int j = 0; j < count; ++j) + { + int index = indices[j]; + points[index] = points[index].Normalize(zs[j]); + } + } + + public abstract ECPoint Infinity { get; } + + public virtual IFiniteField Field + { + get { return m_field; } + } + + public virtual ECFieldElement A + { + get { return m_a; } + } - if (other == null) + public virtual ECFieldElement B + { + get { return m_b; } + } + + public virtual BigInteger Order + { + get { return m_order; } + } + + public virtual BigInteger Cofactor + { + get { return m_cofactor; } + } + + public virtual int CoordinateSystem + { + get { return m_coord; } + } + + protected virtual void CheckPoint(ECPoint point) + { + if (null == point || (this != point.Curve)) + throw new ArgumentException("must be non-null and on this curve", "point"); + } + + protected virtual void CheckPoints(ECPoint[] points) + { + if (points == null) + throw new ArgumentNullException("points"); + + for (int i = 0; i < points.Length; ++i) + { + ECPoint point = points[i]; + if (null != point && this != point.Curve) + throw new ArgumentException("entries must be null or on this curve", "points"); + } + } + + public virtual bool Equals(ECCurve other) + { + if (this == other) + return true; + if (null == other) return false; + return Field.Equals(other.Field) + && A.ToBigInteger().Equals(other.A.ToBigInteger()) + && B.ToBigInteger().Equals(other.B.ToBigInteger()); + } + + public override bool Equals(object obj) + { + return Equals(obj as ECCurve); + } + + public override int GetHashCode() + { + return Field.GetHashCode() + ^ Integers.RotateLeft(A.ToBigInteger().GetHashCode(), 8) + ^ Integers.RotateLeft(B.ToBigInteger().GetHashCode(), 16); + } + + protected abstract ECPoint DecompressPoint(int yTilde, BigInteger X1); + + public virtual ECEndomorphism GetEndomorphism() + { + return m_endomorphism; + } + + /** + * Sets the default <code>ECMultiplier</code>, unless already set. + */ + public virtual ECMultiplier GetMultiplier() + { + lock (this) + { + if (this.m_multiplier == null) + { + this.m_multiplier = CreateDefaultMultiplier(); + } + return this.m_multiplier; + } + } + + /** + * 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 virtual ECPoint DecodePoint(byte[] encoded) + { + ECPoint p = null; + int expectedLength = (FieldSize + 7) / 8; + + byte type = encoded[0]; + switch (type) + { + 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 = type & 1; + BigInteger X = new BigInteger(1, encoded, 1, expectedLength); + + p = DecompressPoint(yTilde, X); + if (!p.SatisfiesCofactor()) + throw new ArgumentException("Invalid point"); + + break; + } + + case 0x04: // uncompressed + { + if (encoded.Length != (2 * expectedLength + 1)) + throw new ArgumentException("Incorrect length for uncompressed encoding", "encoded"); + + BigInteger X = new BigInteger(1, encoded, 1, expectedLength); + BigInteger Y = new BigInteger(1, encoded, 1 + expectedLength, expectedLength); + + p = ValidatePoint(X, Y); + break; + } + + case 0x06: // hybrid + case 0x07: // hybrid + { + if (encoded.Length != (2 * expectedLength + 1)) + throw new ArgumentException("Incorrect length for hybrid encoding", "encoded"); + + BigInteger X = new BigInteger(1, encoded, 1, expectedLength); + BigInteger Y = new BigInteger(1, encoded, 1 + expectedLength, expectedLength); + + if (Y.TestBit(0) != (type == 0x07)) + throw new ArgumentException("Inconsistent Y coordinate in hybrid encoding", "encoded"); + + p = ValidatePoint(X, Y); + break; + } + + default: + throw new FormatException("Invalid point encoding " + type); + } + + if (type != 0x00 && p.IsInfinity) + throw new ArgumentException("Invalid infinity encoding", "encoded"); - return Equals(other); + return p; } + } - protected bool Equals( - FpCurve other) - { - return base.Equals(other) && q.Equals(other.q); - } + public abstract class AbstractFpCurve + : ECCurve + { + protected AbstractFpCurve(BigInteger q) + : base(FiniteFields.GetPrimeField(q)) + { + } - public override int GetHashCode() + protected override ECPoint DecompressPoint(int yTilde, BigInteger X1) { - return base.GetHashCode() ^ q.GetHashCode(); + ECFieldElement x = FromBigInteger(X1); + ECFieldElement rhs = x.Square().Add(A).Multiply(x).Add(B); + ECFieldElement y = rhs.Sqrt(); + + /* + * If y is not a square, then we haven't got a point on the curve + */ + if (y == null) + throw new ArgumentException("Invalid point compression"); + + if (y.TestBitZero() != (yTilde == 1)) + { + // Use the other root + y = y.Negate(); + } + + return CreateRawPoint(x, y, true); } } - /** + /** + * Elliptic curve over Fp + */ + public class FpCurve + : AbstractFpCurve + { + private const int FP_DEFAULT_COORDS = COORD_JACOBIAN_MODIFIED; + + protected readonly BigInteger m_q, m_r; + protected readonly FpPoint m_infinity; + + public FpCurve(BigInteger q, BigInteger a, BigInteger b) + : this(q, a, b, null, null) + { + } + + public FpCurve(BigInteger q, BigInteger a, BigInteger b, BigInteger order, BigInteger cofactor) + : base(q) + { + this.m_q = q; + this.m_r = FpFieldElement.CalculateResidue(q); + this.m_infinity = new FpPoint(this, null, null); + + this.m_a = FromBigInteger(a); + this.m_b = FromBigInteger(b); + this.m_order = order; + this.m_cofactor = cofactor; + this.m_coord = FP_DEFAULT_COORDS; + } + + protected FpCurve(BigInteger q, BigInteger r, ECFieldElement a, ECFieldElement b) + : this(q, r, a, b, null, null) + { + } + + protected FpCurve(BigInteger q, BigInteger r, ECFieldElement a, ECFieldElement b, BigInteger order, BigInteger cofactor) + : base(q) + { + this.m_q = q; + this.m_r = r; + this.m_infinity = new FpPoint(this, null, null); + + this.m_a = a; + this.m_b = b; + this.m_order = order; + this.m_cofactor = cofactor; + this.m_coord = FP_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new FpCurve(m_q, m_r, m_a, m_b, m_order, m_cofactor); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_AFFINE: + case COORD_HOMOGENEOUS: + case COORD_JACOBIAN: + case COORD_JACOBIAN_MODIFIED: + return true; + default: + return false; + } + } + + public virtual BigInteger Q + { + get { return m_q; } + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return m_q.BitLength; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new FpFieldElement(this.m_q, this.m_r, x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new FpPoint(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new FpPoint(this, x, y, zs, withCompression); + } + + public override ECPoint ImportPoint(ECPoint p) + { + if (this != p.Curve && this.CoordinateSystem == COORD_JACOBIAN && !p.IsInfinity) + { + switch (p.Curve.CoordinateSystem) + { + case COORD_JACOBIAN: + case COORD_JACOBIAN_CHUDNOVSKY: + case COORD_JACOBIAN_MODIFIED: + return new FpPoint(this, + FromBigInteger(p.RawXCoord.ToBigInteger()), + FromBigInteger(p.RawYCoord.ToBigInteger()), + new ECFieldElement[] { FromBigInteger(p.GetZCoord(0).ToBigInteger()) }, + p.IsCompressed); + default: + break; + } + } + + return base.ImportPoint(p); + } + } + + public abstract class AbstractF2mCurve + : ECCurve + { + private static IFiniteField BuildField(int m, int k1, int k2, int k3) + { + 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"); + } + + return FiniteFields.GetBinaryExtensionField(new int[]{ 0, k1, m }); + } + + if (k2 <= k1) + { + throw new ArgumentException("k2 must be > k1"); + } + + if (k3 <= k2) + { + throw new ArgumentException("k3 must be > k2"); + } + + return FiniteFields.GetBinaryExtensionField(new int[]{ 0, k1, k2, k3, m }); + } + + protected AbstractF2mCurve(int m, int k1, int k2, int k3) + : base(BuildField(m, k1, k2, k3)) + { + } + } + + /** * 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 + public class F2mCurve + : AbstractF2mCurve { + private const int F2M_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + /** * The exponent <code>m</code> of <code>F<sub>2<sup>m</sup></sub></code>. */ @@ -257,161 +662,152 @@ namespace Org.BouncyCastle.Math.EC */ 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) + /** + * The point at infinity on this curve. + */ + protected readonly F2mPoint m_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 order The order of the main subgroup of the elliptic curve. + * @param cofactor 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 order, + BigInteger cofactor) + : this(m, k, 0, 0, a, b, order, cofactor) + { + } + + /** + * 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 order The order of the main subgroup of the elliptic curve. + * @param cofactor 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 order, + BigInteger cofactor) + : base(m, k1, k2, k3) + { + this.m = m; + this.k1 = k1; + this.k2 = k2; + this.k3 = k3; + this.m_order = order; + this.m_cofactor = cofactor; + this.m_infinity = new F2mPoint(this, null, null); + + if (k1 == 0) throw new ArgumentException("k1 must be > 0"); - if (k2 == 0) + if (k2 == 0) { if (k3 != 0) throw new ArgumentException("k3 must be 0 if k2 == 0"); @@ -421,135 +817,212 @@ namespace Org.BouncyCastle.Math.EC if (k2 <= k1) throw new ArgumentException("k2 must be > k1"); - if (k3 <= k2) + if (k3 <= k2) throw new ArgumentException("k3 must be > k2"); } - this.a = FromBigInteger(a); - this.b = FromBigInteger(b); + this.m_a = FromBigInteger(a); + this.m_b = FromBigInteger(b); + this.m_coord = F2M_DEFAULT_COORDS; + } + + protected F2mCurve(int m, int k1, int k2, int k3, ECFieldElement a, ECFieldElement b, BigInteger order, BigInteger cofactor) + : base(m, k1, k2, k3) + { + this.m = m; + this.k1 = k1; + this.k2 = k2; + this.k3 = k3; + this.m_order = order; + this.m_cofactor = cofactor; + + this.m_infinity = new F2mPoint(this, null, null); + this.m_a = a; + this.m_b = b; + this.m_coord = F2M_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new F2mCurve(m, k1, k2, k3, m_a, m_b, m_order, m_cofactor); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_AFFINE: + case COORD_HOMOGENEOUS: + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } } - public override ECPoint Infinity - { - get { return infinity; } - } + protected override ECMultiplier CreateDefaultMultiplier() + { + if (IsKoblitz) + { + return new WTauNafMultiplier(); + } - public override int FieldSize - { - get { return m; } - } + return base.CreateDefaultMultiplier(); + } + + public override int FieldSize + { + get { return m; } + } - public override ECFieldElement FromBigInteger(BigInteger x) + 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); - } - - /** + [Obsolete("Per-point compression property will be removed")] + public override ECPoint CreatePoint(BigInteger x, BigInteger y, bool withCompression) + { + ECFieldElement X = FromBigInteger(x), Y = FromBigInteger(y); + + switch (this.CoordinateSystem) + { + case COORD_LAMBDA_AFFINE: + case COORD_LAMBDA_PROJECTIVE: + { + if (X.IsZero) + { + if (!Y.Square().Equals(B)) + throw new ArgumentException(); + } + else + { + // Y becomes Lambda (X + Y/X) here + Y = Y.Divide(X).Add(X); + } + break; + } + default: + { + break; + } + } + + return CreateRawPoint(X, Y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new F2mPoint(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new F2mPoint(this, x, y, zs, withCompression); + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + /** + * Returns true if this is a Koblitz curve (ABC curve). + * @return true if this is a Koblitz curve (ABC curve), false otherwise + */ + public virtual bool IsKoblitz + { + get + { + return m_order != null && m_cofactor != null && m_b.IsOne && (m_a.IsZero || m_a.IsOne); + } + } + + /** + * 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 virtual 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 virtual BigInteger[] GetSi() + { + if (si == null) + { + lock (this) + { + if (si == null) + { + si = Tnaf.GetSi(this); + } + } + } + return si; + } + + protected override ECPoint DecompressPoint(int yTilde, BigInteger X1) + { + ECFieldElement xp = FromBigInteger(X1), yp = null; + if (xp.IsZero) + { + yp = m_b.Sqrt(); + } + else + { + ECFieldElement beta = xp.Square().Invert().Multiply(B).Add(A).Add(xp); + ECFieldElement z = SolveQuadradicEquation(beta); + + if (z != null) + { + if (z.TestBitZero() != (yTilde == 1)) + { + z = z.AddOne(); + } + + switch (this.CoordinateSystem) + { + case COORD_LAMBDA_AFFINE: + case COORD_LAMBDA_PROJECTIVE: + { + yp = z.Add(xp); + break; + } + default: + { + yp = z.Multiply(xp); + break; + } + } + } + } + + if (yp == null) + throw new ArgumentException("Invalid point compression"); + + return CreateRawPoint(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>. * @@ -558,72 +1031,47 @@ namespace Org.BouncyCastle.Math.EC * @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) + private ECFieldElement SolveQuadradicEquation(ECFieldElement beta) { - if (beta.ToBigInteger().SignValue == 0) + if (beta.IsZero) { - return FromBigInteger(BigInteger.Zero); + return beta; } - ECFieldElement z = null; - ECFieldElement gamma = FromBigInteger(BigInteger.Zero); + ECFieldElement zeroElement = FromBigInteger(BigInteger.Zero); - while (gamma.ToBigInteger().SignValue == 0) - { - ECFieldElement t = FromBigInteger(new BigInteger(m, new Random())); - z = FromBigInteger(BigInteger.Zero); + ECFieldElement z = null; + ECFieldElement gamma = null; - ECFieldElement w = beta; + Random rand = new Random(); + do + { + ECFieldElement t = FromBigInteger(new BigInteger(m, rand)); + z = zeroElement; + ECFieldElement w = beta; for (int i = 1; i <= m - 1; i++) { - ECFieldElement w2 = w.Square(); + ECFieldElement w2 = w.Square(); z = z.Square().Add(w2.Multiply(t)); w = w2.Add(beta); } - if (w.ToBigInteger().SignValue != 0) + if (!w.IsZero) { 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); - } + while (gamma.IsZero); - 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; + return z; } - public int M + public int M { - get { return m; } + get { return m; } } - /** + /** * Return true if curve uses a Trinomial basis. * * @return true if curve Trinomial, false otherwise. @@ -633,29 +1081,31 @@ namespace Org.BouncyCastle.Math.EC return k2 == 0 && k3 == 0; } - public int K1 + public int K1 { - get { return k1; } + get { return k1; } } - public int K2 + public int K2 { - get { return k2; } + get { return k2; } } - public int K3 + public int K3 { - get { return k3; } + get { return k3; } } - public BigInteger N - { - get { return n; } - } + [Obsolete("Use 'Order' property instead")] + public BigInteger N + { + get { return m_order; } + } - public BigInteger H - { - get { return h; } - } - } + [Obsolete("Use 'Cofactor' property instead")] + public BigInteger H + { + get { return m_cofactor; } + } + } } diff --git a/crypto/src/math/ec/ECFieldElement.cs b/crypto/src/math/ec/ECFieldElement.cs
index 5235c6c0e..e589fc737 100644 --- a/crypto/src/math/ec/ECFieldElement.cs +++ b/crypto/src/math/ec/ECFieldElement.cs
@@ -5,1249 +5,913 @@ 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(); - } - } + 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 AddOne(); + 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 virtual int BitLength + { + get { return ToBigInteger().BitLength; } + } + + public virtual bool IsOne + { + get { return BitLength == 1; } + } + + public virtual bool IsZero + { + get { return 0 == ToBigInteger().SignValue; } + } + + public virtual ECFieldElement MultiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + return Multiply(b).Subtract(x.Multiply(y)); + } + + public virtual ECFieldElement MultiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + return Multiply(b).Add(x.Multiply(y)); + } + + public virtual ECFieldElement SquareMinusProduct(ECFieldElement x, ECFieldElement y) + { + return Square().Subtract(x.Multiply(y)); + } + + public virtual ECFieldElement SquarePlusProduct(ECFieldElement x, ECFieldElement y) + { + return Square().Add(x.Multiply(y)); + } + + public virtual bool TestBitZero() + { + return ToBigInteger().TestBit(0); + } + + public override bool Equals(object obj) + { + return Equals(obj as ECFieldElement); + } + + public virtual bool Equals(ECFieldElement other) + { + if (this == other) + return true; + if (null == other) + return false; + return ToBigInteger().Equals(other.ToBigInteger()); + } + + public override int GetHashCode() + { + return ToBigInteger().GetHashCode(); + } + + public override string ToString() + { + return this.ToBigInteger().ToString(16); + } + + public virtual byte[] GetEncoded() + { + return BigIntegers.AsUnsignedByteArray((FieldSize + 7) / 8, ToBigInteger()); + } + } + + public class FpFieldElement + : ECFieldElement + { + private readonly BigInteger q, r, x; + + internal static BigInteger CalculateResidue(BigInteger p) + { + int bitLength = p.BitLength; + if (bitLength >= 96) + { + BigInteger firstWord = p.ShiftRight(bitLength - 64); + if (firstWord.LongValue == -1L) + { + return BigInteger.One.ShiftLeft(bitLength).Subtract(p); + } + if ((bitLength & 7) == 0) + { + return BigInteger.One.ShiftLeft(bitLength << 1).Divide(p).Negate(); + } + } + return null; + } + + [Obsolete("Use ECCurve.FromBigInteger to construct field elements")] + public FpFieldElement(BigInteger q, BigInteger x) + : this(q, CalculateResidue(q), x) + { + } + + internal FpFieldElement(BigInteger q, BigInteger r, BigInteger x) + { + if (x == null || x.SignValue < 0 || x.CompareTo(q) >= 0) + throw new ArgumentException("value invalid in Fp field element", "x"); + + this.q = q; + this.r = r; + 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, r, ModAdd(x, b.ToBigInteger())); + } + + public override ECFieldElement AddOne() + { + BigInteger x2 = x.Add(BigInteger.One); + if (x2.CompareTo(q) == 0) + { + x2 = BigInteger.Zero; + } + return new FpFieldElement(q, r, x2); + } + + public override ECFieldElement Subtract( + ECFieldElement b) + { + return new FpFieldElement(q, r, ModSubtract(x, b.ToBigInteger())); + } + + public override ECFieldElement Multiply( + ECFieldElement b) + { + return new FpFieldElement(q, r, ModMult(x, b.ToBigInteger())); + } + + public override ECFieldElement MultiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + BigInteger ax = this.x, bx = b.ToBigInteger(), xx = x.ToBigInteger(), yx = y.ToBigInteger(); + BigInteger ab = ax.Multiply(bx); + BigInteger xy = xx.Multiply(yx); + return new FpFieldElement(q, r, ModReduce(ab.Subtract(xy))); + } + + public override ECFieldElement MultiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + BigInteger ax = this.x, bx = b.ToBigInteger(), xx = x.ToBigInteger(), yx = y.ToBigInteger(); + BigInteger ab = ax.Multiply(bx); + BigInteger xy = xx.Multiply(yx); + BigInteger sum = ab.Add(xy); + if (r != null && r.SignValue < 0 && sum.BitLength > (q.BitLength << 1)) + { + sum = sum.Subtract(q.ShiftLeft(q.BitLength)); + } + return new FpFieldElement(q, r, ModReduce(sum)); + } + + public override ECFieldElement Divide( + ECFieldElement b) + { + return new FpFieldElement(q, r, ModMult(x, ModInverse(b.ToBigInteger()))); + } + + public override ECFieldElement Negate() + { + return x.SignValue == 0 ? this : new FpFieldElement(q, r, q.Subtract(x)); + } + + public override ECFieldElement Square() + { + return new FpFieldElement(q, r, ModMult(x, x)); + } + + public override ECFieldElement SquareMinusProduct(ECFieldElement x, ECFieldElement y) + { + BigInteger ax = this.x, xx = x.ToBigInteger(), yx = y.ToBigInteger(); + BigInteger aa = ax.Multiply(ax); + BigInteger xy = xx.Multiply(yx); + return new FpFieldElement(q, r, ModReduce(aa.Subtract(xy))); + } + + public override ECFieldElement SquarePlusProduct(ECFieldElement x, ECFieldElement y) + { + BigInteger ax = this.x, xx = x.ToBigInteger(), yx = y.ToBigInteger(); + BigInteger aa = ax.Multiply(ax); + BigInteger xy = xx.Multiply(yx); + BigInteger sum = aa.Add(xy); + if (r != null && r.SignValue < 0 && sum.BitLength > (q.BitLength << 1)) + { + sum = sum.Subtract(q.ShiftLeft(q.BitLength)); + } + return new FpFieldElement(q, r, ModReduce(sum)); + } + + public override ECFieldElement Invert() + { + // TODO Modular inversion can be faster for a (Generalized) Mersenne Prime. + return new FpFieldElement(q, r, ModInverse(x)); + } + + /** + * 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 (IsZero || IsOne) + return this; + + if (!q.TestBit(0)) + throw Platform.CreateNotImplementedException("even value of q"); + + if (q.TestBit(1)) // q == 4m + 3 + { + BigInteger e = q.ShiftRight(2).Add(BigInteger.One); + return CheckSqrt(new FpFieldElement(q, r, x.ModPow(e, q))); + } + + if (q.TestBit(2)) // q == 8m + 5 + { + BigInteger t1 = x.ModPow(q.ShiftRight(3), q); + BigInteger t2 = ModMult(t1, x); + BigInteger t3 = ModMult(t2, t1); + + if (t3.Equals(BigInteger.One)) + { + return CheckSqrt(new FpFieldElement(q, r, t2)); + } + + // TODO This is constant and could be precomputed + BigInteger t4 = BigInteger.Two.ModPow(q.ShiftRight(2), q); + + BigInteger y = ModMult(t2, t4); + + return CheckSqrt(new FpFieldElement(q, r, y)); + } + + // q == 8m + 1 + + BigInteger legendreExponent = q.ShiftRight(1); + if (!(x.ModPow(legendreExponent, q).Equals(BigInteger.One))) + return null; + + BigInteger X = this.x; + BigInteger fourX = ModDouble(ModDouble(X)); ; + + BigInteger k = legendreExponent.Add(BigInteger.One), qMinusOne = q.Subtract(BigInteger.One); + + BigInteger U, V; + Random rand = new Random(); + do + { + BigInteger P; + do + { + P = new BigInteger(q.BitLength, rand); + } + while (P.CompareTo(q) >= 0 + || !ModReduce(P.Multiply(P).Subtract(fourX)).ModPow(legendreExponent, q).Equals(qMinusOne)); + + BigInteger[] result = LucasSequence(P, X, k); + U = result[0]; + V = result[1]; + + if (ModMult(V, V).Equals(fourX)) + { + return new FpFieldElement(q, r, ModHalfAbs(V)); + } + } + while (U.Equals(BigInteger.One) || U.Equals(qMinusOne)); + + return null; + } + + private ECFieldElement CheckSqrt(ECFieldElement z) + { + return z.Square().Equals(this) ? z : null; + } + + private BigInteger[] LucasSequence( + 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 = ModMult(Ql, Qh); + + if (k.TestBit(j)) + { + Qh = ModMult(Ql, Q); + Uh = ModMult(Uh, Vh); + Vl = ModReduce(Vh.Multiply(Vl).Subtract(P.Multiply(Ql))); + Vh = ModReduce(Vh.Multiply(Vh).Subtract(Qh.ShiftLeft(1))); + } + else + { + Qh = Ql; + Uh = ModReduce(Uh.Multiply(Vl).Subtract(Ql)); + Vh = ModReduce(Vh.Multiply(Vl).Subtract(P.Multiply(Ql))); + Vl = ModReduce(Vl.Multiply(Vl).Subtract(Ql.ShiftLeft(1))); + } + } + + Ql = ModMult(Ql, Qh); + Qh = ModMult(Ql, Q); + Uh = ModReduce(Uh.Multiply(Vl).Subtract(Ql)); + Vl = ModReduce(Vh.Multiply(Vl).Subtract(P.Multiply(Ql))); + Ql = ModMult(Ql, Qh); + + for (int j = 1; j <= s; ++j) + { + Uh = ModMult(Uh, Vl); + Vl = ModReduce(Vl.Multiply(Vl).Subtract(Ql.ShiftLeft(1))); + Ql = ModMult(Ql, Ql); + } + + return new BigInteger[] { Uh, Vl }; + } + + protected virtual BigInteger ModAdd(BigInteger x1, BigInteger x2) + { + BigInteger x3 = x1.Add(x2); + if (x3.CompareTo(q) >= 0) + { + x3 = x3.Subtract(q); + } + return x3; + } + + protected virtual BigInteger ModDouble(BigInteger x) + { + BigInteger _2x = x.ShiftLeft(1); + if (_2x.CompareTo(q) >= 0) + { + _2x = _2x.Subtract(q); + } + return _2x; + } + + protected virtual BigInteger ModHalf(BigInteger x) + { + if (x.TestBit(0)) + { + x = q.Add(x); + } + return x.ShiftRight(1); + } + + protected virtual BigInteger ModHalfAbs(BigInteger x) + { + if (x.TestBit(0)) + { + x = q.Subtract(x); + } + return x.ShiftRight(1); + } + + protected virtual BigInteger ModInverse(BigInteger x) + { + int bits = FieldSize; + int len = (bits + 31) >> 5; + uint[] p = Nat.FromBigInteger(bits, q); + uint[] n = Nat.FromBigInteger(bits, x); + uint[] z = Nat.Create(len); + Mod.Invert(p, n, z); + return Nat.ToBigInteger(len, z); + } + + protected virtual BigInteger ModMult(BigInteger x1, BigInteger x2) + { + return ModReduce(x1.Multiply(x2)); + } + + protected virtual BigInteger ModReduce(BigInteger x) + { + if (r == null) + { + x = x.Mod(q); + } + else + { + bool negative = x.SignValue < 0; + if (negative) + { + x = x.Abs(); + } + int qLen = q.BitLength; + if (r.SignValue > 0) + { + BigInteger qMod = BigInteger.One.ShiftLeft(qLen); + bool rIsOne = r.Equals(BigInteger.One); + while (x.BitLength > (qLen + 1)) + { + BigInteger u = x.ShiftRight(qLen); + BigInteger v = x.Remainder(qMod); + if (!rIsOne) + { + u = u.Multiply(r); + } + x = u.Add(v); + } + } + else + { + int d = ((qLen - 1) & 31) + 1; + BigInteger mu = r.Negate(); + BigInteger u = mu.Multiply(x.ShiftRight(qLen - d)); + BigInteger quot = u.ShiftRight(qLen + d); + BigInteger v = quot.Multiply(q); + BigInteger bk1 = BigInteger.One.ShiftLeft(qLen + d); + v = v.Remainder(bk1); + x = x.Remainder(bk1); + x = x.Subtract(v); + if (x.SignValue < 0) + { + x = x.Add(bk1); + } + } + while (x.CompareTo(q) >= 0) + { + x = x.Subtract(q); + } + if (negative && x.SignValue != 0) + { + x = q.Subtract(x); + } + } + return x; + } + + protected virtual BigInteger ModSubtract(BigInteger x1, BigInteger x2) + { + BigInteger x3 = x1.Subtract(x2); + if (x3.SignValue < 0) + { + x3 = x3.Add(q); + } + return x3; + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + FpFieldElement other = obj as FpFieldElement; + + if (other == null) + return false; + + return Equals(other); + } + + public virtual 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; + + private int[] ks; + + /** + * The <code>LongArray</code> holding the bits. + */ + private LongArray x; + + /** + * 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) + { + if ((k2 == 0) && (k3 == 0)) + { + this.representation = Tpb; + this.ks = new int[] { k1 }; + } + 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; + this.ks = new int[] { k1, k2, k3 }; + } + + this.m = m; + this.x = new LongArray(x); + } + + /** + * 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[] ks, LongArray x) + { + this.m = m; + this.representation = (ks.Length == 1) ? Tpb : Ppb; + this.ks = ks; + this.x = x; + } + + public override int BitLength + { + get { return x.Degree(); } + } + + public override bool IsOne + { + get { return x.IsOne(); } + } + + public override bool IsZero + { + get { return x.IsZero(); } + } + + public override bool TestBitZero() + { + return x.TestBitZero(); + } + + 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.representation != bF2m.representation) + { + // Should never occur + throw new ArgumentException("One of the F2m field elements has incorrect representation"); + } + + if ((aF2m.m != bF2m.m) || !Arrays.AreEqual(aF2m.ks, bF2m.ks)) + { + throw new ArgumentException("Field elements are not elements of the same field F2m"); + } + } + + 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); + LongArray iarrClone = this.x.Copy(); + F2mFieldElement bF2m = (F2mFieldElement)b; + iarrClone.AddShiftedByWords(bF2m.x, 0); + return new F2mFieldElement(m, ks, iarrClone); + } + + public override ECFieldElement AddOne() + { + return new F2mFieldElement(m, ks, x.AddOne()); + } + + 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 LongArray + // 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); + return new F2mFieldElement(m, ks, x.ModMultiply(((F2mFieldElement)b).x, m, ks)); + } + + public override ECFieldElement MultiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + return MultiplyPlusProduct(b, x, y); + } + + public override ECFieldElement MultiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + LongArray ax = this.x, bx = ((F2mFieldElement)b).x, xx = ((F2mFieldElement)x).x, yx = ((F2mFieldElement)y).x; + + LongArray ab = ax.Multiply(bx, m, ks); + LongArray xy = xx.Multiply(yx, m, ks); + + if (ab == ax || ab == bx) + { + ab = (LongArray)ab.Copy(); + } + + ab.AddShiftedByWords(xy, 0); + ab.Reduce(m, ks); + + return new F2mFieldElement(m, ks, ab); + } + + 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() + { + return new F2mFieldElement(m, ks, x.ModSquare(m, ks)); + } + + public override ECFieldElement SquareMinusProduct(ECFieldElement x, ECFieldElement y) + { + return SquarePlusProduct(x, y); + } + + public override ECFieldElement SquarePlusProduct(ECFieldElement x, ECFieldElement y) + { + LongArray ax = this.x, xx = ((F2mFieldElement)x).x, yx = ((F2mFieldElement)y).x; + + LongArray aa = ax.Square(m, ks); + LongArray xy = xx.Multiply(yx, m, ks); + + if (aa == ax) + { + aa = (LongArray)aa.Copy(); + } + + aa.AddShiftedByWords(xy, 0); + aa.Reduce(m, ks); + + return new F2mFieldElement(m, ks, aa); + } + + public override ECFieldElement Invert() + { + return new F2mFieldElement(this.m, this.ks, this.x.ModInverse(m, ks)); + } + + public override ECFieldElement Sqrt() + { + LongArray x1 = this.x; + if (x1.IsOne() || x1.IsZero()) + { + return this; + } + + LongArray x2 = x1.ModSquareN(m - 1, m, ks); + return new F2mFieldElement(m, ks, x2); + } + + /** + * @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.ks[0]; } + } + + /** + * @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.ks.Length >= 2 ? this.ks[1] : 0; } + } + + /** + * @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.ks.Length >= 3 ? this.ks[2] : 0; } + } + + public override bool Equals( + object obj) + { + if (obj == this) + return true; + + F2mFieldElement other = obj as F2mFieldElement; + + if (other == null) + return false; + + return Equals(other); + } + + public virtual bool Equals( + F2mFieldElement other) + { + return ((this.m == other.m) + && (this.representation == other.representation) + && Arrays.AreEqual(this.ks, other.ks) + && (this.x.Equals(other.x))); + } + + public override int GetHashCode() + { + return x.GetHashCode() ^ m ^ Arrays.GetHashCode(ks); + } + } } diff --git a/crypto/src/math/ec/ECPoint.cs b/crypto/src/math/ec/ECPoint.cs
index f95f09974..3e206e65f 100644 --- a/crypto/src/math/ec/ECPoint.cs +++ b/crypto/src/math/ec/ECPoint.cs
@@ -1,567 +1,2089 @@ using System; using System.Collections; using System.Diagnostics; - -using Org.BouncyCastle.Asn1.X9; +using System.Text; 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"); + /** + * base class for points on elliptic curves. + */ + public abstract class ECPoint + { + protected static ECFieldElement[] EMPTY_ZS = new ECFieldElement[0]; + + protected static ECFieldElement[] GetInitialZCoords(ECCurve curve) + { + // Cope with null curve, most commonly used by implicitlyCa + int coord = null == curve ? ECCurve.COORD_AFFINE : curve.CoordinateSystem; + + switch (coord) + { + case ECCurve.COORD_AFFINE: + case ECCurve.COORD_LAMBDA_AFFINE: + return EMPTY_ZS; + default: + break; + } + + ECFieldElement one = curve.FromBigInteger(BigInteger.One); + + switch (coord) + { + case ECCurve.COORD_HOMOGENEOUS: + case ECCurve.COORD_JACOBIAN: + case ECCurve.COORD_LAMBDA_PROJECTIVE: + return new ECFieldElement[] { one }; + case ECCurve.COORD_JACOBIAN_CHUDNOVSKY: + return new ECFieldElement[] { one, one, one }; + case ECCurve.COORD_JACOBIAN_MODIFIED: + return new ECFieldElement[] { one, curve.A }; + default: + throw new ArgumentException("unknown coordinate system"); + } + } + + protected internal readonly ECCurve m_curve; + protected internal readonly ECFieldElement m_x, m_y; + protected internal readonly ECFieldElement[] m_zs; + protected internal readonly bool m_withCompression; + + // Dictionary is (string -> PreCompInfo) + protected internal IDictionary m_preCompTable = null; + + protected ECPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : this(curve, x, y, GetInitialZCoords(curve), withCompression) + { + } + + internal ECPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + this.m_curve = curve; + this.m_x = x; + this.m_y = y; + this.m_zs = zs; + this.m_withCompression = withCompression; + } + + protected internal bool SatisfiesCofactor() + { + BigInteger h = Curve.Cofactor; + return h == null || h.Equals(BigInteger.One) || !ECAlgorithms.ReferenceMultiply(this, h).IsInfinity; + } + + protected abstract bool SatisfiesCurveEquation(); + + public ECPoint GetDetachedPoint() + { + return Normalize().Detach(); + } + + public virtual ECCurve Curve + { + get { return m_curve; } + } + + protected abstract ECPoint Detach(); + + protected virtual int CurveCoordinateSystem + { + get + { + // Cope with null curve, most commonly used by implicitlyCa + return null == m_curve ? ECCurve.COORD_AFFINE : m_curve.CoordinateSystem; + } + } + + /** + * Normalizes this point, and then returns the affine x-coordinate. + * + * Note: normalization can be expensive, this method is deprecated in favour + * of caller-controlled normalization. + */ + [Obsolete("Use AffineXCoord, or Normalize() and XCoord, instead")] + public virtual ECFieldElement X + { + get { return Normalize().XCoord; } + } + + /** + * Normalizes this point, and then returns the affine y-coordinate. + * + * Note: normalization can be expensive, this method is deprecated in favour + * of caller-controlled normalization. + */ + [Obsolete("Use AffineYCoord, or Normalize() and YCoord, instead")] + public virtual ECFieldElement Y + { + get { return Normalize().YCoord; } + } + + /** + * Returns the affine x-coordinate after checking that this point is normalized. + * + * @return The affine x-coordinate of this point + * @throws IllegalStateException if the point is not normalized + */ + public virtual ECFieldElement AffineXCoord + { + get + { + CheckNormalized(); + return XCoord; + } + } + + /** + * Returns the affine y-coordinate after checking that this point is normalized + * + * @return The affine y-coordinate of this point + * @throws IllegalStateException if the point is not normalized + */ + public virtual ECFieldElement AffineYCoord + { + get + { + CheckNormalized(); + return YCoord; + } + } + + /** + * Returns the x-coordinate. + * + * Caution: depending on the curve's coordinate system, this may not be the same value as in an + * affine coordinate system; use Normalize() to get a point where the coordinates have their + * affine values, or use AffineXCoord if you expect the point to already have been normalized. + * + * @return the x-coordinate of this point + */ + public virtual ECFieldElement XCoord + { + get { return m_x; } + } + + /** + * Returns the y-coordinate. + * + * Caution: depending on the curve's coordinate system, this may not be the same value as in an + * affine coordinate system; use Normalize() to get a point where the coordinates have their + * affine values, or use AffineYCoord if you expect the point to already have been normalized. + * + * @return the y-coordinate of this point + */ + public virtual ECFieldElement YCoord + { + get { return m_y; } + } + + public virtual ECFieldElement GetZCoord(int index) + { + return (index < 0 || index >= m_zs.Length) ? null : m_zs[index]; + } + + public virtual ECFieldElement[] GetZCoords() + { + int zsLen = m_zs.Length; + if (zsLen == 0) + { + return m_zs; + } + ECFieldElement[] copy = new ECFieldElement[zsLen]; + Array.Copy(m_zs, 0, copy, 0, zsLen); + return copy; + } + + protected internal ECFieldElement RawXCoord + { + get { return m_x; } + } + + protected internal ECFieldElement RawYCoord + { + get { return m_y; } + } + + protected internal ECFieldElement[] RawZCoords + { + get { return m_zs; } + } + + protected virtual void CheckNormalized() + { + if (!IsNormalized()) + throw new InvalidOperationException("point not in normal form"); + } + + public virtual bool IsNormalized() + { + int coord = this.CurveCoordinateSystem; + + return coord == ECCurve.COORD_AFFINE + || coord == ECCurve.COORD_LAMBDA_AFFINE + || IsInfinity + || RawZCoords[0].IsOne; + } + + /** + * Normalization ensures that any projective coordinate is 1, and therefore that the x, y + * coordinates reflect those of the equivalent point in an affine coordinate system. + * + * @return a new ECPoint instance representing the same point, but with normalized coordinates + */ + public virtual ECPoint Normalize() + { + if (this.IsInfinity) + { + return this; + } + + switch (this.CurveCoordinateSystem) + { + case ECCurve.COORD_AFFINE: + case ECCurve.COORD_LAMBDA_AFFINE: + { + return this; + } + default: + { + ECFieldElement Z1 = RawZCoords[0]; + if (Z1.IsOne) + { + return this; + } + + return Normalize(Z1.Invert()); + } + } + } + + internal virtual ECPoint Normalize(ECFieldElement zInv) + { + switch (this.CurveCoordinateSystem) + { + case ECCurve.COORD_HOMOGENEOUS: + case ECCurve.COORD_LAMBDA_PROJECTIVE: + { + return CreateScaledPoint(zInv, zInv); + } + case ECCurve.COORD_JACOBIAN: + case ECCurve.COORD_JACOBIAN_CHUDNOVSKY: + case ECCurve.COORD_JACOBIAN_MODIFIED: + { + ECFieldElement zInv2 = zInv.Square(), zInv3 = zInv2.Multiply(zInv); + return CreateScaledPoint(zInv2, zInv3); + } + default: + { + throw new InvalidOperationException("not a projective coordinate system"); + } + } + } + + protected virtual ECPoint CreateScaledPoint(ECFieldElement sx, ECFieldElement sy) + { + return Curve.CreateRawPoint(RawXCoord.Multiply(sx), RawYCoord.Multiply(sy), IsCompressed); + } + + public bool IsInfinity + { + get { return m_x == null && m_y == null; } + } + + public bool IsCompressed + { + get { return m_withCompression; } + } + + public bool IsValid() + { + if (IsInfinity) + return true; + + // TODO Sanity-check the field elements + + ECCurve curve = Curve; + if (curve != null) + { + if (!SatisfiesCurveEquation()) + return false; + + if (!SatisfiesCofactor()) + return false; + } + + return true; + } + + public virtual ECPoint ScaleX(ECFieldElement scale) + { + return IsInfinity + ? this + : Curve.CreateRawPoint(RawXCoord.Multiply(scale), RawYCoord, RawZCoords, IsCompressed); + } + + public virtual ECPoint ScaleY(ECFieldElement scale) + { + return IsInfinity + ? this + : Curve.CreateRawPoint(RawXCoord, RawYCoord.Multiply(scale), RawZCoords, IsCompressed); + } + + public override bool Equals(object obj) + { + return Equals(obj as ECPoint); + } + + public virtual bool Equals(ECPoint other) + { + if (this == other) + return true; + if (null == other) + return false; + + ECCurve c1 = this.Curve, c2 = other.Curve; + bool n1 = (null == c1), n2 = (null == c2); + bool i1 = IsInfinity, i2 = other.IsInfinity; + + if (i1 || i2) + { + return (i1 && i2) && (n1 || n2 || c1.Equals(c2)); + } + + ECPoint p1 = this, p2 = other; + if (n1 && n2) + { + // Points with null curve are in affine form, so already normalized + } + else if (n1) + { + p2 = p2.Normalize(); + } + else if (n2) + { + p1 = p1.Normalize(); + } + else if (!c1.Equals(c2)) + { + return false; + } + else + { + // TODO Consider just requiring already normalized, to avoid silent performance degradation + + ECPoint[] points = new ECPoint[] { this, c1.ImportPoint(p2) }; + + // TODO This is a little strong, really only requires coZNormalizeAll to get Zs equal + c1.NormalizeAll(points); + + p1 = points[0]; + p2 = points[1]; + } + + return p1.XCoord.Equals(p2.XCoord) && p1.YCoord.Equals(p2.YCoord); + } + + public override int GetHashCode() + { + ECCurve c = this.Curve; + int hc = (null == c) ? 0 : ~c.GetHashCode(); + + if (!this.IsInfinity) + { + // TODO Consider just requiring already normalized, to avoid silent performance degradation + + ECPoint p = Normalize(); + + hc ^= p.XCoord.GetHashCode() * 17; + hc ^= p.YCoord.GetHashCode() * 257; + } + + return hc; + } + + public override string ToString() + { + if (this.IsInfinity) + { + return "INF"; + } + + StringBuilder sb = new StringBuilder(); + sb.Append('('); + sb.Append(RawXCoord); + sb.Append(','); + sb.Append(RawYCoord); + for (int i = 0; i < m_zs.Length; ++i) + { + sb.Append(','); + sb.Append(m_zs[i]); + } + sb.Append(')'); + return sb.ToString(); + } + + public virtual byte[] GetEncoded() + { + return GetEncoded(m_withCompression); + } + + public abstract byte[] GetEncoded(bool compressed); + + protected internal abstract bool CompressionYTilde { get; } + + public abstract ECPoint Add(ECPoint b); + public abstract ECPoint Subtract(ECPoint b); + public abstract ECPoint Negate(); + + public virtual ECPoint TimesPow2(int e) + { + if (e < 0) + throw new ArgumentException("cannot be negative", "e"); + + ECPoint p = this; + while (--e >= 0) + { + p = p.Twice(); + } + return p; + } + + public abstract ECPoint Twice(); + public abstract ECPoint Multiply(BigInteger b); + + public virtual ECPoint TwicePlus(ECPoint b) + { + return Twice().Add(b); + } + + public virtual ECPoint ThreeTimes() + { + return TwicePlus(this); + } + } + + public abstract class ECPointBase + : ECPoint + { + protected internal ECPointBase( + ECCurve curve, + ECFieldElement x, + ECFieldElement y, + bool withCompression) + : base(curve, x, y, withCompression) + { + } + + protected internal ECPointBase(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + /** + * return the field element encoded with point compression. (S 4.3.6) + */ + public override byte[] GetEncoded(bool compressed) + { + if (this.IsInfinity) + { + return new byte[1]; + } + + ECPoint normed = Normalize(); + + byte[] X = normed.XCoord.GetEncoded(); + + if (compressed) + { + byte[] PO = new byte[X.Length + 1]; + PO[0] = (byte)(normed.CompressionYTilde ? 0x03 : 0x02); + Array.Copy(X, 0, PO, 1, X.Length); + return PO; + } + + byte[] Y = normed.YCoord.GetEncoded(); + + { + byte[] PO = new byte[X.Length + Y.Length + 1]; + PO[0] = 0x04; + Array.Copy(X, 0, PO, 1, X.Length); + Array.Copy(Y, 0, PO, X.Length + 1, Y.Length); + 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) + { + return this.Curve.GetMultiplier().Multiply(this, k); + } + } + + public abstract class AbstractFpPoint + : ECPointBase + { + protected AbstractFpPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + } + + protected AbstractFpPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected internal override bool CompressionYTilde + { + get { return this.AffineYCoord.TestBitZero(); } + } + + protected override bool SatisfiesCurveEquation() + { + ECFieldElement X = this.RawXCoord, Y = this.RawYCoord, A = Curve.A, B = Curve.B; + ECFieldElement lhs = Y.Square(); + + switch (CurveCoordinateSystem) + { + case ECCurve.COORD_AFFINE: + break; + case ECCurve.COORD_HOMOGENEOUS: + { + ECFieldElement Z = this.RawZCoords[0]; + if (!Z.IsOne) + { + ECFieldElement Z2 = Z.Square(), Z3 = Z.Multiply(Z2); + lhs = lhs.Multiply(Z); + A = A.Multiply(Z2); + B = B.Multiply(Z3); + } + break; + } + case ECCurve.COORD_JACOBIAN: + case ECCurve.COORD_JACOBIAN_CHUDNOVSKY: + case ECCurve.COORD_JACOBIAN_MODIFIED: + { + ECFieldElement Z = this.RawZCoords[0]; + if (!Z.IsOne) + { + ECFieldElement Z2 = Z.Square(), Z4 = Z2.Square(), Z6 = Z2.Multiply(Z4); + A = A.Multiply(Z4); + B = B.Multiply(Z6); + } + break; + } + default: + throw new InvalidOperationException("unsupported coordinate system"); + } + + ECFieldElement rhs = X.Square().Add(A).Multiply(X).Add(B); + return lhs.Equals(rhs); + } + + public override ECPoint Subtract(ECPoint b) + { + if (b.IsInfinity) + return this; + + // Add -b + return Add(b.Negate()); + } + } + + /** + * Elliptic curve points over Fp + */ + public class FpPoint + : AbstractFpPoint + { + /** + * Create a point which encodes without 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 compression. + * + * @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)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal FpPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new FpPoint(null, AffineXCoord, AffineYCoord); + } + + public override ECFieldElement GetZCoord(int index) + { + if (index == 1 && ECCurve.COORD_JACOBIAN_MODIFIED == this.CurveCoordinateSystem) + { + return GetJacobianModifiedW(); + } + + return base.GetZCoord(index); + } + + // B.3 pg 62 + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + if (this == b) + return Twice(); + + ECCurve curve = this.Curve; + int coord = curve.CoordinateSystem; + + ECFieldElement X1 = this.RawXCoord, Y1 = this.RawYCoord; + ECFieldElement X2 = b.RawXCoord, Y2 = b.RawYCoord; + + switch (coord) + { + case ECCurve.COORD_AFFINE: + { + ECFieldElement dx = X2.Subtract(X1), dy = Y2.Subtract(Y1); + + if (dx.IsZero) + { + if (dy.IsZero) + { + // this == b, i.e. this must be doubled + return Twice(); + } + + // this == -b, i.e. the result is the point at infinity + return Curve.Infinity; + } + + ECFieldElement gamma = dy.Divide(dx); + ECFieldElement X3 = gamma.Square().Subtract(X1).Subtract(X2); + ECFieldElement Y3 = gamma.Multiply(X1.Subtract(X3)).Subtract(Y1); + + return new FpPoint(Curve, X3, Y3, IsCompressed); + } + + case ECCurve.COORD_HOMOGENEOUS: + { + ECFieldElement Z1 = this.RawZCoords[0]; + ECFieldElement Z2 = b.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + bool Z2IsOne = Z2.IsOne; + + ECFieldElement u1 = Z1IsOne ? Y2 : Y2.Multiply(Z1); + ECFieldElement u2 = Z2IsOne ? Y1 : Y1.Multiply(Z2); + ECFieldElement u = u1.Subtract(u2); + ECFieldElement v1 = Z1IsOne ? X2 : X2.Multiply(Z1); + ECFieldElement v2 = Z2IsOne ? X1 : X1.Multiply(Z2); + ECFieldElement v = v1.Subtract(v2); + + // Check if b == this or b == -this + if (v.IsZero) + { + if (u.IsZero) + { + // this == b, i.e. this must be doubled + return this.Twice(); + } + + // this == -b, i.e. the result is the point at infinity + return curve.Infinity; + } + + // TODO Optimize for when w == 1 + ECFieldElement w = Z1IsOne ? Z2 : Z2IsOne ? Z1 : Z1.Multiply(Z2); + ECFieldElement vSquared = v.Square(); + ECFieldElement vCubed = vSquared.Multiply(v); + ECFieldElement vSquaredV2 = vSquared.Multiply(v2); + ECFieldElement A = u.Square().Multiply(w).Subtract(vCubed).Subtract(Two(vSquaredV2)); + + ECFieldElement X3 = v.Multiply(A); + ECFieldElement Y3 = vSquaredV2.Subtract(A).MultiplyMinusProduct(u, u2, vCubed); + ECFieldElement Z3 = vCubed.Multiply(w); + + return new FpPoint(curve, X3, Y3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + case ECCurve.COORD_JACOBIAN: + case ECCurve.COORD_JACOBIAN_MODIFIED: + { + ECFieldElement Z1 = this.RawZCoords[0]; + ECFieldElement Z2 = b.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + + ECFieldElement X3, Y3, Z3, Z3Squared = null; + + if (!Z1IsOne && Z1.Equals(Z2)) + { + // TODO Make this available as public method coZAdd? + + ECFieldElement dx = X1.Subtract(X2), dy = Y1.Subtract(Y2); + if (dx.IsZero) + { + if (dy.IsZero) + { + return Twice(); + } + return curve.Infinity; + } + + ECFieldElement C = dx.Square(); + ECFieldElement W1 = X1.Multiply(C), W2 = X2.Multiply(C); + ECFieldElement A1 = W1.Subtract(W2).Multiply(Y1); + + X3 = dy.Square().Subtract(W1).Subtract(W2); + Y3 = W1.Subtract(X3).Multiply(dy).Subtract(A1); + Z3 = dx; + + if (Z1IsOne) + { + Z3Squared = C; + } + else + { + Z3 = Z3.Multiply(Z1); + } + } + else + { + ECFieldElement Z1Squared, U2, S2; + if (Z1IsOne) + { + Z1Squared = Z1; U2 = X2; S2 = Y2; + } + else + { + Z1Squared = Z1.Square(); + U2 = Z1Squared.Multiply(X2); + ECFieldElement Z1Cubed = Z1Squared.Multiply(Z1); + S2 = Z1Cubed.Multiply(Y2); + } + + bool Z2IsOne = Z2.IsOne; + ECFieldElement Z2Squared, U1, S1; + if (Z2IsOne) + { + Z2Squared = Z2; U1 = X1; S1 = Y1; + } + else + { + Z2Squared = Z2.Square(); + U1 = Z2Squared.Multiply(X1); + ECFieldElement Z2Cubed = Z2Squared.Multiply(Z2); + S1 = Z2Cubed.Multiply(Y1); + } + + ECFieldElement H = U1.Subtract(U2); + ECFieldElement R = S1.Subtract(S2); + + // Check if b == this or b == -this + if (H.IsZero) + { + if (R.IsZero) + { + // this == b, i.e. this must be doubled + return this.Twice(); + } + + // this == -b, i.e. the result is the point at infinity + return curve.Infinity; + } + + ECFieldElement HSquared = H.Square(); + ECFieldElement G = HSquared.Multiply(H); + ECFieldElement V = HSquared.Multiply(U1); + + X3 = R.Square().Add(G).Subtract(Two(V)); + Y3 = V.Subtract(X3).MultiplyMinusProduct(R, G, S1); + + Z3 = H; + if (!Z1IsOne) + { + Z3 = Z3.Multiply(Z1); + } + if (!Z2IsOne) + { + Z3 = Z3.Multiply(Z2); + } + + // Alternative calculation of Z3 using fast square + //X3 = four(X3); + //Y3 = eight(Y3); + //Z3 = doubleProductFromSquares(Z1, Z2, Z1Squared, Z2Squared).Multiply(H); + + if (Z3 == H) + { + Z3Squared = HSquared; + } + } + + ECFieldElement[] zs; + if (coord == ECCurve.COORD_JACOBIAN_MODIFIED) + { + // TODO If the result will only be used in a subsequent addition, we don't need W3 + ECFieldElement W3 = CalculateJacobianModifiedW(Z3, Z3Squared); + + zs = new ECFieldElement[] { Z3, W3 }; + } + else + { + zs = new ECFieldElement[] { Z3 }; + } + + return new FpPoint(curve, X3, Y3, zs, IsCompressed); + } + + default: + { + throw new InvalidOperationException("unsupported coordinate system"); + } + } + } + + // B.3 pg 62 + public override ECPoint Twice() + { + if (this.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + ECFieldElement Y1 = this.RawYCoord; + if (Y1.IsZero) + return curve.Infinity; + + int coord = curve.CoordinateSystem; + + ECFieldElement X1 = this.RawXCoord; + + switch (coord) + { + case ECCurve.COORD_AFFINE: + { + ECFieldElement X1Squared = X1.Square(); + ECFieldElement gamma = Three(X1Squared).Add(this.Curve.A).Divide(Two(Y1)); + ECFieldElement X3 = gamma.Square().Subtract(Two(X1)); + ECFieldElement Y3 = gamma.Multiply(X1.Subtract(X3)).Subtract(Y1); + + return new FpPoint(Curve, X3, Y3, IsCompressed); + } + + case ECCurve.COORD_HOMOGENEOUS: + { + ECFieldElement Z1 = this.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + + // TODO Optimize for small negative a4 and -3 + ECFieldElement w = curve.A; + if (!w.IsZero && !Z1IsOne) + { + w = w.Multiply(Z1.Square()); + } + w = w.Add(Three(X1.Square())); + + ECFieldElement s = Z1IsOne ? Y1 : Y1.Multiply(Z1); + ECFieldElement t = Z1IsOne ? Y1.Square() : s.Multiply(Y1); + ECFieldElement B = X1.Multiply(t); + ECFieldElement _4B = Four(B); + ECFieldElement h = w.Square().Subtract(Two(_4B)); + + ECFieldElement _2s = Two(s); + ECFieldElement X3 = h.Multiply(_2s); + ECFieldElement _2t = Two(t); + ECFieldElement Y3 = _4B.Subtract(h).Multiply(w).Subtract(Two(_2t.Square())); + ECFieldElement _4sSquared = Z1IsOne ? Two(_2t) : _2s.Square(); + ECFieldElement Z3 = Two(_4sSquared).Multiply(s); + + return new FpPoint(curve, X3, Y3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + case ECCurve.COORD_JACOBIAN: + { + ECFieldElement Z1 = this.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + + ECFieldElement Y1Squared = Y1.Square(); + ECFieldElement T = Y1Squared.Square(); + + ECFieldElement a4 = curve.A; + ECFieldElement a4Neg = a4.Negate(); + + ECFieldElement M, S; + if (a4Neg.ToBigInteger().Equals(BigInteger.ValueOf(3))) + { + ECFieldElement Z1Squared = Z1IsOne ? Z1 : Z1.Square(); + M = Three(X1.Add(Z1Squared).Multiply(X1.Subtract(Z1Squared))); + S = Four(Y1Squared.Multiply(X1)); + } + else + { + ECFieldElement X1Squared = X1.Square(); + M = Three(X1Squared); + if (Z1IsOne) + { + M = M.Add(a4); + } + else if (!a4.IsZero) + { + ECFieldElement Z1Squared = Z1IsOne ? Z1 : Z1.Square(); + ECFieldElement Z1Pow4 = Z1Squared.Square(); + if (a4Neg.BitLength < a4.BitLength) + { + M = M.Subtract(Z1Pow4.Multiply(a4Neg)); + } + else + { + M = M.Add(Z1Pow4.Multiply(a4)); + } + } + //S = two(doubleProductFromSquares(X1, Y1Squared, X1Squared, T)); + S = Four(X1.Multiply(Y1Squared)); + } + + ECFieldElement X3 = M.Square().Subtract(Two(S)); + ECFieldElement Y3 = S.Subtract(X3).Multiply(M).Subtract(Eight(T)); + + ECFieldElement Z3 = Two(Y1); + if (!Z1IsOne) + { + Z3 = Z3.Multiply(Z1); + } + + // Alternative calculation of Z3 using fast square + //ECFieldElement Z3 = doubleProductFromSquares(Y1, Z1, Y1Squared, Z1Squared); + + return new FpPoint(curve, X3, Y3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + case ECCurve.COORD_JACOBIAN_MODIFIED: + { + return TwiceJacobianModified(true); + } + + default: + { + throw new InvalidOperationException("unsupported coordinate system"); + } + } + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this == b) + return ThreeTimes(); + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECFieldElement Y1 = this.RawYCoord; + if (Y1.IsZero) + return b; + + ECCurve curve = this.Curve; + int coord = curve.CoordinateSystem; + + switch (coord) + { + case ECCurve.COORD_AFFINE: + { + ECFieldElement X1 = this.RawXCoord; + ECFieldElement X2 = b.RawXCoord, Y2 = b.RawYCoord; + + ECFieldElement dx = X2.Subtract(X1), dy = Y2.Subtract(Y1); + + if (dx.IsZero) + { + if (dy.IsZero) + { + // this == b i.e. the result is 3P + return ThreeTimes(); + } + + // this == -b, i.e. the result is P + return this; + } + + /* + * Optimized calculation of 2P + Q, as described in "Trading Inversions for + * Multiplications in Elliptic Curve Cryptography", by Ciet, Joye, Lauter, Montgomery. + */ + + ECFieldElement X = dx.Square(), Y = dy.Square(); + ECFieldElement d = X.Multiply(Two(X1).Add(X2)).Subtract(Y); + if (d.IsZero) + { + return Curve.Infinity; + } + + ECFieldElement D = d.Multiply(dx); + ECFieldElement I = D.Invert(); + ECFieldElement L1 = d.Multiply(I).Multiply(dy); + ECFieldElement L2 = Two(Y1).Multiply(X).Multiply(dx).Multiply(I).Subtract(L1); + ECFieldElement X4 = (L2.Subtract(L1)).Multiply(L1.Add(L2)).Add(X2); + ECFieldElement Y4 = (X1.Subtract(X4)).Multiply(L2).Subtract(Y1); + + return new FpPoint(Curve, X4, Y4, IsCompressed); + } + case ECCurve.COORD_JACOBIAN_MODIFIED: + { + return TwiceJacobianModified(false).Add(b); + } + default: + { + return Twice().Add(b); + } + } + } + + public override ECPoint ThreeTimes() + { + if (this.IsInfinity) + return this; + + ECFieldElement Y1 = this.RawYCoord; + if (Y1.IsZero) + return this; + + ECCurve curve = this.Curve; + int coord = curve.CoordinateSystem; + + switch (coord) + { + case ECCurve.COORD_AFFINE: + { + ECFieldElement X1 = this.RawXCoord; + + ECFieldElement _2Y1 = Two(Y1); + ECFieldElement X = _2Y1.Square(); + ECFieldElement Z = Three(X1.Square()).Add(Curve.A); + ECFieldElement Y = Z.Square(); + + ECFieldElement d = Three(X1).Multiply(X).Subtract(Y); + if (d.IsZero) + { + return Curve.Infinity; + } + + ECFieldElement D = d.Multiply(_2Y1); + ECFieldElement I = D.Invert(); + ECFieldElement L1 = d.Multiply(I).Multiply(Z); + ECFieldElement L2 = X.Square().Multiply(I).Subtract(L1); + + ECFieldElement X4 = (L2.Subtract(L1)).Multiply(L1.Add(L2)).Add(X1); + ECFieldElement Y4 = (X1.Subtract(X4)).Multiply(L2).Subtract(Y1); + return new FpPoint(Curve, X4, Y4, IsCompressed); + } + case ECCurve.COORD_JACOBIAN_MODIFIED: + { + return TwiceJacobianModified(false).Add(this); + } + default: + { + // NOTE: Be careful about recursions between TwicePlus and ThreeTimes + return Twice().Add(this); + } + } + } + + public override ECPoint TimesPow2(int e) + { + if (e < 0) + throw new ArgumentException("cannot be negative", "e"); + if (e == 0 || this.IsInfinity) + return this; + if (e == 1) + return Twice(); + + ECCurve curve = this.Curve; + + ECFieldElement Y1 = this.RawYCoord; + if (Y1.IsZero) + return curve.Infinity; + + int coord = curve.CoordinateSystem; + + ECFieldElement W1 = curve.A; + ECFieldElement X1 = this.RawXCoord; + ECFieldElement Z1 = this.RawZCoords.Length < 1 ? curve.FromBigInteger(BigInteger.One) : this.RawZCoords[0]; + + if (!Z1.IsOne) + { + switch (coord) + { + case ECCurve.COORD_HOMOGENEOUS: + ECFieldElement Z1Sq = Z1.Square(); + X1 = X1.Multiply(Z1); + Y1 = Y1.Multiply(Z1Sq); + W1 = CalculateJacobianModifiedW(Z1, Z1Sq); + break; + case ECCurve.COORD_JACOBIAN: + W1 = CalculateJacobianModifiedW(Z1, null); + break; + case ECCurve.COORD_JACOBIAN_MODIFIED: + W1 = GetJacobianModifiedW(); + break; + } + } + + for (int i = 0; i < e; ++i) + { + if (Y1.IsZero) + return curve.Infinity; + + ECFieldElement X1Squared = X1.Square(); + ECFieldElement M = Three(X1Squared); + ECFieldElement _2Y1 = Two(Y1); + ECFieldElement _2Y1Squared = _2Y1.Multiply(Y1); + ECFieldElement S = Two(X1.Multiply(_2Y1Squared)); + ECFieldElement _4T = _2Y1Squared.Square(); + ECFieldElement _8T = Two(_4T); + + if (!W1.IsZero) + { + M = M.Add(W1); + W1 = Two(_8T.Multiply(W1)); + } + + X1 = M.Square().Subtract(Two(S)); + Y1 = M.Multiply(S.Subtract(X1)).Subtract(_8T); + Z1 = Z1.IsOne ? _2Y1 : _2Y1.Multiply(Z1); + } + + switch (coord) + { + case ECCurve.COORD_AFFINE: + ECFieldElement zInv = Z1.Invert(), zInv2 = zInv.Square(), zInv3 = zInv2.Multiply(zInv); + return new FpPoint(curve, X1.Multiply(zInv2), Y1.Multiply(zInv3), IsCompressed); + case ECCurve.COORD_HOMOGENEOUS: + X1 = X1.Multiply(Z1); + Z1 = Z1.Multiply(Z1.Square()); + return new FpPoint(curve, X1, Y1, new ECFieldElement[] { Z1 }, IsCompressed); + case ECCurve.COORD_JACOBIAN: + return new FpPoint(curve, X1, Y1, new ECFieldElement[] { Z1 }, IsCompressed); + case ECCurve.COORD_JACOBIAN_MODIFIED: + return new FpPoint(curve, X1, Y1, new ECFieldElement[] { Z1, W1 }, IsCompressed); + default: + throw new InvalidOperationException("unsupported coordinate system"); + } + } + + protected virtual ECFieldElement Two(ECFieldElement x) + { + return x.Add(x); + } + + protected virtual ECFieldElement Three(ECFieldElement x) + { + return Two(x).Add(x); + } + + protected virtual ECFieldElement Four(ECFieldElement x) + { + return Two(Two(x)); + } + + protected virtual ECFieldElement Eight(ECFieldElement x) + { + return Four(Two(x)); + } + + protected virtual ECFieldElement DoubleProductFromSquares(ECFieldElement a, ECFieldElement b, + ECFieldElement aSquared, ECFieldElement bSquared) + { + /* + * NOTE: If squaring in the field is faster than multiplication, then this is a quicker + * way to calculate 2.A.B, if A^2 and B^2 are already known. + */ + return a.Add(b).Square().Subtract(aSquared).Subtract(bSquared); + } + + public override ECPoint Negate() + { + if (IsInfinity) + return this; + + ECCurve curve = Curve; + int coord = curve.CoordinateSystem; + + if (ECCurve.COORD_AFFINE != coord) + { + return new FpPoint(curve, RawXCoord, RawYCoord.Negate(), RawZCoords, IsCompressed); + } + + return new FpPoint(curve, RawXCoord, RawYCoord.Negate(), IsCompressed); + } + + protected virtual ECFieldElement CalculateJacobianModifiedW(ECFieldElement Z, ECFieldElement ZSquared) + { + ECFieldElement a4 = this.Curve.A; + if (a4.IsZero || Z.IsOne) + return a4; + + if (ZSquared == null) + { + ZSquared = Z.Square(); + } + + ECFieldElement W = ZSquared.Square(); + ECFieldElement a4Neg = a4.Negate(); + if (a4Neg.BitLength < a4.BitLength) + { + W = W.Multiply(a4Neg).Negate(); + } + else + { + W = W.Multiply(a4); + } + return W; + } + + protected virtual ECFieldElement GetJacobianModifiedW() + { + ECFieldElement[] ZZ = this.RawZCoords; + ECFieldElement W = ZZ[1]; + if (W == null) + { + // NOTE: Rarely, TwicePlus will result in the need for a lazy W1 calculation here + ZZ[1] = W = CalculateJacobianModifiedW(ZZ[0], null); + } + return W; + } + + protected virtual FpPoint TwiceJacobianModified(bool calculateW) + { + ECFieldElement X1 = this.RawXCoord, Y1 = this.RawYCoord, Z1 = this.RawZCoords[0], W1 = GetJacobianModifiedW(); + + ECFieldElement X1Squared = X1.Square(); + ECFieldElement M = Three(X1Squared).Add(W1); + ECFieldElement _2Y1 = Two(Y1); + ECFieldElement _2Y1Squared = _2Y1.Multiply(Y1); + ECFieldElement S = Two(X1.Multiply(_2Y1Squared)); + ECFieldElement X3 = M.Square().Subtract(Two(S)); + ECFieldElement _4T = _2Y1Squared.Square(); + ECFieldElement _8T = Two(_4T); + ECFieldElement Y3 = M.Multiply(S.Subtract(X3)).Subtract(_8T); + ECFieldElement W3 = calculateW ? Two(_8T.Multiply(W1)) : null; + ECFieldElement Z3 = Z1.IsOne ? _2Y1 : _2Y1.Multiply(Z1); + + return new FpPoint(this.Curve, X3, Y3, new ECFieldElement[] { Z3, W3 }, IsCompressed); + } + } + + public abstract class AbstractF2mPoint + : ECPointBase + { + protected AbstractF2mPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + } + + protected AbstractF2mPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override bool SatisfiesCurveEquation() + { + ECCurve curve = Curve; + ECFieldElement X = this.RawXCoord, Y = this.RawYCoord, A = curve.A, B = curve.B; + ECFieldElement lhs, rhs; + + int coord = curve.CoordinateSystem; + if (coord == ECCurve.COORD_LAMBDA_PROJECTIVE) + { + ECFieldElement Z = this.RawZCoords[0]; + bool ZIsOne = Z.IsOne; + + if (X.IsZero) + { + // NOTE: For x == 0, we expect the affine-y instead of the lambda-y + lhs = Y.Square(); + rhs = B; + if (!ZIsOne) + { + ECFieldElement Z2 = Z.Square(); + rhs = rhs.Multiply(Z2); + } + } + else + { + ECFieldElement L = Y, X2 = X.Square(); + if (ZIsOne) + { + lhs = L.Square().Add(L).Add(A); + rhs = X2.Square().Add(B); + } + else + { + ECFieldElement Z2 = Z.Square(), Z4 = Z2.Square(); + lhs = L.Add(Z).MultiplyPlusProduct(L, A, Z2); + // TODO If sqrt(b) is precomputed this can be simplified to a single square + rhs = X2.SquarePlusProduct(B, Z4); + } + lhs = lhs.Multiply(X2); + } + } + else + { + lhs = Y.Add(X).Multiply(Y); + + switch (coord) + { + case ECCurve.COORD_AFFINE: + break; + case ECCurve.COORD_HOMOGENEOUS: + { + ECFieldElement Z = this.RawZCoords[0]; + if (!Z.IsOne) + { + ECFieldElement Z2 = Z.Square(), Z3 = Z.Multiply(Z2); + lhs = lhs.Multiply(Z); + A = A.Multiply(Z); + B = B.Multiply(Z3); + } + break; + } + default: + throw new InvalidOperationException("unsupported coordinate system"); + } + + rhs = X.Add(A).Multiply(X.Square()).Add(B); + } + + return lhs.Equals(rhs); + } + } + + /** + * Elliptic curve points over F2m + */ + public class F2mPoint + : AbstractF2mPoint + { + /** + * @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)) + { + 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(x, y); + + // Check if x and a are elements of the same field + if (curve != null) + { + F2mFieldElement.CheckFieldElements(x, curve.A); + } + } + } + + internal F2mPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + /** + * Constructor for point at infinity + */ + [Obsolete("Use ECCurve.Infinity property")] + public F2mPoint( + ECCurve curve) + : this(curve, null, null) + { + } + + protected override ECPoint Detach() + { + return new F2mPoint(null, AffineXCoord, AffineYCoord); + } + + public override ECFieldElement YCoord + { + get + { + int coord = this.CurveCoordinateSystem; + + switch (coord) + { + case ECCurve.COORD_LAMBDA_AFFINE: + case ECCurve.COORD_LAMBDA_PROJECTIVE: + { + ECFieldElement X = RawXCoord, L = RawYCoord; + + if (this.IsInfinity || X.IsZero) + return L; + + // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly + ECFieldElement Y = L.Add(X).Multiply(X); + if (ECCurve.COORD_LAMBDA_PROJECTIVE == coord) + { + ECFieldElement Z = RawZCoords[0]; + if (!Z.IsOne) + { + Y = Y.Divide(Z); + } + } + return Y; + } + default: + { + return RawYCoord; + } + } + } + } + + public override ECPoint ScaleX(ECFieldElement scale) + { + if (this.IsInfinity) + return this; + + switch (CurveCoordinateSystem) + { + case ECCurve.COORD_LAMBDA_AFFINE: + { + // Y is actually Lambda (X + Y/X) here + ECFieldElement X = RawXCoord, L = RawYCoord; + + ECFieldElement X2 = X.Multiply(scale); + ECFieldElement L2 = L.Add(X).Divide(scale).Add(X2); + + return Curve.CreateRawPoint(X, L2, RawZCoords, IsCompressed); + } + case ECCurve.COORD_LAMBDA_PROJECTIVE: + { + // Y is actually Lambda (X + Y/X) here + ECFieldElement X = RawXCoord, L = RawYCoord, Z = RawZCoords[0]; + + // We scale the Z coordinate also, to avoid an inversion + ECFieldElement X2 = X.Multiply(scale.Square()); + ECFieldElement L2 = L.Add(X).Add(X2); + ECFieldElement Z2 = Z.Multiply(scale); + + return Curve.CreateRawPoint(X, L2, new ECFieldElement[] { Z2 }, IsCompressed); + } + default: + { + return base.ScaleX(scale); + } + } + } + + public override ECPoint ScaleY(ECFieldElement scale) + { + if (this.IsInfinity) + return this; + + switch (CurveCoordinateSystem) + { + case ECCurve.COORD_LAMBDA_AFFINE: + case ECCurve.COORD_LAMBDA_PROJECTIVE: + { + ECFieldElement X = RawXCoord, L = RawYCoord; + + // Y is actually Lambda (X + Y/X) here + ECFieldElement L2 = L.Add(X).Multiply(scale).Add(X); + + return Curve.CreateRawPoint(X, L2, RawZCoords, IsCompressed); + } + default: + { + return base.ScaleY(scale); + } + } + } + + protected internal override bool CompressionYTilde + { + get + { + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + { + return false; + } + + ECFieldElement Y = this.RawYCoord; + + switch (this.CurveCoordinateSystem) + { + case ECCurve.COORD_LAMBDA_AFFINE: + case ECCurve.COORD_LAMBDA_PROJECTIVE: + { + // Y is actually Lambda (X + Y/X) here + return Y.TestBitZero() != X.TestBitZero(); + } + default: + { + return Y.Divide(X).TestBitZero(); + } + } + } + } + + /** + * 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(); - } - } - } - } - } - } + } + + /* (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; + + ECCurve curve = this.Curve; + int coord = curve.CoordinateSystem; + + ECFieldElement X1 = this.RawXCoord; + ECFieldElement X2 = b.RawXCoord; + + switch (coord) + { + case ECCurve.COORD_AFFINE: + { + ECFieldElement Y1 = this.RawYCoord; + ECFieldElement Y2 = b.RawYCoord; + + ECFieldElement dx = X1.Add(X2), dy = Y1.Add(Y2); + if (dx.IsZero) + { + if (dy.IsZero) + { + return (F2mPoint)Twice(); + } + + return (F2mPoint)curve.Infinity; + } + + ECFieldElement L = dy.Divide(dx); + + ECFieldElement X3 = L.Square().Add(L).Add(dx).Add(curve.A); + ECFieldElement Y3 = L.Multiply(X1.Add(X3)).Add(X3).Add(Y1); + + return new F2mPoint(curve, X3, Y3, IsCompressed); + } + case ECCurve.COORD_HOMOGENEOUS: + { + ECFieldElement Y1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement Y2 = b.RawYCoord, Z2 = b.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement U1 = Y2, V1 = X2; + if (!Z1IsOne) + { + U1 = U1.Multiply(Z1); + V1 = V1.Multiply(Z1); + } + + bool Z2IsOne = Z2.IsOne; + ECFieldElement U2 = Y1, V2 = X1; + if (!Z2IsOne) + { + U2 = U2.Multiply(Z2); + V2 = V2.Multiply(Z2); + } + + ECFieldElement U = U1.Add(U2); + ECFieldElement V = V1.Add(V2); + + if (V.IsZero) + { + if (U.IsZero) + { + return (F2mPoint)Twice(); + } + + return (F2mPoint)curve.Infinity; + } + + ECFieldElement VSq = V.Square(); + ECFieldElement VCu = VSq.Multiply(V); + ECFieldElement W = Z1IsOne ? Z2 : Z2IsOne ? Z1 : Z1.Multiply(Z2); + ECFieldElement uv = U.Add(V); + ECFieldElement A = uv.MultiplyPlusProduct(U, VSq, curve.A).Multiply(W).Add(VCu); + + ECFieldElement X3 = V.Multiply(A); + ECFieldElement VSqZ2 = Z2IsOne ? VSq : VSq.Multiply(Z2); + ECFieldElement Y3 = U.MultiplyPlusProduct(X1, V, Y1).MultiplyPlusProduct(VSqZ2, uv, A); + ECFieldElement Z3 = VCu.Multiply(W); + + return new F2mPoint(curve, X3, Y3, new ECFieldElement[] { Z3 }, IsCompressed); + } + case ECCurve.COORD_LAMBDA_PROJECTIVE: + { + if (X1.IsZero) + { + if (X2.IsZero) + return (F2mPoint)curve.Infinity; + + return b.AddSimple(this); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord, Z2 = b.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.Multiply(Z1); + S2 = S2.Multiply(Z1); + } + + bool Z2IsOne = Z2.IsOne; + ECFieldElement U1 = X1, S1 = L1; + if (!Z2IsOne) + { + U1 = U1.Multiply(Z2); + S1 = S1.Multiply(Z2); + } + + ECFieldElement A = S1.Add(S2); + ECFieldElement B = U1.Add(U2); + + if (B.IsZero) + { + if (A.IsZero) + { + return (F2mPoint)Twice(); + } + + return (F2mPoint)curve.Infinity; + } + + ECFieldElement X3, L3, Z3; + if (X2.IsZero) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.Normalize(); + X1 = p.RawXCoord; + ECFieldElement Y1 = p.YCoord; + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.Add(Y2).Divide(X1); + + X3 = L.Square().Add(L).Add(X1).Add(curve.A); + if (X3.IsZero) + { + return new F2mPoint(curve, X3, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement Y3 = L.Multiply(X1.Add(X3)).Add(X3).Add(Y1); + L3 = Y3.Divide(X3).Add(X3); + Z3 = curve.FromBigInteger(BigInteger.One); + } + else + { + B = B.Square(); + + ECFieldElement AU1 = A.Multiply(U1); + ECFieldElement AU2 = A.Multiply(U2); + + X3 = AU1.Multiply(AU2); + if (X3.IsZero) + { + return new F2mPoint(curve, X3, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement ABZ2 = A.Multiply(B); + if (!Z2IsOne) + { + ABZ2 = ABZ2.Multiply(Z2); + } + + L3 = AU2.Add(B).SquarePlusProduct(ABZ2, L1.Add(Z1)); + + Z3 = ABZ2; + if (!Z1IsOne) + { + Z3 = Z3.Multiply(Z1); + } + } + + return new F2mPoint(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + default: + { + throw new InvalidOperationException("unsupported coordinate system"); + } + } + } + + /* (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()); + } + + public virtual F2mPoint Tau() + { + if (this.IsInfinity) + { + return this; + } + + ECCurve curve = this.Curve; + int coord = curve.CoordinateSystem; + + ECFieldElement X1 = this.RawXCoord; + + switch (coord) + { + case ECCurve.COORD_AFFINE: + case ECCurve.COORD_LAMBDA_AFFINE: + { + ECFieldElement Y1 = this.RawYCoord; + return new F2mPoint(curve, X1.Square(), Y1.Square(), IsCompressed); + } + case ECCurve.COORD_HOMOGENEOUS: + case ECCurve.COORD_LAMBDA_PROJECTIVE: + { + ECFieldElement Y1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + return new F2mPoint(curve, X1.Square(), Y1.Square(), new ECFieldElement[] { Z1.Square() }, IsCompressed); + } + default: + { + throw new InvalidOperationException("unsupported coordinate system"); + } + } + } + + /* (non-Javadoc) + * @see Org.BouncyCastle.Math.EC.ECPoint#twice() + */ + public override ECPoint Twice() + { + if (this.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own additive inverse + return curve.Infinity; + } + + int coord = curve.CoordinateSystem; + + switch (coord) + { + case ECCurve.COORD_AFFINE: + { + ECFieldElement Y1 = this.RawYCoord; + + ECFieldElement L1 = Y1.Divide(X1).Add(X1); + + ECFieldElement X3 = L1.Square().Add(L1).Add(curve.A); + ECFieldElement Y3 = X1.SquarePlusProduct(X3, L1.AddOne()); + + return new F2mPoint(curve, X3, Y3, IsCompressed); + } + case ECCurve.COORD_HOMOGENEOUS: + { + ECFieldElement Y1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.Multiply(Z1); + ECFieldElement Y1Z1 = Z1IsOne ? Y1 : Y1.Multiply(Z1); + + ECFieldElement X1Sq = X1.Square(); + ECFieldElement S = X1Sq.Add(Y1Z1); + ECFieldElement V = X1Z1; + ECFieldElement vSquared = V.Square(); + ECFieldElement sv = S.Add(V); + ECFieldElement h = sv.MultiplyPlusProduct(S, vSquared, curve.A); + + ECFieldElement X3 = V.Multiply(h); + ECFieldElement Y3 = X1Sq.Square().MultiplyPlusProduct(V, h, sv); + ECFieldElement Z3 = V.Multiply(vSquared); + + return new F2mPoint(curve, X3, Y3, new ECFieldElement[] { Z3 }, IsCompressed); + } + case ECCurve.COORD_LAMBDA_PROJECTIVE: + { + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + + bool Z1IsOne = Z1.IsOne; + ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.Multiply(Z1); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.Square(); + ECFieldElement a = curve.A; + ECFieldElement aZ1Sq = Z1IsOne ? a : a.Multiply(Z1Sq); + ECFieldElement T = L1.Square().Add(L1Z1).Add(aZ1Sq); + if (T.IsZero) + { + return new F2mPoint(curve, T, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement X3 = T.Square(); + ECFieldElement Z3 = Z1IsOne ? T : T.Multiply(Z1Sq); + + ECFieldElement b = curve.B; + ECFieldElement L3; + if (b.BitLength < (curve.FieldSize >> 1)) + { + ECFieldElement t1 = L1.Add(X1).Square(); + ECFieldElement t2; + if (b.IsOne) + { + t2 = aZ1Sq.Add(Z1Sq).Square(); + } + else + { + // TODO Can be calculated with one square if we pre-compute sqrt(b) + t2 = aZ1Sq.SquarePlusProduct(b, Z1Sq.Square()); + } + L3 = t1.Add(T).Add(Z1Sq).Multiply(t1).Add(t2).Add(X3); + if (a.IsZero) + { + L3 = L3.Add(Z3); + } + else if (!a.IsOne) + { + L3 = L3.Add(a.AddOne().Multiply(Z3)); + } + } + else + { + ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.Multiply(Z1); + L3 = X1Z1.SquarePlusProduct(T, L1Z1).Add(X3).Add(Z3); + } + + return new F2mPoint(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + default: + { + throw new InvalidOperationException("unsupported coordinate system"); + } + } + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECCurve curve = this.Curve; + + ECFieldElement X1 = this.RawXCoord; + if (X1.IsZero) + { + // A point with X == 0 is it's own additive inverse + return b; + } + + int coord = curve.CoordinateSystem; + + switch (coord) + { + case ECCurve.COORD_LAMBDA_PROJECTIVE: + { + // NOTE: twicePlus() only optimized for lambda-affine argument + ECFieldElement X2 = b.RawXCoord, Z2 = b.RawZCoords[0]; + if (X2.IsZero || !Z2.IsOne) + { + return Twice().Add(b); + } + + ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0]; + ECFieldElement L2 = b.RawYCoord; + + ECFieldElement X1Sq = X1.Square(); + ECFieldElement L1Sq = L1.Square(); + ECFieldElement Z1Sq = Z1.Square(); + ECFieldElement L1Z1 = L1.Multiply(Z1); + + ECFieldElement T = curve.A.Multiply(Z1Sq).Add(L1Sq).Add(L1Z1); + ECFieldElement L2plus1 = L2.AddOne(); + ECFieldElement A = curve.A.Add(L2plus1).Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement X2Z1Sq = X2.Multiply(Z1Sq); + ECFieldElement B = X2Z1Sq.Add(T).Square(); + + if (B.IsZero) + { + if (A.IsZero) + { + return b.Twice(); + } + + return curve.Infinity; + } + + if (A.IsZero) + { + return new F2mPoint(curve, A, curve.B.Sqrt(), IsCompressed); + } + + ECFieldElement X3 = A.Square().Multiply(X2Z1Sq); + ECFieldElement Z3 = A.Multiply(B).Multiply(Z1Sq); + ECFieldElement L3 = A.Add(B).Square().MultiplyPlusProduct(T, L2plus1, Z3); + + return new F2mPoint(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed); + } + default: + { + return Twice().Add(b); + } + } + } + + public override ECPoint Negate() + { + if (this.IsInfinity) + return this; + + ECFieldElement X = this.RawXCoord; + if (X.IsZero) + return this; + + ECCurve curve = this.Curve; + int coord = curve.CoordinateSystem; + + switch (coord) + { + case ECCurve.COORD_AFFINE: + { + ECFieldElement Y = this.RawYCoord; + return new F2mPoint(curve, X, Y.Add(X), IsCompressed); + } + case ECCurve.COORD_HOMOGENEOUS: + { + ECFieldElement Y = this.RawYCoord, Z = this.RawZCoords[0]; + return new F2mPoint(curve, X, Y.Add(X), new ECFieldElement[] { Z }, IsCompressed); + } + case ECCurve.COORD_LAMBDA_AFFINE: + { + ECFieldElement L = this.RawYCoord; + return new F2mPoint(curve, X, L.AddOne(), IsCompressed); + } + case ECCurve.COORD_LAMBDA_PROJECTIVE: + { + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.RawYCoord, Z = this.RawZCoords[0]; + return new F2mPoint(curve, X, L.Add(Z), new ECFieldElement[] { Z }, IsCompressed); + } + default: + { + throw new InvalidOperationException("unsupported coordinate system"); + } + } + } + } } diff --git a/crypto/src/math/ec/ECPointMap.cs b/crypto/src/math/ec/ECPointMap.cs new file mode 100644
index 000000000..e78c80065 --- /dev/null +++ b/crypto/src/math/ec/ECPointMap.cs
@@ -0,0 +1,9 @@ +using System; + +namespace Org.BouncyCastle.Math.EC +{ + public interface ECPointMap + { + ECPoint Map(ECPoint p); + } +} diff --git a/crypto/src/math/ec/LongArray.cs b/crypto/src/math/ec/LongArray.cs new file mode 100644
index 000000000..c4e3dacbc --- /dev/null +++ b/crypto/src/math/ec/LongArray.cs
@@ -0,0 +1,2201 @@ +using System; +using System.Text; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Math.EC +{ + internal class LongArray + { + //private static long DEInterleave_MASK = 0x5555555555555555L; + + /* + * This expands 8 bit indices into 16 bit contents (high bit 14), by inserting 0s between bits. + * In a binary field, this operation is the same as squaring an 8 bit number. + */ + private static readonly int[] INTERLEAVE2_TABLE = new int[] + { + 0x0000, 0x0001, 0x0004, 0x0005, 0x0010, 0x0011, 0x0014, 0x0015, + 0x0040, 0x0041, 0x0044, 0x0045, 0x0050, 0x0051, 0x0054, 0x0055, + 0x0100, 0x0101, 0x0104, 0x0105, 0x0110, 0x0111, 0x0114, 0x0115, + 0x0140, 0x0141, 0x0144, 0x0145, 0x0150, 0x0151, 0x0154, 0x0155, + 0x0400, 0x0401, 0x0404, 0x0405, 0x0410, 0x0411, 0x0414, 0x0415, + 0x0440, 0x0441, 0x0444, 0x0445, 0x0450, 0x0451, 0x0454, 0x0455, + 0x0500, 0x0501, 0x0504, 0x0505, 0x0510, 0x0511, 0x0514, 0x0515, + 0x0540, 0x0541, 0x0544, 0x0545, 0x0550, 0x0551, 0x0554, 0x0555, + 0x1000, 0x1001, 0x1004, 0x1005, 0x1010, 0x1011, 0x1014, 0x1015, + 0x1040, 0x1041, 0x1044, 0x1045, 0x1050, 0x1051, 0x1054, 0x1055, + 0x1100, 0x1101, 0x1104, 0x1105, 0x1110, 0x1111, 0x1114, 0x1115, + 0x1140, 0x1141, 0x1144, 0x1145, 0x1150, 0x1151, 0x1154, 0x1155, + 0x1400, 0x1401, 0x1404, 0x1405, 0x1410, 0x1411, 0x1414, 0x1415, + 0x1440, 0x1441, 0x1444, 0x1445, 0x1450, 0x1451, 0x1454, 0x1455, + 0x1500, 0x1501, 0x1504, 0x1505, 0x1510, 0x1511, 0x1514, 0x1515, + 0x1540, 0x1541, 0x1544, 0x1545, 0x1550, 0x1551, 0x1554, 0x1555, + 0x4000, 0x4001, 0x4004, 0x4005, 0x4010, 0x4011, 0x4014, 0x4015, + 0x4040, 0x4041, 0x4044, 0x4045, 0x4050, 0x4051, 0x4054, 0x4055, + 0x4100, 0x4101, 0x4104, 0x4105, 0x4110, 0x4111, 0x4114, 0x4115, + 0x4140, 0x4141, 0x4144, 0x4145, 0x4150, 0x4151, 0x4154, 0x4155, + 0x4400, 0x4401, 0x4404, 0x4405, 0x4410, 0x4411, 0x4414, 0x4415, + 0x4440, 0x4441, 0x4444, 0x4445, 0x4450, 0x4451, 0x4454, 0x4455, + 0x4500, 0x4501, 0x4504, 0x4505, 0x4510, 0x4511, 0x4514, 0x4515, + 0x4540, 0x4541, 0x4544, 0x4545, 0x4550, 0x4551, 0x4554, 0x4555, + 0x5000, 0x5001, 0x5004, 0x5005, 0x5010, 0x5011, 0x5014, 0x5015, + 0x5040, 0x5041, 0x5044, 0x5045, 0x5050, 0x5051, 0x5054, 0x5055, + 0x5100, 0x5101, 0x5104, 0x5105, 0x5110, 0x5111, 0x5114, 0x5115, + 0x5140, 0x5141, 0x5144, 0x5145, 0x5150, 0x5151, 0x5154, 0x5155, + 0x5400, 0x5401, 0x5404, 0x5405, 0x5410, 0x5411, 0x5414, 0x5415, + 0x5440, 0x5441, 0x5444, 0x5445, 0x5450, 0x5451, 0x5454, 0x5455, + 0x5500, 0x5501, 0x5504, 0x5505, 0x5510, 0x5511, 0x5514, 0x5515, + 0x5540, 0x5541, 0x5544, 0x5545, 0x5550, 0x5551, 0x5554, 0x5555 + }; + + /* + * This expands 7 bit indices into 21 bit contents (high bit 18), by inserting 0s between bits. + */ + private static readonly int[] INTERLEAVE3_TABLE = new int[] + { + 0x00000, 0x00001, 0x00008, 0x00009, 0x00040, 0x00041, 0x00048, 0x00049, + 0x00200, 0x00201, 0x00208, 0x00209, 0x00240, 0x00241, 0x00248, 0x00249, + 0x01000, 0x01001, 0x01008, 0x01009, 0x01040, 0x01041, 0x01048, 0x01049, + 0x01200, 0x01201, 0x01208, 0x01209, 0x01240, 0x01241, 0x01248, 0x01249, + 0x08000, 0x08001, 0x08008, 0x08009, 0x08040, 0x08041, 0x08048, 0x08049, + 0x08200, 0x08201, 0x08208, 0x08209, 0x08240, 0x08241, 0x08248, 0x08249, + 0x09000, 0x09001, 0x09008, 0x09009, 0x09040, 0x09041, 0x09048, 0x09049, + 0x09200, 0x09201, 0x09208, 0x09209, 0x09240, 0x09241, 0x09248, 0x09249, + 0x40000, 0x40001, 0x40008, 0x40009, 0x40040, 0x40041, 0x40048, 0x40049, + 0x40200, 0x40201, 0x40208, 0x40209, 0x40240, 0x40241, 0x40248, 0x40249, + 0x41000, 0x41001, 0x41008, 0x41009, 0x41040, 0x41041, 0x41048, 0x41049, + 0x41200, 0x41201, 0x41208, 0x41209, 0x41240, 0x41241, 0x41248, 0x41249, + 0x48000, 0x48001, 0x48008, 0x48009, 0x48040, 0x48041, 0x48048, 0x48049, + 0x48200, 0x48201, 0x48208, 0x48209, 0x48240, 0x48241, 0x48248, 0x48249, + 0x49000, 0x49001, 0x49008, 0x49009, 0x49040, 0x49041, 0x49048, 0x49049, + 0x49200, 0x49201, 0x49208, 0x49209, 0x49240, 0x49241, 0x49248, 0x49249 + }; + + /* + * This expands 8 bit indices into 32 bit contents (high bit 28), by inserting 0s between bits. + */ + private static readonly int[] INTERLEAVE4_TABLE = new int[] + { + 0x00000000, 0x00000001, 0x00000010, 0x00000011, 0x00000100, 0x00000101, 0x00000110, 0x00000111, + 0x00001000, 0x00001001, 0x00001010, 0x00001011, 0x00001100, 0x00001101, 0x00001110, 0x00001111, + 0x00010000, 0x00010001, 0x00010010, 0x00010011, 0x00010100, 0x00010101, 0x00010110, 0x00010111, + 0x00011000, 0x00011001, 0x00011010, 0x00011011, 0x00011100, 0x00011101, 0x00011110, 0x00011111, + 0x00100000, 0x00100001, 0x00100010, 0x00100011, 0x00100100, 0x00100101, 0x00100110, 0x00100111, + 0x00101000, 0x00101001, 0x00101010, 0x00101011, 0x00101100, 0x00101101, 0x00101110, 0x00101111, + 0x00110000, 0x00110001, 0x00110010, 0x00110011, 0x00110100, 0x00110101, 0x00110110, 0x00110111, + 0x00111000, 0x00111001, 0x00111010, 0x00111011, 0x00111100, 0x00111101, 0x00111110, 0x00111111, + 0x01000000, 0x01000001, 0x01000010, 0x01000011, 0x01000100, 0x01000101, 0x01000110, 0x01000111, + 0x01001000, 0x01001001, 0x01001010, 0x01001011, 0x01001100, 0x01001101, 0x01001110, 0x01001111, + 0x01010000, 0x01010001, 0x01010010, 0x01010011, 0x01010100, 0x01010101, 0x01010110, 0x01010111, + 0x01011000, 0x01011001, 0x01011010, 0x01011011, 0x01011100, 0x01011101, 0x01011110, 0x01011111, + 0x01100000, 0x01100001, 0x01100010, 0x01100011, 0x01100100, 0x01100101, 0x01100110, 0x01100111, + 0x01101000, 0x01101001, 0x01101010, 0x01101011, 0x01101100, 0x01101101, 0x01101110, 0x01101111, + 0x01110000, 0x01110001, 0x01110010, 0x01110011, 0x01110100, 0x01110101, 0x01110110, 0x01110111, + 0x01111000, 0x01111001, 0x01111010, 0x01111011, 0x01111100, 0x01111101, 0x01111110, 0x01111111, + 0x10000000, 0x10000001, 0x10000010, 0x10000011, 0x10000100, 0x10000101, 0x10000110, 0x10000111, + 0x10001000, 0x10001001, 0x10001010, 0x10001011, 0x10001100, 0x10001101, 0x10001110, 0x10001111, + 0x10010000, 0x10010001, 0x10010010, 0x10010011, 0x10010100, 0x10010101, 0x10010110, 0x10010111, + 0x10011000, 0x10011001, 0x10011010, 0x10011011, 0x10011100, 0x10011101, 0x10011110, 0x10011111, + 0x10100000, 0x10100001, 0x10100010, 0x10100011, 0x10100100, 0x10100101, 0x10100110, 0x10100111, + 0x10101000, 0x10101001, 0x10101010, 0x10101011, 0x10101100, 0x10101101, 0x10101110, 0x10101111, + 0x10110000, 0x10110001, 0x10110010, 0x10110011, 0x10110100, 0x10110101, 0x10110110, 0x10110111, + 0x10111000, 0x10111001, 0x10111010, 0x10111011, 0x10111100, 0x10111101, 0x10111110, 0x10111111, + 0x11000000, 0x11000001, 0x11000010, 0x11000011, 0x11000100, 0x11000101, 0x11000110, 0x11000111, + 0x11001000, 0x11001001, 0x11001010, 0x11001011, 0x11001100, 0x11001101, 0x11001110, 0x11001111, + 0x11010000, 0x11010001, 0x11010010, 0x11010011, 0x11010100, 0x11010101, 0x11010110, 0x11010111, + 0x11011000, 0x11011001, 0x11011010, 0x11011011, 0x11011100, 0x11011101, 0x11011110, 0x11011111, + 0x11100000, 0x11100001, 0x11100010, 0x11100011, 0x11100100, 0x11100101, 0x11100110, 0x11100111, + 0x11101000, 0x11101001, 0x11101010, 0x11101011, 0x11101100, 0x11101101, 0x11101110, 0x11101111, + 0x11110000, 0x11110001, 0x11110010, 0x11110011, 0x11110100, 0x11110101, 0x11110110, 0x11110111, + 0x11111000, 0x11111001, 0x11111010, 0x11111011, 0x11111100, 0x11111101, 0x11111110, 0x11111111 + }; + + /* + * This expands 7 bit indices into 35 bit contents (high bit 30), by inserting 0s between bits. + */ + private static readonly int[] INTERLEAVE5_TABLE = new int[] { + 0x00000000, 0x00000001, 0x00000020, 0x00000021, 0x00000400, 0x00000401, 0x00000420, 0x00000421, + 0x00008000, 0x00008001, 0x00008020, 0x00008021, 0x00008400, 0x00008401, 0x00008420, 0x00008421, + 0x00100000, 0x00100001, 0x00100020, 0x00100021, 0x00100400, 0x00100401, 0x00100420, 0x00100421, + 0x00108000, 0x00108001, 0x00108020, 0x00108021, 0x00108400, 0x00108401, 0x00108420, 0x00108421, + 0x02000000, 0x02000001, 0x02000020, 0x02000021, 0x02000400, 0x02000401, 0x02000420, 0x02000421, + 0x02008000, 0x02008001, 0x02008020, 0x02008021, 0x02008400, 0x02008401, 0x02008420, 0x02008421, + 0x02100000, 0x02100001, 0x02100020, 0x02100021, 0x02100400, 0x02100401, 0x02100420, 0x02100421, + 0x02108000, 0x02108001, 0x02108020, 0x02108021, 0x02108400, 0x02108401, 0x02108420, 0x02108421, + 0x40000000, 0x40000001, 0x40000020, 0x40000021, 0x40000400, 0x40000401, 0x40000420, 0x40000421, + 0x40008000, 0x40008001, 0x40008020, 0x40008021, 0x40008400, 0x40008401, 0x40008420, 0x40008421, + 0x40100000, 0x40100001, 0x40100020, 0x40100021, 0x40100400, 0x40100401, 0x40100420, 0x40100421, + 0x40108000, 0x40108001, 0x40108020, 0x40108021, 0x40108400, 0x40108401, 0x40108420, 0x40108421, + 0x42000000, 0x42000001, 0x42000020, 0x42000021, 0x42000400, 0x42000401, 0x42000420, 0x42000421, + 0x42008000, 0x42008001, 0x42008020, 0x42008021, 0x42008400, 0x42008401, 0x42008420, 0x42008421, + 0x42100000, 0x42100001, 0x42100020, 0x42100021, 0x42100400, 0x42100401, 0x42100420, 0x42100421, + 0x42108000, 0x42108001, 0x42108020, 0x42108021, 0x42108400, 0x42108401, 0x42108420, 0x42108421 + }; + + /* + * This expands 9 bit indices into 63 bit (long) contents (high bit 56), by inserting 0s between bits. + */ + private static readonly long[] INTERLEAVE7_TABLE = new long[] + { + 0x0000000000000000L, 0x0000000000000001L, 0x0000000000000080L, 0x0000000000000081L, + 0x0000000000004000L, 0x0000000000004001L, 0x0000000000004080L, 0x0000000000004081L, + 0x0000000000200000L, 0x0000000000200001L, 0x0000000000200080L, 0x0000000000200081L, + 0x0000000000204000L, 0x0000000000204001L, 0x0000000000204080L, 0x0000000000204081L, + 0x0000000010000000L, 0x0000000010000001L, 0x0000000010000080L, 0x0000000010000081L, + 0x0000000010004000L, 0x0000000010004001L, 0x0000000010004080L, 0x0000000010004081L, + 0x0000000010200000L, 0x0000000010200001L, 0x0000000010200080L, 0x0000000010200081L, + 0x0000000010204000L, 0x0000000010204001L, 0x0000000010204080L, 0x0000000010204081L, + 0x0000000800000000L, 0x0000000800000001L, 0x0000000800000080L, 0x0000000800000081L, + 0x0000000800004000L, 0x0000000800004001L, 0x0000000800004080L, 0x0000000800004081L, + 0x0000000800200000L, 0x0000000800200001L, 0x0000000800200080L, 0x0000000800200081L, + 0x0000000800204000L, 0x0000000800204001L, 0x0000000800204080L, 0x0000000800204081L, + 0x0000000810000000L, 0x0000000810000001L, 0x0000000810000080L, 0x0000000810000081L, + 0x0000000810004000L, 0x0000000810004001L, 0x0000000810004080L, 0x0000000810004081L, + 0x0000000810200000L, 0x0000000810200001L, 0x0000000810200080L, 0x0000000810200081L, + 0x0000000810204000L, 0x0000000810204001L, 0x0000000810204080L, 0x0000000810204081L, + 0x0000040000000000L, 0x0000040000000001L, 0x0000040000000080L, 0x0000040000000081L, + 0x0000040000004000L, 0x0000040000004001L, 0x0000040000004080L, 0x0000040000004081L, + 0x0000040000200000L, 0x0000040000200001L, 0x0000040000200080L, 0x0000040000200081L, + 0x0000040000204000L, 0x0000040000204001L, 0x0000040000204080L, 0x0000040000204081L, + 0x0000040010000000L, 0x0000040010000001L, 0x0000040010000080L, 0x0000040010000081L, + 0x0000040010004000L, 0x0000040010004001L, 0x0000040010004080L, 0x0000040010004081L, + 0x0000040010200000L, 0x0000040010200001L, 0x0000040010200080L, 0x0000040010200081L, + 0x0000040010204000L, 0x0000040010204001L, 0x0000040010204080L, 0x0000040010204081L, + 0x0000040800000000L, 0x0000040800000001L, 0x0000040800000080L, 0x0000040800000081L, + 0x0000040800004000L, 0x0000040800004001L, 0x0000040800004080L, 0x0000040800004081L, + 0x0000040800200000L, 0x0000040800200001L, 0x0000040800200080L, 0x0000040800200081L, + 0x0000040800204000L, 0x0000040800204001L, 0x0000040800204080L, 0x0000040800204081L, + 0x0000040810000000L, 0x0000040810000001L, 0x0000040810000080L, 0x0000040810000081L, + 0x0000040810004000L, 0x0000040810004001L, 0x0000040810004080L, 0x0000040810004081L, + 0x0000040810200000L, 0x0000040810200001L, 0x0000040810200080L, 0x0000040810200081L, + 0x0000040810204000L, 0x0000040810204001L, 0x0000040810204080L, 0x0000040810204081L, + 0x0002000000000000L, 0x0002000000000001L, 0x0002000000000080L, 0x0002000000000081L, + 0x0002000000004000L, 0x0002000000004001L, 0x0002000000004080L, 0x0002000000004081L, + 0x0002000000200000L, 0x0002000000200001L, 0x0002000000200080L, 0x0002000000200081L, + 0x0002000000204000L, 0x0002000000204001L, 0x0002000000204080L, 0x0002000000204081L, + 0x0002000010000000L, 0x0002000010000001L, 0x0002000010000080L, 0x0002000010000081L, + 0x0002000010004000L, 0x0002000010004001L, 0x0002000010004080L, 0x0002000010004081L, + 0x0002000010200000L, 0x0002000010200001L, 0x0002000010200080L, 0x0002000010200081L, + 0x0002000010204000L, 0x0002000010204001L, 0x0002000010204080L, 0x0002000010204081L, + 0x0002000800000000L, 0x0002000800000001L, 0x0002000800000080L, 0x0002000800000081L, + 0x0002000800004000L, 0x0002000800004001L, 0x0002000800004080L, 0x0002000800004081L, + 0x0002000800200000L, 0x0002000800200001L, 0x0002000800200080L, 0x0002000800200081L, + 0x0002000800204000L, 0x0002000800204001L, 0x0002000800204080L, 0x0002000800204081L, + 0x0002000810000000L, 0x0002000810000001L, 0x0002000810000080L, 0x0002000810000081L, + 0x0002000810004000L, 0x0002000810004001L, 0x0002000810004080L, 0x0002000810004081L, + 0x0002000810200000L, 0x0002000810200001L, 0x0002000810200080L, 0x0002000810200081L, + 0x0002000810204000L, 0x0002000810204001L, 0x0002000810204080L, 0x0002000810204081L, + 0x0002040000000000L, 0x0002040000000001L, 0x0002040000000080L, 0x0002040000000081L, + 0x0002040000004000L, 0x0002040000004001L, 0x0002040000004080L, 0x0002040000004081L, + 0x0002040000200000L, 0x0002040000200001L, 0x0002040000200080L, 0x0002040000200081L, + 0x0002040000204000L, 0x0002040000204001L, 0x0002040000204080L, 0x0002040000204081L, + 0x0002040010000000L, 0x0002040010000001L, 0x0002040010000080L, 0x0002040010000081L, + 0x0002040010004000L, 0x0002040010004001L, 0x0002040010004080L, 0x0002040010004081L, + 0x0002040010200000L, 0x0002040010200001L, 0x0002040010200080L, 0x0002040010200081L, + 0x0002040010204000L, 0x0002040010204001L, 0x0002040010204080L, 0x0002040010204081L, + 0x0002040800000000L, 0x0002040800000001L, 0x0002040800000080L, 0x0002040800000081L, + 0x0002040800004000L, 0x0002040800004001L, 0x0002040800004080L, 0x0002040800004081L, + 0x0002040800200000L, 0x0002040800200001L, 0x0002040800200080L, 0x0002040800200081L, + 0x0002040800204000L, 0x0002040800204001L, 0x0002040800204080L, 0x0002040800204081L, + 0x0002040810000000L, 0x0002040810000001L, 0x0002040810000080L, 0x0002040810000081L, + 0x0002040810004000L, 0x0002040810004001L, 0x0002040810004080L, 0x0002040810004081L, + 0x0002040810200000L, 0x0002040810200001L, 0x0002040810200080L, 0x0002040810200081L, + 0x0002040810204000L, 0x0002040810204001L, 0x0002040810204080L, 0x0002040810204081L, + 0x0100000000000000L, 0x0100000000000001L, 0x0100000000000080L, 0x0100000000000081L, + 0x0100000000004000L, 0x0100000000004001L, 0x0100000000004080L, 0x0100000000004081L, + 0x0100000000200000L, 0x0100000000200001L, 0x0100000000200080L, 0x0100000000200081L, + 0x0100000000204000L, 0x0100000000204001L, 0x0100000000204080L, 0x0100000000204081L, + 0x0100000010000000L, 0x0100000010000001L, 0x0100000010000080L, 0x0100000010000081L, + 0x0100000010004000L, 0x0100000010004001L, 0x0100000010004080L, 0x0100000010004081L, + 0x0100000010200000L, 0x0100000010200001L, 0x0100000010200080L, 0x0100000010200081L, + 0x0100000010204000L, 0x0100000010204001L, 0x0100000010204080L, 0x0100000010204081L, + 0x0100000800000000L, 0x0100000800000001L, 0x0100000800000080L, 0x0100000800000081L, + 0x0100000800004000L, 0x0100000800004001L, 0x0100000800004080L, 0x0100000800004081L, + 0x0100000800200000L, 0x0100000800200001L, 0x0100000800200080L, 0x0100000800200081L, + 0x0100000800204000L, 0x0100000800204001L, 0x0100000800204080L, 0x0100000800204081L, + 0x0100000810000000L, 0x0100000810000001L, 0x0100000810000080L, 0x0100000810000081L, + 0x0100000810004000L, 0x0100000810004001L, 0x0100000810004080L, 0x0100000810004081L, + 0x0100000810200000L, 0x0100000810200001L, 0x0100000810200080L, 0x0100000810200081L, + 0x0100000810204000L, 0x0100000810204001L, 0x0100000810204080L, 0x0100000810204081L, + 0x0100040000000000L, 0x0100040000000001L, 0x0100040000000080L, 0x0100040000000081L, + 0x0100040000004000L, 0x0100040000004001L, 0x0100040000004080L, 0x0100040000004081L, + 0x0100040000200000L, 0x0100040000200001L, 0x0100040000200080L, 0x0100040000200081L, + 0x0100040000204000L, 0x0100040000204001L, 0x0100040000204080L, 0x0100040000204081L, + 0x0100040010000000L, 0x0100040010000001L, 0x0100040010000080L, 0x0100040010000081L, + 0x0100040010004000L, 0x0100040010004001L, 0x0100040010004080L, 0x0100040010004081L, + 0x0100040010200000L, 0x0100040010200001L, 0x0100040010200080L, 0x0100040010200081L, + 0x0100040010204000L, 0x0100040010204001L, 0x0100040010204080L, 0x0100040010204081L, + 0x0100040800000000L, 0x0100040800000001L, 0x0100040800000080L, 0x0100040800000081L, + 0x0100040800004000L, 0x0100040800004001L, 0x0100040800004080L, 0x0100040800004081L, + 0x0100040800200000L, 0x0100040800200001L, 0x0100040800200080L, 0x0100040800200081L, + 0x0100040800204000L, 0x0100040800204001L, 0x0100040800204080L, 0x0100040800204081L, + 0x0100040810000000L, 0x0100040810000001L, 0x0100040810000080L, 0x0100040810000081L, + 0x0100040810004000L, 0x0100040810004001L, 0x0100040810004080L, 0x0100040810004081L, + 0x0100040810200000L, 0x0100040810200001L, 0x0100040810200080L, 0x0100040810200081L, + 0x0100040810204000L, 0x0100040810204001L, 0x0100040810204080L, 0x0100040810204081L, + 0x0102000000000000L, 0x0102000000000001L, 0x0102000000000080L, 0x0102000000000081L, + 0x0102000000004000L, 0x0102000000004001L, 0x0102000000004080L, 0x0102000000004081L, + 0x0102000000200000L, 0x0102000000200001L, 0x0102000000200080L, 0x0102000000200081L, + 0x0102000000204000L, 0x0102000000204001L, 0x0102000000204080L, 0x0102000000204081L, + 0x0102000010000000L, 0x0102000010000001L, 0x0102000010000080L, 0x0102000010000081L, + 0x0102000010004000L, 0x0102000010004001L, 0x0102000010004080L, 0x0102000010004081L, + 0x0102000010200000L, 0x0102000010200001L, 0x0102000010200080L, 0x0102000010200081L, + 0x0102000010204000L, 0x0102000010204001L, 0x0102000010204080L, 0x0102000010204081L, + 0x0102000800000000L, 0x0102000800000001L, 0x0102000800000080L, 0x0102000800000081L, + 0x0102000800004000L, 0x0102000800004001L, 0x0102000800004080L, 0x0102000800004081L, + 0x0102000800200000L, 0x0102000800200001L, 0x0102000800200080L, 0x0102000800200081L, + 0x0102000800204000L, 0x0102000800204001L, 0x0102000800204080L, 0x0102000800204081L, + 0x0102000810000000L, 0x0102000810000001L, 0x0102000810000080L, 0x0102000810000081L, + 0x0102000810004000L, 0x0102000810004001L, 0x0102000810004080L, 0x0102000810004081L, + 0x0102000810200000L, 0x0102000810200001L, 0x0102000810200080L, 0x0102000810200081L, + 0x0102000810204000L, 0x0102000810204001L, 0x0102000810204080L, 0x0102000810204081L, + 0x0102040000000000L, 0x0102040000000001L, 0x0102040000000080L, 0x0102040000000081L, + 0x0102040000004000L, 0x0102040000004001L, 0x0102040000004080L, 0x0102040000004081L, + 0x0102040000200000L, 0x0102040000200001L, 0x0102040000200080L, 0x0102040000200081L, + 0x0102040000204000L, 0x0102040000204001L, 0x0102040000204080L, 0x0102040000204081L, + 0x0102040010000000L, 0x0102040010000001L, 0x0102040010000080L, 0x0102040010000081L, + 0x0102040010004000L, 0x0102040010004001L, 0x0102040010004080L, 0x0102040010004081L, + 0x0102040010200000L, 0x0102040010200001L, 0x0102040010200080L, 0x0102040010200081L, + 0x0102040010204000L, 0x0102040010204001L, 0x0102040010204080L, 0x0102040010204081L, + 0x0102040800000000L, 0x0102040800000001L, 0x0102040800000080L, 0x0102040800000081L, + 0x0102040800004000L, 0x0102040800004001L, 0x0102040800004080L, 0x0102040800004081L, + 0x0102040800200000L, 0x0102040800200001L, 0x0102040800200080L, 0x0102040800200081L, + 0x0102040800204000L, 0x0102040800204001L, 0x0102040800204080L, 0x0102040800204081L, + 0x0102040810000000L, 0x0102040810000001L, 0x0102040810000080L, 0x0102040810000081L, + 0x0102040810004000L, 0x0102040810004001L, 0x0102040810004080L, 0x0102040810004081L, + 0x0102040810200000L, 0x0102040810200001L, 0x0102040810200080L, 0x0102040810200081L, + 0x0102040810204000L, 0x0102040810204001L, 0x0102040810204080L, 0x0102040810204081L + }; + + // For toString(); must have length 64 + private const string ZEROES = "0000000000000000000000000000000000000000000000000000000000000000"; + + internal static readonly 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 + }; + + // TODO make m fixed for the LongArray, and hence compute T once and for all + + private long[] m_ints; + + public LongArray(int intLen) + { + m_ints = new long[intLen]; + } + + public LongArray(long[] ints) + { + m_ints = ints; + } + + public LongArray(long[] ints, int off, int len) + { + if (off == 0 && len == ints.Length) + { + m_ints = ints; + } + else + { + m_ints = new long[len]; + Array.Copy(ints, off, m_ints, 0, len); + } + } + + public LongArray(BigInteger bigInt) + { + if (bigInt == null || bigInt.SignValue < 0) + { + throw new ArgumentException("invalid F2m field value", "bigInt"); + } + + if (bigInt.SignValue == 0) + { + m_ints = new long[] { 0L }; + return; + } + + byte[] barr = bigInt.ToByteArray(); + int barrLen = barr.Length; + int barrStart = 0; + if (barr[0] == 0) + { + // First byte is 0 to enforce highest (=sign) bit is zero. + // In this case ignore barr[0]. + barrLen--; + barrStart = 1; + } + int intLen = (barrLen + 7) / 8; + m_ints = new long[intLen]; + + int iarrJ = intLen - 1; + int rem = barrLen % 8 + barrStart; + long temp = 0; + int barrI = barrStart; + if (barrStart < rem) + { + for (; barrI < rem; barrI++) + { + temp <<= 8; + uint barrBarrI = barr[barrI]; + temp |= barrBarrI; + } + m_ints[iarrJ--] = temp; + } + + for (; iarrJ >= 0; iarrJ--) + { + temp = 0; + for (int i = 0; i < 8; i++) + { + temp <<= 8; + uint barrBarrI = barr[barrI++]; + temp |= barrBarrI; + } + m_ints[iarrJ] = temp; + } + } + + public bool IsOne() + { + long[] a = m_ints; + if (a[0] != 1L) + { + return false; + } + for (int i = 1; i < a.Length; ++i) + { + if (a[i] != 0L) + { + return false; + } + } + return true; + } + + public bool IsZero() + { + long[] a = m_ints; + for (int i = 0; i < a.Length; ++i) + { + if (a[i] != 0L) + { + return false; + } + } + return true; + } + + public int GetUsedLength() + { + return GetUsedLengthFrom(m_ints.Length); + } + + public int GetUsedLengthFrom(int from) + { + long[] a = m_ints; + from = System.Math.Min(from, a.Length); + + if (from < 1) + { + return 0; + } + + // Check if first element will act as sentinel + if (a[0] != 0) + { + while (a[--from] == 0) + { + } + return from + 1; + } + + do + { + if (a[--from] != 0) + { + return from + 1; + } + } + while (from > 0); + + return 0; + } + + public int Degree() + { + int i = m_ints.Length; + long w; + do + { + if (i == 0) + { + return 0; + } + w = m_ints[--i]; + } + while (w == 0); + + return (i << 6) + BitLength(w); + } + + private int DegreeFrom(int limit) + { + int i = (int)(((uint)limit + 62) >> 6); + long w; + do + { + if (i == 0) + { + return 0; + } + w = m_ints[--i]; + } + while (w == 0); + + return (i << 6) + BitLength(w); + } + + // private int lowestCoefficient() + // { + // for (int i = 0; i < m_ints.Length; ++i) + // { + // long mi = m_ints[i]; + // if (mi != 0) + // { + // int j = 0; + // while ((mi & 0xFFL) == 0) + // { + // j += 8; + // mi >>>= 8; + // } + // while ((mi & 1L) == 0) + // { + // ++j; + // mi >>>= 1; + // } + // return (i << 6) + j; + // } + // } + // return -1; + // } + + private static int BitLength(long w) + { + int u = (int)((ulong)w >> 32), b; + if (u == 0) + { + u = (int)w; + b = 0; + } + else + { + b = 32; + } + + int t = (int)((uint)u >> 16), k; + if (t == 0) + { + t = (int)((uint)u >> 8); + k = (t == 0) ? BitLengths[u] : 8 + BitLengths[t]; + } + else + { + int v = (int)((uint)t >> 8); + k = (v == 0) ? 16 + BitLengths[t] : 24 + BitLengths[v]; + } + + return b + k; + } + + private long[] ResizedInts(int newLen) + { + long[] newInts = new long[newLen]; + Array.Copy(m_ints, 0, newInts, 0, System.Math.Min(m_ints.Length, newLen)); + return newInts; + } + + public BigInteger ToBigInteger() + { + int usedLen = GetUsedLength(); + if (usedLen == 0) + { + return BigInteger.Zero; + } + + long highestInt = m_ints[usedLen - 1]; + byte[] temp = new byte[8]; + int barrI = 0; + bool trailingZeroBytesDone = false; + for (int j = 7; j >= 0; j--) + { + byte thisByte = (byte)((ulong)highestInt >> (8 * j)); + if (trailingZeroBytesDone || (thisByte != 0)) + { + trailingZeroBytesDone = true; + temp[barrI++] = thisByte; + } + } + + int barrLen = 8 * (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--) + { + long mi = m_ints[iarrJ]; + for (int j = 7; j >= 0; j--) + { + barr[barrI++] = (byte)((ulong)mi >> (8 * j)); + } + } + return new BigInteger(1, barr); + } + + // private static long shiftUp(long[] x, int xOff, int count) + // { + // long prev = 0; + // for (int i = 0; i < count; ++i) + // { + // long next = x[xOff + i]; + // x[xOff + i] = (next << 1) | prev; + // prev = next >>> 63; + // } + // return prev; + // } + + private static long ShiftUp(long[] x, int xOff, int count, int shift) + { + int shiftInv = 64 - shift; + long prev = 0; + for (int i = 0; i < count; ++i) + { + long next = x[xOff + i]; + x[xOff + i] = (next << shift) | prev; + prev = (long)((ulong)next >> shiftInv); + } + return prev; + } + + private static long ShiftUp(long[] x, int xOff, long[] z, int zOff, int count, int shift) + { + int shiftInv = 64 - shift; + long prev = 0; + for (int i = 0; i < count; ++i) + { + long next = x[xOff + i]; + z[zOff + i] = (next << shift) | prev; + prev = (long)((ulong)next >> shiftInv); + } + return prev; + } + + public LongArray AddOne() + { + if (m_ints.Length == 0) + { + return new LongArray(new long[]{ 1L }); + } + + int resultLen = System.Math.Max(1, GetUsedLength()); + long[] ints = ResizedInts(resultLen); + ints[0] ^= 1L; + return new LongArray(ints); + } + + // private void addShiftedByBits(LongArray other, int bits) + // { + // int words = bits >>> 6; + // int shift = bits & 0x3F; + // + // if (shift == 0) + // { + // addShiftedByWords(other, words); + // return; + // } + // + // int otherUsedLen = other.GetUsedLength(); + // if (otherUsedLen == 0) + // { + // return; + // } + // + // int minLen = otherUsedLen + words + 1; + // if (minLen > m_ints.Length) + // { + // m_ints = resizedInts(minLen); + // } + // + // long carry = addShiftedByBits(m_ints, words, other.m_ints, 0, otherUsedLen, shift); + // m_ints[otherUsedLen + words] ^= carry; + // } + + private void AddShiftedByBitsSafe(LongArray other, int otherDegree, int bits) + { + int otherLen = (int)((uint)(otherDegree + 63) >> 6); + + int words = (int)((uint)bits >> 6); + int shift = bits & 0x3F; + + if (shift == 0) + { + Add(m_ints, words, other.m_ints, 0, otherLen); + return; + } + + long carry = AddShiftedUp(m_ints, words, other.m_ints, 0, otherLen, shift); + if (carry != 0L) + { + m_ints[otherLen + words] ^= carry; + } + } + + private static long AddShiftedUp(long[] x, int xOff, long[] y, int yOff, int count, int shift) + { + int shiftInv = 64 - shift; + long prev = 0; + for (int i = 0; i < count; ++i) + { + long next = y[yOff + i]; + x[xOff + i] ^= (next << shift) | prev; + prev = (long)((ulong)next >> shiftInv); + } + return prev; + } + + private static long AddShiftedDown(long[] x, int xOff, long[] y, int yOff, int count, int shift) + { + int shiftInv = 64 - shift; + long prev = 0; + int i = count; + while (--i >= 0) + { + long next = y[yOff + i]; + x[xOff + i] ^= (long)((ulong)next >> shift) | prev; + prev = next << shiftInv; + } + return prev; + } + + public void AddShiftedByWords(LongArray other, int words) + { + int otherUsedLen = other.GetUsedLength(); + if (otherUsedLen == 0) + { + return; + } + + int minLen = otherUsedLen + words; + if (minLen > m_ints.Length) + { + m_ints = ResizedInts(minLen); + } + + Add(m_ints, words, other.m_ints, 0, otherUsedLen); + } + + private static void Add(long[] x, int xOff, long[] y, int yOff, int count) + { + for (int i = 0; i < count; ++i) + { + x[xOff + i] ^= y[yOff + i]; + } + } + + private static void Add(long[] x, int xOff, long[] y, int yOff, long[] z, int zOff, int count) + { + for (int i = 0; i < count; ++i) + { + z[zOff + i] = x[xOff + i] ^ y[yOff + i]; + } + } + + private static void AddBoth(long[] x, int xOff, long[] y1, int y1Off, long[] y2, int y2Off, int count) + { + for (int i = 0; i < count; ++i) + { + x[xOff + i] ^= y1[y1Off + i] ^ y2[y2Off + i]; + } + } + + private static void Distribute(long[] x, int src, int dst1, int dst2, int count) + { + for (int i = 0; i < count; ++i) + { + long v = x[src + i]; + x[dst1 + i] ^= v; + x[dst2 + i] ^= v; + } + } + + public int Length + { + get { return m_ints.Length; } + } + + private static void FlipWord(long[] buf, int off, int bit, long word) + { + int n = off + (int)((uint)bit >> 6); + int shift = bit & 0x3F; + if (shift == 0) + { + buf[n] ^= word; + } + else + { + buf[n] ^= word << shift; + word = (long)((ulong)word >> (64 - shift)); + if (word != 0) + { + buf[++n] ^= word; + } + } + } + + // private static long getWord(long[] buf, int off, int len, int bit) + // { + // int n = off + (bit >>> 6); + // int shift = bit & 0x3F; + // if (shift == 0) + // { + // return buf[n]; + // } + // long result = buf[n] >>> shift; + // if (++n < len) + // { + // result |= buf[n] << (64 - shift); + // } + // return result; + // } + + public bool TestBitZero() + { + return m_ints.Length > 0 && (m_ints[0] & 1L) != 0; + } + + private static bool TestBit(long[] buf, int off, int n) + { + // theInt = n / 64 + int theInt = (int)((uint)n >> 6); + // theBit = n % 64 + int theBit = n & 0x3F; + long tester = 1L << theBit; + return (buf[off + theInt] & tester) != 0; + } + + private static void FlipBit(long[] buf, int off, int n) + { + // theInt = n / 64 + int theInt = (int)((uint)n >> 6); + // theBit = n % 64 + int theBit = n & 0x3F; + long flipper = 1L << theBit; + buf[off + theInt] ^= flipper; + } + + // private static void SetBit(long[] buf, int off, int n) + // { + // // theInt = n / 64 + // int theInt = n >>> 6; + // // theBit = n % 64 + // int theBit = n & 0x3F; + // long setter = 1L << theBit; + // buf[off + theInt] |= setter; + // } + // + // private static void ClearBit(long[] buf, int off, int n) + // { + // // theInt = n / 64 + // int theInt = n >>> 6; + // // theBit = n % 64 + // int theBit = n & 0x3F; + // long setter = 1L << theBit; + // buf[off + theInt] &= ~setter; + // } + + private static void MultiplyWord(long a, long[] b, int bLen, long[] c, int cOff) + { + if ((a & 1L) != 0L) + { + Add(c, cOff, b, 0, bLen); + } + int k = 1; + while ((a = (long)((ulong)a >> 1)) != 0L) + { + if ((a & 1L) != 0L) + { + long carry = AddShiftedUp(c, cOff, b, 0, bLen, k); + if (carry != 0L) + { + c[cOff + bLen] ^= carry; + } + } + ++k; + } + } + + public LongArray ModMultiplyLD(LongArray other, int m, int[] ks) + { + /* + * Find out the degree of each argument and handle the zero cases + */ + int aDeg = Degree(); + if (aDeg == 0) + { + return this; + } + int bDeg = other.Degree(); + if (bDeg == 0) + { + return other; + } + + /* + * Swap if necessary so that A is the smaller argument + */ + LongArray A = this, B = other; + if (aDeg > bDeg) + { + A = other; B = this; + int tmp = aDeg; aDeg = bDeg; bDeg = tmp; + } + + /* + * Establish the word lengths of the arguments and result + */ + int aLen = (int)((uint)(aDeg + 63) >> 6); + int bLen = (int)((uint)(bDeg + 63) >> 6); + int cLen = (int)((uint)(aDeg + bDeg + 62) >> 6); + + if (aLen == 1) + { + long a0 = A.m_ints[0]; + if (a0 == 1L) + { + return B; + } + + /* + * Fast path for small A, with performance dependent only on the number of set bits + */ + long[] c0 = new long[cLen]; + MultiplyWord(a0, B.m_ints, bLen, c0, 0); + + /* + * Reduce the raw answer against the reduction coefficients + */ + return ReduceResult(c0, 0, cLen, m, ks); + } + + /* + * Determine if B will get bigger during shifting + */ + int bMax = (int)((uint)(bDeg + 7 + 63) >> 6); + + /* + * Lookup table for the offset of each B in the tables + */ + int[] ti = new int[16]; + + /* + * Precompute table of all 4-bit products of B + */ + long[] T0 = new long[bMax << 4]; + int tOff = bMax; + ti[1] = tOff; + Array.Copy(B.m_ints, 0, T0, tOff, bLen); + for (int i = 2; i < 16; ++i) + { + ti[i] = (tOff += bMax); + if ((i & 1) == 0) + { + ShiftUp(T0, (int)((uint)tOff >> 1), T0, tOff, bMax, 1); + } + else + { + Add(T0, bMax, T0, tOff - bMax, T0, tOff, bMax); + } + } + + /* + * Second table with all 4-bit products of B shifted 4 bits + */ + long[] T1 = new long[T0.Length]; + ShiftUp(T0, 0, T1, 0, T0.Length, 4); + // shiftUp(T0, bMax, T1, bMax, tOff, 4); + + long[] a = A.m_ints; + long[] c = new long[cLen]; + + int MASK = 0xF; + + /* + * Lopez-Dahab algorithm + */ + + for (int k = 56; k >= 0; k -= 8) + { + for (int j = 1; j < aLen; j += 2) + { + int aVal = (int)((ulong)a[j] >> k); + int u = aVal & MASK; + int v = (int)((uint)aVal >> 4) & MASK; + AddBoth(c, j - 1, T0, ti[u], T1, ti[v], bMax); + } + ShiftUp(c, 0, cLen, 8); + } + + for (int k = 56; k >= 0; k -= 8) + { + for (int j = 0; j < aLen; j += 2) + { + int aVal = (int)((ulong)a[j] >> k); + int u = aVal & MASK; + int v = (int)((uint)aVal >> 4) & MASK; + AddBoth(c, j, T0, ti[u], T1, ti[v], bMax); + } + if (k > 0) + { + ShiftUp(c, 0, cLen, 8); + } + } + + /* + * Finally the raw answer is collected, reduce it against the reduction coefficients + */ + return ReduceResult(c, 0, cLen, m, ks); + } + + public LongArray ModMultiply(LongArray other, int m, int[] ks) + { + /* + * Find out the degree of each argument and handle the zero cases + */ + int aDeg = Degree(); + if (aDeg == 0) + { + return this; + } + int bDeg = other.Degree(); + if (bDeg == 0) + { + return other; + } + + /* + * Swap if necessary so that A is the smaller argument + */ + LongArray A = this, B = other; + if (aDeg > bDeg) + { + A = other; B = this; + int tmp = aDeg; aDeg = bDeg; bDeg = tmp; + } + + /* + * Establish the word lengths of the arguments and result + */ + int aLen = (int)((uint)(aDeg + 63) >> 6); + int bLen = (int)((uint)(bDeg + 63) >> 6); + int cLen = (int)((uint)(aDeg + bDeg + 62) >> 6); + + if (aLen == 1) + { + long a0 = A.m_ints[0]; + if (a0 == 1L) + { + return B; + } + + /* + * Fast path for small A, with performance dependent only on the number of set bits + */ + long[] c0 = new long[cLen]; + MultiplyWord(a0, B.m_ints, bLen, c0, 0); + + /* + * Reduce the raw answer against the reduction coefficients + */ + return ReduceResult(c0, 0, cLen, m, ks); + } + + /* + * Determine if B will get bigger during shifting + */ + int bMax = (int)((uint)(bDeg + 7 + 63) >> 6); + + /* + * Lookup table for the offset of each B in the tables + */ + int[] ti = new int[16]; + + /* + * Precompute table of all 4-bit products of B + */ + long[] T0 = new long[bMax << 4]; + int tOff = bMax; + ti[1] = tOff; + Array.Copy(B.m_ints, 0, T0, tOff, bLen); + for (int i = 2; i < 16; ++i) + { + ti[i] = (tOff += bMax); + if ((i & 1) == 0) + { + ShiftUp(T0, (int)((uint)tOff >> 1), T0, tOff, bMax, 1); + } + else + { + Add(T0, bMax, T0, tOff - bMax, T0, tOff, bMax); + } + } + + /* + * Second table with all 4-bit products of B shifted 4 bits + */ + long[] T1 = new long[T0.Length]; + ShiftUp(T0, 0, T1, 0, T0.Length, 4); + // ShiftUp(T0, bMax, T1, bMax, tOff, 4); + + long[] a = A.m_ints; + long[] c = new long[cLen << 3]; + + int MASK = 0xF; + + /* + * Lopez-Dahab (Modified) algorithm + */ + + for (int aPos = 0; aPos < aLen; ++aPos) + { + long aVal = a[aPos]; + int cOff = aPos; + for (;;) + { + int u = (int)aVal & MASK; + aVal = (long)((ulong)aVal >> 4); + int v = (int)aVal & MASK; + AddBoth(c, cOff, T0, ti[u], T1, ti[v], bMax); + aVal = (long)((ulong)aVal >> 4); + if (aVal == 0L) + { + break; + } + cOff += cLen; + } + } + + { + int cOff = c.Length; + while ((cOff -= cLen) != 0) + { + AddShiftedUp(c, cOff - cLen, c, cOff, cLen, 8); + } + } + + /* + * Finally the raw answer is collected, reduce it against the reduction coefficients + */ + return ReduceResult(c, 0, cLen, m, ks); + } + + public LongArray ModMultiplyAlt(LongArray other, int m, int[] ks) + { + /* + * Find out the degree of each argument and handle the zero cases + */ + int aDeg = Degree(); + if (aDeg == 0) + { + return this; + } + int bDeg = other.Degree(); + if (bDeg == 0) + { + return other; + } + + /* + * Swap if necessary so that A is the smaller argument + */ + LongArray A = this, B = other; + if (aDeg > bDeg) + { + A = other; B = this; + int tmp = aDeg; aDeg = bDeg; bDeg = tmp; + } + + /* + * Establish the word lengths of the arguments and result + */ + int aLen = (int)((uint)(aDeg + 63) >> 6); + int bLen = (int)((uint)(bDeg + 63) >> 6); + int cLen = (int)((uint)(aDeg + bDeg + 62) >> 6); + + if (aLen == 1) + { + long a0 = A.m_ints[0]; + if (a0 == 1L) + { + return B; + } + + /* + * Fast path for small A, with performance dependent only on the number of set bits + */ + long[] c0 = new long[cLen]; + MultiplyWord(a0, B.m_ints, bLen, c0, 0); + + /* + * Reduce the raw answer against the reduction coefficients + */ + return ReduceResult(c0, 0, cLen, m, ks); + } + + // NOTE: This works, but is slower than width 4 processing + // if (aLen == 2) + // { + // /* + // * Use common-multiplicand optimization to save ~1/4 of the adds + // */ + // long a1 = A.m_ints[0], a2 = A.m_ints[1]; + // long aa = a1 & a2; a1 ^= aa; a2 ^= aa; + // + // long[] b = B.m_ints; + // long[] c = new long[cLen]; + // multiplyWord(aa, b, bLen, c, 1); + // add(c, 0, c, 1, cLen - 1); + // multiplyWord(a1, b, bLen, c, 0); + // multiplyWord(a2, b, bLen, c, 1); + // + // /* + // * Reduce the raw answer against the reduction coefficients + // */ + // return ReduceResult(c, 0, cLen, m, ks); + // } + + /* + * Determine the parameters of the Interleaved window algorithm: the 'width' in bits to + * process together, the number of evaluation 'positions' implied by that width, and the + * 'top' position at which the regular window algorithm stops. + */ + int width, positions, top, banks; + + // NOTE: width 4 is the fastest over the entire range of sizes used in current crypto + // width = 1; positions = 64; top = 64; banks = 4; + // width = 2; positions = 32; top = 64; banks = 4; + // width = 3; positions = 21; top = 63; banks = 3; + width = 4; positions = 16; top = 64; banks = 8; + // width = 5; positions = 13; top = 65; banks = 7; + // width = 7; positions = 9; top = 63; banks = 9; + // width = 8; positions = 8; top = 64; banks = 8; + + /* + * Determine if B will get bigger during shifting + */ + int shifts = top < 64 ? positions : positions - 1; + int bMax = (int)((uint)(bDeg + shifts + 63) >> 6); + + int bTotal = bMax * banks, stride = width * banks; + + /* + * Create a single temporary buffer, with an offset table to find the positions of things in it + */ + int[] ci = new int[1 << width]; + int cTotal = aLen; + { + ci[0] = cTotal; + cTotal += bTotal; + ci[1] = cTotal; + for (int i = 2; i < ci.Length; ++i) + { + cTotal += cLen; + ci[i] = cTotal; + } + cTotal += cLen; + } + // NOTE: Provide a safe dump for "high zeroes" since we are adding 'bMax' and not 'bLen' + ++cTotal; + + long[] c = new long[cTotal]; + + // Prepare A in Interleaved form, according to the chosen width + Interleave(A.m_ints, 0, c, 0, aLen, width); + + // Make a working copy of B, since we will be shifting it + { + int bOff = aLen; + Array.Copy(B.m_ints, 0, c, bOff, bLen); + for (int bank = 1; bank < banks; ++bank) + { + ShiftUp(c, aLen, c, bOff += bMax, bMax, bank); + } + } + + /* + * The main loop analyzes the Interleaved windows in A, and for each non-zero window + * a single word-array XOR is performed to a carefully selected slice of 'c'. The loop is + * breadth-first, checking the lowest window in each word, then looping again for the + * next higher window position. + */ + int MASK = (1 << width) - 1; + + int k = 0; + for (;;) + { + int aPos = 0; + do + { + long aVal = (long)((ulong)c[aPos] >> k); + int bank = 0, bOff = aLen; + for (;;) + { + int index = (int)(aVal) & MASK; + if (index != 0) + { + /* + * Add to a 'c' buffer based on the bit-pattern of 'index'. Since A is in + * Interleaved form, the bits represent the current B shifted by 0, 'positions', + * 'positions' * 2, ..., 'positions' * ('width' - 1) + */ + Add(c, aPos + ci[index], c, bOff, bMax); + } + if (++bank == banks) + { + break; + } + bOff += bMax; + aVal = (long)((ulong)aVal >> width); + } + } + while (++aPos < aLen); + + if ((k += stride) >= top) + { + if (k >= 64) + { + break; + } + + /* + * Adjustment for window setups with top == 63, the final bit (if any) is processed + * as the top-bit of a window + */ + k = 64 - width; + MASK &= MASK << (top - k); + } + + /* + * After each position has been checked for all words of A, B is shifted up 1 place + */ + ShiftUp(c, aLen, bTotal, banks); + } + + int ciPos = ci.Length; + while (--ciPos > 1) + { + if ((ciPos & 1L) == 0L) + { + /* + * For even numbers, shift contents and add to the half-position + */ + AddShiftedUp(c, ci[(uint)ciPos >> 1], c, ci[ciPos], cLen, positions); + } + else + { + /* + * For odd numbers, 'distribute' contents to the result and the next-lowest position + */ + Distribute(c, ci[ciPos], ci[ciPos - 1], ci[1], cLen); + } + } + + /* + * Finally the raw answer is collected, reduce it against the reduction coefficients + */ + return ReduceResult(c, ci[1], cLen, m, ks); + } + + public LongArray ModReduce(int m, int[] ks) + { + long[] buf = Arrays.Clone(m_ints); + int rLen = ReduceInPlace(buf, 0, buf.Length, m, ks); + return new LongArray(buf, 0, rLen); + } + + public LongArray Multiply(LongArray other, int m, int[] ks) + { + /* + * Find out the degree of each argument and handle the zero cases + */ + int aDeg = Degree(); + if (aDeg == 0) + { + return this; + } + int bDeg = other.Degree(); + if (bDeg == 0) + { + return other; + } + + /* + * Swap if necessary so that A is the smaller argument + */ + LongArray A = this, B = other; + if (aDeg > bDeg) + { + A = other; B = this; + int tmp = aDeg; aDeg = bDeg; bDeg = tmp; + } + + /* + * Establish the word lengths of the arguments and result + */ + int aLen = (int)((uint)(aDeg + 63) >> 6); + int bLen = (int)((uint)(bDeg + 63) >> 6); + int cLen = (int)((uint)(aDeg + bDeg + 62) >> 6); + + if (aLen == 1) + { + long a0 = A.m_ints[0]; + if (a0 == 1L) + { + return B; + } + + /* + * Fast path for small A, with performance dependent only on the number of set bits + */ + long[] c0 = new long[cLen]; + MultiplyWord(a0, B.m_ints, bLen, c0, 0); + + /* + * Reduce the raw answer against the reduction coefficients + */ + //return ReduceResult(c0, 0, cLen, m, ks); + return new LongArray(c0, 0, cLen); + } + + /* + * Determine if B will get bigger during shifting + */ + int bMax = (int)((uint)(bDeg + 7 + 63) >> 6); + + /* + * Lookup table for the offset of each B in the tables + */ + int[] ti = new int[16]; + + /* + * Precompute table of all 4-bit products of B + */ + long[] T0 = new long[bMax << 4]; + int tOff = bMax; + ti[1] = tOff; + Array.Copy(B.m_ints, 0, T0, tOff, bLen); + for (int i = 2; i < 16; ++i) + { + ti[i] = (tOff += bMax); + if ((i & 1) == 0) + { + ShiftUp(T0, (int)((uint)tOff >> 1), T0, tOff, bMax, 1); + } + else + { + Add(T0, bMax, T0, tOff - bMax, T0, tOff, bMax); + } + } + + /* + * Second table with all 4-bit products of B shifted 4 bits + */ + long[] T1 = new long[T0.Length]; + ShiftUp(T0, 0, T1, 0, T0.Length, 4); + // ShiftUp(T0, bMax, T1, bMax, tOff, 4); + + long[] a = A.m_ints; + long[] c = new long[cLen << 3]; + + int MASK = 0xF; + + /* + * Lopez-Dahab (Modified) algorithm + */ + + for (int aPos = 0; aPos < aLen; ++aPos) + { + long aVal = a[aPos]; + int cOff = aPos; + for (; ; ) + { + int u = (int)aVal & MASK; + aVal = (long)((ulong)aVal >> 4); + int v = (int)aVal & MASK; + AddBoth(c, cOff, T0, ti[u], T1, ti[v], bMax); + aVal = (long)((ulong)aVal >> 4); + if (aVal == 0L) + { + break; + } + cOff += cLen; + } + } + + { + int cOff = c.Length; + while ((cOff -= cLen) != 0) + { + AddShiftedUp(c, cOff - cLen, c, cOff, cLen, 8); + } + } + + /* + * Finally the raw answer is collected, reduce it against the reduction coefficients + */ + //return ReduceResult(c, 0, cLen, m, ks); + return new LongArray(c, 0, cLen); + } + + public void Reduce(int m, int[] ks) + { + long[] buf = m_ints; + int rLen = ReduceInPlace(buf, 0, buf.Length, m, ks); + if (rLen < buf.Length) + { + m_ints = new long[rLen]; + Array.Copy(buf, 0, m_ints, 0, rLen); + } + } + + private static LongArray ReduceResult(long[] buf, int off, int len, int m, int[] ks) + { + int rLen = ReduceInPlace(buf, off, len, m, ks); + return new LongArray(buf, off, rLen); + } + + // private static void deInterleave(long[] x, int xOff, long[] z, int zOff, int count, int rounds) + // { + // for (int i = 0; i < count; ++i) + // { + // z[zOff + i] = deInterleave(x[zOff + i], rounds); + // } + // } + // + // private static long deInterleave(long x, int rounds) + // { + // while (--rounds >= 0) + // { + // x = deInterleave32(x & DEInterleave_MASK) | (deInterleave32((x >>> 1) & DEInterleave_MASK) << 32); + // } + // return x; + // } + // + // private static long deInterleave32(long x) + // { + // x = (x | (x >>> 1)) & 0x3333333333333333L; + // x = (x | (x >>> 2)) & 0x0F0F0F0F0F0F0F0FL; + // x = (x | (x >>> 4)) & 0x00FF00FF00FF00FFL; + // x = (x | (x >>> 8)) & 0x0000FFFF0000FFFFL; + // x = (x | (x >>> 16)) & 0x00000000FFFFFFFFL; + // return x; + // } + + private static int ReduceInPlace(long[] buf, int off, int len, int m, int[] ks) + { + int mLen = (m + 63) >> 6; + if (len < mLen) + { + return len; + } + + int numBits = System.Math.Min(len << 6, (m << 1) - 1); // TODO use actual degree? + int excessBits = (len << 6) - numBits; + while (excessBits >= 64) + { + --len; + excessBits -= 64; + } + + int kLen = ks.Length, kMax = ks[kLen - 1], kNext = kLen > 1 ? ks[kLen - 2] : 0; + int wordWiseLimit = System.Math.Max(m, kMax + 64); + int vectorableWords = (excessBits + System.Math.Min(numBits - wordWiseLimit, m - kNext)) >> 6; + if (vectorableWords > 1) + { + int vectorWiseWords = len - vectorableWords; + ReduceVectorWise(buf, off, len, vectorWiseWords, m, ks); + while (len > vectorWiseWords) + { + buf[off + --len] = 0L; + } + numBits = vectorWiseWords << 6; + } + + if (numBits > wordWiseLimit) + { + ReduceWordWise(buf, off, len, wordWiseLimit, m, ks); + numBits = wordWiseLimit; + } + + if (numBits > m) + { + ReduceBitWise(buf, off, numBits, m, ks); + } + + return mLen; + } + + private static void ReduceBitWise(long[] buf, int off, int BitLength, int m, int[] ks) + { + while (--BitLength >= m) + { + if (TestBit(buf, off, BitLength)) + { + ReduceBit(buf, off, BitLength, m, ks); + } + } + } + + private static void ReduceBit(long[] buf, int off, int bit, int m, int[] ks) + { + FlipBit(buf, off, bit); + int n = bit - m; + int j = ks.Length; + while (--j >= 0) + { + FlipBit(buf, off, ks[j] + n); + } + FlipBit(buf, off, n); + } + + private static void ReduceWordWise(long[] buf, int off, int len, int toBit, int m, int[] ks) + { + int toPos = (int)((uint)toBit >> 6); + + while (--len > toPos) + { + long word = buf[off + len]; + if (word != 0) + { + buf[off + len] = 0; + ReduceWord(buf, off, (len << 6), word, m, ks); + } + } + + { + int partial = toBit & 0x3F; + long word = (long)((ulong)buf[off + toPos] >> partial); + if (word != 0) + { + buf[off + toPos] ^= word << partial; + ReduceWord(buf, off, toBit, word, m, ks); + } + } + } + + private static void ReduceWord(long[] buf, int off, int bit, long word, int m, int[] ks) + { + int offset = bit - m; + int j = ks.Length; + while (--j >= 0) + { + FlipWord(buf, off, offset + ks[j], word); + } + FlipWord(buf, off, offset, word); + } + + private static void ReduceVectorWise(long[] buf, int off, int len, int words, int m, int[] ks) + { + /* + * NOTE: It's important we go from highest coefficient to lowest, because for the highest + * one (only) we allow the ranges to partially overlap, and therefore any changes must take + * effect for the subsequent lower coefficients. + */ + int baseBit = (words << 6) - m; + int j = ks.Length; + while (--j >= 0) + { + FlipVector(buf, off, buf, off + words, len - words, baseBit + ks[j]); + } + FlipVector(buf, off, buf, off + words, len - words, baseBit); + } + + private static void FlipVector(long[] x, int xOff, long[] y, int yOff, int yLen, int bits) + { + xOff += (int)((uint)bits >> 6); + bits &= 0x3F; + + if (bits == 0) + { + Add(x, xOff, y, yOff, yLen); + } + else + { + long carry = AddShiftedDown(x, xOff + 1, y, yOff, yLen, 64 - bits); + x[xOff] ^= carry; + } + } + + public LongArray ModSquare(int m, int[] ks) + { + int len = GetUsedLength(); + if (len == 0) + { + return this; + } + + int _2len = len << 1; + long[] r = new long[_2len]; + + int pos = 0; + while (pos < _2len) + { + long mi = m_ints[(uint)pos >> 1]; + r[pos++] = Interleave2_32to64((int)mi); + r[pos++] = Interleave2_32to64((int)((ulong)mi >> 32)); + } + + return new LongArray(r, 0, ReduceInPlace(r, 0, r.Length, m, ks)); + } + + public LongArray ModSquareN(int n, int m, int[] ks) + { + int len = GetUsedLength(); + if (len == 0) + { + return this; + } + + int mLen = (m + 63) >> 6; + long[] r = new long[mLen << 1]; + Array.Copy(m_ints, 0, r, 0, len); + + while (--n >= 0) + { + SquareInPlace(r, len, m, ks); + len = ReduceInPlace(r, 0, r.Length, m, ks); + } + + return new LongArray(r, 0, len); + } + + public LongArray Square(int m, int[] ks) + { + int len = GetUsedLength(); + if (len == 0) + { + return this; + } + + int _2len = len << 1; + long[] r = new long[_2len]; + + int pos = 0; + while (pos < _2len) + { + long mi = m_ints[(uint)pos >> 1]; + r[pos++] = Interleave2_32to64((int)mi); + r[pos++] = Interleave2_32to64((int)((ulong)mi >> 32)); + } + + return new LongArray(r, 0, r.Length); + } + + private static void SquareInPlace(long[] x, int xLen, int m, int[] ks) + { + int pos = xLen << 1; + while (--xLen >= 0) + { + long xVal = x[xLen]; + x[--pos] = Interleave2_32to64((int)((ulong)xVal >> 32)); + x[--pos] = Interleave2_32to64((int)xVal); + } + } + + private static void Interleave(long[] x, int xOff, long[] z, int zOff, int count, int width) + { + switch (width) + { + case 3: + Interleave3(x, xOff, z, zOff, count); + break; + case 5: + Interleave5(x, xOff, z, zOff, count); + break; + case 7: + Interleave7(x, xOff, z, zOff, count); + break; + default: + Interleave2_n(x, xOff, z, zOff, count, BitLengths[width] - 1); + break; + } + } + + private static void Interleave3(long[] x, int xOff, long[] z, int zOff, int count) + { + for (int i = 0; i < count; ++i) + { + z[zOff + i] = Interleave3(x[xOff + i]); + } + } + + private static long Interleave3(long x) + { + long z = x & (1L << 63); + return z + | Interleave3_21to63((int)x & 0x1FFFFF) + | Interleave3_21to63((int)((ulong)x >> 21) & 0x1FFFFF) << 1 + | Interleave3_21to63((int)((ulong)x >> 42) & 0x1FFFFF) << 2; + + // int zPos = 0, wPos = 0, xPos = 0; + // for (;;) + // { + // z |= ((x >>> xPos) & 1L) << zPos; + // if (++zPos == 63) + // { + // String sz2 = Long.toBinaryString(z); + // return z; + // } + // if ((xPos += 21) >= 63) + // { + // xPos = ++wPos; + // } + // } + } + + private static long Interleave3_21to63(int x) + { + int r00 = INTERLEAVE3_TABLE[x & 0x7F]; + int r21 = INTERLEAVE3_TABLE[((uint)x >> 7) & 0x7F]; + int r42 = INTERLEAVE3_TABLE[(uint)x >> 14]; + return (r42 & 0xFFFFFFFFL) << 42 | (r21 & 0xFFFFFFFFL) << 21 | (r00 & 0xFFFFFFFFL); + } + + private static void Interleave5(long[] x, int xOff, long[] z, int zOff, int count) + { + for (int i = 0; i < count; ++i) + { + z[zOff + i] = Interleave5(x[xOff + i]); + } + } + + private static long Interleave5(long x) + { + return Interleave3_13to65((int)x & 0x1FFF) + | Interleave3_13to65((int)((ulong)x >> 13) & 0x1FFF) << 1 + | Interleave3_13to65((int)((ulong)x >> 26) & 0x1FFF) << 2 + | Interleave3_13to65((int)((ulong)x >> 39) & 0x1FFF) << 3 + | Interleave3_13to65((int)((ulong)x >> 52) & 0x1FFF) << 4; + + // long z = 0; + // int zPos = 0, wPos = 0, xPos = 0; + // for (;;) + // { + // z |= ((x >>> xPos) & 1L) << zPos; + // if (++zPos == 64) + // { + // return z; + // } + // if ((xPos += 13) >= 64) + // { + // xPos = ++wPos; + // } + // } + } + + private static long Interleave3_13to65(int x) + { + int r00 = INTERLEAVE5_TABLE[x & 0x7F]; + int r35 = INTERLEAVE5_TABLE[(uint)x >> 7]; + return (r35 & 0xFFFFFFFFL) << 35 | (r00 & 0xFFFFFFFFL); + } + + private static void Interleave7(long[] x, int xOff, long[] z, int zOff, int count) + { + for (int i = 0; i < count; ++i) + { + z[zOff + i] = Interleave7(x[xOff + i]); + } + } + + private static long Interleave7(long x) + { + long z = x & (1L << 63); + return z + | INTERLEAVE7_TABLE[(int)x & 0x1FF] + | INTERLEAVE7_TABLE[(int)((ulong)x >> 9) & 0x1FF] << 1 + | INTERLEAVE7_TABLE[(int)((ulong)x >> 18) & 0x1FF] << 2 + | INTERLEAVE7_TABLE[(int)((ulong)x >> 27) & 0x1FF] << 3 + | INTERLEAVE7_TABLE[(int)((ulong)x >> 36) & 0x1FF] << 4 + | INTERLEAVE7_TABLE[(int)((ulong)x >> 45) & 0x1FF] << 5 + | INTERLEAVE7_TABLE[(int)((ulong)x >> 54) & 0x1FF] << 6; + + // int zPos = 0, wPos = 0, xPos = 0; + // for (;;) + // { + // z |= ((x >>> xPos) & 1L) << zPos; + // if (++zPos == 63) + // { + // return z; + // } + // if ((xPos += 9) >= 63) + // { + // xPos = ++wPos; + // } + // } + } + + private static void Interleave2_n(long[] x, int xOff, long[] z, int zOff, int count, int rounds) + { + for (int i = 0; i < count; ++i) + { + z[zOff + i] = Interleave2_n(x[xOff + i], rounds); + } + } + + private static long Interleave2_n(long x, int rounds) + { + while (rounds > 1) + { + rounds -= 2; + x = Interleave4_16to64((int)x & 0xFFFF) + | Interleave4_16to64((int)((ulong)x >> 16) & 0xFFFF) << 1 + | Interleave4_16to64((int)((ulong)x >> 32) & 0xFFFF) << 2 + | Interleave4_16to64((int)((ulong)x >> 48) & 0xFFFF) << 3; + } + if (rounds > 0) + { + x = Interleave2_32to64((int)x) | Interleave2_32to64((int)((ulong)x >> 32)) << 1; + } + return x; + } + + private static long Interleave4_16to64(int x) + { + int r00 = INTERLEAVE4_TABLE[x & 0xFF]; + int r32 = INTERLEAVE4_TABLE[(uint)x >> 8]; + return (r32 & 0xFFFFFFFFL) << 32 | (r00 & 0xFFFFFFFFL); + } + + private static long Interleave2_32to64(int x) + { + int r00 = INTERLEAVE2_TABLE[x & 0xFF] | INTERLEAVE2_TABLE[((uint)x >> 8) & 0xFF] << 16; + int r32 = INTERLEAVE2_TABLE[((uint)x >> 16) & 0xFF] | INTERLEAVE2_TABLE[(uint)x >> 24] << 16; + return (r32 & 0xFFFFFFFFL) << 32 | (r00 & 0xFFFFFFFFL); + } + + // private static LongArray ExpItohTsujii2(LongArray B, int n, int m, int[] ks) + // { + // LongArray t1 = B, t3 = new LongArray(new long[]{ 1L }); + // int scale = 1; + // + // int numTerms = n; + // while (numTerms > 1) + // { + // if ((numTerms & 1) != 0) + // { + // t3 = t3.ModMultiply(t1, m, ks); + // t1 = t1.modSquareN(scale, m, ks); + // } + // + // LongArray t2 = t1.modSquareN(scale, m, ks); + // t1 = t1.ModMultiply(t2, m, ks); + // numTerms >>>= 1; scale <<= 1; + // } + // + // return t3.ModMultiply(t1, m, ks); + // } + // + // private static LongArray ExpItohTsujii23(LongArray B, int n, int m, int[] ks) + // { + // LongArray t1 = B, t3 = new LongArray(new long[]{ 1L }); + // int scale = 1; + // + // int numTerms = n; + // while (numTerms > 1) + // { + // bool m03 = numTerms % 3 == 0; + // bool m14 = !m03 && (numTerms & 1) != 0; + // + // if (m14) + // { + // t3 = t3.ModMultiply(t1, m, ks); + // t1 = t1.modSquareN(scale, m, ks); + // } + // + // LongArray t2 = t1.modSquareN(scale, m, ks); + // t1 = t1.ModMultiply(t2, m, ks); + // + // if (m03) + // { + // t2 = t2.modSquareN(scale, m, ks); + // t1 = t1.ModMultiply(t2, m, ks); + // numTerms /= 3; scale *= 3; + // } + // else + // { + // numTerms >>>= 1; scale <<= 1; + // } + // } + // + // return t3.ModMultiply(t1, m, ks); + // } + // + // private static LongArray ExpItohTsujii235(LongArray B, int n, int m, int[] ks) + // { + // LongArray t1 = B, t4 = new LongArray(new long[]{ 1L }); + // int scale = 1; + // + // int numTerms = n; + // while (numTerms > 1) + // { + // if (numTerms % 5 == 0) + // { + //// t1 = ExpItohTsujii23(t1, 5, m, ks); + // + // LongArray t3 = t1; + // t1 = t1.modSquareN(scale, m, ks); + // + // LongArray t2 = t1.modSquareN(scale, m, ks); + // t1 = t1.ModMultiply(t2, m, ks); + // t2 = t1.modSquareN(scale << 1, m, ks); + // t1 = t1.ModMultiply(t2, m, ks); + // + // t1 = t1.ModMultiply(t3, m, ks); + // + // numTerms /= 5; scale *= 5; + // continue; + // } + // + // bool m03 = numTerms % 3 == 0; + // bool m14 = !m03 && (numTerms & 1) != 0; + // + // if (m14) + // { + // t4 = t4.ModMultiply(t1, m, ks); + // t1 = t1.modSquareN(scale, m, ks); + // } + // + // LongArray t2 = t1.modSquareN(scale, m, ks); + // t1 = t1.ModMultiply(t2, m, ks); + // + // if (m03) + // { + // t2 = t2.modSquareN(scale, m, ks); + // t1 = t1.ModMultiply(t2, m, ks); + // numTerms /= 3; scale *= 3; + // } + // else + // { + // numTerms >>>= 1; scale <<= 1; + // } + // } + // + // return t4.ModMultiply(t1, m, ks); + // } + + public LongArray ModInverse(int m, int[] ks) + { + /* + * Fermat's Little Theorem + */ + // LongArray A = this; + // LongArray B = A.modSquare(m, ks); + // LongArray R0 = B, R1 = B; + // for (int i = 2; i < m; ++i) + // { + // R1 = R1.modSquare(m, ks); + // R0 = R0.ModMultiply(R1, m, ks); + // } + // + // return R0; + + /* + * Itoh-Tsujii + */ + // LongArray B = modSquare(m, ks); + // switch (m) + // { + // case 409: + // return ExpItohTsujii23(B, m - 1, m, ks); + // case 571: + // return ExpItohTsujii235(B, m - 1, m, ks); + // case 163: + // case 233: + // case 283: + // default: + // return ExpItohTsujii2(B, m - 1, m, ks); + // } + + /* + * 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) + */ + int uzDegree = Degree(); + if (uzDegree == 0) + { + throw new InvalidOperationException(); + } + if (uzDegree == 1) + { + return this; + } + + // u(z) := a(z) + LongArray uz = (LongArray)Copy(); + + int t = (m + 63) >> 6; + + // v(z) := f(z) + LongArray vz = new LongArray(t); + ReduceBit(vz.m_ints, 0, m, m, ks); + + // g1(z) := 1, g2(z) := 0 + LongArray g1z = new LongArray(t); + g1z.m_ints[0] = 1L; + LongArray g2z = new LongArray(t); + + int[] uvDeg = new int[]{ uzDegree, m + 1 }; + LongArray[] uv = new LongArray[]{ uz, vz }; + + int[] ggDeg = new int[]{ 1, 0 }; + LongArray[] gg = new LongArray[]{ g1z, g2z }; + + int b = 1; + int duv1 = uvDeg[b]; + int dgg1 = ggDeg[b]; + int j = duv1 - uvDeg[1 - b]; + + for (;;) + { + if (j < 0) + { + j = -j; + uvDeg[b] = duv1; + ggDeg[b] = dgg1; + b = 1 - b; + duv1 = uvDeg[b]; + dgg1 = ggDeg[b]; + } + + uv[b].AddShiftedByBitsSafe(uv[1 - b], uvDeg[1 - b], j); + + int duv2 = uv[b].DegreeFrom(duv1); + if (duv2 == 0) + { + return gg[1 - b]; + } + + { + int dgg2 = ggDeg[1 - b]; + gg[b].AddShiftedByBitsSafe(gg[1 - b], dgg2, j); + dgg2 += j; + + if (dgg2 > dgg1) + { + dgg1 = dgg2; + } + else if (dgg2 == dgg1) + { + dgg1 = gg[b].DegreeFrom(dgg1); + } + } + + j += (duv2 - duv1); + duv1 = duv2; + } + } + + public override bool Equals(object obj) + { + return Equals(obj as LongArray); + } + + public virtual bool Equals(LongArray other) + { + if (this == other) + return true; + if (null == other) + return false; + 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 usedLen = GetUsedLength(); + int hash = 1; + for (int i = 0; i < usedLen; i++) + { + long mi = m_ints[i]; + hash *= 31; + hash ^= (int)mi; + hash *= 31; + hash ^= (int)((ulong)mi >> 32); + } + return hash; + } + + public LongArray Copy() + { + return new LongArray(Arrays.Clone(m_ints)); + } + + public override string ToString() + { + int i = GetUsedLength(); + if (i == 0) + { + return "0"; + } + + StringBuilder sb = new StringBuilder(Convert.ToString(m_ints[--i], 2)); + while (--i >= 0) + { + string s = Convert.ToString(m_ints[i], 2); + + // Add leading zeroes, except for highest significant word + int len = s.Length; + if (len < 64) + { + sb.Append(ZEROES.Substring(len)); + } + + sb.Append(s); + } + return sb.ToString(); + } + } +} diff --git a/crypto/src/math/ec/Mod.cs b/crypto/src/math/ec/Mod.cs new file mode 100644
index 000000000..80534ca9f --- /dev/null +++ b/crypto/src/math/ec/Mod.cs
@@ -0,0 +1,184 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Crypto.Utilities; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Math.EC +{ + internal abstract class Mod + { + public static void Invert(uint[] p, uint[] x, uint[] z) + { + int len = p.Length; + if (Nat.IsZero(len, x)) + throw new ArgumentException("cannot be 0", "x"); + if (Nat.IsOne(len, x)) + { + Array.Copy(x, 0, z, 0, len); + return; + } + + uint[] u = Nat.Copy(len, x); + uint[] a = Nat.Create(len); + a[0] = 1; + int ac = 0; + + if ((u[0] & 1) == 0) + { + InversionStep(p, u, len, a, ref ac); + } + if (Nat.IsOne(len, u)) + { + InversionResult(p, ac, a, z); + return; + } + + uint[] v = Nat.Copy(len, p); + uint[] b = Nat.Create(len); + int bc = 0; + + int uvLen = len; + + for (;;) + { + while (u[uvLen - 1] == 0 && v[uvLen - 1] == 0) + { + --uvLen; + } + + if (Nat.Gte(len, u, v)) + { + Nat.SubFrom(len, v, u); + Debug.Assert((u[0] & 1) == 0); + ac += Nat.SubFrom(len, b, a) - bc; + InversionStep(p, u, uvLen, a, ref ac); + if (Nat.IsOne(len, u)) + { + InversionResult(p, ac, a, z); + return; + } + } + else + { + Nat.SubFrom(len, u, v); + Debug.Assert((v[0] & 1) == 0); + bc += Nat.SubFrom(len, a, b) - ac; + InversionStep(p, v, uvLen, b, ref bc); + if (Nat.IsOne(len, v)) + { + InversionResult(p, bc, b, z); + return; + } + } + } + } + + public static uint[] Random(uint[] p) + { + int len = p.Length; + Random rand = new Random(); + uint[] s = Nat.Create(len); + + uint m = p[len - 1]; + m |= m >> 1; + m |= m >> 2; + m |= m >> 4; + m |= m >> 8; + m |= m >> 16; + + do + { + byte[] bytes = new byte[len << 2]; + rand.NextBytes(bytes); + Pack.BE_To_UInt32(bytes, 0, s); + s[len - 1] &= m; + } + while (Nat.Gte(len, s, p)); + + return s; + } + + public static void Add(uint[] p, uint[] x, uint[] y, uint[] z) + { + int len = p.Length; + uint c = Nat.Add(len, x, y, z); + if (c != 0) + { + Nat.SubFrom(len, p, z); + } + } + + public static void Subtract(uint[] p, uint[] x, uint[] y, uint[] z) + { + int len = p.Length; + int c = Nat.Sub(len, x, y, z); + if (c != 0) + { + Nat.AddTo(len, p, z); + } + } + + private static void InversionResult(uint[] p, int ac, uint[] a, uint[] z) + { + if (ac < 0) + { + Nat.Add(p.Length, a, p, z); + } + else + { + Array.Copy(a, 0, z, 0, p.Length); + } + } + + private static void InversionStep(uint[] p, uint[] u, int uLen, uint[] x, ref int xc) + { + int len = p.Length; + int count = 0; + while (u[0] == 0) + { + Nat.ShiftDownWord(uLen, u, 0); + count += 32; + } + + { + int zeroes = GetTrailingZeroes(u[0]); + if (zeroes > 0) + { + Nat.ShiftDownBits(uLen, u, zeroes, 0); + count += zeroes; + } + } + + for (int i = 0; i < count; ++i) + { + if ((x[0] & 1) != 0) + { + if (xc < 0) + { + xc += (int)Nat.AddTo(len, p, x); + } + else + { + xc += Nat.SubFrom(len, p, x); + } + } + + Debug.Assert(xc == 0 || xc == -1); + Nat.ShiftDownBit(len, x, (uint)xc); + } + } + + private static int GetTrailingZeroes(uint x) + { + Debug.Assert(x != 0); + int count = 0; + while ((x & 1) == 0) + { + x >>= 1; + ++count; + } + return count; + } + } +} diff --git a/crypto/src/math/ec/Nat.cs b/crypto/src/math/ec/Nat.cs new file mode 100644
index 000000000..17b632f26 --- /dev/null +++ b/crypto/src/math/ec/Nat.cs
@@ -0,0 +1,1013 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Math.EC +{ + internal abstract class Nat + { + private const ulong M = 0xFFFFFFFFUL; + + public static uint Add(int len, uint[] x, uint[] y, uint[] z) + { + ulong c = 0; + for (int i = 0; i < len; ++i) + { + c += (ulong)x[i] + y[i]; + z[i] = (uint)c; + c >>= 32; + } + return (uint)c; + } + + public static uint Add33At(int len, uint x, uint[] z, int zPos) + { + Debug.Assert(zPos <= (len - 2)); + ulong c = (ulong)z[zPos + 0] + x; + z[zPos + 0] = (uint)c; + c >>= 32; + c += (ulong)z[zPos + 1] + 1; + z[zPos + 1] = (uint)c; + c >>= 32; + return c == 0 ? 0 : IncAt(len, z, zPos + 2); + } + + public static uint Add33At(int len, uint x, uint[] z, int zOff, int zPos) + { + Debug.Assert(zPos <= (len - 2)); + ulong c = (ulong)z[zOff + zPos] + x; + z[zOff + zPos] = (uint)c; + c >>= 32; + c += (ulong)z[zOff + zPos + 1] + 1; + z[zOff + zPos + 1] = (uint)c; + c >>= 32; + return c == 0 ? 0 : IncAt(len, z, zOff, zPos + 2); + } + + public static uint Add33To(int len, uint x, uint[] z) + { + ulong c = (ulong)z[0] + x; + z[0] = (uint)c; + c >>= 32; + c += (ulong)z[1] + 1; + z[1] = (uint)c; + c >>= 32; + return c == 0 ? 0 : IncAt(len, z, 2); + } + + public static uint Add33To(int len, uint x, uint[] z, int zOff) + { + ulong c = (ulong)z[zOff + 0] + x; + z[zOff + 0] = (uint)c; + c >>= 32; + c += (ulong)z[zOff + 1] + 1; + z[zOff + 1] = (uint)c; + c >>= 32; + return c == 0 ? 0 : IncAt(len, z, zOff, 2); + } + + public static uint AddBothTo(int len, uint[] x, uint[] y, uint[] z) + { + ulong c = 0; + for (int i = 0; i < len; ++i) + { + c += (ulong)x[i] + y[i] + z[i]; + z[i] = (uint)c; + c >>= 32; + } + return (uint)c; + } + + public static uint AddBothTo(int len, uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff) + { + ulong c = 0; + for (int i = 0; i < len; ++i) + { + c += (ulong)x[xOff + i] + y[yOff + i] + z[zOff + i]; + z[zOff + i] = (uint)c; + c >>= 32; + } + return (uint)c; + } + + public static uint AddDWordAt(int len, ulong x, uint[] z, int zPos) + { + Debug.Assert(zPos <= (len - 2)); + ulong c = (ulong)z[zPos + 0] + (x & M); + z[zPos + 0] = (uint)c; + c >>= 32; + c += (ulong)z[zPos + 1] + (x >> 32); + z[zPos + 1] = (uint)c; + c >>= 32; + return c == 0 ? 0 : IncAt(len, z, zPos + 2); + } + + public static uint AddDWordAt(int len, ulong x, uint[] z, int zOff, int zPos) + { + Debug.Assert(zPos <= (len - 2)); + ulong c = (ulong)z[zOff + zPos] + (x & M); + z[zOff + zPos] = (uint)c; + c >>= 32; + c += (ulong)z[zOff + zPos + 1] + (x >> 32); + z[zOff + zPos + 1] = (uint)c; + c >>= 32; + return c == 0 ? 0 : IncAt(len, z, zOff, zPos + 2); + } + + public static uint AddDWordTo(int len, ulong x, uint[] z) + { + ulong c = (ulong)z[0] + (x & M); + z[0] = (uint)c; + c >>= 32; + c += (ulong)z[1] + (x >> 32); + z[1] = (uint)c; + c >>= 32; + return c == 0 ? 0 : IncAt(len, z, 2); + } + + public static uint AddDWordTo(int len, ulong x, uint[] z, int zOff) + { + ulong c = (ulong)z[zOff + 0] + (x & M); + z[zOff + 0] = (uint)c; + c >>= 32; + c += (ulong)z[zOff + 1] + (x >> 32); + z[zOff + 1] = (uint)c; + c >>= 32; + return c == 0 ? 0 : IncAt(len, z, zOff, 2); + } + + public static uint AddTo(int len, uint[] x, uint[] z) + { + ulong c = 0; + for (int i = 0; i < len; ++i) + { + c += (ulong)x[i] + z[i]; + z[i] = (uint)c; + c >>= 32; + } + return (uint)c; + } + + public static uint AddTo(int len, uint[] x, int xOff, uint[] z, int zOff) + { + ulong c = 0; + for (int i = 0; i < len; ++i) + { + c += (ulong)x[xOff + i] + z[zOff + i]; + z[zOff + i] = (uint)c; + c >>= 32; + } + return (uint)c; + } + + public static uint AddWordAt(int len, uint x, uint[] z, int zPos) + { + Debug.Assert(zPos <= (len - 1)); + ulong c = (ulong)x + z[zPos]; + z[zPos] = (uint)c; + c >>= 32; + return c == 0 ? 0 : IncAt(len, z, zPos + 1); + } + + public static uint AddWordAt(int len, uint x, uint[] z, int zOff, int zPos) + { + Debug.Assert(zPos <= (len - 1)); + ulong c = (ulong)x + z[zOff + zPos]; + z[zOff + zPos] = (uint)c; + c >>= 32; + return c == 0 ? 0 : IncAt(len, z, zOff, zPos + 1); + } + + public static uint AddWordTo(int len, uint x, uint[] z) + { + ulong c = (ulong)x + z[0]; + z[0] = (uint)c; + c >>= 32; + return c == 0 ? 0 : IncAt(len, z, 1); + } + + public static uint AddWordTo(int len, uint x, uint[] z, int zOff) + { + ulong c = (ulong)x + z[zOff]; + z[zOff] = (uint)c; + c >>= 32; + return c == 0 ? 0 : IncAt(len, z, zOff, 1); + } + + public static void Copy(int len, uint[] x, uint[] z) + { + Array.Copy(x, 0, z, 0, len); + } + + public static uint[] Copy(int len, uint[] x) + { + uint[] z = new uint[len]; + Array.Copy(x, 0, z, 0, len); + return z; + } + + public static uint[] Create(int len) + { + return new uint[len]; + } + + public static int Dec(int len, uint[] z) + { + for (int i = 0; i < len; ++i) + { + if (--z[i] != uint.MaxValue) + { + return 0; + } + } + return -1; + } + + public static int Dec(int len, uint[] x, uint[] z) + { + int i = 0; + while (i < len) + { + uint c = x[i] - 1; + z[i] = c; + ++i; + if (c != uint.MaxValue) + { + while (i < len) + { + z[i] = x[i]; + ++i; + } + return 0; + } + } + return -1; + } + + public static int DecAt(int len, uint[] z, int zPos) + { + Debug.Assert(zPos <= len); + for (int i = zPos; i < len; ++i) + { + if (--z[i] != uint.MaxValue) + { + return 0; + } + } + return -1; + } + + public static int DecAt(int len, uint[] z, int zOff, int zPos) + { + Debug.Assert(zPos <= len); + for (int i = zPos; i < len; ++i) + { + if (--z[zOff + i] != uint.MaxValue) + { + return 0; + } + } + return -1; + } + + public static bool Eq(int len, uint[] x, uint[] y) + { + for (int i = len - 1; i >= 0; --i) + { + if (x[i] != y[i]) + { + return false; + } + } + return true; + } + + public static uint[] FromBigInteger(int bits, BigInteger x) + { + if (x.SignValue < 0 || x.BitLength > bits) + throw new ArgumentException(); + + int len = (bits + 31) >> 5; + uint[] z = Create(len); + int i = 0; + while (x.SignValue != 0) + { + z[i++] = (uint)x.IntValue; + x = x.ShiftRight(32); + } + return z; + } + + public static uint GetBit(uint[] x, int bit) + { + if (bit == 0) + { + return x[0] & 1; + } + int w = bit >> 5; + if (w < 0 || w >= x.Length) + { + return 0; + } + int b = bit & 31; + return (x[w] >> b) & 1; + } + + public static bool Gte(int len, uint[] x, uint[] y) + { + for (int i = len - 1; i >= 0; --i) + { + uint x_i = x[i], y_i = y[i]; + if (x_i < y_i) + return false; + if (x_i > y_i) + return true; + } + return true; + } + + public static uint Inc(int len, uint[] z) + { + for (int i = 0; i < len; ++i) + { + if (++z[i] != uint.MinValue) + { + return 0; + } + } + return 1; + } + + public static uint Inc(int len, uint[] x, uint[] z) + { + int i = 0; + while (i < len) + { + uint c = x[i] + 1; + z[i] = c; + ++i; + if (c != 0) + { + while (i < len) + { + z[i] = x[i]; + ++i; + } + return 0; + } + } + return 1; + } + + public static uint IncAt(int len, uint[] z, int zPos) + { + Debug.Assert(zPos <= len); + for (int i = zPos; i < len; ++i) + { + if (++z[i] != uint.MinValue) + { + return 0; + } + } + return 1; + } + + public static uint IncAt(int len, uint[] z, int zOff, int zPos) + { + Debug.Assert(zPos <= len); + for (int i = zPos; i < len; ++i) + { + if (++z[zOff + i] != uint.MinValue) + { + return 0; + } + } + return 1; + } + + public static bool IsOne(int len, uint[] x) + { + if (x[0] != 1) + { + return false; + } + for (int i = 1; i < len; ++i) + { + if (x[i] != 0) + { + return false; + } + } + return true; + } + + public static bool IsZero(int len, uint[] x) + { + if (x[0] != 0) + { + return false; + } + for (int i = 1; i < len; ++i) + { + if (x[i] != 0) + { + return false; + } + } + return true; + } + + public static void Mul(int len, uint[] x, uint[] y, uint[] zz) + { + zz[len] = (uint)MulWord(len, x[0], y, zz); + + for (int i = 1; i < len; ++i) + { + zz[i + len] = (uint)MulWordAddTo(len, x[i], y, 0, zz, i); + } + } + + public static void Mul(int len, uint[] x, int xOff, uint[] y, int yOff, uint[] zz, int zzOff) + { + zz[zzOff + len] = (uint)MulWord(len, x[xOff], y, yOff, zz, zzOff); + + for (int i = 1; i < len; ++i) + { + zz[zzOff + i + len] = (uint)MulWordAddTo(len, x[xOff + i], y, yOff, zz, zzOff + i); + } + } + + public static uint Mul31BothAdd(int len, uint a, uint[] x, uint b, uint[] y, uint[] z, int zOff) + { + ulong c = 0, aVal = (ulong)a, bVal = (ulong)b; + int i = 0; + do + { + c += aVal * x[i] + bVal * y[i] + z[zOff + i]; + z[zOff + i] = (uint)c; + c >>= 32; + } + while (++i < len); + return (uint)c; + } + + public static uint MulWord(int len, uint x, uint[] y, uint[] z) + { + ulong c = 0, xVal = (ulong)x; + int i = 0; + do + { + c += xVal * y[i]; + z[i] = (uint)c; + c >>= 32; + } + while (++i < len); + return (uint)c; + } + + public static uint MulWord(int len, uint x, uint[] y, int yOff, uint[] z, int zOff) + { + ulong c = 0, xVal = (ulong)x; + int i = 0; + do + { + c += xVal * y[yOff + i]; + z[zOff + i] = (uint)c; + c >>= 32; + } + while (++i < len); + return (uint)c; + } + + public static uint MulWordAddTo(int len, uint x, uint[] y, int yOff, uint[] z, int zOff) + { + ulong c = 0, xVal = (ulong)x; + int i = 0; + do + { + c += xVal * y[yOff + i] + z[zOff + i]; + z[zOff + i] = (uint)c; + c >>= 32; + } + while (++i < len); + return (uint)c; + } + + public static uint MulWordDwordAddAt(int len, uint x, ulong y, uint[] z, int zPos) + { + Debug.Assert(zPos <= (len - 3)); + ulong c = 0, xVal = (ulong)x; + c += xVal * (uint)y + z[zPos + 0]; + z[zPos + 0] = (uint)c; + c >>= 32; + c += xVal * (y >> 32) + z[zPos + 1]; + z[zPos + 1] = (uint)c; + c >>= 32; + c += (ulong)z[zPos + 2]; + z[zPos + 2] = (uint)c; + c >>= 32; + return c == 0 ? 0 : IncAt(len, z, zPos + 3); + } + + public static uint ShiftDownBit(int len, uint[] z, uint c) + { + int i = len; + while (--i >= 0) + { + uint next = z[i]; + z[i] = (next >> 1) | (c << 31); + c = next; + } + return c << 31; + } + + public static uint ShiftDownBit(int len, uint[] z, int zOff, uint c) + { + int i = len; + while (--i >= 0) + { + uint next = z[zOff + i]; + z[zOff + i] = (next >> 1) | (c << 31); + c = next; + } + return c << 31; + } + + public static uint ShiftDownBit(int len, uint[] x, uint c, uint[] z) + { + int i = len; + while (--i >= 0) + { + uint next = x[i]; + z[i] = (next >> 1) | (c << 31); + c = next; + } + return c << 31; + } + + public static uint ShiftDownBit(int len, uint[] x, int xOff, uint c, uint[] z, int zOff) + { + int i = len; + while (--i >= 0) + { + uint next = x[xOff + i]; + z[zOff + i] = (next >> 1) | (c << 31); + c = next; + } + return c << 31; + } + + public static uint ShiftDownBits(int len, uint[] z, int bits, uint c) + { + Debug.Assert(bits > 0 && bits < 32); + int i = len; + while (--i >= 0) + { + uint next = z[i]; + z[i] = (next >> bits) | (c << -bits); + c = next; + } + return c << -bits; + } + + public static uint ShiftDownBits(int len, uint[] z, int zOff, int bits, uint c) + { + Debug.Assert(bits > 0 && bits < 32); + int i = len; + while (--i >= 0) + { + uint next = z[zOff + i]; + z[zOff + i] = (next >> bits) | (c << -bits); + c = next; + } + return c << -bits; + } + + public static uint ShiftDownBits(int len, uint[] x, int bits, uint c, uint[] z) + { + Debug.Assert(bits > 0 && bits < 32); + int i = len; + while (--i >= 0) + { + uint next = x[i]; + z[i] = (next >> bits) | (c << -bits); + c = next; + } + return c << -bits; + } + + public static uint ShiftDownBits(int len, uint[] x, int xOff, int bits, uint c, uint[] z, int zOff) + { + Debug.Assert(bits > 0 && bits < 32); + int i = len; + while (--i >= 0) + { + uint next = x[xOff + i]; + z[zOff + i] = (next >> bits) | (c << -bits); + c = next; + } + return c << -bits; + } + + public static uint ShiftDownWord(int len, uint[] z, uint c) + { + int i = len; + while (--i >= 0) + { + uint next = z[i]; + z[i] = c; + c = next; + } + return c; + } + + public static uint ShiftUpBit(int len, uint[] z, uint c) + { + for (int i = 0; i < len; ++i) + { + uint next = z[i]; + z[i] = (next << 1) | (c >> 31); + c = next; + } + return c >> 31; + } + + public static uint ShiftUpBit(int len, uint[] z, int zOff, uint c) + { + for (int i = 0; i < len; ++i) + { + uint next = z[zOff + i]; + z[zOff + i] = (next << 1) | (c >> 31); + c = next; + } + return c >> 31; + } + + public static uint ShiftUpBit(int len, uint[] x, uint c, uint[] z) + { + for (int i = 0; i < len; ++i) + { + uint next = x[i]; + z[i] = (next << 1) | (c >> 31); + c = next; + } + return c >> 31; + } + + public static uint ShiftUpBit(int len, uint[] x, int xOff, uint c, uint[] z, int zOff) + { + for (int i = 0; i < len; ++i) + { + uint next = x[xOff + i]; + z[zOff + i] = (next << 1) | (c >> 31); + c = next; + } + return c >> 31; + } + + public static uint ShiftUpBits(int len, uint[] z, int bits, uint c) + { + Debug.Assert(bits > 0 && bits < 32); + for (int i = 0; i < len; ++i) + { + uint next = z[i]; + z[i] = (next << bits) | (c >> -bits); + c = next; + } + return c >> -bits; + } + + public static uint ShiftUpBits(int len, uint[] z, int zOff, int bits, uint c) + { + Debug.Assert(bits > 0 && bits < 32); + for (int i = 0; i < len; ++i) + { + uint next = z[zOff + i]; + z[zOff + i] = (next << bits) | (c >> -bits); + c = next; + } + return c >> -bits; + } + + public static uint ShiftUpBits(int len, uint[] x, int bits, uint c, uint[] z) + { + Debug.Assert(bits > 0 && bits < 32); + for (int i = 0; i < len; ++i) + { + uint next = x[i]; + z[i] = (next << bits) | (c >> -bits); + c = next; + } + return c >> -bits; + } + + public static uint ShiftUpBits(int len, uint[] x, int xOff, int bits, uint c, uint[] z, int zOff) + { + Debug.Assert(bits > 0 && bits < 32); + for (int i = 0; i < len; ++i) + { + uint next = x[xOff + i]; + z[zOff + i] = (next << bits) | (c >> -bits); + c = next; + } + return c >> -bits; + } + + public static void Square(int len, uint[] x, uint[] zz) + { + int extLen = len << 1; + uint c = 0; + int j = len, k = extLen; + do + { + ulong xVal = (ulong)x[--j]; + ulong p = xVal * xVal; + zz[--k] = (c << 31) | (uint)(p >> 33); + zz[--k] = (uint)(p >> 1); + c = (uint)p; + } + while (j > 0); + + for (int i = 1; i < len; ++i) + { + c = SquareWordAdd(x, i, zz); + AddWordAt(extLen, c, zz, i << 1); + } + + ShiftUpBit(extLen, zz, x[0] << 31); + } + + public static void Square(int len, uint[] x, int xOff, uint[] zz, int zzOff) + { + int extLen = len << 1; + uint c = 0; + int j = len, k = extLen; + do + { + ulong xVal = (ulong)x[xOff + --j]; + ulong p = xVal * xVal; + zz[zzOff + --k] = (c << 31) | (uint)(p >> 33); + zz[zzOff + --k] = (uint)(p >> 1); + c = (uint)p; + } + while (j > 0); + + for (int i = 1; i < len; ++i) + { + c = SquareWordAdd(x, xOff, i, zz, zzOff); + AddWordAt(extLen, c, zz, zzOff, i << 1); + } + + ShiftUpBit(extLen, zz, zzOff, x[xOff] << 31); + } + + public static uint SquareWordAdd(uint[] x, int xPos, uint[] z) + { + ulong c = 0, xVal = (ulong)x[xPos]; + int i = 0; + do + { + c += xVal * x[i] + z[xPos + i]; + z[xPos + i] = (uint)c; + c >>= 32; + } + while (++i < xPos); + return (uint)c; + } + + public static uint SquareWordAdd(uint[] x, int xOff, int xPos, uint[] z, int zOff) + { + ulong c = 0, xVal = (ulong)x[xOff + xPos]; + int i = 0; + do + { + c += xVal * (x[xOff + i] & M) + (z[xPos + zOff] & M); + z[xPos + zOff] = (uint)c; + c >>= 32; + ++zOff; + } + while (++i < xPos); + return (uint)c; + } + + public static int Sub(int len, uint[] x, uint[] y, uint[] z) + { + long c = 0; + for (int i = 0; i < len; ++i) + { + c += (long)x[i] - y[i]; + z[i] = (uint)c; + c >>= 32; + } + return (int)c; + } + + public static int Sub(int len, uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff) + { + long c = 0; + for (int i = 0; i < len; ++i) + { + c += (long)x[xOff + i] - y[yOff + i]; + z[zOff + i] = (uint)c; + c >>= 32; + } + return (int)c; + } + public static int Sub33At(int len, uint x, uint[] z, int zPos) + { + Debug.Assert(zPos <= (len - 2)); + long c = (long)z[zPos + 0] - x; + z[zPos + 0] = (uint)c; + c >>= 32; + c += (long)z[zPos + 1] - 1; + z[zPos + 1] = (uint)c; + c >>= 32; + return c == 0 ? 0 : DecAt(len, z, zPos + 2); + } + + public static int Sub33At(int len, uint x, uint[] z, int zOff, int zPos) + { + Debug.Assert(zPos <= (len - 2)); + long c = (long)z[zOff + zPos] - x; + z[zOff + zPos] = (uint)c; + c >>= 32; + c += (long)z[zOff + zPos + 1] - 1; + z[zOff + zPos + 1] = (uint)c; + c >>= 32; + return c == 0 ? 0 : DecAt(len, z, zOff, zPos + 2); + } + + public static int Sub33From(int len, uint x, uint[] z) + { + long c = (long)z[0] - x; + z[0] = (uint)c; + c >>= 32; + c += (long)z[1] - 1; + z[1] = (uint)c; + c >>= 32; + return c == 0 ? 0 : DecAt(len, z, 2); + } + + public static int Sub33From(int len, uint x, uint[] z, int zOff) + { + long c = (long)z[zOff + 0] - x; + z[zOff + 0] = (uint)c; + c >>= 32; + c += (long)z[zOff + 1] - 1; + z[zOff + 1] = (uint)c; + c >>= 32; + return c == 0 ? 0 : DecAt(len, z, zOff, 2); + } + + public static int SubBothFrom(int len, uint[] x, uint[] y, uint[] z) + { + long c = 0; + for (int i = 0; i < len; ++i) + { + c += (long)z[i] - x[i] - y[i]; + z[i] = (uint)c; + c >>= 32; + } + return (int)c; + } + + public static int SubBothFrom(int len, uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff) + { + long c = 0; + for (int i = 0; i < len; ++i) + { + c += (long)z[zOff + i] - x[xOff + i] - y[yOff + i]; + z[zOff + i] = (uint)c; + c >>= 32; + } + return (int)c; + } + + public static int SubDWordAt(int len, ulong x, uint[] z, int zPos) + { + Debug.Assert(zPos <= (len - 2)); + long c = (long)z[zPos + 0] - (long)(x & M); + z[zPos + 0] = (uint)c; + c >>= 32; + c += (long)z[zPos + 1] - (long)(x >> 32); + z[zPos + 1] = (uint)c; + c >>= 32; + return c == 0 ? 0 : DecAt(len, z, zPos + 2); + } + + public static int SubDWordAt(int len, ulong x, uint[] z, int zOff, int zPos) + { + Debug.Assert(zPos <= (len - 2)); + long c = (long)z[zOff + zPos] - (long)(x & M); + z[zOff + zPos] = (uint)c; + c >>= 32; + c += (long)z[zOff + zPos + 1] - (long)(x >> 32); + z[zOff + zPos + 1] = (uint)c; + c >>= 32; + return c == 0 ? 0 : DecAt(len, z, zOff, zPos + 2); + } + + public static int SubDWordFrom(int len, ulong x, uint[] z) + { + long c = (long)z[0] - (long)(x & M); + z[0] = (uint)c; + c >>= 32; + c += (long)z[1] - (long)(x >> 32); + z[1] = (uint)c; + c >>= 32; + return c == 0 ? 0 : DecAt(len, z, 2); + } + + public static int SubDWordFrom(int len, ulong x, uint[] z, int zOff) + { + long c = (long)z[zOff + 0] - (long)(x & M); + z[zOff + 0] = (uint)c; + c >>= 32; + c += (long)z[zOff + 1] - (long)(x >> 32); + z[zOff + 1] = (uint)c; + c >>= 32; + return c == 0 ? 0 : DecAt(len, z, zOff, 2); + } + + public static int SubFrom(int len, uint[] x, uint[] z) + { + long c = 0; + for (int i = 0; i < len; ++i) + { + c += (long)z[i] - x[i]; + z[i] = (uint)c; + c >>= 32; + } + return (int)c; + } + + public static int SubFrom(int len, uint[] x, int xOff, uint[] z, int zOff) + { + long c = 0; + for (int i = 0; i < len; ++i) + { + c += (long)z[zOff + i] - x[xOff + i]; + z[zOff + i] = (uint)c; + c >>= 32; + } + return (int)c; + } + + public static int SubWordAt(int len, uint x, uint[] z, int zPos) + { + Debug.Assert(zPos <= (len - 1)); + long c = (long)z[zPos] - x; + z[zPos] = (uint)c; + c >>= 32; + return c == 0 ? 0 : DecAt(len, z, zPos + 1); + } + + public static int SubWordAt(int len, uint x, uint[] z, int zOff, int zPos) + { + Debug.Assert(zPos <= (len - 1)); + long c = (long)z[zOff + zPos] - x; + z[zOff + zPos] = (uint)c; + c >>= 32; + return c == 0 ? 0 : DecAt(len, z, zOff, zPos + 1); + } + + public static int SubWordFrom(int len, uint x, uint[] z) + { + long c = (long)z[0] - x; + z[0] = (uint)c; + c >>= 32; + return c == 0 ? 0 : DecAt(len, z, 1); + } + + public static int SubWordFrom(int len, uint x, uint[] z, int zOff) + { + long c = (long)z[zOff + 0] - x; + z[zOff + 0] = (uint)c; + c >>= 32; + return c == 0 ? 0 : DecAt(len, z, zOff, 1); + } + + public static BigInteger ToBigInteger(int len, uint[] x) + { + byte[] bs = new byte[len << 2]; + for (int i = 0; i < len; ++i) + { + uint x_i = x[i]; + if (x_i != 0) + { + Pack.UInt32_To_BE(x_i, bs, (len - 1 - i) << 2); + } + } + return new BigInteger(1, bs); + } + + public static void Zero(int len, uint[] z) + { + for (int i = 0; i < len; ++i) + { + z[i] = 0; + } + } + } +} diff --git a/crypto/src/math/ec/ScaleXPointMap.cs b/crypto/src/math/ec/ScaleXPointMap.cs new file mode 100644
index 000000000..f8a363b24 --- /dev/null +++ b/crypto/src/math/ec/ScaleXPointMap.cs
@@ -0,0 +1,20 @@ +using System; + +namespace Org.BouncyCastle.Math.EC +{ + public class ScaleXPointMap + : ECPointMap + { + protected readonly ECFieldElement scale; + + public ScaleXPointMap(ECFieldElement scale) + { + this.scale = scale; + } + + public virtual ECPoint Map(ECPoint p) + { + return p.ScaleX(scale); + } + } +} diff --git a/crypto/src/math/ec/ScaleYPointMap.cs b/crypto/src/math/ec/ScaleYPointMap.cs new file mode 100644
index 000000000..1c4795b70 --- /dev/null +++ b/crypto/src/math/ec/ScaleYPointMap.cs
@@ -0,0 +1,20 @@ +using System; + +namespace Org.BouncyCastle.Math.EC +{ + public class ScaleYPointMap + : ECPointMap + { + protected readonly ECFieldElement scale; + + public ScaleYPointMap(ECFieldElement scale) + { + this.scale = scale; + } + + public virtual ECPoint Map(ECPoint p) + { + return p.ScaleY(scale); + } + } +} 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
index 225fc3075..9f16886f5 100644 --- a/crypto/src/math/ec/abc/Tnaf.cs +++ b/crypto/src/math/ec/abc/Tnaf.cs
@@ -2,833 +2,827 @@ 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; - } - } + /** + * 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) + { + return p.Tau(); + } + + /** + * 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.Cofactor.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/custom/djb/Curve25519.cs b/crypto/src/math/ec/custom/djb/Curve25519.cs new file mode 100644
index 000000000..712b68f29 --- /dev/null +++ b/crypto/src/math/ec/custom/djb/Curve25519.cs
@@ -0,0 +1,77 @@ +using System; + +using Org.BouncyCastle.Math.EC.Custom.Sec; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Djb +{ + internal class Curve25519 + : AbstractFpCurve + { + public static readonly BigInteger q = Nat256.ToBigInteger(Curve25519Field.P); + + private const int Curve25519_DEFAULT_COORDS = COORD_JACOBIAN_MODIFIED; + + protected readonly Curve25519Point m_infinity; + + public Curve25519() + : base(q) + { + this.m_infinity = new Curve25519Point(this, null, null); + + this.m_a = FromBigInteger(new BigInteger(1, + Hex.Decode("2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA984914A144"))); + this.m_b = FromBigInteger(new BigInteger(1, + Hex.Decode("7B425ED097B425ED097B425ED097B425ED097B425ED097B4260B5E9C7710C864"))); + this.m_order = new BigInteger(1, Hex.Decode("1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED")); + this.m_cofactor = BigInteger.ValueOf(8); + this.m_coord = Curve25519_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new Curve25519(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_JACOBIAN_MODIFIED: + return true; + default: + return false; + } + } + + public virtual BigInteger Q + { + get { return q; } + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return q.BitLength; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new Curve25519FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new Curve25519Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new Curve25519Point(this, x, y, zs, withCompression); + } + } +} diff --git a/crypto/src/math/ec/custom/djb/Curve25519Field.cs b/crypto/src/math/ec/custom/djb/Curve25519Field.cs new file mode 100644
index 000000000..809e51b80 --- /dev/null +++ b/crypto/src/math/ec/custom/djb/Curve25519Field.cs
@@ -0,0 +1,253 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Math.EC.Custom.Sec; + +namespace Org.BouncyCastle.Math.EC.Custom.Djb +{ + internal class Curve25519Field + { + // 2^255 - 2^4 - 2^1 - 1 + internal static readonly uint[] P = new uint[]{ 0xFFFFFFED, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0x7FFFFFFF }; + private const uint P7 = 0x7FFFFFFF; + private static readonly uint[] PExt = new uint[]{ 0x00000169, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFED, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0x3FFFFFFF }; + private const uint PInv = 0x13; + + public static void Add(uint[] x, uint[] y, uint[] z) + { + Nat256.Add(x, y, z); + if (Nat256.Gte(z, P)) + { + SubPFrom(z); + } + } + + public static void AddExt(uint[] xx, uint[] yy, uint[] zz) + { + Nat.Add(16, xx, yy, zz); + if (Nat.Gte(16, zz, PExt)) + { + SubPExtFrom(zz); + } + } + + public static void AddOne(uint[] x, uint[] z) + { + Nat.Inc(8, x, z); + if (Nat256.Gte(z, P)) + { + SubPFrom(z); + } + } + + public static uint[] FromBigInteger(BigInteger x) + { + uint[] z = Nat256.FromBigInteger(x); + while (Nat256.Gte(z, P)) + { + Nat256.SubFrom(P, z); + } + return z; + } + + public static void Half(uint[] x, uint[] z) + { + if ((x[0] & 1) == 0) + { + Nat.ShiftDownBit(8, x, 0, z); + } + else + { + Nat256.Add(x, P, z); + Nat.ShiftDownBit(8, z, 0); + } + } + + public static void Multiply(uint[] x, uint[] y, uint[] z) + { + uint[] tt = Nat256.CreateExt(); + Nat256.Mul(x, y, tt); + Reduce(tt, z); + } + + public static void MultiplyAddToExt(uint[] x, uint[] y, uint[] zz) + { + Nat256.MulAddTo(x, y, zz); + if (Nat.Gte(16, zz, PExt)) + { + SubPExtFrom(zz); + } + } + + public static void Negate(uint[] x, uint[] z) + { + if (Nat256.IsZero(x)) + { + Nat256.Zero(z); + } + else + { + Nat256.Sub(P, x, z); + } + } + + public static void Reduce(uint[] xx, uint[] z) + { + Debug.Assert(xx[15] >> 30 == 0); + + uint xx07 = xx[7]; + Nat.ShiftUpBit(8, xx, 8, xx07, z, 0); + uint c = Nat256.MulByWordAddTo(PInv, xx, z) << 1; + uint z7 = z[7]; + c += (z7 >> 31) - (xx07 >> 31); + z7 &= P7; + z7 += Nat.AddWordTo(7, c * PInv, z); + z[7] = z7; + if (z7 >= P7 && Nat256.Gte(z, P)) + { + SubPFrom(z); + } + } + + public static void Reduce27(uint x, uint[] z) + { + Debug.Assert(x >> 26 == 0); + + uint z7 = z[7]; + uint c = (x << 1 | z7 >> 31); + z7 &= P7; + z7 += Nat.AddWordTo(7, c * PInv, z); + z[7] = z7; + if (z7 >= P7 && Nat256.Gte(z, P)) + { + SubPFrom(z); + } + } + + public static void Square(uint[] x, uint[] z) + { + uint[] tt = Nat256.CreateExt(); + Nat256.Square(x, tt); + Reduce(tt, z); + } + + public static void SquareN(uint[] x, int n, uint[] z) + { + Debug.Assert(n > 0); + + uint[] tt = Nat256.CreateExt(); + Nat256.Square(x, tt); + Reduce(tt, z); + + while (--n > 0) + { + Nat256.Square(z, tt); + Reduce(tt, z); + } + } + + public static void Subtract(uint[] x, uint[] y, uint[] z) + { + int c = Nat256.Sub(x, y, z); + if (c != 0) + { + AddPTo(z); + } + } + + public static void SubtractExt(uint[] xx, uint[] yy, uint[] zz) + { + int c = Nat.Sub(16, xx, yy, zz); + if (c != 0) + { + AddPExtTo(zz); + } + } + + public static void Twice(uint[] x, uint[] z) + { + Nat.ShiftUpBit(8, x, 0, z); + if (Nat256.Gte(z, P)) + { + SubPFrom(z); + } + } + + private static uint AddPTo(uint[] z) + { + long c = (long)z[0] - PInv; + z[0] = (uint)c; + c >>= 32; + if (c != 0) + { + c = Nat.DecAt(7, z, 1); + } + c += (long)z[7] + (P7 + 1); + z[7] = (uint)c; + c >>= 32; + return (uint)c; + } + + private static uint AddPExtTo(uint[] zz) + { + long c = (long)zz[0] + PExt[0]; + zz[0] = (uint)c; + c >>= 32; + if (c != 0) + { + c = Nat.IncAt(8, zz, 1); + } + c += (long)zz[8] - PInv; + zz[8] = (uint)c; + c >>= 32; + if (c != 0) + { + c = Nat.DecAt(15, zz, 9); + } + c += (long)zz[15] + (PExt[15] + 1); + zz[15] = (uint)c; + c >>= 32; + return (uint)c; + } + + private static int SubPFrom(uint[] z) + { + long c = (long)z[0] + PInv; + z[0] = (uint)c; + c >>= 32; + if (c != 0) + { + c = Nat.IncAt(7, z, 1); + } + c += (long)z[7] - (P7 + 1); + z[7] = (uint)c; + c >>= 32; + return (int)c; + } + + private static int SubPExtFrom(uint[] zz) + { + long c = (long)zz[0] - PExt[0]; + zz[0] = (uint)c; + c >>= 32; + if (c != 0) + { + c = Nat.DecAt(8, zz, 1); + } + c += (long)zz[8] + PInv; + zz[8] = (uint)c; + c >>= 32; + if (c != 0) + { + c = Nat.IncAt(15, zz, 9); + } + c += (long)zz[15] - (PExt[15] + 1); + zz[15] = (uint)c; + c >>= 32; + return (int)c; + } + } +} diff --git a/crypto/src/math/ec/custom/djb/Curve25519FieldElement.cs b/crypto/src/math/ec/custom/djb/Curve25519FieldElement.cs new file mode 100644
index 000000000..8d5a80326 --- /dev/null +++ b/crypto/src/math/ec/custom/djb/Curve25519FieldElement.cs
@@ -0,0 +1,233 @@ +using System; + +using Org.BouncyCastle.Math.EC.Custom.Sec; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Math.EC.Custom.Djb +{ + internal class Curve25519FieldElement + : ECFieldElement + { + public static readonly BigInteger Q = Curve25519.q; + + // Calculated as ECConstants.TWO.modPow(Q.shiftRight(2), Q) + private static readonly uint[] PRECOMP_POW2 = new uint[]{ 0x4a0ea0b0, 0xc4ee1b27, 0xad2fe478, 0x2f431806, + 0x3dfbd7a7, 0x2b4d0099, 0x4fc1df0b, 0x2b832480 }; + + protected internal readonly uint[] x; + + public Curve25519FieldElement(BigInteger x) + { + if (x == null || x.SignValue < 0 || x.CompareTo(Q) >= 0) + throw new ArgumentException("value invalid for Curve25519FieldElement", "x"); + + this.x = Curve25519Field.FromBigInteger(x); + } + + public Curve25519FieldElement() + { + this.x = Nat256.Create(); + } + + protected internal Curve25519FieldElement(uint[] x) + { + this.x = x; + } + + public override bool IsZero + { + get { return Nat256.IsZero(x); } + } + + public override bool IsOne + { + get { return Nat256.IsOne(x); } + } + + public override bool TestBitZero() + { + return Nat256.GetBit(x, 0) == 1; + } + + public override BigInteger ToBigInteger() + { + return Nat256.ToBigInteger(x); + } + + public override string FieldName + { + get { return "Curve25519Field"; } + } + + public override int FieldSize + { + get { return Q.BitLength; } + } + + public override ECFieldElement Add(ECFieldElement b) + { + uint[] z = Nat256.Create(); + Curve25519Field.Add(x, ((Curve25519FieldElement)b).x, z); + return new Curve25519FieldElement(z); + } + + public override ECFieldElement AddOne() + { + uint[] z = Nat256.Create(); + Curve25519Field.AddOne(x, z); + return new Curve25519FieldElement(z); + } + + public override ECFieldElement Subtract(ECFieldElement b) + { + uint[] z = Nat256.Create(); + Curve25519Field.Subtract(x, ((Curve25519FieldElement)b).x, z); + return new Curve25519FieldElement(z); + } + + public override ECFieldElement Multiply(ECFieldElement b) + { + uint[] z = Nat256.Create(); + Curve25519Field.Multiply(x, ((Curve25519FieldElement)b).x, z); + return new Curve25519FieldElement(z); + } + + public override ECFieldElement Divide(ECFieldElement b) + { + //return Multiply(b.Invert()); + uint[] z = Nat256.Create(); + Mod.Invert(Curve25519Field.P, ((Curve25519FieldElement)b).x, z); + Curve25519Field.Multiply(z, x, z); + return new Curve25519FieldElement(z); + } + + public override ECFieldElement Negate() + { + uint[] z = Nat256.Create(); + Curve25519Field.Negate(x, z); + return new Curve25519FieldElement(z); + } + + public override ECFieldElement Square() + { + uint[] z = Nat256.Create(); + Curve25519Field.Square(x, z); + return new Curve25519FieldElement(z); + } + + public override ECFieldElement Invert() + { + //return new Curve25519FieldElement(ToBigInteger().ModInverse(Q)); + uint[] z = Nat256.Create(); + Mod.Invert(Curve25519Field.P, x, z); + return new Curve25519FieldElement(z); + } + + /** + * return a sqrt root - the routine verifies that the calculation returns the right value - if + * none exists it returns null. + */ + public override ECFieldElement Sqrt() + { + /* + * Q == 8m + 5, so we use Pocklington's method for this case. + * + * First, raise this element to the exponent 2^252 - 2^1 (i.e. m + 1) + * + * Breaking up the exponent's binary representation into "repunits", we get: + * { 251 1s } { 1 0s } + * + * Therefore we need an addition chain containing 251 (the lengths of the repunits) + * We use: 1, 2, 3, 4, 7, 11, 15, 30, 60, 120, 131, [251] + */ + + uint[] x1 = this.x; + if (Nat256.IsZero(x1) || Nat256.IsOne(x1)) + return this; + + uint[] x2 = Nat256.Create(); + Curve25519Field.Square(x1, x2); + Curve25519Field.Multiply(x2, x1, x2); + uint[] x3 = x2; + Curve25519Field.Square(x2, x3); + Curve25519Field.Multiply(x3, x1, x3); + uint[] x4 = Nat256.Create(); + Curve25519Field.Square(x3, x4); + Curve25519Field.Multiply(x4, x1, x4); + uint[] x7 = Nat256.Create(); + Curve25519Field.SquareN(x4, 3, x7); + Curve25519Field.Multiply(x7, x3, x7); + uint[] x11 = x3; + Curve25519Field.SquareN(x7, 4, x11); + Curve25519Field.Multiply(x11, x4, x11); + uint[] x15 = x7; + Curve25519Field.SquareN(x11, 4, x15); + Curve25519Field.Multiply(x15, x4, x15); + uint[] x30 = x4; + Curve25519Field.SquareN(x15, 15, x30); + Curve25519Field.Multiply(x30, x15, x30); + uint[] x60 = x15; + Curve25519Field.SquareN(x30, 30, x60); + Curve25519Field.Multiply(x60, x30, x60); + uint[] x120 = x30; + Curve25519Field.SquareN(x60, 60, x120); + Curve25519Field.Multiply(x120, x60, x120); + uint[] x131 = x60; + Curve25519Field.SquareN(x120, 11, x131); + Curve25519Field.Multiply(x131, x11, x131); + uint[] x251 = x11; + Curve25519Field.SquareN(x131, 120, x251); + Curve25519Field.Multiply(x251, x120, x251); + + uint[] t1 = x251; + Curve25519Field.Square(t1, t1); + + uint[] t2 = x120; + Curve25519Field.Square(t1, t2); + + if (Nat256.Eq(x1, t2)) + { + return new Curve25519FieldElement(t1); + } + + /* + * If the first guess is incorrect, we multiply by a precomputed power of 2 to get the second guess, + * which is ((4x)^(m + 1))/2 mod Q + */ + Curve25519Field.Multiply(t1, PRECOMP_POW2, t1); + + Curve25519Field.Square(t1, t2); + + if (Nat256.Eq(x1, t2)) + { + return new Curve25519FieldElement(t1); + } + + return null; + } + + public override bool Equals(object obj) + { + return Equals(obj as Curve25519FieldElement); + } + + public override bool Equals(ECFieldElement other) + { + return Equals(other as Curve25519FieldElement); + } + + public virtual bool Equals(Curve25519FieldElement other) + { + if (this == other) + return true; + if (null == other) + return false; + return Nat256.Eq(x, other.x); + } + + public override int GetHashCode() + { + return Q.GetHashCode() ^ Arrays.GetHashCode(x, 0, 8); + } + } +} diff --git a/crypto/src/math/ec/custom/djb/Curve25519Point.cs b/crypto/src/math/ec/custom/djb/Curve25519Point.cs new file mode 100644
index 000000000..bfec1d11d --- /dev/null +++ b/crypto/src/math/ec/custom/djb/Curve25519Point.cs
@@ -0,0 +1,313 @@ +using System; + +using Org.BouncyCastle.Math.EC.Custom.Sec; + +namespace Org.BouncyCastle.Math.EC.Custom.Djb +{ + internal class Curve25519Point + : AbstractFpPoint + { + /** + * 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 + * + * @deprecated Use ECCurve.CreatePoint to construct points + */ + public Curve25519Point(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 + * + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(bool)} + */ + public Curve25519Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal Curve25519Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new Curve25519Point(null, AffineXCoord, AffineYCoord); + } + + public override ECFieldElement GetZCoord(int index) + { + if (index == 1) + { + return GetJacobianModifiedW(); + } + + return base.GetZCoord(index); + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + if (this == b) + return Twice(); + + ECCurve curve = this.Curve; + + Curve25519FieldElement X1 = (Curve25519FieldElement)this.RawXCoord, Y1 = (Curve25519FieldElement)this.RawYCoord, + Z1 = (Curve25519FieldElement)this.RawZCoords[0]; + Curve25519FieldElement X2 = (Curve25519FieldElement)b.RawXCoord, Y2 = (Curve25519FieldElement)b.RawYCoord, + Z2 = (Curve25519FieldElement)b.RawZCoords[0]; + + uint c; + uint[] tt1 = Nat256.CreateExt(); + uint[] t2 = Nat256.Create(); + uint[] t3 = Nat256.Create(); + uint[] t4 = Nat256.Create(); + + bool Z1IsOne = Z1.IsOne; + uint[] U2, S2; + if (Z1IsOne) + { + U2 = X2.x; + S2 = Y2.x; + } + else + { + S2 = t3; + Curve25519Field.Square(Z1.x, S2); + + U2 = t2; + Curve25519Field.Multiply(S2, X2.x, U2); + + Curve25519Field.Multiply(S2, Z1.x, S2); + Curve25519Field.Multiply(S2, Y2.x, S2); + } + + bool Z2IsOne = Z2.IsOne; + uint[] U1, S1; + if (Z2IsOne) + { + U1 = X1.x; + S1 = Y1.x; + } + else + { + S1 = t4; + Curve25519Field.Square(Z2.x, S1); + + U1 = tt1; + Curve25519Field.Multiply(S1, X1.x, U1); + + Curve25519Field.Multiply(S1, Z2.x, S1); + Curve25519Field.Multiply(S1, Y1.x, S1); + } + + uint[] H = Nat256.Create(); + Curve25519Field.Subtract(U1, U2, H); + + uint[] R = t2; + Curve25519Field.Subtract(S1, S2, R); + + // Check if b == this or b == -this + if (Nat256.IsZero(H)) + { + if (Nat256.IsZero(R)) + { + // this == b, i.e. this must be doubled + return this.Twice(); + } + + // this == -b, i.e. the result is the point at infinity + return curve.Infinity; + } + + uint[] HSquared = Nat256.Create(); + Curve25519Field.Square(H, HSquared); + + uint[] G = Nat256.Create(); + Curve25519Field.Multiply(HSquared, H, G); + + uint[] V = t3; + Curve25519Field.Multiply(HSquared, U1, V); + + Curve25519Field.Negate(G, G); + Nat256.Mul(S1, G, tt1); + + c = Nat256.AddBothTo(V, V, G); + Curve25519Field.Reduce27(c, G); + + Curve25519FieldElement X3 = new Curve25519FieldElement(t4); + Curve25519Field.Square(R, X3.x); + Curve25519Field.Subtract(X3.x, G, X3.x); + + Curve25519FieldElement Y3 = new Curve25519FieldElement(G); + Curve25519Field.Subtract(V, X3.x, Y3.x); + Curve25519Field.MultiplyAddToExt(Y3.x, R, tt1); + Curve25519Field.Reduce(tt1, Y3.x); + + Curve25519FieldElement Z3 = new Curve25519FieldElement(H); + if (!Z1IsOne) + { + Curve25519Field.Multiply(Z3.x, Z1.x, Z3.x); + } + if (!Z2IsOne) + { + Curve25519Field.Multiply(Z3.x, Z2.x, Z3.x); + } + + uint[] Z3Squared = (Z1IsOne && Z2IsOne) ? HSquared : null; + + // TODO If the result will only be used in a subsequent addition, we don't need W3 + Curve25519FieldElement W3 = CalculateJacobianModifiedW((Curve25519FieldElement)Z3, Z3Squared); + + ECFieldElement[] zs = new ECFieldElement[] { Z3, W3 }; + + return new Curve25519Point(curve, X3, Y3, zs, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + ECFieldElement Y1 = this.RawYCoord; + if (Y1.IsZero) + return curve.Infinity; + + return TwiceJacobianModified(true); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this == b) + return ThreeTimes(); + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECFieldElement Y1 = this.RawYCoord; + if (Y1.IsZero) + return b; + + return TwiceJacobianModified(false).Add(b); + } + + public override ECPoint ThreeTimes() + { + if (this.IsInfinity || this.RawYCoord.IsZero) + return this; + + return TwiceJacobianModified(false).Add(this); + } + + public override ECPoint Negate() + { + if (IsInfinity) + return this; + + return new Curve25519Point(Curve, RawXCoord, RawYCoord.Negate(), RawZCoords, IsCompressed); + } + + protected virtual Curve25519FieldElement CalculateJacobianModifiedW(Curve25519FieldElement Z, uint[] ZSquared) + { + Curve25519FieldElement a4 = (Curve25519FieldElement)this.Curve.A; + if (Z.IsOne) + return a4; + + Curve25519FieldElement W = new Curve25519FieldElement(); + if (ZSquared == null) + { + ZSquared = W.x; + Curve25519Field.Square(Z.x, ZSquared); + } + Curve25519Field.Square(ZSquared, W.x); + Curve25519Field.Multiply(W.x, a4.x, W.x); + return W; + } + + protected virtual Curve25519FieldElement GetJacobianModifiedW() + { + ECFieldElement[] ZZ = this.RawZCoords; + Curve25519FieldElement W = (Curve25519FieldElement)ZZ[1]; + if (W == null) + { + // NOTE: Rarely, TwicePlus will result in the need for a lazy W1 calculation here + ZZ[1] = W = CalculateJacobianModifiedW((Curve25519FieldElement)ZZ[0], null); + } + return W; + } + + protected virtual Curve25519Point TwiceJacobianModified(bool calculateW) + { + Curve25519FieldElement X1 = (Curve25519FieldElement)this.RawXCoord, Y1 = (Curve25519FieldElement)this.RawYCoord, + Z1 = (Curve25519FieldElement)this.RawZCoords[0], W1 = GetJacobianModifiedW(); + + uint c; + + uint[] M = Nat256.Create(); + Curve25519Field.Square(X1.x, M); + c = Nat256.AddBothTo(M, M, M); + c += Nat256.AddTo(W1.x, M); + Curve25519Field.Reduce27(c, M); + + uint[] _2Y1 = Nat256.Create(); + Curve25519Field.Twice(Y1.x, _2Y1); + + uint[] _2Y1Squared = Nat256.Create(); + Curve25519Field.Multiply(_2Y1, Y1.x, _2Y1Squared); + + uint[] S = Nat256.Create(); + Curve25519Field.Multiply(_2Y1Squared, X1.x, S); + Curve25519Field.Twice(S, S); + + uint[] _8T = Nat256.Create(); + Curve25519Field.Square(_2Y1Squared, _8T); + Curve25519Field.Twice(_8T, _8T); + + Curve25519FieldElement X3 = new Curve25519FieldElement(_2Y1Squared); + Curve25519Field.Square(M, X3.x); + Curve25519Field.Subtract(X3.x, S, X3.x); + Curve25519Field.Subtract(X3.x, S, X3.x); + + Curve25519FieldElement Y3 = new Curve25519FieldElement(S); + Curve25519Field.Subtract(S, X3.x, Y3.x); + Curve25519Field.Multiply(Y3.x, M, Y3.x); + Curve25519Field.Subtract(Y3.x, _8T, Y3.x); + + Curve25519FieldElement Z3 = new Curve25519FieldElement(_2Y1); + if (!Nat256.IsOne(Z1.x)) + { + Curve25519Field.Multiply(Z3.x, Z1.x, Z3.x); + } + + Curve25519FieldElement W3 = null; + if (calculateW) + { + W3 = new Curve25519FieldElement(_8T); + Curve25519Field.Multiply(W3.x, W1.x, W3.x); + Curve25519Field.Twice(W3.x, W3.x); + } + + return new Curve25519Point(this.Curve, X3, Y3, new ECFieldElement[] { Z3, W3 }, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/Nat192.cs b/crypto/src/math/ec/custom/sec/Nat192.cs new file mode 100644
index 000000000..94d7ed17c --- /dev/null +++ b/crypto/src/math/ec/custom/sec/Nat192.cs
@@ -0,0 +1,962 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal abstract class Nat192 + { + private const ulong M = 0xFFFFFFFFUL; + + public static uint Add(uint[] x, uint[] y, uint[] z) + { + ulong c = 0; + c += (ulong)x[0] + y[0]; + z[0] = (uint)c; + c >>= 32; + c += (ulong)x[1] + y[1]; + z[1] = (uint)c; + c >>= 32; + c += (ulong)x[2] + y[2]; + z[2] = (uint)c; + c >>= 32; + c += (ulong)x[3] + y[3]; + z[3] = (uint)c; + c >>= 32; + c += (ulong)x[4] + y[4]; + z[4] = (uint)c; + c >>= 32; + c += (ulong)x[5] + y[5]; + z[5] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint AddBothTo(uint[] x, uint[] y, uint[] z) + { + ulong c = 0; + c += (ulong)x[0] + y[0] + z[0]; + z[0] = (uint)c; + c >>= 32; + c += (ulong)x[1] + y[1] + z[1]; + z[1] = (uint)c; + c >>= 32; + c += (ulong)x[2] + y[2] + z[2]; + z[2] = (uint)c; + c >>= 32; + c += (ulong)x[3] + y[3] + z[3]; + z[3] = (uint)c; + c >>= 32; + c += (ulong)x[4] + y[4] + z[4]; + z[4] = (uint)c; + c >>= 32; + c += (ulong)x[5] + y[5] + z[5]; + z[5] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint AddTo(uint[] x, uint[] z) + { + ulong c = 0; + c += (ulong)x[0] + z[0]; + z[0] = (uint)c; + c >>= 32; + c += (ulong)x[1] + z[1]; + z[1] = (uint)c; + c >>= 32; + c += (ulong)x[2] + z[2]; + z[2] = (uint)c; + c >>= 32; + c += (ulong)x[3] + z[3]; + z[3] = (uint)c; + c >>= 32; + c += (ulong)x[4] + z[4]; + z[4] = (uint)c; + c >>= 32; + c += (ulong)x[5] + z[5]; + z[5] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint AddTo(uint[] x, int xOff, uint[] z, int zOff, uint cIn) + { + ulong c = cIn; + c += (ulong)x[xOff + 0] + z[zOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 1] + z[zOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 2] + z[zOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 3] + z[zOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 4] + z[zOff + 4]; + z[zOff + 4] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 5] + z[zOff + 5]; + z[zOff + 5] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint AddToEachOther(uint[] u, int uOff, uint[] v, int vOff) + { + ulong c = 0; + c += (ulong)u[uOff + 0] + v[vOff + 0]; + u[uOff + 0] = (uint)c; + v[vOff + 0] = (uint)c; + c >>= 32; + c += (ulong)u[uOff + 1] + v[vOff + 1]; + u[uOff + 1] = (uint)c; + v[vOff + 1] = (uint)c; + c >>= 32; + c += (ulong)u[uOff + 2] + v[vOff + 2]; + u[uOff + 2] = (uint)c; + v[vOff + 2] = (uint)c; + c >>= 32; + c += (ulong)u[uOff + 3] + v[vOff + 3]; + u[uOff + 3] = (uint)c; + v[vOff + 3] = (uint)c; + c >>= 32; + c += (ulong)u[uOff + 4] + v[vOff + 4]; + u[uOff + 4] = (uint)c; + v[vOff + 4] = (uint)c; + c >>= 32; + c += (ulong)u[uOff + 5] + v[vOff + 5]; + u[uOff + 5] = (uint)c; + v[vOff + 5] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static void Copy(uint[] x, uint[] z) + { + z[0] = x[0]; + z[1] = x[1]; + z[2] = x[2]; + z[3] = x[3]; + z[4] = x[4]; + z[5] = x[5]; + } + + public static uint[] Create() + { + return new uint[6]; + } + + public static uint[] CreateExt() + { + return new uint[12]; + } + + public static bool Diff(uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff) + { + bool pos = Gte(x, xOff, y, yOff); + if (pos) + { + Sub(x, xOff, y, yOff, z, zOff); + } + else + { + Sub(y, yOff, x, xOff, z, zOff); + } + return pos; + } + + public static bool Eq(uint[] x, uint[] y) + { + for (int i = 5; i >= 0; --i) + { + if (x[i] != y[i]) + return false; + } + return true; + } + + public static uint[] FromBigInteger(BigInteger x) + { + if (x.SignValue < 0 || x.BitLength > 192) + throw new ArgumentException(); + + uint[] z = Create(); + int i = 0; + while (x.SignValue != 0) + { + z[i++] = (uint)x.IntValue; + x = x.ShiftRight(32); + } + return z; + } + + public static uint GetBit(uint[] x, int bit) + { + if (bit == 0) + { + return x[0] & 1; + } + int w = bit >> 5; + if (w < 0 || w >= 6) + { + return 0; + } + int b = bit & 31; + return (x[w] >> b) & 1; + } + + public static bool Gte(uint[] x, uint[] y) + { + for (int i = 5; i >= 0; --i) + { + uint x_i = x[i], y_i = y[i]; + if (x_i < y_i) + return false; + if (x_i > y_i) + return true; + } + return true; + } + + public static bool Gte(uint[] x, int xOff, uint[] y, int yOff) + { + for (int i = 5; i >= 0; --i) + { + uint x_i = x[xOff + i], y_i = y[yOff + i]; + if (x_i < y_i) + return false; + if (x_i > y_i) + return true; + } + return true; + } + + public static bool IsOne(uint[] x) + { + if (x[0] != 1) + { + return false; + } + for (int i = 1; i < 6; ++i) + { + if (x[i] != 0) + { + return false; + } + } + return true; + } + + public static bool IsZero(uint[] x) + { + for (int i = 0; i < 6; ++i) + { + if (x[i] != 0) + { + return false; + } + } + return true; + } + + public static void Mul(uint[] x, uint[] y, uint[] zz) + { + ulong y_0 = y[0]; + ulong y_1 = y[1]; + ulong y_2 = y[2]; + ulong y_3 = y[3]; + ulong y_4 = y[4]; + ulong y_5 = y[5]; + + { + ulong c = 0, x_0 = x[0]; + c += x_0 * y_0; + zz[0] = (uint)c; + c >>= 32; + c += x_0 * y_1; + zz[1] = (uint)c; + c >>= 32; + c += x_0 * y_2; + zz[2] = (uint)c; + c >>= 32; + c += x_0 * y_3; + zz[3] = (uint)c; + c >>= 32; + c += x_0 * y_4; + zz[4] = (uint)c; + c >>= 32; + c += x_0 * y_5; + zz[5] = (uint)c; + c >>= 32; + zz[6] = (uint)c; + } + + for (int i = 1; i < 6; ++i) + { + ulong c = 0, x_i = x[i]; + c += x_i * y_0 + zz[i + 0]; + zz[i + 0] = (uint)c; + c >>= 32; + c += x_i * y_1 + zz[i + 1]; + zz[i + 1] = (uint)c; + c >>= 32; + c += x_i * y_2 + zz[i + 2]; + zz[i + 2] = (uint)c; + c >>= 32; + c += x_i * y_3 + zz[i + 3]; + zz[i + 3] = (uint)c; + c >>= 32; + c += x_i * y_4 + zz[i + 4]; + zz[i + 4] = (uint)c; + c >>= 32; + c += x_i * y_5 + zz[i + 5]; + zz[i + 5] = (uint)c; + c >>= 32; + zz[i + 6] = (uint)c; + } + } + + public static void Mul(uint[] x, int xOff, uint[] y, int yOff, uint[] zz, int zzOff) + { + ulong y_0 = y[yOff + 0]; + ulong y_1 = y[yOff + 1]; + ulong y_2 = y[yOff + 2]; + ulong y_3 = y[yOff + 3]; + ulong y_4 = y[yOff + 4]; + ulong y_5 = y[yOff + 5]; + + { + ulong c = 0, x_0 = x[xOff + 0]; + c += x_0 * y_0; + zz[zzOff + 0] = (uint)c; + c >>= 32; + c += x_0 * y_1; + zz[zzOff + 1] = (uint)c; + c >>= 32; + c += x_0 * y_2; + zz[zzOff + 2] = (uint)c; + c >>= 32; + c += x_0 * y_3; + zz[zzOff + 3] = (uint)c; + c >>= 32; + c += x_0 * y_4; + zz[zzOff + 4] = (uint)c; + c >>= 32; + c += x_0 * y_5; + zz[zzOff + 5] = (uint)c; + c >>= 32; + zz[zzOff + 6] = (uint)c; + } + + for (int i = 1; i < 6; ++i) + { + ++zzOff; + ulong c = 0, x_i = x[xOff + i]; + c += x_i * y_0 + zz[zzOff + 0]; + zz[zzOff + 0] = (uint)c; + c >>= 32; + c += x_i * y_1 + zz[zzOff + 1]; + zz[zzOff + 1] = (uint)c; + c >>= 32; + c += x_i * y_2 + zz[zzOff + 2]; + zz[zzOff + 2] = (uint)c; + c >>= 32; + c += x_i * y_3 + zz[zzOff + 3]; + zz[zzOff + 3] = (uint)c; + c >>= 32; + c += x_i * y_4 + zz[zzOff + 4]; + zz[zzOff + 4] = (uint)c; + c >>= 32; + c += x_i * y_5 + zz[zzOff + 5]; + zz[zzOff + 5] = (uint)c; + c >>= 32; + zz[zzOff + 6] = (uint)c; + } + } + + public static uint MulAddTo(uint[] x, uint[] y, uint[] zz) + { + ulong y_0 = y[0]; + ulong y_1 = y[1]; + ulong y_2 = y[2]; + ulong y_3 = y[3]; + ulong y_4 = y[4]; + ulong y_5 = y[5]; + + ulong zc = 0; + for (int i = 0; i < 6; ++i) + { + ulong c = 0, x_i = x[i]; + c += x_i * y_0 + zz[i + 0]; + zz[i + 0] = (uint)c; + c >>= 32; + c += x_i * y_1 + zz[i + 1]; + zz[i + 1] = (uint)c; + c >>= 32; + c += x_i * y_2 + zz[i + 2]; + zz[i + 2] = (uint)c; + c >>= 32; + c += x_i * y_3 + zz[i + 3]; + zz[i + 3] = (uint)c; + c >>= 32; + c += x_i * y_4 + zz[i + 4]; + zz[i + 4] = (uint)c; + c >>= 32; + c += x_i * y_5 + zz[i + 5]; + zz[i + 5] = (uint)c; + c >>= 32; + c += zc + zz[i + 6]; + zz[i + 6] = (uint)c; + zc = c >> 32; + } + return (uint)zc; + } + + public static uint MulAddTo(uint[] x, int xOff, uint[] y, int yOff, uint[] zz, int zzOff) + { + ulong y_0 = y[yOff + 0]; + ulong y_1 = y[yOff + 1]; + ulong y_2 = y[yOff + 2]; + ulong y_3 = y[yOff + 3]; + ulong y_4 = y[yOff + 4]; + ulong y_5 = y[yOff + 5]; + + ulong zc = 0; + for (int i = 0; i < 6; ++i) + { + ulong c = 0, x_i = x[xOff + i]; + c += x_i * y_0 + zz[zzOff + 0]; + zz[zzOff + 0] = (uint)c; + c >>= 32; + c += x_i * y_1 + zz[zzOff + 1]; + zz[zzOff + 1] = (uint)c; + c >>= 32; + c += x_i * y_2 + zz[zzOff + 2]; + zz[zzOff + 2] = (uint)c; + c >>= 32; + c += x_i * y_3 + zz[zzOff + 3]; + zz[zzOff + 3] = (uint)c; + c >>= 32; + c += x_i * y_4 + zz[zzOff + 4]; + zz[zzOff + 4] = (uint)c; + c >>= 32; + c += x_i * y_5 + zz[zzOff + 5]; + zz[zzOff + 5] = (uint)c; + c >>= 32; + c += zc + zz[zzOff + 6]; + zz[zzOff + 6] = (uint)c; + zc = c >> 32; + ++zzOff; + } + return (uint)zc; + } + + public static ulong Mul33Add(uint w, uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff) + { + Debug.Assert(w >> 31 == 0); + + ulong c = 0, wVal = w; + ulong x0 = x[xOff + 0]; + c += wVal * x0 + y[yOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + ulong x1 = x[xOff + 1]; + c += wVal * x1 + x0 + y[yOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + ulong x2 = x[xOff + 2]; + c += wVal * x2 + x1 + y[yOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + ulong x3 = x[xOff + 3]; + c += wVal * x3 + x2 + y[yOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + ulong x4 = x[xOff + 4]; + c += wVal * x4 + x3 + y[yOff + 4]; + z[zOff + 4] = (uint)c; + c >>= 32; + ulong x5 = x[xOff + 5]; + c += wVal * x5 + x4 + y[yOff + 5]; + z[zOff + 5] = (uint)c; + c >>= 32; + c += x5; + return c; + } + + public static uint MulWordAddExt(uint x, uint[] yy, int yyOff, uint[] zz, int zzOff) + { + Debug.Assert(yyOff <= 6); + Debug.Assert(zzOff <= 6); + ulong c = 0, xVal = x; + c += xVal * yy[yyOff + 0] + zz[zzOff + 0]; + zz[zzOff + 0] = (uint)c; + c >>= 32; + c += xVal * yy[yyOff + 1] + zz[zzOff + 1]; + zz[zzOff + 1] = (uint)c; + c >>= 32; + c += xVal * yy[yyOff + 2] + zz[zzOff + 2]; + zz[zzOff + 2] = (uint)c; + c >>= 32; + c += xVal * yy[yyOff + 3] + zz[zzOff + 3]; + zz[zzOff + 3] = (uint)c; + c >>= 32; + c += xVal * yy[yyOff + 4] + zz[zzOff + 4]; + zz[zzOff + 4] = (uint)c; + c >>= 32; + c += xVal * yy[yyOff + 5] + zz[zzOff + 5]; + zz[zzOff + 5] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint Mul33DWordAdd(uint x, ulong y, uint[] z, int zOff) + { + Debug.Assert(x >> 31 == 0); + Debug.Assert(zOff <= 2); + ulong c = 0, xVal = x; + ulong y00 = y & M; + c += xVal * y00 + z[zOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + ulong y01 = y >> 32; + c += xVal * y01 + y00 + z[zOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += y01 + z[zOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + c += z[zOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + return c == 0 ? 0 : Nat.IncAt(6, z, zOff, 4); + } + + public static uint Mul33WordAdd(uint x, uint y, uint[] z, int zOff) + { + Debug.Assert(x >> 31 == 0); + Debug.Assert(zOff <=3); + ulong c = 0, yVal = y; + c += yVal * x + z[zOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += yVal + z[zOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += z[zOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + return c == 0 ? 0 : Nat.IncAt(6, z, zOff, 3); + } + + public static uint MulWordDwordAdd(uint x, ulong y, uint[] z, int zOff) + { + Debug.Assert(zOff <= 3); + ulong c = 0, xVal = x; + c += xVal * y + z[zOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += xVal * (y >> 32) + z[zOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += z[zOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + return c == 0 ? 0 : Nat.IncAt(6, z, zOff, 3); + } + + public static uint MulWord(uint x, uint[] y, uint[] z, int zOff) + { + ulong c = 0, xVal = x; + int i = 0; + do + { + c += xVal * y[i]; + z[zOff + i] = (uint)c; + c >>= 32; + } + while (++i < 6); + return (uint)c; + } + + public static void Square(uint[] x, uint[] zz) + { + ulong x_0 = x[0]; + ulong zz_1; + + uint c = 0, w; + { + int i = 5, j = 12; + do + { + ulong xVal = x[i--]; + ulong p = xVal * xVal; + zz[--j] = (c << 31) | (uint)(p >> 33); + zz[--j] = (uint)(p >> 1); + c = (uint)p; + } + while (i > 0); + + { + ulong p = x_0 * x_0; + zz_1 = (ulong)(c << 31) | (p >> 33); + zz[0] = (uint)p; + c = (uint)(p >> 32) & 1; + } + } + + ulong x_1 = x[1]; + ulong zz_2 = zz[2]; + + { + zz_1 += x_1 * x_0; + w = (uint)zz_1; + zz[1] = (w << 1) | c; + c = w >> 31; + zz_2 += zz_1 >> 32; + } + + ulong x_2 = x[2]; + ulong zz_3 = zz[3]; + ulong zz_4 = zz[4]; + { + zz_2 += x_2 * x_0; + w = (uint)zz_2; + zz[2] = (w << 1) | c; + c = w >> 31; + zz_3 += (zz_2 >> 32) + x_2 * x_1; + zz_4 += zz_3 >> 32; + zz_3 &= M; + } + + ulong x_3 = x[3]; + ulong zz_5 = zz[5]; + ulong zz_6 = zz[6]; + { + zz_3 += x_3 * x_0; + w = (uint)zz_3; + zz[3] = (w << 1) | c; + c = w >> 31; + zz_4 += (zz_3 >> 32) + x_3 * x_1; + zz_5 += (zz_4 >> 32) + x_3 * x_2; + zz_4 &= M; + zz_6 += zz_5 >> 32; + zz_5 &= M; + } + + ulong x_4 = x[4]; + ulong zz_7 = zz[7]; + ulong zz_8 = zz[8]; + { + zz_4 += x_4 * x_0; + w = (uint)zz_4; + zz[4] = (w << 1) | c; + c = w >> 31; + zz_5 += (zz_4 >> 32) + x_4 * x_1; + zz_6 += (zz_5 >> 32) + x_4 * x_2; + zz_5 &= M; + zz_7 += (zz_6 >> 32) + x_4 * x_3; + zz_6 &= M; + zz_8 += zz_7 >> 32; + zz_7 &= M; + } + + ulong x_5 = x[5]; + ulong zz_9 = zz[9]; + ulong zz_10 = zz[10]; + { + zz_5 += x_5 * x_0; + w = (uint)zz_5; + zz[5] = (w << 1) | c; + c = w >> 31; + zz_6 += (zz_5 >> 32) + x_5 * x_1; + zz_7 += (zz_6 >> 32) + x_5 * x_2; + zz_8 += (zz_7 >> 32) + x_5 * x_3; + zz_9 += (zz_8 >> 32) + x_5 * x_4; + zz_10 += zz_9 >> 32; + } + + w = (uint)zz_6; + zz[6] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_7; + zz[7] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_8; + zz[8] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_9; + zz[9] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_10; + zz[10] = (w << 1) | c; + c = w >> 31; + w = zz[11] + (uint)(zz_10 >> 32); + zz[11] = (w << 1) | c; + } + + public static void Square(uint[] x, int xOff, uint[] zz, int zzOff) + { + ulong x_0 = x[xOff + 0]; + ulong zz_1; + + uint c = 0, w; + { + int i = 5, j = 12; + do + { + ulong xVal = x[xOff + i--]; + ulong p = xVal * xVal; + zz[zzOff + --j] = (c << 31) | (uint)(p >> 33); + zz[zzOff + --j] = (uint)(p >> 1); + c = (uint)p; + } + while (i > 0); + + { + ulong p = x_0 * x_0; + zz_1 = (ulong)(c << 31) | (p >> 33); + zz[zzOff + 0] = (uint)p; + c = (uint)(p >> 32) & 1; + } + } + + ulong x_1 = x[xOff + 1]; + ulong zz_2 = zz[zzOff + 2]; + + { + zz_1 += x_1 * x_0; + w = (uint)zz_1; + zz[zzOff + 1] = (w << 1) | c; + c = w >> 31; + zz_2 += zz_1 >> 32; + } + + ulong x_2 = x[xOff + 2]; + ulong zz_3 = zz[zzOff + 3]; + ulong zz_4 = zz[zzOff + 4]; + { + zz_2 += x_2 * x_0; + w = (uint)zz_2; + zz[zzOff + 2] = (w << 1) | c; + c = w >> 31; + zz_3 += (zz_2 >> 32) + x_2 * x_1; + zz_4 += zz_3 >> 32; + zz_3 &= M; + } + + ulong x_3 = x[xOff + 3]; + ulong zz_5 = zz[zzOff + 5]; + ulong zz_6 = zz[zzOff + 6]; + { + zz_3 += x_3 * x_0; + w = (uint)zz_3; + zz[zzOff + 3] = (w << 1) | c; + c = w >> 31; + zz_4 += (zz_3 >> 32) + x_3 * x_1; + zz_5 += (zz_4 >> 32) + x_3 * x_2; + zz_4 &= M; + zz_6 += zz_5 >> 32; + zz_5 &= M; + } + + ulong x_4 = x[xOff + 4]; + ulong zz_7 = zz[zzOff + 7]; + ulong zz_8 = zz[zzOff + 8]; + { + zz_4 += x_4 * x_0; + w = (uint)zz_4; + zz[zzOff + 4] = (w << 1) | c; + c = w >> 31; + zz_5 += (zz_4 >> 32) + x_4 * x_1; + zz_6 += (zz_5 >> 32) + x_4 * x_2; + zz_5 &= M; + zz_7 += (zz_6 >> 32) + x_4 * x_3; + zz_6 &= M; + zz_8 += zz_7 >> 32; + zz_7 &= M; + } + + ulong x_5 = x[xOff + 5]; + ulong zz_9 = zz[zzOff + 9]; + ulong zz_10 = zz[zzOff + 10]; + { + zz_5 += x_5 * x_0; + w = (uint)zz_5; + zz[zzOff + 5] = (w << 1) | c; + c = w >> 31; + zz_6 += (zz_5 >> 32) + x_5 * x_1; + zz_7 += (zz_6 >> 32) + x_5 * x_2; + zz_8 += (zz_7 >> 32) + x_5 * x_3; + zz_9 += (zz_8 >> 32) + x_5 * x_4; + zz_10 += zz_9 >> 32; + } + + w = (uint)zz_6; + zz[zzOff + 6] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_7; + zz[zzOff + 7] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_8; + zz[zzOff + 8] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_9; + zz[zzOff + 9] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_10; + zz[zzOff + 10] = (w << 1) | c; + c = w >> 31; + w = zz[zzOff + 11] + (uint)(zz_10 >> 32); + zz[zzOff + 11] = (w << 1) | c; + } + + public static int Sub(uint[] x, uint[] y, uint[] z) + { + long c = 0; + c += (long)x[0] - y[0]; + z[0] = (uint)c; + c >>= 32; + c += (long)x[1] - y[1]; + z[1] = (uint)c; + c >>= 32; + c += (long)x[2] - y[2]; + z[2] = (uint)c; + c >>= 32; + c += (long)x[3] - y[3]; + z[3] = (uint)c; + c >>= 32; + c += (long)x[4] - y[4]; + z[4] = (uint)c; + c >>= 32; + c += (long)x[5] - y[5]; + z[5] = (uint)c; + c >>= 32; + return (int)c; + } + + public static int Sub(uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff) + { + long c = 0; + c += (long)x[xOff + 0] - y[yOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += (long)x[xOff + 1] - y[yOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += (long)x[xOff + 2] - y[yOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + c += (long)x[xOff + 3] - y[yOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + c += (long)x[xOff + 4] - y[yOff + 4]; + z[zOff + 4] = (uint)c; + c >>= 32; + c += (long)x[xOff + 5] - y[yOff + 5]; + z[zOff + 5] = (uint)c; + c >>= 32; + return (int)c; + } + + public static int SubBothFrom(uint[] x, uint[] y, uint[] z) + { + long c = 0; + c += (long)z[0] - x[0] - y[0]; + z[0] = (uint)c; + c >>= 32; + c += (long)z[1] - x[1] - y[1]; + z[1] = (uint)c; + c >>= 32; + c += (long)z[2] - x[2] - y[2]; + z[2] = (uint)c; + c >>= 32; + c += (long)z[3] - x[3] - y[3]; + z[3] = (uint)c; + c >>= 32; + c += (long)z[4] - x[4] - y[4]; + z[4] = (uint)c; + c >>= 32; + c += (long)z[5] - x[5] - y[5]; + z[5] = (uint)c; + c >>= 32; + return (int)c; + } + + public static int SubFrom(uint[] x, uint[] z) + { + long c = 0; + c += (long)z[0] - x[0]; + z[0] = (uint)c; + c >>= 32; + c += (long)z[1] - x[1]; + z[1] = (uint)c; + c >>= 32; + c += (long)z[2] - x[2]; + z[2] = (uint)c; + c >>= 32; + c += (long)z[3] - x[3]; + z[3] = (uint)c; + c >>= 32; + c += (long)z[4] - x[4]; + z[4] = (uint)c; + c >>= 32; + c += (long)z[5] - x[5]; + z[5] = (uint)c; + c >>= 32; + return (int)c; + } + + public static int SubFrom(uint[] x, int xOff, uint[] z, int zOff) + { + long c = 0; + c += (long)z[zOff + 0] - x[xOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += (long)z[zOff + 1] - x[xOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += (long)z[zOff + 2] - x[xOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + c += (long)z[zOff + 3] - x[xOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + c += (long)z[zOff + 4] - x[xOff + 4]; + z[zOff + 4] = (uint)c; + c >>= 32; + c += (long)z[zOff + 5] - x[xOff + 5]; + z[zOff + 5] = (uint)c; + c >>= 32; + return (int)c; + } + + public static BigInteger ToBigInteger(uint[] x) + { + byte[] bs = new byte[24]; + for (int i = 0; i < 6; ++i) + { + uint x_i = x[i]; + if (x_i != 0) + { + Pack.UInt32_To_BE(x_i, bs, (5 - i) << 2); + } + } + return new BigInteger(1, bs); + } + + public static void Zero(uint[] z) + { + z[0] = 0; + z[1] = 0; + z[2] = 0; + z[3] = 0; + z[4] = 0; + z[5] = 0; + } + } +} diff --git a/crypto/src/math/ec/custom/sec/Nat224.cs b/crypto/src/math/ec/custom/sec/Nat224.cs new file mode 100644
index 000000000..d5b916a54 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/Nat224.cs
@@ -0,0 +1,1176 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal abstract class Nat224 + { + private const ulong M = 0xFFFFFFFFUL; + + public static uint Add(uint[] x, uint[] y, uint[] z) + { + ulong c = 0; + c += (ulong)x[0] + y[0]; + z[0] = (uint)c; + c >>= 32; + c += (ulong)x[1] + y[1]; + z[1] = (uint)c; + c >>= 32; + c += (ulong)x[2] + y[2]; + z[2] = (uint)c; + c >>= 32; + c += (ulong)x[3] + y[3]; + z[3] = (uint)c; + c >>= 32; + c += (ulong)x[4] + y[4]; + z[4] = (uint)c; + c >>= 32; + c += (ulong)x[5] + y[5]; + z[5] = (uint)c; + c >>= 32; + c += (ulong)x[6] + y[6]; + z[6] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint Add(uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff) + { + ulong c = 0; + c += (ulong)x[xOff + 0] + y[yOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 1] + y[yOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 2] + y[yOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 3] + y[yOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 4] + y[yOff + 4]; + z[zOff + 4] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 5] + y[yOff + 5]; + z[zOff + 5] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 6] + y[yOff + 6]; + z[zOff + 6] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint AddBothTo(uint[] x, uint[] y, uint[] z) + { + ulong c = 0; + c += (ulong)x[0] + y[0] + z[0]; + z[0] = (uint)c; + c >>= 32; + c += (ulong)x[1] + y[1] + z[1]; + z[1] = (uint)c; + c >>= 32; + c += (ulong)x[2] + y[2] + z[2]; + z[2] = (uint)c; + c >>= 32; + c += (ulong)x[3] + y[3] + z[3]; + z[3] = (uint)c; + c >>= 32; + c += (ulong)x[4] + y[4] + z[4]; + z[4] = (uint)c; + c >>= 32; + c += (ulong)x[5] + y[5] + z[5]; + z[5] = (uint)c; + c >>= 32; + c += (ulong)x[6] + y[6] + z[6]; + z[6] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint AddBothTo(uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff) + { + ulong c = 0; + c += (ulong)x[xOff + 0] + y[yOff + 0] + z[zOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 1] + y[yOff + 1] + z[zOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 2] + y[yOff + 2] + z[zOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 3] + y[yOff + 3] + z[zOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 4] + y[yOff + 4] + z[zOff + 4]; + z[zOff + 4] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 5] + y[yOff + 5] + z[zOff + 5]; + z[zOff + 5] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 6] + y[yOff + 6] + z[zOff + 6]; + z[zOff + 6] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint AddTo(uint[] x, uint[] z) + { + ulong c = 0; + c += (ulong)x[0] + z[0]; + z[0] = (uint)c; + c >>= 32; + c += (ulong)x[1] + z[1]; + z[1] = (uint)c; + c >>= 32; + c += (ulong)x[2] + z[2]; + z[2] = (uint)c; + c >>= 32; + c += (ulong)x[3] + z[3]; + z[3] = (uint)c; + c >>= 32; + c += (ulong)x[4] + z[4]; + z[4] = (uint)c; + c >>= 32; + c += (ulong)x[5] + z[5]; + z[5] = (uint)c; + c >>= 32; + c += (ulong)x[6] + z[6]; + z[6] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint AddTo(uint[] x, int xOff, uint[] z, int zOff, uint cIn) + { + ulong c = cIn; + c += (ulong)x[xOff + 0] + z[zOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 1] + z[zOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 2] + z[zOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 3] + z[zOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 4] + z[zOff + 4]; + z[zOff + 4] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 5] + z[zOff + 5]; + z[zOff + 5] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 6] + z[zOff + 6]; + z[zOff + 6] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint AddToEachOther(uint[] u, int uOff, uint[] v, int vOff) + { + ulong c = 0; + c += (ulong)u[uOff + 0] + v[vOff + 0]; + u[uOff + 0] = (uint)c; + v[vOff + 0] = (uint)c; + c >>= 32; + c += (ulong)u[uOff + 1] + v[vOff + 1]; + u[uOff + 1] = (uint)c; + v[vOff + 1] = (uint)c; + c >>= 32; + c += (ulong)u[uOff + 2] + v[vOff + 2]; + u[uOff + 2] = (uint)c; + v[vOff + 2] = (uint)c; + c >>= 32; + c += (ulong)u[uOff + 3] + v[vOff + 3]; + u[uOff + 3] = (uint)c; + v[vOff + 3] = (uint)c; + c >>= 32; + c += (ulong)u[uOff + 4] + v[vOff + 4]; + u[uOff + 4] = (uint)c; + v[vOff + 4] = (uint)c; + c >>= 32; + c += (ulong)u[uOff + 5] + v[vOff + 5]; + u[uOff + 5] = (uint)c; + v[vOff + 5] = (uint)c; + c >>= 32; + c += (ulong)u[uOff + 6] + v[vOff + 6]; + u[uOff + 6] = (uint)c; + v[vOff + 6] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static void Copy(uint[] x, uint[] z) + { + z[0] = x[0]; + z[1] = x[1]; + z[2] = x[2]; + z[3] = x[3]; + z[4] = x[4]; + z[5] = x[5]; + z[6] = x[6]; + } + + public static uint[] Create() + { + return new uint[7]; + } + + public static uint[] CreateExt() + { + return new uint[14]; + } + + public static bool Diff(uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff) + { + bool pos = Gte(x, xOff, y, yOff); + if (pos) + { + Sub(x, xOff, y, yOff, z, zOff); + } + else + { + Sub(y, yOff, x, xOff, z, zOff); + } + return pos; + } + + public static bool Eq(uint[] x, uint[] y) + { + for (int i = 6; i >= 0; --i) + { + if (x[i] != y[i]) + return false; + } + return true; + } + + public static uint[] FromBigInteger(BigInteger x) + { + if (x.SignValue < 0 || x.BitLength > 224) + throw new ArgumentException(); + + uint[] z = Create(); + int i = 0; + while (x.SignValue != 0) + { + z[i++] = (uint)x.IntValue; + x = x.ShiftRight(32); + } + return z; + } + + public static uint GetBit(uint[] x, int bit) + { + if (bit == 0) + { + return x[0] & 1; + } + int w = bit >> 5; + if (w < 0 || w >= 7) + { + return 0; + } + int b = bit & 31; + return (x[w] >> b) & 1; + } + + public static bool Gte(uint[] x, uint[] y) + { + for (int i = 6; i >= 0; --i) + { + uint x_i = x[i], y_i = y[i]; + if (x_i < y_i) + return false; + if (x_i > y_i) + return true; + } + return true; + } + + public static bool Gte(uint[] x, int xOff, uint[] y, int yOff) + { + for (int i = 6; i >= 0; --i) + { + uint x_i = x[xOff + i], y_i = y[yOff + i]; + if (x_i < y_i) + return false; + if (x_i > y_i) + return true; + } + return true; + } + + public static bool IsOne(uint[] x) + { + if (x[0] != 1) + { + return false; + } + for (int i = 1; i < 7; ++i) + { + if (x[i] != 0) + { + return false; + } + } + return true; + } + + public static bool IsZero(uint[] x) + { + for (int i = 0; i < 7; ++i) + { + if (x[i] != 0) + { + return false; + } + } + return true; + } + + public static void Mul(uint[] x, uint[] y, uint[] zz) + { + ulong y_0 = y[0]; + ulong y_1 = y[1]; + ulong y_2 = y[2]; + ulong y_3 = y[3]; + ulong y_4 = y[4]; + ulong y_5 = y[5]; + ulong y_6 = y[6]; + + { + ulong c = 0, x_0 = x[0]; + c += x_0 * y_0; + zz[0] = (uint)c; + c >>= 32; + c += x_0 * y_1; + zz[1] = (uint)c; + c >>= 32; + c += x_0 * y_2; + zz[2] = (uint)c; + c >>= 32; + c += x_0 * y_3; + zz[3] = (uint)c; + c >>= 32; + c += x_0 * y_4; + zz[4] = (uint)c; + c >>= 32; + c += x_0 * y_5; + zz[5] = (uint)c; + c >>= 32; + c += x_0 * y_6; + zz[6] = (uint)c; + c >>= 32; + zz[7] = (uint)c; + } + + for (int i = 1; i < 7; ++i) + { + ulong c = 0, x_i = x[i]; + c += x_i * y_0 + zz[i + 0]; + zz[i + 0] = (uint)c; + c >>= 32; + c += x_i * y_1 + zz[i + 1]; + zz[i + 1] = (uint)c; + c >>= 32; + c += x_i * y_2 + zz[i + 2]; + zz[i + 2] = (uint)c; + c >>= 32; + c += x_i * y_3 + zz[i + 3]; + zz[i + 3] = (uint)c; + c >>= 32; + c += x_i * y_4 + zz[i + 4]; + zz[i + 4] = (uint)c; + c >>= 32; + c += x_i * y_5 + zz[i + 5]; + zz[i + 5] = (uint)c; + c >>= 32; + c += x_i * y_6 + zz[i + 6]; + zz[i + 6] = (uint)c; + c >>= 32; + zz[i + 7] = (uint)c; + } + } + + public static void Mul(uint[] x, int xOff, uint[] y, int yOff, uint[] zz, int zzOff) + { + ulong y_0 = y[yOff + 0]; + ulong y_1 = y[yOff + 1]; + ulong y_2 = y[yOff + 2]; + ulong y_3 = y[yOff + 3]; + ulong y_4 = y[yOff + 4]; + ulong y_5 = y[yOff + 5]; + ulong y_6 = y[yOff + 6]; + + { + ulong c = 0, x_0 = x[xOff + 0]; + c += x_0 * y_0; + zz[zzOff + 0] = (uint)c; + c >>= 32; + c += x_0 * y_1; + zz[zzOff + 1] = (uint)c; + c >>= 32; + c += x_0 * y_2; + zz[zzOff + 2] = (uint)c; + c >>= 32; + c += x_0 * y_3; + zz[zzOff + 3] = (uint)c; + c >>= 32; + c += x_0 * y_4; + zz[zzOff + 4] = (uint)c; + c >>= 32; + c += x_0 * y_5; + zz[zzOff + 5] = (uint)c; + c >>= 32; + c += x_0 * y_6; + zz[zzOff + 6] = (uint)c; + c >>= 32; + zz[zzOff + 7] = (uint)c; + } + + for (int i = 1; i < 7; ++i) + { + ++zzOff; + ulong c = 0, x_i = x[xOff + i]; + c += x_i * y_0 + zz[zzOff + 0]; + zz[zzOff + 0] = (uint)c; + c >>= 32; + c += x_i * y_1 + zz[zzOff + 1]; + zz[zzOff + 1] = (uint)c; + c >>= 32; + c += x_i * y_2 + zz[zzOff + 2]; + zz[zzOff + 2] = (uint)c; + c >>= 32; + c += x_i * y_3 + zz[zzOff + 3]; + zz[zzOff + 3] = (uint)c; + c >>= 32; + c += x_i * y_4 + zz[zzOff + 4]; + zz[zzOff + 4] = (uint)c; + c >>= 32; + c += x_i * y_5 + zz[zzOff + 5]; + zz[zzOff + 5] = (uint)c; + c >>= 32; + c += x_i * y_6 + zz[zzOff + 6]; + zz[zzOff + 6] = (uint)c; + c >>= 32; + zz[zzOff + 7] = (uint)c; + } + } + + public static uint MulAddTo(uint[] x, uint[] y, uint[] zz) + { + ulong y_0 = y[0]; + ulong y_1 = y[1]; + ulong y_2 = y[2]; + ulong y_3 = y[3]; + ulong y_4 = y[4]; + ulong y_5 = y[5]; + ulong y_6 = y[6]; + + ulong zc = 0; + for (int i = 0; i < 7; ++i) + { + ulong c = 0, x_i = x[i]; + c += x_i * y_0 + zz[i + 0]; + zz[i + 0] = (uint)c; + c >>= 32; + c += x_i * y_1 + zz[i + 1]; + zz[i + 1] = (uint)c; + c >>= 32; + c += x_i * y_2 + zz[i + 2]; + zz[i + 2] = (uint)c; + c >>= 32; + c += x_i * y_3 + zz[i + 3]; + zz[i + 3] = (uint)c; + c >>= 32; + c += x_i * y_4 + zz[i + 4]; + zz[i + 4] = (uint)c; + c >>= 32; + c += x_i * y_5 + zz[i + 5]; + zz[i + 5] = (uint)c; + c >>= 32; + c += x_i * y_6 + zz[i + 6]; + zz[i + 6] = (uint)c; + c >>= 32; + c += zc + zz[i + 7]; + zz[i + 7] = (uint)c; + zc = c >> 32; + } + return (uint)zc; + } + + public static uint MulAddTo(uint[] x, int xOff, uint[] y, int yOff, uint[] zz, int zzOff) + { + ulong y_0 = y[yOff + 0]; + ulong y_1 = y[yOff + 1]; + ulong y_2 = y[yOff + 2]; + ulong y_3 = y[yOff + 3]; + ulong y_4 = y[yOff + 4]; + ulong y_5 = y[yOff + 5]; + ulong y_6 = y[yOff + 6]; + + ulong zc = 0; + for (int i = 0; i < 7; ++i) + { + ulong c = 0, x_i = x[xOff + i]; + c += x_i * y_0 + zz[zzOff + 0]; + zz[zzOff + 0] = (uint)c; + c >>= 32; + c += x_i * y_1 + zz[zzOff + 1]; + zz[zzOff + 1] = (uint)c; + c >>= 32; + c += x_i * y_2 + zz[zzOff + 2]; + zz[zzOff + 2] = (uint)c; + c >>= 32; + c += x_i * y_3 + zz[zzOff + 3]; + zz[zzOff + 3] = (uint)c; + c >>= 32; + c += x_i * y_4 + zz[zzOff + 4]; + zz[zzOff + 4] = (uint)c; + c >>= 32; + c += x_i * y_5 + zz[zzOff + 5]; + zz[zzOff + 5] = (uint)c; + c >>= 32; + c += x_i * y_6 + zz[zzOff + 6]; + zz[zzOff + 6] = (uint)c; + c >>= 32; + c += zc + zz[zzOff + 7]; + zz[zzOff + 7] = (uint)c; + zc = c >> 32; + ++zzOff; + } + return (uint)zc; + } + + public static ulong Mul33Add(uint w, uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff) + { + Debug.Assert(w >> 31 == 0); + + ulong c = 0, wVal = w; + ulong x0 = x[xOff + 0]; + c += wVal * x0 + y[yOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + ulong x1 = x[xOff + 1]; + c += wVal * x1 + x0 + y[yOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + ulong x2 = x[xOff + 2]; + c += wVal * x2 + x1 + y[yOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + ulong x3 = x[xOff + 3]; + c += wVal * x3 + x2 + y[yOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + ulong x4 = x[xOff + 4]; + c += wVal * x4 + x3 + y[yOff + 4]; + z[zOff + 4] = (uint)c; + c >>= 32; + ulong x5 = x[xOff + 5]; + c += wVal * x5 + x4 + y[yOff + 5]; + z[zOff + 5] = (uint)c; + c >>= 32; + ulong x6 = x[xOff + 6]; + c += wVal * x6 + x5 + y[yOff + 6]; + z[zOff + 6] = (uint)c; + c >>= 32; + c += x6; + return c; + } + + public static uint MulByWord(uint x, uint[] z) + { + ulong c = 0, xVal = x; + c += xVal * (ulong)z[0]; + z[0] = (uint)c; + c >>= 32; + c += xVal * (ulong)z[1]; + z[1] = (uint)c; + c >>= 32; + c += xVal * (ulong)z[2]; + z[2] = (uint)c; + c >>= 32; + c += xVal * (ulong)z[3]; + z[3] = (uint)c; + c >>= 32; + c += xVal * (ulong)z[4]; + z[4] = (uint)c; + c >>= 32; + c += xVal * (ulong)z[5]; + z[5] = (uint)c; + c >>= 32; + c += xVal * (ulong)z[6]; + z[6] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint MulByWordAddTo(uint x, uint[] y, uint[] z) + { + ulong c = 0, xVal = x; + c += xVal * (ulong)z[0] + y[0]; + z[0] = (uint)c; + c >>= 32; + c += xVal * (ulong)z[1] + y[1]; + z[1] = (uint)c; + c >>= 32; + c += xVal * (ulong)z[2] + y[2]; + z[2] = (uint)c; + c >>= 32; + c += xVal * (ulong)z[3] + y[3]; + z[3] = (uint)c; + c >>= 32; + c += xVal * (ulong)z[4] + y[4]; + z[4] = (uint)c; + c >>= 32; + c += xVal * (ulong)z[5] + y[5]; + z[5] = (uint)c; + c >>= 32; + c += xVal * (ulong)z[6] + y[6]; + z[6] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint MulWordAddTo(uint x, uint[] y, int yOff, uint[] z, int zOff) + { + ulong c = 0, xVal = x; + c += xVal * y[yOff + 0] + z[zOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += xVal * y[yOff + 1] + z[zOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += xVal * y[yOff + 2] + z[zOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + c += xVal * y[yOff + 3] + z[zOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + c += xVal * y[yOff + 4] + z[zOff + 4]; + z[zOff + 4] = (uint)c; + c >>= 32; + c += xVal * y[yOff + 5] + z[zOff + 5]; + z[zOff + 5] = (uint)c; + c >>= 32; + c += xVal * y[yOff + 6] + z[zOff + 6]; + z[zOff + 6] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint Mul33DWordAdd(uint x, ulong y, uint[] z, int zOff) + { + Debug.Assert(x >> 31 == 0); + Debug.Assert(zOff <= 3); + ulong c = 0, xVal = x; + ulong y00 = y & M; + c += xVal * y00 + z[zOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + ulong y01 = y >> 32; + c += xVal * y01 + y00 + z[zOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += y01 + z[zOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + c += z[zOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + return c == 0 ? 0 : Nat.IncAt(7, z, zOff, 4); + } + + public static uint Mul33WordAdd(uint x, uint y, uint[] z, int zOff) + { + Debug.Assert(x >> 31 == 0); + Debug.Assert(zOff <= 4); + ulong c = 0, yVal = y; + c += yVal * x + z[zOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += yVal + z[zOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += z[zOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + return c == 0 ? 0 : Nat.IncAt(7, z, zOff, 3); + } + + public static uint MulWordDwordAdd(uint x, ulong y, uint[] z, int zOff) + { + Debug.Assert(zOff <= 4); + ulong c = 0, xVal = x; + c += xVal * y + z[zOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += xVal * (y >> 32) + z[zOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += z[zOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + return c == 0 ? 0 : Nat.IncAt(7, z, zOff, 3); + } + + public static uint MulWord(uint x, uint[] y, uint[] z, int zOff) + { + ulong c = 0, xVal = x; + int i = 0; + do + { + c += xVal * y[i]; + z[zOff + i] = (uint)c; + c >>= 32; + } + while (++i < 7); + return (uint)c; + } + + public static void Square(uint[] x, uint[] zz) + { + ulong x_0 = x[0]; + ulong zz_1; + + uint c = 0, w; + { + int i = 6, j = 14; + do + { + ulong xVal = x[i--]; + ulong p = xVal * xVal; + zz[--j] = (c << 31) | (uint)(p >> 33); + zz[--j] = (uint)(p >> 1); + c = (uint)p; + } + while (i > 0); + + { + ulong p = x_0 * x_0; + zz_1 = (ulong)(c << 31) | (p >> 33); + zz[0] = (uint)p; + c = (uint)(p >> 32) & 1; + } + } + + ulong x_1 = x[1]; + ulong zz_2 = zz[2]; + + { + zz_1 += x_1 * x_0; + w = (uint)zz_1; + zz[1] = (w << 1) | c; + c = w >> 31; + zz_2 += zz_1 >> 32; + } + + ulong x_2 = x[2]; + ulong zz_3 = zz[3]; + ulong zz_4 = zz[4]; + { + zz_2 += x_2 * x_0; + w = (uint)zz_2; + zz[2] = (w << 1) | c; + c = w >> 31; + zz_3 += (zz_2 >> 32) + x_2 * x_1; + zz_4 += zz_3 >> 32; + zz_3 &= M; + } + + ulong x_3 = x[3]; + ulong zz_5 = zz[5]; + ulong zz_6 = zz[6]; + { + zz_3 += x_3 * x_0; + w = (uint)zz_3; + zz[3] = (w << 1) | c; + c = w >> 31; + zz_4 += (zz_3 >> 32) + x_3 * x_1; + zz_5 += (zz_4 >> 32) + x_3 * x_2; + zz_4 &= M; + zz_6 += zz_5 >> 32; + zz_5 &= M; + } + + ulong x_4 = x[4]; + ulong zz_7 = zz[7]; + ulong zz_8 = zz[8]; + { + zz_4 += x_4 * x_0; + w = (uint)zz_4; + zz[4] = (w << 1) | c; + c = w >> 31; + zz_5 += (zz_4 >> 32) + x_4 * x_1; + zz_6 += (zz_5 >> 32) + x_4 * x_2; + zz_5 &= M; + zz_7 += (zz_6 >> 32) + x_4 * x_3; + zz_6 &= M; + zz_8 += zz_7 >> 32; + zz_7 &= M; + } + + ulong x_5 = x[5]; + ulong zz_9 = zz[9]; + ulong zz_10 = zz[10]; + { + zz_5 += x_5 * x_0; + w = (uint)zz_5; + zz[5] = (w << 1) | c; + c = w >> 31; + zz_6 += (zz_5 >> 32) + x_5 * x_1; + zz_7 += (zz_6 >> 32) + x_5 * x_2; + zz_6 &= M; + zz_8 += (zz_7 >> 32) + x_5 * x_3; + zz_7 &= M; + zz_9 += (zz_8 >> 32) + x_5 * x_4; + zz_8 &= M; + zz_10 += zz_9 >> 32; + zz_9 &= M; + } + + ulong x_6 = x[6]; + ulong zz_11 = zz[11]; + ulong zz_12 = zz[12]; + { + zz_6 += x_6 * x_0; + w = (uint)zz_6; + zz[6] = (w << 1) | c; + c = w >> 31; + zz_7 += (zz_6 >> 32) + x_6 * x_1; + zz_8 += (zz_7 >> 32) + x_6 * x_2; + zz_9 += (zz_8 >> 32) + x_6 * x_3; + zz_10 += (zz_9 >> 32) + x_6 * x_4; + zz_11 += (zz_10 >> 32) + x_6 * x_5; + zz_12 += zz_11 >> 32; + } + + w = (uint)zz_7; + zz[7] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_8; + zz[8] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_9; + zz[9] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_10; + zz[10] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_11; + zz[11] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_12; + zz[12] = (w << 1) | c; + c = w >> 31; + w = zz[13] + (uint)(zz_12 >> 32); + zz[13] = (w << 1) | c; + } + + public static void Square(uint[] x, int xOff, uint[] zz, int zzOff) + { + ulong x_0 = x[xOff + 0]; + ulong zz_1; + + uint c = 0, w; + { + int i = 6, j = 14; + do + { + ulong xVal = x[xOff + i--]; + ulong p = xVal * xVal; + zz[zzOff + --j] = (c << 31) | (uint)(p >> 33); + zz[zzOff + --j] = (uint)(p >> 1); + c = (uint)p; + } + while (i > 0); + + { + ulong p = x_0 * x_0; + zz_1 = (ulong)(c << 31) | (p >> 33); + zz[zzOff + 0] = (uint)p; + c = (uint)(p >> 32) & 1; + } + } + + ulong x_1 = x[xOff + 1]; + ulong zz_2 = zz[zzOff + 2]; + + { + zz_1 += x_1 * x_0; + w = (uint)zz_1; + zz[zzOff + 1] = (w << 1) | c; + c = w >> 31; + zz_2 += zz_1 >> 32; + } + + ulong x_2 = x[xOff + 2]; + ulong zz_3 = zz[zzOff + 3]; + ulong zz_4 = zz[zzOff + 4]; + { + zz_2 += x_2 * x_0; + w = (uint)zz_2; + zz[zzOff + 2] = (w << 1) | c; + c = w >> 31; + zz_3 += (zz_2 >> 32) + x_2 * x_1; + zz_4 += zz_3 >> 32; + zz_3 &= M; + } + + ulong x_3 = x[xOff + 3]; + ulong zz_5 = zz[zzOff + 5]; + ulong zz_6 = zz[zzOff + 6]; + { + zz_3 += x_3 * x_0; + w = (uint)zz_3; + zz[zzOff + 3] = (w << 1) | c; + c = w >> 31; + zz_4 += (zz_3 >> 32) + x_3 * x_1; + zz_5 += (zz_4 >> 32) + x_3 * x_2; + zz_4 &= M; + zz_6 += zz_5 >> 32; + zz_5 &= M; + } + + ulong x_4 = x[xOff + 4]; + ulong zz_7 = zz[zzOff + 7]; + ulong zz_8 = zz[zzOff + 8]; + { + zz_4 += x_4 * x_0; + w = (uint)zz_4; + zz[zzOff + 4] = (w << 1) | c; + c = w >> 31; + zz_5 += (zz_4 >> 32) + x_4 * x_1; + zz_6 += (zz_5 >> 32) + x_4 * x_2; + zz_5 &= M; + zz_7 += (zz_6 >> 32) + x_4 * x_3; + zz_6 &= M; + zz_8 += zz_7 >> 32; + zz_7 &= M; + } + + ulong x_5 = x[xOff + 5]; + ulong zz_9 = zz[zzOff + 9]; + ulong zz_10 = zz[zzOff + 10]; + { + zz_5 += x_5 * x_0; + w = (uint)zz_5; + zz[zzOff + 5] = (w << 1) | c; + c = w >> 31; + zz_6 += (zz_5 >> 32) + x_5 * x_1; + zz_7 += (zz_6 >> 32) + x_5 * x_2; + zz_6 &= M; + zz_8 += (zz_7 >> 32) + x_5 * x_3; + zz_7 &= M; + zz_9 += (zz_8 >> 32) + x_5 * x_4; + zz_8 &= M; + zz_10 += zz_9 >> 32; + zz_9 &= M; + } + + ulong x_6 = x[xOff + 6]; + ulong zz_11 = zz[zzOff + 11]; + ulong zz_12 = zz[zzOff + 12]; + { + zz_6 += x_6 * x_0; + w = (uint)zz_6; + zz[zzOff + 6] = (w << 1) | c; + c = w >> 31; + zz_7 += (zz_6 >> 32) + x_6 * x_1; + zz_8 += (zz_7 >> 32) + x_6 * x_2; + zz_9 += (zz_8 >> 32) + x_6 * x_3; + zz_10 += (zz_9 >> 32) + x_6 * x_4; + zz_11 += (zz_10 >> 32) + x_6 * x_5; + zz_12 += zz_11 >> 32; + } + + w = (uint)zz_7; + zz[zzOff + 7] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_8; + zz[zzOff + 8] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_9; + zz[zzOff + 9] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_10; + zz[zzOff + 10] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_11; + zz[zzOff + 11] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_12; + zz[zzOff + 12] = (w << 1) | c; + c = w >> 31; + w = zz[zzOff + 13] + (uint)(zz_12 >> 32); + zz[zzOff + 13] = (w << 1) | c; + } + + public static int Sub(uint[] x, uint[] y, uint[] z) + { + long c = 0; + c += (long)x[0] - y[0]; + z[0] = (uint)c; + c >>= 32; + c += (long)x[1] - y[1]; + z[1] = (uint)c; + c >>= 32; + c += (long)x[2] - y[2]; + z[2] = (uint)c; + c >>= 32; + c += (long)x[3] - y[3]; + z[3] = (uint)c; + c >>= 32; + c += (long)x[4] - y[4]; + z[4] = (uint)c; + c >>= 32; + c += (long)x[5] - y[5]; + z[5] = (uint)c; + c >>= 32; + c += (long)x[6] - y[6]; + z[6] = (uint)c; + c >>= 32; + return (int)c; + } + + public static int Sub(uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff) + { + long c = 0; + c += (long)x[xOff + 0] - y[yOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += (long)x[xOff + 1] - y[yOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += (long)x[xOff + 2] - y[yOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + c += (long)x[xOff + 3] - y[yOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + c += (long)x[xOff + 4] - y[yOff + 4]; + z[zOff + 4] = (uint)c; + c >>= 32; + c += (long)x[xOff + 5] - y[yOff + 5]; + z[zOff + 5] = (uint)c; + c >>= 32; + c += (long)x[xOff + 6] - y[yOff + 6]; + z[zOff + 6] = (uint)c; + c >>= 32; + return (int)c; + } + + public static int SubBothFrom(uint[] x, uint[] y, uint[] z) + { + long c = 0; + c += (long)z[0] - x[0] - y[0]; + z[0] = (uint)c; + c >>= 32; + c += (long)z[1] - x[1] - y[1]; + z[1] = (uint)c; + c >>= 32; + c += (long)z[2] - x[2] - y[2]; + z[2] = (uint)c; + c >>= 32; + c += (long)z[3] - x[3] - y[3]; + z[3] = (uint)c; + c >>= 32; + c += (long)z[4] - x[4] - y[4]; + z[4] = (uint)c; + c >>= 32; + c += (long)z[5] - x[5] - y[5]; + z[5] = (uint)c; + c >>= 32; + c += (long)z[6] - x[6] - y[6]; + z[6] = (uint)c; + c >>= 32; + return (int)c; + } + + public static int SubFrom(uint[] x, uint[] z) + { + long c = 0; + c += (long)z[0] - x[0]; + z[0] = (uint)c; + c >>= 32; + c += (long)z[1] - x[1]; + z[1] = (uint)c; + c >>= 32; + c += (long)z[2] - x[2]; + z[2] = (uint)c; + c >>= 32; + c += (long)z[3] - x[3]; + z[3] = (uint)c; + c >>= 32; + c += (long)z[4] - x[4]; + z[4] = (uint)c; + c >>= 32; + c += (long)z[5] - x[5]; + z[5] = (uint)c; + c >>= 32; + c += (long)z[6] - x[6]; + z[6] = (uint)c; + c >>= 32; + return (int)c; + } + + public static int SubFrom(uint[] x, int xOff, uint[] z, int zOff) + { + long c = 0; + c += (long)z[zOff + 0] - x[xOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += (long)z[zOff + 1] - x[xOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += (long)z[zOff + 2] - x[xOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + c += (long)z[zOff + 3] - x[xOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + c += (long)z[zOff + 4] - x[xOff + 4]; + z[zOff + 4] = (uint)c; + c >>= 32; + c += (long)z[zOff + 5] - x[xOff + 5]; + z[zOff + 5] = (uint)c; + c >>= 32; + c += (long)z[zOff + 6] - x[xOff + 6]; + z[zOff + 6] = (uint)c; + c >>= 32; + return (int)c; + } + + public static BigInteger ToBigInteger(uint[] x) + { + byte[] bs = new byte[28]; + for (int i = 0; i < 7; ++i) + { + uint x_i = x[i]; + if (x_i != 0) + { + Pack.UInt32_To_BE(x_i, bs, (6 - i) << 2); + } + } + return new BigInteger(1, bs); + } + + public static void Zero(uint[] z) + { + z[0] = 0; + z[1] = 0; + z[2] = 0; + z[3] = 0; + z[4] = 0; + z[5] = 0; + z[6] = 0; + } + } +} diff --git a/crypto/src/math/ec/custom/sec/Nat256.cs b/crypto/src/math/ec/custom/sec/Nat256.cs new file mode 100644
index 000000000..bd2d6da47 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/Nat256.cs
@@ -0,0 +1,1300 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal abstract class Nat256 + { + private const ulong M = 0xFFFFFFFFUL; + + public static uint Add(uint[] x, uint[] y, uint[] z) + { + ulong c = 0; + c += (ulong)x[0] + y[0]; + z[0] = (uint)c; + c >>= 32; + c += (ulong)x[1] + y[1]; + z[1] = (uint)c; + c >>= 32; + c += (ulong)x[2] + y[2]; + z[2] = (uint)c; + c >>= 32; + c += (ulong)x[3] + y[3]; + z[3] = (uint)c; + c >>= 32; + c += (ulong)x[4] + y[4]; + z[4] = (uint)c; + c >>= 32; + c += (ulong)x[5] + y[5]; + z[5] = (uint)c; + c >>= 32; + c += (ulong)x[6] + y[6]; + z[6] = (uint)c; + c >>= 32; + c += (ulong)x[7] + y[7]; + z[7] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint Add(uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff) + { + ulong c = 0; + c += (ulong)x[xOff + 0] + y[yOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 1] + y[yOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 2] + y[yOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 3] + y[yOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 4] + y[yOff + 4]; + z[zOff + 4] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 5] + y[yOff + 5]; + z[zOff + 5] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 6] + y[yOff + 6]; + z[zOff + 6] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 7] + y[yOff + 7]; + z[zOff + 7] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint AddBothTo(uint[] x, uint[] y, uint[] z) + { + ulong c = 0; + c += (ulong)x[0] + y[0] + z[0]; + z[0] = (uint)c; + c >>= 32; + c += (ulong)x[1] + y[1] + z[1]; + z[1] = (uint)c; + c >>= 32; + c += (ulong)x[2] + y[2] + z[2]; + z[2] = (uint)c; + c >>= 32; + c += (ulong)x[3] + y[3] + z[3]; + z[3] = (uint)c; + c >>= 32; + c += (ulong)x[4] + y[4] + z[4]; + z[4] = (uint)c; + c >>= 32; + c += (ulong)x[5] + y[5] + z[5]; + z[5] = (uint)c; + c >>= 32; + c += (ulong)x[6] + y[6] + z[6]; + z[6] = (uint)c; + c >>= 32; + c += (ulong)x[7] + y[7] + z[7]; + z[7] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint AddBothTo(uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff) + { + ulong c = 0; + c += (ulong)x[xOff + 0] + y[yOff + 0] + z[zOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 1] + y[yOff + 1] + z[zOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 2] + y[yOff + 2] + z[zOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 3] + y[yOff + 3] + z[zOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 4] + y[yOff + 4] + z[zOff + 4]; + z[zOff + 4] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 5] + y[yOff + 5] + z[zOff + 5]; + z[zOff + 5] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 6] + y[yOff + 6] + z[zOff + 6]; + z[zOff + 6] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 7] + y[yOff + 7] + z[zOff + 7]; + z[zOff + 7] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint AddTo(uint[] x, uint[] z) + { + ulong c = 0; + c += (ulong)x[0] + z[0]; + z[0] = (uint)c; + c >>= 32; + c += (ulong)x[1] + z[1]; + z[1] = (uint)c; + c >>= 32; + c += (ulong)x[2] + z[2]; + z[2] = (uint)c; + c >>= 32; + c += (ulong)x[3] + z[3]; + z[3] = (uint)c; + c >>= 32; + c += (ulong)x[4] + z[4]; + z[4] = (uint)c; + c >>= 32; + c += (ulong)x[5] + z[5]; + z[5] = (uint)c; + c >>= 32; + c += (ulong)x[6] + z[6]; + z[6] = (uint)c; + c >>= 32; + c += (ulong)x[7] + z[7]; + z[7] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint AddTo(uint[] x, int xOff, uint[] z, int zOff, uint cIn) + { + ulong c = cIn; + c += (ulong)x[xOff + 0] + z[zOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 1] + z[zOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 2] + z[zOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 3] + z[zOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 4] + z[zOff + 4]; + z[zOff + 4] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 5] + z[zOff + 5]; + z[zOff + 5] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 6] + z[zOff + 6]; + z[zOff + 6] = (uint)c; + c >>= 32; + c += (ulong)x[xOff + 7] + z[zOff + 7]; + z[zOff + 7] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint AddToEachOther(uint[] u, int uOff, uint[] v, int vOff) + { + ulong c = 0; + c += (ulong)u[uOff + 0] + v[vOff + 0]; + u[uOff + 0] = (uint)c; + v[vOff + 0] = (uint)c; + c >>= 32; + c += (ulong)u[uOff + 1] + v[vOff + 1]; + u[uOff + 1] = (uint)c; + v[vOff + 1] = (uint)c; + c >>= 32; + c += (ulong)u[uOff + 2] + v[vOff + 2]; + u[uOff + 2] = (uint)c; + v[vOff + 2] = (uint)c; + c >>= 32; + c += (ulong)u[uOff + 3] + v[vOff + 3]; + u[uOff + 3] = (uint)c; + v[vOff + 3] = (uint)c; + c >>= 32; + c += (ulong)u[uOff + 4] + v[vOff + 4]; + u[uOff + 4] = (uint)c; + v[vOff + 4] = (uint)c; + c >>= 32; + c += (ulong)u[uOff + 5] + v[vOff + 5]; + u[uOff + 5] = (uint)c; + v[vOff + 5] = (uint)c; + c >>= 32; + c += (ulong)u[uOff + 6] + v[vOff + 6]; + u[uOff + 6] = (uint)c; + v[vOff + 6] = (uint)c; + c >>= 32; + c += (ulong)u[uOff + 7] + v[vOff + 7]; + u[uOff + 7] = (uint)c; + v[vOff + 7] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static void Copy(uint[] x, uint[] z) + { + z[0] = x[0]; + z[1] = x[1]; + z[2] = x[2]; + z[3] = x[3]; + z[4] = x[4]; + z[5] = x[5]; + z[6] = x[6]; + z[7] = x[7]; + } + + public static uint[] Create() + { + return new uint[8]; + } + + public static uint[] CreateExt() + { + return new uint[16]; + } + + public static bool Diff(uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff) + { + bool pos = Gte(x, xOff, y, yOff); + if (pos) + { + Sub(x, xOff, y, yOff, z, zOff); + } + else + { + Sub(y, yOff, x, xOff, z, zOff); + } + return pos; + } + + public static bool Eq(uint[] x, uint[] y) + { + for (int i = 7; i >= 0; --i) + { + if (x[i] != y[i]) + return false; + } + return true; + } + + public static uint[] FromBigInteger(BigInteger x) + { + if (x.SignValue < 0 || x.BitLength > 256) + throw new ArgumentException(); + + uint[] z = Create(); + int i = 0; + while (x.SignValue != 0) + { + z[i++] = (uint)x.IntValue; + x = x.ShiftRight(32); + } + return z; + } + + public static uint GetBit(uint[] x, int bit) + { + if (bit == 0) + { + return x[0] & 1; + } + if ((bit & 255) != bit) + { + return 0; + } + int w = bit >> 5; + int b = bit & 31; + return (x[w] >> b) & 1; + } + + public static bool Gte(uint[] x, uint[] y) + { + for (int i = 7; i >= 0; --i) + { + uint x_i = x[i], y_i = y[i]; + if (x_i < y_i) + return false; + if (x_i > y_i) + return true; + } + return true; + } + + public static bool Gte(uint[] x, int xOff, uint[] y, int yOff) + { + for (int i = 7; i >= 0; --i) + { + uint x_i = x[xOff + i], y_i = y[yOff + i]; + if (x_i < y_i) + return false; + if (x_i > y_i) + return true; + } + return true; + } + + public static bool IsOne(uint[] x) + { + if (x[0] != 1) + { + return false; + } + for (int i = 1; i < 8; ++i) + { + if (x[i] != 0) + { + return false; + } + } + return true; + } + + public static bool IsZero(uint[] x) + { + for (int i = 0; i < 8; ++i) + { + if (x[i] != 0) + { + return false; + } + } + return true; + } + + public static void Mul(uint[] x, uint[] y, uint[] zz) + { + ulong y_0 = y[0]; + ulong y_1 = y[1]; + ulong y_2 = y[2]; + ulong y_3 = y[3]; + ulong y_4 = y[4]; + ulong y_5 = y[5]; + ulong y_6 = y[6]; + ulong y_7 = y[7]; + + { + ulong c = 0, x_0 = x[0]; + c += x_0 * y_0; + zz[0] = (uint)c; + c >>= 32; + c += x_0 * y_1; + zz[1] = (uint)c; + c >>= 32; + c += x_0 * y_2; + zz[2] = (uint)c; + c >>= 32; + c += x_0 * y_3; + zz[3] = (uint)c; + c >>= 32; + c += x_0 * y_4; + zz[4] = (uint)c; + c >>= 32; + c += x_0 * y_5; + zz[5] = (uint)c; + c >>= 32; + c += x_0 * y_6; + zz[6] = (uint)c; + c >>= 32; + c += x_0 * y_7; + zz[7] = (uint)c; + c >>= 32; + zz[8] = (uint)c; + } + + for (int i = 1; i < 8; ++i) + { + ulong c = 0, x_i = x[i]; + c += x_i * y_0 + zz[i + 0]; + zz[i + 0] = (uint)c; + c >>= 32; + c += x_i * y_1 + zz[i + 1]; + zz[i + 1] = (uint)c; + c >>= 32; + c += x_i * y_2 + zz[i + 2]; + zz[i + 2] = (uint)c; + c >>= 32; + c += x_i * y_3 + zz[i + 3]; + zz[i + 3] = (uint)c; + c >>= 32; + c += x_i * y_4 + zz[i + 4]; + zz[i + 4] = (uint)c; + c >>= 32; + c += x_i * y_5 + zz[i + 5]; + zz[i + 5] = (uint)c; + c >>= 32; + c += x_i * y_6 + zz[i + 6]; + zz[i + 6] = (uint)c; + c >>= 32; + c += x_i * y_7 + zz[i + 7]; + zz[i + 7] = (uint)c; + c >>= 32; + zz[i + 8] = (uint)c; + } + } + + public static void Mul(uint[] x, int xOff, uint[] y, int yOff, uint[] zz, int zzOff) + { + ulong y_0 = y[yOff + 0]; + ulong y_1 = y[yOff + 1]; + ulong y_2 = y[yOff + 2]; + ulong y_3 = y[yOff + 3]; + ulong y_4 = y[yOff + 4]; + ulong y_5 = y[yOff + 5]; + ulong y_6 = y[yOff + 6]; + ulong y_7 = y[yOff + 7]; + + { + ulong c = 0, x_0 = x[xOff + 0]; + c += x_0 * y_0; + zz[zzOff + 0] = (uint)c; + c >>= 32; + c += x_0 * y_1; + zz[zzOff + 1] = (uint)c; + c >>= 32; + c += x_0 * y_2; + zz[zzOff + 2] = (uint)c; + c >>= 32; + c += x_0 * y_3; + zz[zzOff + 3] = (uint)c; + c >>= 32; + c += x_0 * y_4; + zz[zzOff + 4] = (uint)c; + c >>= 32; + c += x_0 * y_5; + zz[zzOff + 5] = (uint)c; + c >>= 32; + c += x_0 * y_6; + zz[zzOff + 6] = (uint)c; + c >>= 32; + c += x_0 * y_7; + zz[zzOff + 7] = (uint)c; + c >>= 32; + zz[zzOff + 8] = (uint)c; + } + + for (int i = 1; i < 8; ++i) + { + ++zzOff; + ulong c = 0, x_i = x[xOff + i]; + c += x_i * y_0 + zz[zzOff + 0]; + zz[zzOff + 0] = (uint)c; + c >>= 32; + c += x_i * y_1 + zz[zzOff + 1]; + zz[zzOff + 1] = (uint)c; + c >>= 32; + c += x_i * y_2 + zz[zzOff + 2]; + zz[zzOff + 2] = (uint)c; + c >>= 32; + c += x_i * y_3 + zz[zzOff + 3]; + zz[zzOff + 3] = (uint)c; + c >>= 32; + c += x_i * y_4 + zz[zzOff + 4]; + zz[zzOff + 4] = (uint)c; + c >>= 32; + c += x_i * y_5 + zz[zzOff + 5]; + zz[zzOff + 5] = (uint)c; + c >>= 32; + c += x_i * y_6 + zz[zzOff + 6]; + zz[zzOff + 6] = (uint)c; + c >>= 32; + c += x_i * y_7 + zz[zzOff + 7]; + zz[zzOff + 7] = (uint)c; + c >>= 32; + zz[zzOff + 8] = (uint)c; + } + } + + public static uint MulAddTo(uint[] x, uint[] y, uint[] zz) + { + ulong y_0 = y[0]; + ulong y_1 = y[1]; + ulong y_2 = y[2]; + ulong y_3 = y[3]; + ulong y_4 = y[4]; + ulong y_5 = y[5]; + ulong y_6 = y[6]; + ulong y_7 = y[7]; + + ulong zc = 0; + for (int i = 0; i < 8; ++i) + { + ulong c = 0, x_i = x[i]; + c += x_i * y_0 + zz[i + 0]; + zz[i + 0] = (uint)c; + c >>= 32; + c += x_i * y_1 + zz[i + 1]; + zz[i + 1] = (uint)c; + c >>= 32; + c += x_i * y_2 + zz[i + 2]; + zz[i + 2] = (uint)c; + c >>= 32; + c += x_i * y_3 + zz[i + 3]; + zz[i + 3] = (uint)c; + c >>= 32; + c += x_i * y_4 + zz[i + 4]; + zz[i + 4] = (uint)c; + c >>= 32; + c += x_i * y_5 + zz[i + 5]; + zz[i + 5] = (uint)c; + c >>= 32; + c += x_i * y_6 + zz[i + 6]; + zz[i + 6] = (uint)c; + c >>= 32; + c += x_i * y_7 + zz[i + 7]; + zz[i + 7] = (uint)c; + c >>= 32; + c += zc + zz[i + 8]; + zz[i + 8] = (uint)c; + zc = c >> 32; + } + return (uint)zc; + } + + public static uint MulAddTo(uint[] x, int xOff, uint[] y, int yOff, uint[] zz, int zzOff) + { + ulong y_0 = y[yOff + 0]; + ulong y_1 = y[yOff + 1]; + ulong y_2 = y[yOff + 2]; + ulong y_3 = y[yOff + 3]; + ulong y_4 = y[yOff + 4]; + ulong y_5 = y[yOff + 5]; + ulong y_6 = y[yOff + 6]; + ulong y_7 = y[yOff + 7]; + + ulong zc = 0; + for (int i = 0; i < 8; ++i) + { + ulong c = 0, x_i = x[xOff + i]; + c += x_i * y_0 + zz[zzOff + 0]; + zz[zzOff + 0] = (uint)c; + c >>= 32; + c += x_i * y_1 + zz[zzOff + 1]; + zz[zzOff + 1] = (uint)c; + c >>= 32; + c += x_i * y_2 + zz[zzOff + 2]; + zz[zzOff + 2] = (uint)c; + c >>= 32; + c += x_i * y_3 + zz[zzOff + 3]; + zz[zzOff + 3] = (uint)c; + c >>= 32; + c += x_i * y_4 + zz[zzOff + 4]; + zz[zzOff + 4] = (uint)c; + c >>= 32; + c += x_i * y_5 + zz[zzOff + 5]; + zz[zzOff + 5] = (uint)c; + c >>= 32; + c += x_i * y_6 + zz[zzOff + 6]; + zz[zzOff + 6] = (uint)c; + c >>= 32; + c += x_i * y_7 + zz[zzOff + 7]; + zz[zzOff + 7] = (uint)c; + c >>= 32; + c += zc + zz[zzOff + 8]; + zz[zzOff + 8] = (uint)c; + zc = c >> 32; + ++zzOff; + } + return (uint)zc; + } + + public static ulong Mul33Add(uint w, uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff) + { + Debug.Assert(w >> 31 == 0); + + ulong c = 0, wVal = w; + ulong x0 = x[xOff + 0]; + c += wVal * x0 + y[yOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + ulong x1 = x[xOff + 1]; + c += wVal * x1 + x0 + y[yOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + ulong x2 = x[xOff + 2]; + c += wVal * x2 + x1 + y[yOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + ulong x3 = x[xOff + 3]; + c += wVal * x3 + x2 + y[yOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + ulong x4 = x[xOff + 4]; + c += wVal * x4 + x3 + y[yOff + 4]; + z[zOff + 4] = (uint)c; + c >>= 32; + ulong x5 = x[xOff + 5]; + c += wVal * x5 + x4 + y[yOff + 5]; + z[zOff + 5] = (uint)c; + c >>= 32; + ulong x6 = x[xOff + 6]; + c += wVal * x6 + x5 + y[yOff + 6]; + z[zOff + 6] = (uint)c; + c >>= 32; + ulong x7 = x[xOff + 7]; + c += wVal * x7 + x6 + y[yOff + 7]; + z[zOff + 7] = (uint)c; + c >>= 32; + c += x7; + return c; + } + + public static uint MulByWord(uint x, uint[] z) + { + ulong c = 0, xVal = x; + c += xVal * (ulong)z[0]; + z[0] = (uint)c; + c >>= 32; + c += xVal * (ulong)z[1]; + z[1] = (uint)c; + c >>= 32; + c += xVal * (ulong)z[2]; + z[2] = (uint)c; + c >>= 32; + c += xVal * (ulong)z[3]; + z[3] = (uint)c; + c >>= 32; + c += xVal * (ulong)z[4]; + z[4] = (uint)c; + c >>= 32; + c += xVal * (ulong)z[5]; + z[5] = (uint)c; + c >>= 32; + c += xVal * (ulong)z[6]; + z[6] = (uint)c; + c >>= 32; + c += xVal * (ulong)z[7]; + z[7] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint MulByWordAddTo(uint x, uint[] y, uint[] z) + { + ulong c = 0, xVal = x; + c += xVal * (ulong)z[0] + y[0]; + z[0] = (uint)c; + c >>= 32; + c += xVal * (ulong)z[1] + y[1]; + z[1] = (uint)c; + c >>= 32; + c += xVal * (ulong)z[2] + y[2]; + z[2] = (uint)c; + c >>= 32; + c += xVal * (ulong)z[3] + y[3]; + z[3] = (uint)c; + c >>= 32; + c += xVal * (ulong)z[4] + y[4]; + z[4] = (uint)c; + c >>= 32; + c += xVal * (ulong)z[5] + y[5]; + z[5] = (uint)c; + c >>= 32; + c += xVal * (ulong)z[6] + y[6]; + z[6] = (uint)c; + c >>= 32; + c += xVal * (ulong)z[7] + y[7]; + z[7] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint MulWordAddTo(uint x, uint[] y, int yOff, uint[] z, int zOff) + { + ulong c = 0, xVal = x; + c += xVal * y[yOff + 0] + z[zOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += xVal * y[yOff + 1] + z[zOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += xVal * y[yOff + 2] + z[zOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + c += xVal * y[yOff + 3] + z[zOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + c += xVal * y[yOff + 4] + z[zOff + 4]; + z[zOff + 4] = (uint)c; + c >>= 32; + c += xVal * y[yOff + 5] + z[zOff + 5]; + z[zOff + 5] = (uint)c; + c >>= 32; + c += xVal * y[yOff + 6] + z[zOff + 6]; + z[zOff + 6] = (uint)c; + c >>= 32; + c += xVal * y[yOff + 7] + z[zOff + 7]; + z[zOff + 7] = (uint)c; + c >>= 32; + return (uint)c; + } + + public static uint Mul33DWordAdd(uint x, ulong y, uint[] z, int zOff) + { + Debug.Assert(x >> 31 == 0); + Debug.Assert(zOff <= 4); + ulong c = 0, xVal = x; + ulong y00 = y & M; + c += xVal * y00 + z[zOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + ulong y01 = y >> 32; + c += xVal * y01 + y00 + z[zOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += y01 + z[zOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + c += z[zOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + return c == 0 ? 0 : Nat.IncAt(8, z, zOff, 4); + } + + public static uint Mul33WordAdd(uint x, uint y, uint[] z, int zOff) + { + Debug.Assert(x >> 31 == 0); + Debug.Assert(zOff <= 5); + ulong c = 0, yVal = y; + c += yVal * x + z[zOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += yVal + z[zOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += z[zOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + return c == 0 ? 0 : Nat.IncAt(8, z, zOff, 3); + } + + public static uint MulWordDwordAdd(uint x, ulong y, uint[] z, int zOff) + { + Debug.Assert(zOff <= 5); + ulong c = 0, xVal = x; + c += xVal * y + z[zOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += xVal * (y >> 32) + z[zOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += z[zOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + return c == 0 ? 0 : Nat.IncAt(8, z, zOff, 3); + } + + public static uint MulWord(uint x, uint[] y, uint[] z, int zOff) + { + ulong c = 0, xVal = x; + int i = 0; + do + { + c += xVal * y[i]; + z[zOff + i] = (uint)c; + c >>= 32; + } + while (++i < 8); + return (uint)c; + } + + public static void Square(uint[] x, uint[] zz) + { + ulong x_0 = x[0]; + ulong zz_1; + + uint c = 0, w; + { + int i = 7, j = 16; + do + { + ulong xVal = x[i--]; + ulong p = xVal * xVal; + zz[--j] = (c << 31) | (uint)(p >> 33); + zz[--j] = (uint)(p >> 1); + c = (uint)p; + } + while (i > 0); + + { + ulong p = x_0 * x_0; + zz_1 = (ulong)(c << 31) | (p >> 33); + zz[0] = (uint)p; + c = (uint)(p >> 32) & 1; + } + } + + ulong x_1 = x[1]; + ulong zz_2 = zz[2]; + + { + zz_1 += x_1 * x_0; + w = (uint)zz_1; + zz[1] = (w << 1) | c; + c = w >> 31; + zz_2 += zz_1 >> 32; + } + + ulong x_2 = x[2]; + ulong zz_3 = zz[3]; + ulong zz_4 = zz[4]; + { + zz_2 += x_2 * x_0; + w = (uint)zz_2; + zz[2] = (w << 1) | c; + c = w >> 31; + zz_3 += (zz_2 >> 32) + x_2 * x_1; + zz_4 += zz_3 >> 32; + zz_3 &= M; + } + + ulong x_3 = x[3]; + ulong zz_5 = zz[5]; + ulong zz_6 = zz[6]; + { + zz_3 += x_3 * x_0; + w = (uint)zz_3; + zz[3] = (w << 1) | c; + c = w >> 31; + zz_4 += (zz_3 >> 32) + x_3 * x_1; + zz_5 += (zz_4 >> 32) + x_3 * x_2; + zz_4 &= M; + zz_6 += zz_5 >> 32; + zz_5 &= M; + } + + ulong x_4 = x[4]; + ulong zz_7 = zz[7]; + ulong zz_8 = zz[8]; + { + zz_4 += x_4 * x_0; + w = (uint)zz_4; + zz[4] = (w << 1) | c; + c = w >> 31; + zz_5 += (zz_4 >> 32) + x_4 * x_1; + zz_6 += (zz_5 >> 32) + x_4 * x_2; + zz_5 &= M; + zz_7 += (zz_6 >> 32) + x_4 * x_3; + zz_6 &= M; + zz_8 += zz_7 >> 32; + zz_7 &= M; + } + + ulong x_5 = x[5]; + ulong zz_9 = zz[9]; + ulong zz_10 = zz[10]; + { + zz_5 += x_5 * x_0; + w = (uint)zz_5; + zz[5] = (w << 1) | c; + c = w >> 31; + zz_6 += (zz_5 >> 32) + x_5 * x_1; + zz_7 += (zz_6 >> 32) + x_5 * x_2; + zz_6 &= M; + zz_8 += (zz_7 >> 32) + x_5 * x_3; + zz_7 &= M; + zz_9 += (zz_8 >> 32) + x_5 * x_4; + zz_8 &= M; + zz_10 += zz_9 >> 32; + zz_9 &= M; + } + + ulong x_6 = x[6]; + ulong zz_11 = zz[11]; + ulong zz_12 = zz[12]; + { + zz_6 += x_6 * x_0; + w = (uint)zz_6; + zz[6] = (w << 1) | c; + c = w >> 31; + zz_7 += (zz_6 >> 32) + x_6 * x_1; + zz_8 += (zz_7 >> 32) + x_6 * x_2; + zz_7 &= M; + zz_9 += (zz_8 >> 32) + x_6 * x_3; + zz_8 &= M; + zz_10 += (zz_9 >> 32) + x_6 * x_4; + zz_9 &= M; + zz_11 += (zz_10 >> 32) + x_6 * x_5; + zz_10 &= M; + zz_12 += zz_11 >> 32; + zz_11 &= M; + } + + ulong x_7 = x[7]; + ulong zz_13 = zz[13]; + ulong zz_14 = zz[14]; + { + zz_7 += x_7 * x_0; + w = (uint)zz_7; + zz[7] = (w << 1) | c; + c = w >> 31; + zz_8 += (zz_7 >> 32) + x_7 * x_1; + zz_9 += (zz_8 >> 32) + x_7 * x_2; + zz_10 += (zz_9 >> 32) + x_7 * x_3; + zz_11 += (zz_10 >> 32) + x_7 * x_4; + zz_12 += (zz_11 >> 32) + x_7 * x_5; + zz_13 += (zz_12 >> 32) + x_7 * x_6; + zz_14 += zz_13 >> 32; + } + + w = (uint)zz_8; + zz[8] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_9; + zz[9] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_10; + zz[10] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_11; + zz[11] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_12; + zz[12] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_13; + zz[13] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_14; + zz[14] = (w << 1) | c; + c = w >> 31; + w = zz[15] + (uint)(zz_14 >> 32); + zz[15] = (w << 1) | c; + } + + public static void Square(uint[] x, int xOff, uint[] zz, int zzOff) + { + ulong x_0 = x[xOff + 0]; + ulong zz_1; + + uint c = 0, w; + { + int i = 7, j = 16; + do + { + ulong xVal = x[xOff + i--]; + ulong p = xVal * xVal; + zz[zzOff + --j] = (c << 31) | (uint)(p >> 33); + zz[zzOff + --j] = (uint)(p >> 1); + c = (uint)p; + } + while (i > 0); + + { + ulong p = x_0 * x_0; + zz_1 = (ulong)(c << 31) | (p >> 33); + zz[zzOff + 0] = (uint)p; + c = (uint)(p >> 32) & 1; + } + } + + ulong x_1 = x[xOff + 1]; + ulong zz_2 = zz[zzOff + 2]; + + { + zz_1 += x_1 * x_0; + w = (uint)zz_1; + zz[zzOff + 1] = (w << 1) | c; + c = w >> 31; + zz_2 += zz_1 >> 32; + } + + ulong x_2 = x[xOff + 2]; + ulong zz_3 = zz[zzOff + 3]; + ulong zz_4 = zz[zzOff + 4]; + { + zz_2 += x_2 * x_0; + w = (uint)zz_2; + zz[zzOff + 2] = (w << 1) | c; + c = w >> 31; + zz_3 += (zz_2 >> 32) + x_2 * x_1; + zz_4 += zz_3 >> 32; + zz_3 &= M; + } + + ulong x_3 = x[xOff + 3]; + ulong zz_5 = zz[zzOff + 5]; + ulong zz_6 = zz[zzOff + 6]; + { + zz_3 += x_3 * x_0; + w = (uint)zz_3; + zz[zzOff + 3] = (w << 1) | c; + c = w >> 31; + zz_4 += (zz_3 >> 32) + x_3 * x_1; + zz_5 += (zz_4 >> 32) + x_3 * x_2; + zz_4 &= M; + zz_6 += zz_5 >> 32; + zz_5 &= M; + } + + ulong x_4 = x[xOff + 4]; + ulong zz_7 = zz[zzOff + 7]; + ulong zz_8 = zz[zzOff + 8]; + { + zz_4 += x_4 * x_0; + w = (uint)zz_4; + zz[zzOff + 4] = (w << 1) | c; + c = w >> 31; + zz_5 += (zz_4 >> 32) + x_4 * x_1; + zz_6 += (zz_5 >> 32) + x_4 * x_2; + zz_5 &= M; + zz_7 += (zz_6 >> 32) + x_4 * x_3; + zz_6 &= M; + zz_8 += zz_7 >> 32; + zz_7 &= M; + } + + ulong x_5 = x[xOff + 5]; + ulong zz_9 = zz[zzOff + 9]; + ulong zz_10 = zz[zzOff + 10]; + { + zz_5 += x_5 * x_0; + w = (uint)zz_5; + zz[zzOff + 5] = (w << 1) | c; + c = w >> 31; + zz_6 += (zz_5 >> 32) + x_5 * x_1; + zz_7 += (zz_6 >> 32) + x_5 * x_2; + zz_6 &= M; + zz_8 += (zz_7 >> 32) + x_5 * x_3; + zz_7 &= M; + zz_9 += (zz_8 >> 32) + x_5 * x_4; + zz_8 &= M; + zz_10 += zz_9 >> 32; + zz_9 &= M; + } + + ulong x_6 = x[xOff + 6]; + ulong zz_11 = zz[zzOff + 11]; + ulong zz_12 = zz[zzOff + 12]; + { + zz_6 += x_6 * x_0; + w = (uint)zz_6; + zz[zzOff + 6] = (w << 1) | c; + c = w >> 31; + zz_7 += (zz_6 >> 32) + x_6 * x_1; + zz_8 += (zz_7 >> 32) + x_6 * x_2; + zz_7 &= M; + zz_9 += (zz_8 >> 32) + x_6 * x_3; + zz_8 &= M; + zz_10 += (zz_9 >> 32) + x_6 * x_4; + zz_9 &= M; + zz_11 += (zz_10 >> 32) + x_6 * x_5; + zz_10 &= M; + zz_12 += zz_11 >> 32; + zz_11 &= M; + } + + ulong x_7 = x[xOff + 7]; + ulong zz_13 = zz[zzOff + 13]; + ulong zz_14 = zz[zzOff + 14]; + { + zz_7 += x_7 * x_0; + w = (uint)zz_7; + zz[zzOff + 7] = (w << 1) | c; + c = w >> 31; + zz_8 += (zz_7 >> 32) + x_7 * x_1; + zz_9 += (zz_8 >> 32) + x_7 * x_2; + zz_10 += (zz_9 >> 32) + x_7 * x_3; + zz_11 += (zz_10 >> 32) + x_7 * x_4; + zz_12 += (zz_11 >> 32) + x_7 * x_5; + zz_13 += (zz_12 >> 32) + x_7 * x_6; + zz_14 += zz_13 >> 32; + } + + w = (uint)zz_8; + zz[zzOff + 8] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_9; + zz[zzOff + 9] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_10; + zz[zzOff + 10] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_11; + zz[zzOff + 11] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_12; + zz[zzOff + 12] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_13; + zz[zzOff + 13] = (w << 1) | c; + c = w >> 31; + w = (uint)zz_14; + zz[zzOff + 14] = (w << 1) | c; + c = w >> 31; + w = zz[zzOff + 15] + (uint)(zz_14 >> 32); + zz[zzOff + 15] = (w << 1) | c; + } + + public static int Sub(uint[] x, uint[] y, uint[] z) + { + long c = 0; + c += (long)x[0] - y[0]; + z[0] = (uint)c; + c >>= 32; + c += (long)x[1] - y[1]; + z[1] = (uint)c; + c >>= 32; + c += (long)x[2] - y[2]; + z[2] = (uint)c; + c >>= 32; + c += (long)x[3] - y[3]; + z[3] = (uint)c; + c >>= 32; + c += (long)x[4] - y[4]; + z[4] = (uint)c; + c >>= 32; + c += (long)x[5] - y[5]; + z[5] = (uint)c; + c >>= 32; + c += (long)x[6] - y[6]; + z[6] = (uint)c; + c >>= 32; + c += (long)x[7] - y[7]; + z[7] = (uint)c; + c >>= 32; + return (int)c; + } + + public static int Sub(uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff) + { + long c = 0; + c += (long)x[xOff + 0] - y[yOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += (long)x[xOff + 1] - y[yOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += (long)x[xOff + 2] - y[yOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + c += (long)x[xOff + 3] - y[yOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + c += (long)x[xOff + 4] - y[yOff + 4]; + z[zOff + 4] = (uint)c; + c >>= 32; + c += (long)x[xOff + 5] - y[yOff + 5]; + z[zOff + 5] = (uint)c; + c >>= 32; + c += (long)x[xOff + 6] - y[yOff + 6]; + z[zOff + 6] = (uint)c; + c >>= 32; + c += (long)x[xOff + 7] - y[yOff + 7]; + z[zOff + 7] = (uint)c; + c >>= 32; + return (int)c; + } + + public static int SubBothFrom(uint[] x, uint[] y, uint[] z) + { + long c = 0; + c += (long)z[0] - x[0] - y[0]; + z[0] = (uint)c; + c >>= 32; + c += (long)z[1] - x[1] - y[1]; + z[1] = (uint)c; + c >>= 32; + c += (long)z[2] - x[2] - y[2]; + z[2] = (uint)c; + c >>= 32; + c += (long)z[3] - x[3] - y[3]; + z[3] = (uint)c; + c >>= 32; + c += (long)z[4] - x[4] - y[4]; + z[4] = (uint)c; + c >>= 32; + c += (long)z[5] - x[5] - y[5]; + z[5] = (uint)c; + c >>= 32; + c += (long)z[6] - x[6] - y[6]; + z[6] = (uint)c; + c >>= 32; + c += (long)z[7] - x[7] - y[7]; + z[7] = (uint)c; + c >>= 32; + return (int)c; + } + + public static int SubFrom(uint[] x, uint[] z) + { + long c = 0; + c += (long)z[0] - x[0]; + z[0] = (uint)c; + c >>= 32; + c += (long)z[1] - x[1]; + z[1] = (uint)c; + c >>= 32; + c += (long)z[2] - x[2]; + z[2] = (uint)c; + c >>= 32; + c += (long)z[3] - x[3]; + z[3] = (uint)c; + c >>= 32; + c += (long)z[4] - x[4]; + z[4] = (uint)c; + c >>= 32; + c += (long)z[5] - x[5]; + z[5] = (uint)c; + c >>= 32; + c += (long)z[6] - x[6]; + z[6] = (uint)c; + c >>= 32; + c += (long)z[7] - x[7]; + z[7] = (uint)c; + c >>= 32; + return (int)c; + } + + public static int SubFrom(uint[] x, int xOff, uint[] z, int zOff) + { + long c = 0; + c += (long)z[zOff + 0] - x[xOff + 0]; + z[zOff + 0] = (uint)c; + c >>= 32; + c += (long)z[zOff + 1] - x[xOff + 1]; + z[zOff + 1] = (uint)c; + c >>= 32; + c += (long)z[zOff + 2] - x[xOff + 2]; + z[zOff + 2] = (uint)c; + c >>= 32; + c += (long)z[zOff + 3] - x[xOff + 3]; + z[zOff + 3] = (uint)c; + c >>= 32; + c += (long)z[zOff + 4] - x[xOff + 4]; + z[zOff + 4] = (uint)c; + c >>= 32; + c += (long)z[zOff + 5] - x[xOff + 5]; + z[zOff + 5] = (uint)c; + c >>= 32; + c += (long)z[zOff + 6] - x[xOff + 6]; + z[zOff + 6] = (uint)c; + c >>= 32; + c += (long)z[zOff + 7] - x[xOff + 7]; + z[zOff + 7] = (uint)c; + c >>= 32; + return (int)c; + } + + public static BigInteger ToBigInteger(uint[] x) + { + byte[] bs = new byte[32]; + for (int i = 0; i < 8; ++i) + { + uint x_i = x[i]; + if (x_i != 0) + { + Pack.UInt32_To_BE(x_i, bs, (7 - i) << 2); + } + } + return new BigInteger(1, bs); + } + + public static void Zero(uint[] z) + { + z[0] = 0; + z[1] = 0; + z[2] = 0; + z[3] = 0; + z[4] = 0; + z[5] = 0; + z[6] = 0; + z[7] = 0; + } + } +} diff --git a/crypto/src/math/ec/custom/sec/Nat384.cs b/crypto/src/math/ec/custom/sec/Nat384.cs new file mode 100644
index 000000000..dd93e68b6 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/Nat384.cs
@@ -0,0 +1,46 @@ +using System; +using System.Diagnostics; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal abstract class Nat384 + { + public static void Mul(uint[] x, uint[] y, uint[] zz) + { + Nat192.Mul(x, y, zz); + Nat192.Mul(x, 6, y, 6, zz, 12); + + uint c18 = Nat192.AddToEachOther(zz, 6, zz, 12); + uint c12 = c18 + Nat192.AddTo(zz, 0, zz, 6, 0); + c18 += Nat192.AddTo(zz, 18, zz, 12, c12); + + uint[] dx = Nat192.Create(), dy = Nat192.Create(); + bool neg = Nat192.Diff(x, 6, x, 0, dx, 0) != Nat192.Diff(y, 6, y, 0, dy, 0); + + uint[] tt = Nat192.CreateExt(); + Nat192.Mul(dx, dy, tt); + + c18 += neg ? Nat.AddTo(12, tt, 0, zz, 6) : (uint)Nat.SubFrom(12, tt, 0, zz, 6); + Nat.AddWordAt(24, c18, zz, 18); + } + + public static void Square(uint[] x, uint[] zz) + { + Nat192.Square(x, zz); + Nat192.Square(x, 6, zz, 12); + + uint c18 = Nat192.AddToEachOther(zz, 6, zz, 12); + uint c12 = c18 + Nat192.AddTo(zz, 0, zz, 6, 0); + c18 += Nat192.AddTo(zz, 18, zz, 12, c12); + + uint[] dx = Nat192.Create(); + Nat192.Diff(x, 6, x, 0, dx, 0); + + uint[] m = Nat192.CreateExt(); + Nat192.Square(dx, m); + + c18 += (uint)Nat.SubFrom(12, m, 0, zz, 6); + Nat.AddWordAt(24, c18, zz, 18); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/Nat512.cs b/crypto/src/math/ec/custom/sec/Nat512.cs new file mode 100644
index 000000000..46e10f995 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/Nat512.cs
@@ -0,0 +1,46 @@ +using System; +using System.Diagnostics; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal abstract class Nat512 + { + public static void Mul(uint[] x, uint[] y, uint[] zz) + { + Nat256.Mul(x, y, zz); + Nat256.Mul(x, 8, y, 8, zz, 16); + + uint c24 = Nat256.AddToEachOther(zz, 8, zz, 16); + uint c16 = c24 + Nat256.AddTo(zz, 0, zz, 8, 0); + c24 += Nat256.AddTo(zz, 24, zz, 16, c16); + + uint[] dx = Nat256.Create(), dy = Nat256.Create(); + bool neg = Nat256.Diff(x, 8, x, 0, dx, 0) != Nat256.Diff(y, 8, y, 0, dy, 0); + + uint[] tt = Nat256.CreateExt(); + Nat256.Mul(dx, dy, tt); + + c24 += neg ? Nat.AddTo(16, tt, 0, zz, 8) : (uint)Nat.SubFrom(16, tt, 0, zz, 8); + Nat.AddWordAt(32, c24, zz, 24); + } + + public static void Square(uint[] x, uint[] zz) + { + Nat256.Square(x, zz); + Nat256.Square(x, 8, zz, 16); + + uint c24 = Nat256.AddToEachOther(zz, 8, zz, 16); + uint c16 = c24 + Nat256.AddTo(zz, 0, zz, 8, 0); + c24 += Nat256.AddTo(zz, 24, zz, 16, c16); + + uint[] dx = Nat256.Create(); + Nat256.Diff(x, 8, x, 0, dx, 0); + + uint[] m = Nat256.CreateExt(); + Nat256.Square(dx, m); + + c24 += (uint)Nat.SubFrom(16, m, 0, zz, 8); + Nat.AddWordAt(32, c24, zz, 24); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP192K1Curve.cs b/crypto/src/math/ec/custom/sec/SecP192K1Curve.cs new file mode 100644
index 000000000..81f77197e --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP192K1Curve.cs
@@ -0,0 +1,75 @@ +using System; + +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP192K1Curve + : AbstractFpCurve + { + public static readonly BigInteger q = new BigInteger(1, + Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37")); + + private const int SECP192K1_DEFAULT_COORDS = COORD_JACOBIAN; + + protected readonly SecP192K1Point m_infinity; + + public SecP192K1Curve() + : base(q) + { + this.m_infinity = new SecP192K1Point(this, null, null); + + this.m_a = FromBigInteger(BigInteger.Zero); + this.m_b = FromBigInteger(BigInteger.ValueOf(3)); + this.m_order = new BigInteger(1, Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D")); + this.m_cofactor = BigInteger.One; + this.m_coord = SECP192K1_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecP192K1Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_JACOBIAN: + return true; + default: + return false; + } + } + + public virtual BigInteger Q + { + get { return q; } + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return q.BitLength; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecP192K1FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecP192K1Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecP192K1Point(this, x, y, zs, withCompression); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP192K1Field.cs b/crypto/src/math/ec/custom/sec/SecP192K1Field.cs new file mode 100644
index 000000000..d5ca903d1 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP192K1Field.cs
@@ -0,0 +1,176 @@ +using System; +using System.Diagnostics; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP192K1Field + { + // 2^192 - 2^32 - 2^12 - 2^8 - 2^7 - 2^6 - 2^3 - 1 + internal static readonly uint[] P = new uint[]{ 0xFFFFEE37, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + internal static readonly uint[] PExt = new uint[]{ 0x013C4FD1, 0x00002392, 0x00000001, 0x00000000, 0x00000000, + 0x00000000, 0xFFFFDC6E, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + private static readonly uint[] PExtInv = new uint[]{ 0xFEC3B02F, 0xFFFFDC6D, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0x00002391, 0x00000002 }; + private const uint P5 = 0xFFFFFFFF; + private const uint PExt11 = 0xFFFFFFFF; + private const uint PInv33 = 0x11C9; + + public static void Add(uint[] x, uint[] y, uint[] z) + { + uint c = Nat192.Add(x, y, z); + if (c != 0 || (z[5] == P5 && Nat192.Gte(z, P))) + { + Nat.Add33To(6, PInv33, z); + } + } + + public static void AddExt(uint[] xx, uint[] yy, uint[] zz) + { + uint c = Nat.Add(12, xx, yy, zz); + if (c != 0 || (zz[11] == PExt11 && Nat.Gte(12, zz, PExt))) + { + if (Nat.AddTo(PExtInv.Length, PExtInv, zz) != 0) + { + Nat.IncAt(12, zz, PExtInv.Length); + } + } + } + + public static void AddOne(uint[] x, uint[] z) + { + uint c = Nat.Inc(6, x, z); + if (c != 0 || (z[5] == P5 && Nat192.Gte(z, P))) + { + Nat.Add33To(6, PInv33, z); + } + } + + public static uint[] FromBigInteger(BigInteger x) + { + uint[] z = Nat192.FromBigInteger(x); + if (z[5] == P5 && Nat192.Gte(z, P)) + { + Nat192.SubFrom(P, z); + } + return z; + } + + public static void Half(uint[] x, uint[] z) + { + if ((x[0] & 1) == 0) + { + Nat.ShiftDownBit(6, x, 0, z); + } + else + { + uint c = Nat192.Add(x, P, z); + Nat.ShiftDownBit(6, z, c); + } + } + + public static void Multiply(uint[] x, uint[] y, uint[] z) + { + uint[] tt = Nat192.CreateExt(); + Nat192.Mul(x, y, tt); + Reduce(tt, z); + } + + public static void MultiplyAddToExt(uint[] x, uint[] y, uint[] zz) + { + uint c = Nat192.MulAddTo(x, y, zz); + if (c != 0 || (zz[11] == PExt11 && Nat.Gte(12, zz, PExt))) + { + if (Nat.AddTo(PExtInv.Length, PExtInv, zz) != 0) + { + Nat.IncAt(12, zz, PExtInv.Length); + } + } + } + + public static void Negate(uint[] x, uint[] z) + { + if (Nat192.IsZero(x)) + { + Nat192.Zero(z); + } + else + { + Nat192.Sub(P, x, z); + } + } + + public static void Reduce(uint[] xx, uint[] z) + { + ulong cc = Nat192.Mul33Add(PInv33, xx, 6, xx, 0, z, 0); + uint c = Nat192.Mul33DWordAdd(PInv33, cc, z, 0); + + Debug.Assert(c == 0 || c == 1); + + if (c != 0 || (z[5] == P5 && Nat192.Gte(z, P))) + { + Nat.Add33To(6, PInv33, z); + } + } + + public static void Reduce32(uint x, uint[] z) + { + if ((x != 0 && Nat192.Mul33WordAdd(PInv33, x, z, 0) != 0) + || (z[5] == P5 && Nat192.Gte(z, P))) + { + Nat.Add33To(6, PInv33, z); + } + } + + public static void Square(uint[] x, uint[] z) + { + uint[] tt = Nat192.CreateExt(); + Nat192.Square(x, tt); + Reduce(tt, z); + } + + public static void SquareN(uint[] x, int n, uint[] z) + { + Debug.Assert(n > 0); + + uint[] tt = Nat192.CreateExt(); + Nat192.Square(x, tt); + Reduce(tt, z); + + while (--n > 0) + { + Nat192.Square(z, tt); + Reduce(tt, z); + } + } + + public static void Subtract(uint[] x, uint[] y, uint[] z) + { + int c = Nat192.Sub(x, y, z); + if (c != 0) + { + Nat.Sub33From(6, PInv33, z); + } + } + + public static void SubtractExt(uint[] xx, uint[] yy, uint[] zz) + { + int c = Nat.Sub(12, xx, yy, zz); + if (c != 0) + { + if (Nat.SubFrom(PExtInv.Length, PExtInv, zz) != 0) + { + Nat.DecAt(12, zz, PExtInv.Length); + } + } + } + + public static void Twice(uint[] x, uint[] z) + { + uint c = Nat.ShiftUpBit(6, x, 0, z); + if (c != 0 || (z[5] == P5 && Nat192.Gte(z, P))) + { + Nat.Add33To(6, PInv33, z); + } + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP192K1FieldElement.cs b/crypto/src/math/ec/custom/sec/SecP192K1FieldElement.cs new file mode 100644
index 000000000..78886dd8c --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP192K1FieldElement.cs
@@ -0,0 +1,212 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP192K1FieldElement + : ECFieldElement + { + public static readonly BigInteger Q = SecP192K1Curve.q; + + protected internal readonly uint[] x; + + public SecP192K1FieldElement(BigInteger x) + { + if (x == null || x.SignValue < 0 || x.CompareTo(Q) >= 0) + throw new ArgumentException("value invalid for SecP192K1FieldElement", "x"); + + this.x = SecP192K1Field.FromBigInteger(x); + } + + public SecP192K1FieldElement() + { + this.x = Nat192.Create(); + } + + protected internal SecP192K1FieldElement(uint[] x) + { + this.x = x; + } + + public override bool IsZero + { + get { return Nat192.IsZero(x); } + } + + public override bool IsOne + { + get { return Nat192.IsOne(x); } + } + + public override bool TestBitZero() + { + return Nat192.GetBit(x, 0) == 1; + } + + public override BigInteger ToBigInteger() + { + return Nat192.ToBigInteger(x); + } + + public override string FieldName + { + get { return "SecP192K1Field"; } + } + + public override int FieldSize + { + get { return Q.BitLength; } + } + + public override ECFieldElement Add(ECFieldElement b) + { + uint[] z = Nat192.Create(); + SecP192K1Field.Add(x, ((SecP192K1FieldElement)b).x, z); + return new SecP192K1FieldElement(z); + } + + public override ECFieldElement AddOne() + { + uint[] z = Nat192.Create(); + SecP192K1Field.AddOne(x, z); + return new SecP192K1FieldElement(z); + } + + public override ECFieldElement Subtract(ECFieldElement b) + { + uint[] z = Nat192.Create(); + SecP192K1Field.Subtract(x, ((SecP192K1FieldElement)b).x, z); + return new SecP192K1FieldElement(z); + } + + public override ECFieldElement Multiply(ECFieldElement b) + { + uint[] z = Nat192.Create(); + SecP192K1Field.Multiply(x, ((SecP192K1FieldElement)b).x, z); + return new SecP192K1FieldElement(z); + } + + public override ECFieldElement Divide(ECFieldElement b) + { + //return Multiply(b.Invert()); + uint[] z = Nat192.Create(); + Mod.Invert(SecP192K1Field.P, ((SecP192K1FieldElement)b).x, z); + SecP192K1Field.Multiply(z, x, z); + return new SecP192K1FieldElement(z); + } + + public override ECFieldElement Negate() + { + uint[] z = Nat192.Create(); + SecP192K1Field.Negate(x, z); + return new SecP192K1FieldElement(z); + } + + public override ECFieldElement Square() + { + uint[] z = Nat192.Create(); + SecP192K1Field.Square(x, z); + return new SecP192K1FieldElement(z); + } + + public override ECFieldElement Invert() + { + //return new SecP192K1FieldElement(ToBigInteger().ModInverse(Q)); + uint[] z = Nat192.Create(); + Mod.Invert(SecP192K1Field.P, x, z); + return new SecP192K1FieldElement(z); + } + + /** + * return a sqrt root - the routine verifies that the calculation returns the right value - if + * none exists it returns null. + */ + public override ECFieldElement Sqrt() + { + /* + * Raise this element to the exponent 2^190 - 2^30 - 2^10 - 2^6 - 2^5 - 2^4 - 2^1 + * + * Breaking up the exponent's binary representation into "repunits", we get: + * { 159 1s } { 1 0s } { 19 1s } { 1 0s } { 3 1s } { 3 0s} { 3 1s } { 1 0s } + * + * Therefore we need an addition chain containing 3, 19, 159 (the lengths of the repunits) + * We use: 1, 2, [3], 6, 8, 16, [19], 35, 70, 140, [159] + */ + + uint[] x1 = this.x; + if (Nat192.IsZero(x1) || Nat192.IsOne(x1)) + return this; + + uint[] x2 = Nat192.Create(); + SecP192K1Field.Square(x1, x2); + SecP192K1Field.Multiply(x2, x1, x2); + uint[] x3 = Nat192.Create(); + SecP192K1Field.Square(x2, x3); + SecP192K1Field.Multiply(x3, x1, x3); + uint[] x6 = Nat192.Create(); + SecP192K1Field.SquareN(x3, 3, x6); + SecP192K1Field.Multiply(x6, x3, x6); + uint[] x8 = x6; + SecP192K1Field.SquareN(x6, 2, x8); + SecP192K1Field.Multiply(x8, x2, x8); + uint[] x16 = x2; + SecP192K1Field.SquareN(x8, 8, x16); + SecP192K1Field.Multiply(x16, x8, x16); + uint[] x19 = x8; + SecP192K1Field.SquareN(x16, 3, x19); + SecP192K1Field.Multiply(x19, x3, x19); + uint[] x35 = Nat192.Create(); + SecP192K1Field.SquareN(x19, 16, x35); + SecP192K1Field.Multiply(x35, x16, x35); + uint[] x70 = x16; + SecP192K1Field.SquareN(x35, 35, x70); + SecP192K1Field.Multiply(x70, x35, x70); + uint[] x140 = x35; + SecP192K1Field.SquareN(x70, 70, x140); + SecP192K1Field.Multiply(x140, x70, x140); + uint[] x159 = x70; + SecP192K1Field.SquareN(x140, 19, x159); + SecP192K1Field.Multiply(x159, x19, x159); + + uint[] t1 = x159; + SecP192K1Field.SquareN(t1, 20, t1); + SecP192K1Field.Multiply(t1, x19, t1); + SecP192K1Field.SquareN(t1, 4, t1); + SecP192K1Field.Multiply(t1, x3, t1); + SecP192K1Field.SquareN(t1, 6, t1); + SecP192K1Field.Multiply(t1, x3, t1); + SecP192K1Field.Square(t1, t1); + + uint[] t2 = x3; + SecP192K1Field.Square(t1, t2); + + return Nat192.Eq(x1, t2) ? new SecP192K1FieldElement(t1) : null; + } + + public override bool Equals(object obj) + { + return Equals(obj as SecP192K1FieldElement); + } + + public override bool Equals(ECFieldElement other) + { + return Equals(other as SecP192K1FieldElement); + } + + public virtual bool Equals(SecP192K1FieldElement other) + { + if (this == other) + return true; + if (null == other) + return false; + return Nat192.Eq(x, other.x); + } + + public override int GetHashCode() + { + return Q.GetHashCode() ^ Arrays.GetHashCode(x, 0, 6); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP192K1Point.cs b/crypto/src/math/ec/custom/sec/SecP192K1Point.cs new file mode 100644
index 000000000..648aca502 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP192K1Point.cs
@@ -0,0 +1,265 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP192K1Point + : AbstractFpPoint + { + /** + * 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 + * + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecP192K1Point(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 + * + * @deprecated per-point compression property will be removed, refer + * {@link #getEncoded(bool)} + */ + public SecP192K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecP192K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, + bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecP192K1Point(null, AffineXCoord, AffineYCoord); + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + if (this == b) + return Twice(); + + ECCurve curve = this.Curve; + + SecP192K1FieldElement X1 = (SecP192K1FieldElement)this.RawXCoord, Y1 = (SecP192K1FieldElement)this.RawYCoord; + SecP192K1FieldElement X2 = (SecP192K1FieldElement)b.RawXCoord, Y2 = (SecP192K1FieldElement)b.RawYCoord; + + SecP192K1FieldElement Z1 = (SecP192K1FieldElement)this.RawZCoords[0]; + SecP192K1FieldElement Z2 = (SecP192K1FieldElement)b.RawZCoords[0]; + + uint c; + uint[] tt1 = Nat192.CreateExt(); + uint[] t2 = Nat192.Create(); + uint[] t3 = Nat192.Create(); + uint[] t4 = Nat192.Create(); + + bool Z1IsOne = Z1.IsOne; + uint[] U2, S2; + if (Z1IsOne) + { + U2 = X2.x; + S2 = Y2.x; + } + else + { + S2 = t3; + SecP192K1Field.Square(Z1.x, S2); + + U2 = t2; + SecP192K1Field.Multiply(S2, X2.x, U2); + + SecP192K1Field.Multiply(S2, Z1.x, S2); + SecP192K1Field.Multiply(S2, Y2.x, S2); + } + + bool Z2IsOne = Z2.IsOne; + uint[] U1, S1; + if (Z2IsOne) + { + U1 = X1.x; + S1 = Y1.x; + } + else + { + S1 = t4; + SecP192K1Field.Square(Z2.x, S1); + + U1 = tt1; + SecP192K1Field.Multiply(S1, X1.x, U1); + + SecP192K1Field.Multiply(S1, Z2.x, S1); + SecP192K1Field.Multiply(S1, Y1.x, S1); + } + + uint[] H = Nat192.Create(); + SecP192K1Field.Subtract(U1, U2, H); + + uint[] R = t2; + SecP192K1Field.Subtract(S1, S2, R); + + // Check if b == this or b == -this + if (Nat192.IsZero(H)) + { + if (Nat192.IsZero(R)) + { + // this == b, i.e. this must be doubled + return this.Twice(); + } + + // this == -b, i.e. the result is the point at infinity + return curve.Infinity; + } + + uint[] HSquared = t3; + SecP192K1Field.Square(H, HSquared); + + uint[] G = Nat192.Create(); + SecP192K1Field.Multiply(HSquared, H, G); + + uint[] V = t3; + SecP192K1Field.Multiply(HSquared, U1, V); + + SecP192K1Field.Negate(G, G); + Nat192.Mul(S1, G, tt1); + + c = Nat192.AddBothTo(V, V, G); + SecP192K1Field.Reduce32(c, G); + + SecP192K1FieldElement X3 = new SecP192K1FieldElement(t4); + SecP192K1Field.Square(R, X3.x); + SecP192K1Field.Subtract(X3.x, G, X3.x); + + SecP192K1FieldElement Y3 = new SecP192K1FieldElement(G); + SecP192K1Field.Subtract(V, X3.x, Y3.x); + SecP192K1Field.MultiplyAddToExt(Y3.x, R, tt1); + SecP192K1Field.Reduce(tt1, Y3.x); + + SecP192K1FieldElement Z3 = new SecP192K1FieldElement(H); + if (!Z1IsOne) + { + SecP192K1Field.Multiply(Z3.x, Z1.x, Z3.x); + } + if (!Z2IsOne) + { + SecP192K1Field.Multiply(Z3.x, Z2.x, Z3.x); + } + + ECFieldElement[] zs = new ECFieldElement[] { Z3 }; + + return new SecP192K1Point(curve, X3, Y3, zs, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + SecP192K1FieldElement Y1 = (SecP192K1FieldElement)this.RawYCoord; + if (Y1.IsZero) + return curve.Infinity; + + SecP192K1FieldElement X1 = (SecP192K1FieldElement)this.RawXCoord, Z1 = (SecP192K1FieldElement)this.RawZCoords[0]; + + uint c; + + uint[] Y1Squared = Nat192.Create(); + SecP192K1Field.Square(Y1.x, Y1Squared); + + uint[] T = Nat192.Create(); + SecP192K1Field.Square(Y1Squared, T); + + uint[] M = Nat192.Create(); + SecP192K1Field.Square(X1.x, M); + c = Nat192.AddBothTo(M, M, M); + SecP192K1Field.Reduce32(c, M); + + uint[] S = Y1Squared; + SecP192K1Field.Multiply(Y1Squared, X1.x, S); + c = Nat.ShiftUpBits(6, S, 2, 0); + SecP192K1Field.Reduce32(c, S); + + uint[] t1 = Nat192.Create(); + c = Nat.ShiftUpBits(6, T, 3, 0, t1); + SecP192K1Field.Reduce32(c, t1); + + SecP192K1FieldElement X3 = new SecP192K1FieldElement(T); + SecP192K1Field.Square(M, X3.x); + SecP192K1Field.Subtract(X3.x, S, X3.x); + SecP192K1Field.Subtract(X3.x, S, X3.x); + + SecP192K1FieldElement Y3 = new SecP192K1FieldElement(S); + SecP192K1Field.Subtract(S, X3.x, Y3.x); + SecP192K1Field.Multiply(Y3.x, M, Y3.x); + SecP192K1Field.Subtract(Y3.x, t1, Y3.x); + + SecP192K1FieldElement Z3 = new SecP192K1FieldElement(M); + SecP192K1Field.Twice(Y1.x, Z3.x); + if (!Z1.IsOne) + { + SecP192K1Field.Multiply(Z3.x, Z1.x, Z3.x); + } + + return new SecP192K1Point(curve, X3, Y3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this == b) + return ThreeTimes(); + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECFieldElement Y1 = this.RawYCoord; + if (Y1.IsZero) + return b; + + return Twice().Add(b); + } + + public override ECPoint ThreeTimes() + { + if (this.IsInfinity || this.RawYCoord.IsZero) + return this; + + // NOTE: Be careful about recursions between TwicePlus and ThreeTimes + return Twice().Add(this); + } + + public override ECPoint Negate() + { + if (IsInfinity) + return this; + + return new SecP192K1Point(Curve, RawXCoord, RawYCoord.Negate(), RawZCoords, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP192R1Curve.cs b/crypto/src/math/ec/custom/sec/SecP192R1Curve.cs new file mode 100644
index 000000000..cb3a981c8 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP192R1Curve.cs
@@ -0,0 +1,78 @@ +using System; + +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP192R1Curve + : AbstractFpCurve + { + public static readonly BigInteger q = new BigInteger(1, + Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF")); + + private const int SecP192R1_DEFAULT_COORDS = COORD_JACOBIAN; + + protected readonly SecP192R1Point m_infinity; + + public SecP192R1Curve() + : base(q) + { + this.m_infinity = new SecP192R1Point(this, null, null); + + this.m_a = FromBigInteger(new BigInteger(1, + Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC"))); + this.m_b = FromBigInteger(new BigInteger(1, + Hex.Decode("64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1"))); + this.m_order = new BigInteger(1, Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831")); + this.m_cofactor = BigInteger.One; + + this.m_coord = SecP192R1_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecP192R1Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_JACOBIAN: + return true; + default: + return false; + } + } + + public virtual BigInteger Q + { + get { return q; } + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return q.BitLength; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecP192R1FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecP192R1Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecP192R1Point(this, x, y, zs, withCompression); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP192R1Field.cs b/crypto/src/math/ec/custom/sec/SecP192R1Field.cs new file mode 100644
index 000000000..85e3a0394 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP192R1Field.cs
@@ -0,0 +1,281 @@ +using System; +using System.Diagnostics; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP192R1Field + { + // 2^192 - 2^64 - 1 + internal static readonly uint[] P = new uint[]{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + internal static readonly uint[] PExt = new uint[]{ 0x00000001, 0x00000000, 0x00000002, 0x00000000, 0x00000001, + 0x00000000, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + private static readonly uint[] PExtInv = new uint[]{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFE, + 0xFFFFFFFF, 0x00000001, 0x00000000, 0x00000002 }; + private const uint P5 = 0xFFFFFFFF; + private const uint PExt11 = 0xFFFFFFFF; + + public static void Add(uint[] x, uint[] y, uint[] z) + { + uint c = Nat192.Add(x, y, z); + if (c != 0 || (z[5] == P5 && Nat192.Gte(z, P))) + { + AddPInvTo(z); + } + } + + public static void AddExt(uint[] xx, uint[] yy, uint[] zz) + { + uint c = Nat.Add(12, xx, yy, zz); + if (c != 0 || (zz[11] == PExt11 && Nat.Gte(12, zz, PExt))) + { + if (Nat.AddTo(PExtInv.Length, PExtInv, zz) != 0) + { + Nat.IncAt(12, zz, PExtInv.Length); + } + } + } + + public static void AddOne(uint[] x, uint[] z) + { + uint c = Nat.Inc(6, x, z); + if (c != 0 || (z[5] == P5 && Nat192.Gte(z, P))) + { + AddPInvTo(z); + } + } + + public static uint[] FromBigInteger(BigInteger x) + { + uint[] z = Nat192.FromBigInteger(x); + if (z[5] == P5 && Nat192.Gte(z, P)) + { + Nat192.SubFrom(P, z); + } + return z; + } + + public static void Half(uint[] x, uint[] z) + { + if ((x[0] & 1) == 0) + { + Nat.ShiftDownBit(6, x, 0, z); + } + else + { + uint c = Nat192.Add(x, P, z); + Nat.ShiftDownBit(6, z, c); + } + } + + public static void Multiply(uint[] x, uint[] y, uint[] z) + { + uint[] tt = Nat192.CreateExt(); + Nat192.Mul(x, y, tt); + Reduce(tt, z); + } + + public static void MultiplyAddToExt(uint[] x, uint[] y, uint[] zz) + { + uint c = Nat192.MulAddTo(x, y, zz); + if (c != 0 || (zz[11] == PExt11 && Nat.Gte(12, zz, PExt))) + { + if (Nat.AddTo(PExtInv.Length, PExtInv, zz) != 0) + { + Nat.IncAt(12, zz, PExtInv.Length); + } + } + } + + public static void Negate(uint[] x, uint[] z) + { + if (Nat192.IsZero(x)) + { + Nat192.Zero(z); + } + else + { + Nat192.Sub(P, x, z); + } + } + + public static void Reduce(uint[] xx, uint[] z) + { + ulong xx06 = xx[6], xx07 = xx[7], xx08 = xx[8]; + ulong xx09 = xx[9], xx10 = xx[10], xx11 = xx[11]; + + ulong t0 = xx06 + xx10; + ulong t1 = xx07 + xx11; + + ulong cc = 0; + cc += (ulong)xx[0] + t0; + uint z0 = (uint)cc; + cc >>= 32; + cc += (ulong)xx[1] + t1; + z[1] = (uint)cc; + cc >>= 32; + + t0 += xx08; + t1 += xx09; + + cc += (ulong)xx[2] + t0; + ulong z2 = (uint)cc; + cc >>= 32; + cc += (ulong)xx[3] + t1; + z[3] = (uint)cc; + cc >>= 32; + + t0 -= xx06; + t1 -= xx07; + + cc += (ulong)xx[4] + t0; + z[4] = (uint)cc; + cc >>= 32; + cc += (ulong)xx[5] + t1; + z[5] = (uint)cc; + cc >>= 32; + + z2 += cc; + + cc += z0; + z[0] = (uint)cc; + cc >>= 32; + if (cc != 0) + { + cc += z[1]; + z[1] = (uint)cc; + z2 += cc >> 32; + } + z[2] = (uint)z2; + cc = z2 >> 32; + + Debug.Assert(cc == 0 || cc == 1); + + if ((cc != 0 && Nat.IncAt(6, z, 3) != 0) + || (z[5] == P5 && Nat192.Gte(z, P))) + { + AddPInvTo(z); + } + } + + public static void Reduce32(uint x, uint[] z) + { + ulong cc = 0; + + if (x != 0) + { + cc += (ulong)z[0] + x; + z[0] = (uint)cc; + cc >>= 32; + if (cc != 0) + { + cc += (ulong)z[1]; + z[1] = (uint)cc; + cc >>= 32; + } + cc += (ulong)z[2] + x; + z[2] = (uint)cc; + cc >>= 32; + + Debug.Assert(cc == 0 || cc == 1); + } + + if ((cc != 0 && Nat.IncAt(6, z, 3) != 0) + || (z[5] == P5 && Nat192.Gte(z, P))) + { + AddPInvTo(z); + } + } + + public static void Square(uint[] x, uint[] z) + { + uint[] tt = Nat192.CreateExt(); + Nat192.Square(x, tt); + Reduce(tt, z); + } + + public static void SquareN(uint[] x, int n, uint[] z) + { + Debug.Assert(n > 0); + + uint[] tt = Nat192.CreateExt(); + Nat192.Square(x, tt); + Reduce(tt, z); + + while (--n > 0) + { + Nat192.Square(z, tt); + Reduce(tt, z); + } + } + + public static void Subtract(uint[] x, uint[] y, uint[] z) + { + int c = Nat192.Sub(x, y, z); + if (c != 0) + { + SubPInvFrom(z); + } + } + + public static void SubtractExt(uint[] xx, uint[] yy, uint[] zz) + { + int c = Nat.Sub(12, xx, yy, zz); + if (c != 0) + { + if (Nat.SubFrom(PExtInv.Length, PExtInv, zz) != 0) + { + Nat.DecAt(12, zz, PExtInv.Length); + } + } + } + + public static void Twice(uint[] x, uint[] z) + { + uint c = Nat.ShiftUpBit(6, x, 0, z); + if (c != 0 || (z[5] == P5 && Nat192.Gte(z, P))) + { + AddPInvTo(z); + } + } + + private static void AddPInvTo(uint[] z) + { + long c = (long)z[0] + 1; + z[0] = (uint)c; + c >>= 32; + if (c != 0) + { + c += (long)z[1]; + z[1] = (uint)c; + c >>= 32; + } + c += (long)z[2] + 1; + z[2] = (uint)c; + c >>= 32; + if (c != 0) + { + Nat.IncAt(6, z, 3); + } + } + + private static void SubPInvFrom(uint[] z) + { + long c = (long)z[0] - 1; + z[0] = (uint)c; + c >>= 32; + if (c != 0) + { + c += (long)z[1]; + z[1] = (uint)c; + c >>= 32; + } + c += (long)z[2] - 1; + z[2] = (uint)c; + c >>= 32; + if (c != 0) + { + Nat.DecAt(6, z, 3); + } + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP192R1FieldElement.cs b/crypto/src/math/ec/custom/sec/SecP192R1FieldElement.cs new file mode 100644
index 000000000..020c5cdbb --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP192R1FieldElement.cs
@@ -0,0 +1,187 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP192R1FieldElement + : ECFieldElement + { + public static readonly BigInteger Q = SecP192R1Curve.q; + + protected internal readonly uint[] x; + + public SecP192R1FieldElement(BigInteger x) + { + if (x == null || x.SignValue < 0 || x.CompareTo(Q) >= 0) + throw new ArgumentException("value invalid for SecP192R1FieldElement", "x"); + + this.x = SecP192R1Field.FromBigInteger(x); + } + + public SecP192R1FieldElement() + { + this.x = Nat192.Create(); + } + + protected internal SecP192R1FieldElement(uint[] x) + { + this.x = x; + } + + public override bool IsZero + { + get { return Nat192.IsZero(x); } + } + + public override bool IsOne + { + get { return Nat192.IsOne(x); } + } + + public override bool TestBitZero() + { + return Nat192.GetBit(x, 0) == 1; + } + + public override BigInteger ToBigInteger() + { + return Nat192.ToBigInteger(x); + } + + public override string FieldName + { + get { return "SecP192R1Field"; } + } + + public override int FieldSize + { + get { return Q.BitLength; } + } + + public override ECFieldElement Add(ECFieldElement b) + { + uint[] z = Nat192.Create(); + SecP192R1Field.Add(x, ((SecP192R1FieldElement)b).x, z); + return new SecP192R1FieldElement(z); + } + + public override ECFieldElement AddOne() + { + uint[] z = Nat192.Create(); + SecP192R1Field.AddOne(x, z); + return new SecP192R1FieldElement(z); + } + + public override ECFieldElement Subtract(ECFieldElement b) + { + uint[] z = Nat192.Create(); + SecP192R1Field.Subtract(x, ((SecP192R1FieldElement)b).x, z); + return new SecP192R1FieldElement(z); + } + + public override ECFieldElement Multiply(ECFieldElement b) + { + uint[] z = Nat192.Create(); + SecP192R1Field.Multiply(x, ((SecP192R1FieldElement)b).x, z); + return new SecP192R1FieldElement(z); + } + + public override ECFieldElement Divide(ECFieldElement b) + { + //return Multiply(b.Invert()); + uint[] z = Nat192.Create(); + Mod.Invert(SecP192R1Field.P, ((SecP192R1FieldElement)b).x, z); + SecP192R1Field.Multiply(z, x, z); + return new SecP192R1FieldElement(z); + } + + public override ECFieldElement Negate() + { + uint[] z = Nat192.Create(); + SecP192R1Field.Negate(x, z); + return new SecP192R1FieldElement(z); + } + + public override ECFieldElement Square() + { + uint[] z = Nat192.Create(); + SecP192R1Field.Square(x, z); + return new SecP192R1FieldElement(z); + } + + public override ECFieldElement Invert() + { + //return new SecP192R1FieldElement(ToBigInteger().ModInverse(Q)); + uint[] z = Nat192.Create(); + Mod.Invert(SecP192R1Field.P, x, z); + return new SecP192R1FieldElement(z); + } + + /** + * return a sqrt root - the routine verifies that the calculation returns the right value - if + * none exists it returns null. + */ + public override ECFieldElement Sqrt() + { + // Raise this element to the exponent 2^190 - 2^62 + + uint[] x1 = this.x; + if (Nat192.IsZero(x1) || Nat192.IsOne(x1)) + return this; + + uint[] t1 = Nat192.Create(); + uint[] t2 = Nat192.Create(); + + SecP192R1Field.Square(x1, t1); + SecP192R1Field.Multiply(t1, x1, t1); + + SecP192R1Field.SquareN(t1, 2, t2); + SecP192R1Field.Multiply(t2, t1, t2); + + SecP192R1Field.SquareN(t2, 4, t1); + SecP192R1Field.Multiply(t1, t2, t1); + + SecP192R1Field.SquareN(t1, 8, t2); + SecP192R1Field.Multiply(t2, t1, t2); + + SecP192R1Field.SquareN(t2, 16, t1); + SecP192R1Field.Multiply(t1, t2, t1); + + SecP192R1Field.SquareN(t1, 32, t2); + SecP192R1Field.Multiply(t2, t1, t2); + + SecP192R1Field.SquareN(t2, 64, t1); + SecP192R1Field.Multiply(t1, t2, t1); + + SecP192R1Field.SquareN(t1, 62, t1); + SecP192R1Field.Square(t1, t2); + + return Nat192.Eq(x1, t2) ? new SecP192R1FieldElement(t1) : null; + } + + public override bool Equals(object obj) + { + return Equals(obj as SecP192R1FieldElement); + } + + public override bool Equals(ECFieldElement other) + { + return Equals(other as SecP192R1FieldElement); + } + + public virtual bool Equals(SecP192R1FieldElement other) + { + if (this == other) + return true; + if (null == other) + return false; + return Nat192.Eq(x, other.x); + } + + public override int GetHashCode() + { + return Q.GetHashCode() ^ Arrays.GetHashCode(x, 0, 6); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP192R1Point.cs b/crypto/src/math/ec/custom/sec/SecP192R1Point.cs new file mode 100644
index 000000000..797a8de35 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP192R1Point.cs
@@ -0,0 +1,277 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP192R1Point + : AbstractFpPoint + { + /** + * 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 + * + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecP192R1Point(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 + * + * @deprecated per-point compression property will be removed, refer + * {@link #getEncoded(bool)} + */ + public SecP192R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecP192R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecP192R1Point(null, AffineXCoord, AffineYCoord); + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + if (this == b) + return Twice(); + + ECCurve curve = this.Curve; + + SecP192R1FieldElement X1 = (SecP192R1FieldElement)this.RawXCoord, Y1 = (SecP192R1FieldElement)this.RawYCoord; + SecP192R1FieldElement X2 = (SecP192R1FieldElement)b.RawXCoord, Y2 = (SecP192R1FieldElement)b.RawYCoord; + + SecP192R1FieldElement Z1 = (SecP192R1FieldElement)this.RawZCoords[0]; + SecP192R1FieldElement Z2 = (SecP192R1FieldElement)b.RawZCoords[0]; + + uint c; + uint[] tt1 = Nat192.CreateExt(); + uint[] t2 = Nat192.Create(); + uint[] t3 = Nat192.Create(); + uint[] t4 = Nat192.Create(); + + bool Z1IsOne = Z1.IsOne; + uint[] U2, S2; + if (Z1IsOne) + { + U2 = X2.x; + S2 = Y2.x; + } + else + { + S2 = t3; + SecP192R1Field.Square(Z1.x, S2); + + U2 = t2; + SecP192R1Field.Multiply(S2, X2.x, U2); + + SecP192R1Field.Multiply(S2, Z1.x, S2); + SecP192R1Field.Multiply(S2, Y2.x, S2); + } + + bool Z2IsOne = Z2.IsOne; + uint[] U1, S1; + if (Z2IsOne) + { + U1 = X1.x; + S1 = Y1.x; + } + else + { + S1 = t4; + SecP192R1Field.Square(Z2.x, S1); + + U1 = tt1; + SecP192R1Field.Multiply(S1, X1.x, U1); + + SecP192R1Field.Multiply(S1, Z2.x, S1); + SecP192R1Field.Multiply(S1, Y1.x, S1); + } + + uint[] H = Nat192.Create(); + SecP192R1Field.Subtract(U1, U2, H); + + uint[] R = t2; + SecP192R1Field.Subtract(S1, S2, R); + + // Check if b == this or b == -this + if (Nat192.IsZero(H)) + { + if (Nat192.IsZero(R)) + { + // this == b, i.e. this must be doubled + return this.Twice(); + } + + // this == -b, i.e. the result is the point at infinity + return curve.Infinity; + } + + uint[] HSquared = t3; + SecP192R1Field.Square(H, HSquared); + + uint[] G = Nat192.Create(); + SecP192R1Field.Multiply(HSquared, H, G); + + uint[] V = t3; + SecP192R1Field.Multiply(HSquared, U1, V); + + SecP192R1Field.Negate(G, G); + Nat192.Mul(S1, G, tt1); + + c = Nat192.AddBothTo(V, V, G); + SecP192R1Field.Reduce32(c, G); + + SecP192R1FieldElement X3 = new SecP192R1FieldElement(t4); + SecP192R1Field.Square(R, X3.x); + SecP192R1Field.Subtract(X3.x, G, X3.x); + + SecP192R1FieldElement Y3 = new SecP192R1FieldElement(G); + SecP192R1Field.Subtract(V, X3.x, Y3.x); + SecP192R1Field.MultiplyAddToExt(Y3.x, R, tt1); + SecP192R1Field.Reduce(tt1, Y3.x); + + SecP192R1FieldElement Z3 = new SecP192R1FieldElement(H); + if (!Z1IsOne) + { + SecP192R1Field.Multiply(Z3.x, Z1.x, Z3.x); + } + if (!Z2IsOne) + { + SecP192R1Field.Multiply(Z3.x, Z2.x, Z3.x); + } + + ECFieldElement[] zs = new ECFieldElement[] { Z3 }; + + return new SecP192R1Point(curve, X3, Y3, zs, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + SecP192R1FieldElement Y1 = (SecP192R1FieldElement)this.RawYCoord; + if (Y1.IsZero) + return curve.Infinity; + + SecP192R1FieldElement X1 = (SecP192R1FieldElement)this.RawXCoord, Z1 = (SecP192R1FieldElement)this.RawZCoords[0]; + + uint c; + uint[] t1 = Nat192.Create(); + uint[] t2 = Nat192.Create(); + + uint[] Y1Squared = Nat192.Create(); + SecP192R1Field.Square(Y1.x, Y1Squared); + + uint[] T = Nat192.Create(); + SecP192R1Field.Square(Y1Squared, T); + + bool Z1IsOne = Z1.IsOne; + + uint[] Z1Squared = Z1.x; + if (!Z1IsOne) + { + Z1Squared = t2; + SecP192R1Field.Square(Z1.x, Z1Squared); + } + + SecP192R1Field.Subtract(X1.x, Z1Squared, t1); + + uint[] M = t2; + SecP192R1Field.Add(X1.x, Z1Squared, M); + SecP192R1Field.Multiply(M, t1, M); + c = Nat192.AddBothTo(M, M, M); + SecP192R1Field.Reduce32(c, M); + + uint[] S = Y1Squared; + SecP192R1Field.Multiply(Y1Squared, X1.x, S); + c = Nat.ShiftUpBits(6, S, 2, 0); + SecP192R1Field.Reduce32(c, S); + + c = Nat.ShiftUpBits(6, T, 3, 0, t1); + SecP192R1Field.Reduce32(c, t1); + + SecP192R1FieldElement X3 = new SecP192R1FieldElement(T); + SecP192R1Field.Square(M, X3.x); + SecP192R1Field.Subtract(X3.x, S, X3.x); + SecP192R1Field.Subtract(X3.x, S, X3.x); + + SecP192R1FieldElement Y3 = new SecP192R1FieldElement(S); + SecP192R1Field.Subtract(S, X3.x, Y3.x); + SecP192R1Field.Multiply(Y3.x, M, Y3.x); + SecP192R1Field.Subtract(Y3.x, t1, Y3.x); + + SecP192R1FieldElement Z3 = new SecP192R1FieldElement(M); + SecP192R1Field.Twice(Y1.x, Z3.x); + if (!Z1IsOne) + { + SecP192R1Field.Multiply(Z3.x, Z1.x, Z3.x); + } + + return new SecP192R1Point(curve, X3, Y3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this == b) + return ThreeTimes(); + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECFieldElement Y1 = this.RawYCoord; + if (Y1.IsZero) + return b; + + return Twice().Add(b); + } + + public override ECPoint ThreeTimes() + { + if (this.IsInfinity || this.RawYCoord.IsZero) + return this; + + // NOTE: Be careful about recursions between TwicePlus and ThreeTimes + return Twice().Add(this); + } + + public override ECPoint Negate() + { + if (IsInfinity) + return this; + + return new SecP192R1Point(Curve, RawXCoord, RawYCoord.Negate(), RawZCoords, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP224K1Curve.cs b/crypto/src/math/ec/custom/sec/SecP224K1Curve.cs new file mode 100644
index 000000000..d4be7d8de --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP224K1Curve.cs
@@ -0,0 +1,75 @@ +using System; + +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP224K1Curve + : AbstractFpCurve + { + public static readonly BigInteger q = new BigInteger(1, + Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D")); + + private const int SECP224K1_DEFAULT_COORDS = COORD_JACOBIAN; + + protected readonly SecP224K1Point m_infinity; + + public SecP224K1Curve() + : base(q) + { + this.m_infinity = new SecP224K1Point(this, null, null); + + this.m_a = FromBigInteger(BigInteger.Zero); + this.m_b = FromBigInteger(BigInteger.ValueOf(5)); + this.m_order = new BigInteger(1, Hex.Decode("010000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7")); + this.m_cofactor = BigInteger.One; + this.m_coord = SECP224K1_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecP224K1Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_JACOBIAN: + return true; + default: + return false; + } + } + + public virtual BigInteger Q + { + get { return q; } + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return q.BitLength; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecP224K1FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecP224K1Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecP224K1Point(this, x, y, zs, withCompression); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP224K1Field.cs b/crypto/src/math/ec/custom/sec/SecP224K1Field.cs new file mode 100644
index 000000000..a55810c6d --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP224K1Field.cs
@@ -0,0 +1,177 @@ +using System; +using System.Diagnostics; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP224K1Field + { + // 2^224 - 2^32 - 2^12 - 2^11 - 2^9 - 2^7 - 2^4 - 2 - 1 + internal static readonly uint[] P = new uint[]{ 0xFFFFE56D, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF }; + internal static readonly uint[] PExt = new uint[]{ 0x02C23069, 0x00003526, 0x00000001, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFCADA, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + private static readonly uint[] PExtInv = new uint[]{ 0xFD3DCF97, 0xFFFFCAD9, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00003525, 0x00000002 }; + private const uint P6 = 0xFFFFFFFF; + private const uint PExt13 = 0xFFFFFFFF; + private const uint PInv33 = 0x1A93; + + public static void Add(uint[] x, uint[] y, uint[] z) + { + uint c = Nat224.Add(x, y, z); + if (c != 0 || (z[6] == P6 && Nat224.Gte(z, P))) + { + Nat.Add33To(7, PInv33, z); + } + } + + public static void AddExt(uint[] xx, uint[] yy, uint[] zz) + { + uint c = Nat.Add(14, xx, yy, zz); + if (c != 0 || (zz[13] == PExt13 && Nat.Gte(14, zz, PExt))) + { + if (Nat.AddTo(PExtInv.Length, PExtInv, zz) != 0) + { + Nat.IncAt(14, zz, PExtInv.Length); + } + } + } + + public static void AddOne(uint[] x, uint[] z) + { + uint c = Nat.Inc(7, x, z); + if (c != 0 || (z[6] == P6 && Nat224.Gte(z, P))) + { + Nat.Add33To(7, PInv33, z); + } + } + + public static uint[] FromBigInteger(BigInteger x) + { + uint[] z = Nat224.FromBigInteger(x); + if (z[6] == P6 && Nat224.Gte(z, P)) + { + Nat224.SubFrom(P, z); + } + return z; + } + + public static void Half(uint[] x, uint[] z) + { + if ((x[0] & 1) == 0) + { + Nat.ShiftDownBit(7, x, 0, z); + } + else + { + uint c = Nat224.Add(x, P, z); + Nat.ShiftDownBit(7, z, c); + } + } + + public static void Multiply(uint[] x, uint[] y, uint[] z) + { + uint[] tt = Nat224.CreateExt(); + Nat224.Mul(x, y, tt); + Reduce(tt, z); + } + + public static void MultiplyAddToExt(uint[] x, uint[] y, uint[] zz) + { + uint c = Nat224.MulAddTo(x, y, zz); + if (c != 0 || (zz[13] == PExt13 && Nat.Gte(14, zz, PExt))) + { + if (Nat.AddTo(PExtInv.Length, PExtInv, zz) != 0) + { + Nat.IncAt(14, zz, PExtInv.Length); + } + } + } + + public static void Negate(uint[] x, uint[] z) + { + if (Nat224.IsZero(x)) + { + Nat224.Zero(z); + } + else + { + Nat224.Sub(P, x, z); + } + } + + public static void Reduce(uint[] xx, uint[] z) + { + ulong cc = Nat224.Mul33Add(PInv33, xx, 7, xx, 0, z, 0); + uint c = Nat224.Mul33DWordAdd(PInv33, cc, z, 0); + + Debug.Assert(c == 0 || c == 1); + + if (c != 0 || (z[6] == P6 && Nat224.Gte(z, P))) + { + Nat.Add33To(7, PInv33, z); + } + } + + public static void Reduce32(uint x, uint[] z) + { + if ((x != 0 && Nat224.Mul33WordAdd(PInv33, x, z, 0) != 0) + || (z[6] == P6 && Nat224.Gte(z, P))) + { + Nat.Add33To(7, PInv33, z); + } + } + + public static void Square(uint[] x, uint[] z) + { + uint[] tt = Nat224.CreateExt(); + Nat224.Square(x, tt); + Reduce(tt, z); + } + + public static void SquareN(uint[] x, int n, uint[] z) + { + Debug.Assert(n > 0); + + uint[] tt = Nat224.CreateExt(); + Nat224.Square(x, tt); + Reduce(tt, z); + + while (--n > 0) + { + Nat224.Square(z, tt); + Reduce(tt, z); + } + } + + public static void Subtract(uint[] x, uint[] y, uint[] z) + { + int c = Nat224.Sub(x, y, z); + if (c != 0) + { + Nat.Sub33From(7, PInv33, z); + } + } + + public static void SubtractExt(uint[] xx, uint[] yy, uint[] zz) + { + int c = Nat.Sub(14, xx, yy, zz); + if (c != 0) + { + if (Nat.SubFrom(PExtInv.Length, PExtInv, zz) != 0) + { + Nat.DecAt(14, zz, PExtInv.Length); + } + } + } + + public static void Twice(uint[] x, uint[] z) + { + uint c = Nat.ShiftUpBit(7, x, 0, z); + if (c != 0 || (z[6] == P6 && Nat224.Gte(z, P))) + { + Nat.Add33To(7, PInv33, z); + } + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP224K1FieldElement.cs b/crypto/src/math/ec/custom/sec/SecP224K1FieldElement.cs new file mode 100644
index 000000000..72ff4b099 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP224K1FieldElement.cs
@@ -0,0 +1,241 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP224K1FieldElement + : ECFieldElement + { + public static readonly BigInteger Q = SecP224K1Curve.q; + + // Calculated as BigInteger.Two.ModPow(Q.ShiftRight(2), Q) + private static readonly uint[] PRECOMP_POW2 = new uint[]{ 0x33bfd202, 0xdcfad133, 0x2287624a, 0xc3811ba8, + 0xa85558fc, 0x1eaef5d7, 0x8edf154c }; + + protected internal readonly uint[] x; + + public SecP224K1FieldElement(BigInteger x) + { + if (x == null || x.SignValue < 0 || x.CompareTo(Q) >= 0) + throw new ArgumentException("value invalid for SecP224K1FieldElement", "x"); + + this.x = SecP224K1Field.FromBigInteger(x); + } + + public SecP224K1FieldElement() + { + this.x = Nat224.Create(); + } + + protected internal SecP224K1FieldElement(uint[] x) + { + this.x = x; + } + + public override bool IsZero + { + get { return Nat224.IsZero(x); } + } + + public override bool IsOne + { + get { return Nat224.IsOne(x); } + } + + public override bool TestBitZero() + { + return Nat224.GetBit(x, 0) == 1; + } + + public override BigInteger ToBigInteger() + { + return Nat224.ToBigInteger(x); + } + + public override string FieldName + { + get { return "SecP224K1Field"; } + } + + public override int FieldSize + { + get { return Q.BitLength; } + } + + public override ECFieldElement Add(ECFieldElement b) + { + uint[] z = Nat224.Create(); + SecP224K1Field.Add(x, ((SecP224K1FieldElement)b).x, z); + return new SecP224K1FieldElement(z); + } + + public override ECFieldElement AddOne() + { + uint[] z = Nat224.Create(); + SecP224K1Field.AddOne(x, z); + return new SecP224K1FieldElement(z); + } + + public override ECFieldElement Subtract(ECFieldElement b) + { + uint[] z = Nat224.Create(); + SecP224K1Field.Subtract(x, ((SecP224K1FieldElement)b).x, z); + return new SecP224K1FieldElement(z); + } + + public override ECFieldElement Multiply(ECFieldElement b) + { + uint[] z = Nat224.Create(); + SecP224K1Field.Multiply(x, ((SecP224K1FieldElement)b).x, z); + return new SecP224K1FieldElement(z); + } + + public override ECFieldElement Divide(ECFieldElement b) + { + //return Multiply(b.Invert()); + uint[] z = Nat224.Create(); + Mod.Invert(SecP224K1Field.P, ((SecP224K1FieldElement)b).x, z); + SecP224K1Field.Multiply(z, x, z); + return new SecP224K1FieldElement(z); + } + + public override ECFieldElement Negate() + { + uint[] z = Nat224.Create(); + SecP224K1Field.Negate(x, z); + return new SecP224K1FieldElement(z); + } + + public override ECFieldElement Square() + { + uint[] z = Nat224.Create(); + SecP224K1Field.Square(x, z); + return new SecP224K1FieldElement(z); + } + + public override ECFieldElement Invert() + { + //return new SecP224K1FieldElement(ToBigInteger().ModInverse(Q)); + uint[] z = Nat224.Create(); + Mod.Invert(SecP224K1Field.P, x, z); + return new SecP224K1FieldElement(z); + } + + /** + * return a sqrt root - the routine verifies that the calculation returns the right value - if + * none exists it returns null. + */ + public override ECFieldElement Sqrt() + { + /* + * Q == 8m + 5, so we use Pocklington's method for this case. + * + * First, raise this element to the exponent 2^221 - 2^29 - 2^9 - 2^8 - 2^6 - 2^4 - 2^1 (i.e. m + 1) + * + * Breaking up the exponent's binary representation into "repunits", we get: + * { 191 1s } { 1 0s } { 19 1s } { 2 0s } { 1 1s } { 1 0s} { 1 1s } { 1 0s} { 3 1s } { 1 0s} + * + * Therefore we need an addition chain containing 1, 3, 19, 191 (the lengths of the repunits) + * We use: [1], 2, [3], 4, 8, 11, [19], 23, 42, 84, 107, [191] + */ + + uint[] x1 = this.x; + if (Nat224.IsZero(x1) || Nat224.IsOne(x1)) + return this; + + uint[] x2 = Nat224.Create(); + SecP224K1Field.Square(x1, x2); + SecP224K1Field.Multiply(x2, x1, x2); + uint[] x3 = x2; + SecP224K1Field.Square(x2, x3); + SecP224K1Field.Multiply(x3, x1, x3); + uint[] x4 = Nat224.Create(); + SecP224K1Field.Square(x3, x4); + SecP224K1Field.Multiply(x4, x1, x4); + uint[] x8 = Nat224.Create(); + SecP224K1Field.SquareN(x4, 4, x8); + SecP224K1Field.Multiply(x8, x4, x8); + uint[] x11 = Nat224.Create(); + SecP224K1Field.SquareN(x8, 3, x11); + SecP224K1Field.Multiply(x11, x3, x11); + uint[] x19 = x11; + SecP224K1Field.SquareN(x11, 8, x19); + SecP224K1Field.Multiply(x19, x8, x19); + uint[] x23 = x8; + SecP224K1Field.SquareN(x19, 4, x23); + SecP224K1Field.Multiply(x23, x4, x23); + uint[] x42 = x4; + SecP224K1Field.SquareN(x23, 19, x42); + SecP224K1Field.Multiply(x42, x19, x42); + uint[] x84 = Nat224.Create(); + SecP224K1Field.SquareN(x42, 42, x84); + SecP224K1Field.Multiply(x84, x42, x84); + uint[] x107 = x42; + SecP224K1Field.SquareN(x84, 23, x107); + SecP224K1Field.Multiply(x107, x23, x107); + uint[] x191 = x23; + SecP224K1Field.SquareN(x107, 84, x191); + SecP224K1Field.Multiply(x191, x84, x191); + + uint[] t1 = x191; + SecP224K1Field.SquareN(t1, 20, t1); + SecP224K1Field.Multiply(t1, x19, t1); + SecP224K1Field.SquareN(t1, 3, t1); + SecP224K1Field.Multiply(t1, x1, t1); + SecP224K1Field.SquareN(t1, 2, t1); + SecP224K1Field.Multiply(t1, x1, t1); + SecP224K1Field.SquareN(t1, 4, t1); + SecP224K1Field.Multiply(t1, x3, t1); + SecP224K1Field.Square(t1, t1); + + uint[] t2 = x84; + SecP224K1Field.Square(t1, t2); + + if (Nat224.Eq(x1, t2)) + { + return new SecP224K1FieldElement(t1); + } + + /* + * If the first guess is incorrect, we multiply by a precomputed power of 2 to get the second guess, + * which is ((4x)^(m + 1))/2 mod Q + */ + SecP224K1Field.Multiply(t1, PRECOMP_POW2, t1); + + SecP224K1Field.Square(t1, t2); + + if (Nat224.Eq(x1, t2)) + { + return new SecP224K1FieldElement(t1); + } + + return null; + } + + public override bool Equals(object obj) + { + return Equals(obj as SecP224K1FieldElement); + } + + public override bool Equals(ECFieldElement other) + { + return Equals(other as SecP224K1FieldElement); + } + + public virtual bool Equals(SecP224K1FieldElement other) + { + if (this == other) + return true; + if (null == other) + return false; + return Nat224.Eq(x, other.x); + } + + public override int GetHashCode() + { + return Q.GetHashCode() ^ Arrays.GetHashCode(x, 0, 7); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP224K1Point.cs b/crypto/src/math/ec/custom/sec/SecP224K1Point.cs new file mode 100644
index 000000000..8cbd29699 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP224K1Point.cs
@@ -0,0 +1,265 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP224K1Point + : AbstractFpPoint + { + /** + * 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 + * + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecP224K1Point(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 + * + * @deprecated per-point compression property will be removed, refer + * {@link #getEncoded(bool)} + */ + public SecP224K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecP224K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, + bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecP224K1Point(null, AffineXCoord, AffineYCoord); + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + if (this == b) + return Twice(); + + ECCurve curve = this.Curve; + + SecP224K1FieldElement X1 = (SecP224K1FieldElement)this.RawXCoord, Y1 = (SecP224K1FieldElement)this.RawYCoord; + SecP224K1FieldElement X2 = (SecP224K1FieldElement)b.RawXCoord, Y2 = (SecP224K1FieldElement)b.RawYCoord; + + SecP224K1FieldElement Z1 = (SecP224K1FieldElement)this.RawZCoords[0]; + SecP224K1FieldElement Z2 = (SecP224K1FieldElement)b.RawZCoords[0]; + + uint c; + uint[] tt1 = Nat224.CreateExt(); + uint[] t2 = Nat224.Create(); + uint[] t3 = Nat224.Create(); + uint[] t4 = Nat224.Create(); + + bool Z1IsOne = Z1.IsOne; + uint[] U2, S2; + if (Z1IsOne) + { + U2 = X2.x; + S2 = Y2.x; + } + else + { + S2 = t3; + SecP224K1Field.Square(Z1.x, S2); + + U2 = t2; + SecP224K1Field.Multiply(S2, X2.x, U2); + + SecP224K1Field.Multiply(S2, Z1.x, S2); + SecP224K1Field.Multiply(S2, Y2.x, S2); + } + + bool Z2IsOne = Z2.IsOne; + uint[] U1, S1; + if (Z2IsOne) + { + U1 = X1.x; + S1 = Y1.x; + } + else + { + S1 = t4; + SecP224K1Field.Square(Z2.x, S1); + + U1 = tt1; + SecP224K1Field.Multiply(S1, X1.x, U1); + + SecP224K1Field.Multiply(S1, Z2.x, S1); + SecP224K1Field.Multiply(S1, Y1.x, S1); + } + + uint[] H = Nat224.Create(); + SecP224K1Field.Subtract(U1, U2, H); + + uint[] R = t2; + SecP224K1Field.Subtract(S1, S2, R); + + // Check if b == this or b == -this + if (Nat224.IsZero(H)) + { + if (Nat224.IsZero(R)) + { + // this == b, i.e. this must be doubled + return this.Twice(); + } + + // this == -b, i.e. the result is the point at infinity + return curve.Infinity; + } + + uint[] HSquared = t3; + SecP224K1Field.Square(H, HSquared); + + uint[] G = Nat224.Create(); + SecP224K1Field.Multiply(HSquared, H, G); + + uint[] V = t3; + SecP224K1Field.Multiply(HSquared, U1, V); + + SecP224K1Field.Negate(G, G); + Nat224.Mul(S1, G, tt1); + + c = Nat224.AddBothTo(V, V, G); + SecP224K1Field.Reduce32(c, G); + + SecP224K1FieldElement X3 = new SecP224K1FieldElement(t4); + SecP224K1Field.Square(R, X3.x); + SecP224K1Field.Subtract(X3.x, G, X3.x); + + SecP224K1FieldElement Y3 = new SecP224K1FieldElement(G); + SecP224K1Field.Subtract(V, X3.x, Y3.x); + SecP224K1Field.MultiplyAddToExt(Y3.x, R, tt1); + SecP224K1Field.Reduce(tt1, Y3.x); + + SecP224K1FieldElement Z3 = new SecP224K1FieldElement(H); + if (!Z1IsOne) + { + SecP224K1Field.Multiply(Z3.x, Z1.x, Z3.x); + } + if (!Z2IsOne) + { + SecP224K1Field.Multiply(Z3.x, Z2.x, Z3.x); + } + + ECFieldElement[] zs = new ECFieldElement[] { Z3 }; + + return new SecP224K1Point(curve, X3, Y3, zs, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + SecP224K1FieldElement Y1 = (SecP224K1FieldElement)this.RawYCoord; + if (Y1.IsZero) + return curve.Infinity; + + SecP224K1FieldElement X1 = (SecP224K1FieldElement)this.RawXCoord, Z1 = (SecP224K1FieldElement)this.RawZCoords[0]; + + uint c; + + uint[] Y1Squared = Nat224.Create(); + SecP224K1Field.Square(Y1.x, Y1Squared); + + uint[] T = Nat224.Create(); + SecP224K1Field.Square(Y1Squared, T); + + uint[] M = Nat224.Create(); + SecP224K1Field.Square(X1.x, M); + c = Nat224.AddBothTo(M, M, M); + SecP224K1Field.Reduce32(c, M); + + uint[] S = Y1Squared; + SecP224K1Field.Multiply(Y1Squared, X1.x, S); + c = Nat.ShiftUpBits(7, S, 2, 0); + SecP224K1Field.Reduce32(c, S); + + uint[] t1 = Nat224.Create(); + c = Nat.ShiftUpBits(7, T, 3, 0, t1); + SecP224K1Field.Reduce32(c, t1); + + SecP224K1FieldElement X3 = new SecP224K1FieldElement(T); + SecP224K1Field.Square(M, X3.x); + SecP224K1Field.Subtract(X3.x, S, X3.x); + SecP224K1Field.Subtract(X3.x, S, X3.x); + + SecP224K1FieldElement Y3 = new SecP224K1FieldElement(S); + SecP224K1Field.Subtract(S, X3.x, Y3.x); + SecP224K1Field.Multiply(Y3.x, M, Y3.x); + SecP224K1Field.Subtract(Y3.x, t1, Y3.x); + + SecP224K1FieldElement Z3 = new SecP224K1FieldElement(M); + SecP224K1Field.Twice(Y1.x, Z3.x); + if (!Z1.IsOne) + { + SecP224K1Field.Multiply(Z3.x, Z1.x, Z3.x); + } + + return new SecP224K1Point(curve, X3, Y3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this == b) + return ThreeTimes(); + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECFieldElement Y1 = this.RawYCoord; + if (Y1.IsZero) + return b; + + return Twice().Add(b); + } + + public override ECPoint ThreeTimes() + { + if (this.IsInfinity || this.RawYCoord.IsZero) + return this; + + // NOTE: Be careful about recursions between TwicePlus and ThreeTimes + return Twice().Add(this); + } + + public override ECPoint Negate() + { + if (IsInfinity) + return this; + + return new SecP224K1Point(Curve, RawXCoord, RawYCoord.Negate(), RawZCoords, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP224R1Curve.cs b/crypto/src/math/ec/custom/sec/SecP224R1Curve.cs new file mode 100644
index 000000000..cda8781ff --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP224R1Curve.cs
@@ -0,0 +1,78 @@ +using System; + +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP224R1Curve + : AbstractFpCurve + { + public static readonly BigInteger q = new BigInteger(1, + Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001")); + + private const int SecP224R1_DEFAULT_COORDS = COORD_JACOBIAN; + + protected readonly SecP224R1Point m_infinity; + + public SecP224R1Curve() + : base(q) + { + this.m_infinity = new SecP224R1Point(this, null, null); + + this.m_a = FromBigInteger(new BigInteger(1, + Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE"))); + this.m_b = FromBigInteger(new BigInteger(1, + Hex.Decode("B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4"))); + this.m_order = new BigInteger(1, Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D")); + this.m_cofactor = BigInteger.One; + + this.m_coord = SecP224R1_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecP224R1Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_JACOBIAN: + return true; + default: + return false; + } + } + + public virtual BigInteger Q + { + get { return q; } + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return q.BitLength; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecP224R1FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecP224R1Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecP224R1Point(this, x, y, zs, withCompression); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP224R1Field.cs b/crypto/src/math/ec/custom/sec/SecP224R1Field.cs new file mode 100644
index 000000000..559593c66 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP224R1Field.cs
@@ -0,0 +1,295 @@ +using System; +using System.Diagnostics; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP224R1Field + { + // 2^224 - 2^96 + 1 + internal static readonly uint[] P = new uint[] { 0x00000001, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + internal static readonly uint[] PExt = new uint[]{ 0x00000001, 0x00000000, 0x00000000, 0xFFFFFFFE, 0xFFFFFFFF, + 0xFFFFFFFF, 0x00000000, 0x00000002, 0x00000000, 0x00000000, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + private static readonly uint[] PExtInv = new uint[]{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000001, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000001 }; + private const uint P6 = 0xFFFFFFFF; + private const uint PExt13 = 0xFFFFFFFF; + + public static void Add(uint[] x, uint[] y, uint[] z) + { + uint c = Nat224.Add(x, y, z); + if (c != 0 || (z[6] == P6 && Nat224.Gte(z, P))) + { + AddPInvTo(z); + } + } + + public static void AddExt(uint[] xx, uint[] yy, uint[] zz) + { + uint c = Nat.Add(14, xx, yy, zz); + if (c != 0 || (zz[13] == PExt13 && Nat.Gte(14, zz, PExt))) + { + if (Nat.AddTo(PExtInv.Length, PExtInv, zz) != 0) + { + Nat.IncAt(14, zz, PExtInv.Length); + } + } + } + + public static void AddOne(uint[] x, uint[] z) + { + uint c = Nat.Inc(7, x, z); + if (c != 0 || (z[6] == P6 && Nat224.Gte(z, P))) + { + AddPInvTo(z); + } + } + + public static uint[] FromBigInteger(BigInteger x) + { + uint[] z = Nat224.FromBigInteger(x); + if (z[6] == P6 && Nat224.Gte(z, P)) + { + Nat224.SubFrom(P, z); + } + return z; + } + + public static void Half(uint[] x, uint[] z) + { + if ((x[0] & 1) == 0) + { + Nat.ShiftDownBit(7, x, 0, z); + } + else + { + uint c = Nat224.Add(x, P, z); + Nat.ShiftDownBit(7, z, c); + } + } + + public static void Multiply(uint[] x, uint[] y, uint[] z) + { + uint[] tt = Nat224.CreateExt(); + Nat224.Mul(x, y, tt); + Reduce(tt, z); + } + + public static void MultiplyAddToExt(uint[] x, uint[] y, uint[] zz) + { + uint c = Nat224.MulAddTo(x, y, zz); + if (c != 0 || (zz[13] == PExt13 && Nat.Gte(14, zz, PExt))) + { + if (Nat.AddTo(PExtInv.Length, PExtInv, zz) != 0) + { + Nat.IncAt(14, zz, PExtInv.Length); + } + } + } + + public static void Negate(uint[] x, uint[] z) + { + if (Nat224.IsZero(x)) + { + Nat224.Zero(z); + } + else + { + Nat224.Sub(P, x, z); + } + } + + public static void Reduce(uint[] xx, uint[] z) + { + long xx10 = xx[10], xx11 = xx[11], xx12 = xx[12], xx13 = xx[13]; + + const long n = 1; + + long t0 = (long)xx[7] + xx11 - n; + long t1 = (long)xx[8] + xx12; + long t2 = (long)xx[9] + xx13; + + long cc = 0; + cc += (long)xx[0] - t0; + long z0 = (uint)cc; + cc >>= 32; + cc += (long)xx[1] - t1; + z[1] = (uint)cc; + cc >>= 32; + cc += (long)xx[2] - t2; + z[2] = (uint)cc; + cc >>= 32; + cc += (long)xx[3] + t0 - xx10; + long z3 = (uint)cc; + cc >>= 32; + cc += (long)xx[4] + t1 - xx11; + z[4] = (uint)cc; + cc >>= 32; + cc += (long)xx[5] + t2 - xx12; + z[5] = (uint)cc; + cc >>= 32; + cc += (long)xx[6] + xx10 - xx13; + z[6] = (uint)cc; + cc >>= 32; + cc += n; + + Debug.Assert(cc >= 0); + + z3 += cc; + + z0 -= cc; + z[0] = (uint)z0; + cc = z0 >> 32; + if (cc != 0) + { + cc += (long)z[1]; + z[1] = (uint)cc; + cc >>= 32; + cc += (long)z[2]; + z[2] = (uint)cc; + z3 += cc >> 32; + } + z[3] = (uint)z3; + cc = z3 >> 32; + + Debug.Assert(cc == 0 || cc == 1); + + if ((cc != 0 && Nat.IncAt(7, z, 4) != 0) + || (z[6] == P6 && Nat224.Gte(z, P))) + { + AddPInvTo(z); + } + } + + public static void Reduce32(uint x, uint[] z) + { + long cc = 0; + + if (x != 0) + { + long xx07 = x; + + cc += (long)z[0] - xx07; + z[0] = (uint)cc; + cc >>= 32; + if (cc != 0) + { + cc += (long)z[1]; + z[1] = (uint)cc; + cc >>= 32; + cc += (long)z[2]; + z[2] = (uint)cc; + cc >>= 32; + } + cc += (long)z[3] + xx07; + z[3] = (uint)cc; + cc >>= 32; + + Debug.Assert(cc == 0 || cc == 1); + } + + if ((cc != 0 && Nat.IncAt(7, z, 4) != 0) + || (z[6] == P6 && Nat224.Gte(z, P))) + { + AddPInvTo(z); + } + } + + public static void Square(uint[] x, uint[] z) + { + uint[] tt = Nat224.CreateExt(); + Nat224.Square(x, tt); + Reduce(tt, z); + } + + public static void SquareN(uint[] x, int n, uint[] z) + { + Debug.Assert(n > 0); + + uint[] tt = Nat224.CreateExt(); + Nat224.Square(x, tt); + Reduce(tt, z); + + while (--n > 0) + { + Nat224.Square(z, tt); + Reduce(tt, z); + } + } + + public static void Subtract(uint[] x, uint[] y, uint[] z) + { + int c = Nat224.Sub(x, y, z); + if (c != 0) + { + SubPInvFrom(z); + } + } + + public static void SubtractExt(uint[] xx, uint[] yy, uint[] zz) + { + int c = Nat.Sub(14, xx, yy, zz); + if (c != 0) + { + if (Nat.SubFrom(PExtInv.Length, PExtInv, zz) != 0) + { + Nat.DecAt(14, zz, PExtInv.Length); + } + } + } + + public static void Twice(uint[] x, uint[] z) + { + uint c = Nat.ShiftUpBit(7, x, 0, z); + if (c != 0 || (z[6] == P6 && Nat224.Gte(z, P))) + { + AddPInvTo(z); + } + } + + private static void AddPInvTo(uint[] z) + { + long c = (long)z[0] - 1; + z[0] = (uint)c; + c >>= 32; + if (c != 0) + { + c += (long)z[1]; + z[1] = (uint)c; + c >>= 32; + c += (long)z[2]; + z[2] = (uint)c; + c >>= 32; + } + c += (long)z[3] + 1; + z[3] = (uint)c; + c >>= 32; + if (c != 0) + { + Nat.IncAt(7, z, 4); + } + } + + private static void SubPInvFrom(uint[] z) + { + long c = (long)z[0] + 1; + z[0] = (uint)c; + c >>= 32; + if (c != 0) + { + c += (long)z[1]; + z[1] = (uint)c; + c >>= 32; + c += (long)z[2]; + z[2] = (uint)c; + c >>= 32; + } + c += (long)z[3] - 1; + z[3] = (uint)c; + c >>= 32; + if (c != 0) + { + Nat.DecAt(7, z, 4); + } + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP224R1FieldElement.cs b/crypto/src/math/ec/custom/sec/SecP224R1FieldElement.cs new file mode 100644
index 000000000..06f47cded --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP224R1FieldElement.cs
@@ -0,0 +1,268 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP224R1FieldElement + : ECFieldElement + { + public static readonly BigInteger Q = SecP224R1Curve.q; + + protected internal readonly uint[] x; + + public SecP224R1FieldElement(BigInteger x) + { + if (x == null || x.SignValue < 0 || x.CompareTo(Q) >= 0) + throw new ArgumentException("value invalid for SecP224R1FieldElement", "x"); + + this.x = SecP224R1Field.FromBigInteger(x); + } + + public SecP224R1FieldElement() + { + this.x = Nat224.Create(); + } + + protected internal SecP224R1FieldElement(uint[] x) + { + this.x = x; + } + + public override bool IsZero + { + get { return Nat224.IsZero(x); } + } + + public override bool IsOne + { + get { return Nat224.IsOne(x); } + } + + public override bool TestBitZero() + { + return Nat224.GetBit(x, 0) == 1; + } + + public override BigInteger ToBigInteger() + { + return Nat224.ToBigInteger(x); + } + + public override string FieldName + { + get { return "SecP224R1Field"; } + } + + public override int FieldSize + { + get { return Q.BitLength; } + } + + public override ECFieldElement Add(ECFieldElement b) + { + uint[] z = Nat224.Create(); + SecP224R1Field.Add(x, ((SecP224R1FieldElement)b).x, z); + return new SecP224R1FieldElement(z); + } + + public override ECFieldElement AddOne() + { + uint[] z = Nat224.Create(); + SecP224R1Field.AddOne(x, z); + return new SecP224R1FieldElement(z); + } + + public override ECFieldElement Subtract(ECFieldElement b) + { + uint[] z = Nat224.Create(); + SecP224R1Field.Subtract(x, ((SecP224R1FieldElement)b).x, z); + return new SecP224R1FieldElement(z); + } + + public override ECFieldElement Multiply(ECFieldElement b) + { + uint[] z = Nat224.Create(); + SecP224R1Field.Multiply(x, ((SecP224R1FieldElement)b).x, z); + return new SecP224R1FieldElement(z); + } + + public override ECFieldElement Divide(ECFieldElement b) + { + //return Multiply(b.Invert()); + uint[] z = Nat224.Create(); + Mod.Invert(SecP224R1Field.P, ((SecP224R1FieldElement)b).x, z); + SecP224R1Field.Multiply(z, x, z); + return new SecP224R1FieldElement(z); + } + + public override ECFieldElement Negate() + { + uint[] z = Nat224.Create(); + SecP224R1Field.Negate(x, z); + return new SecP224R1FieldElement(z); + } + + public override ECFieldElement Square() + { + uint[] z = Nat224.Create(); + SecP224R1Field.Square(x, z); + return new SecP224R1FieldElement(z); + } + + public override ECFieldElement Invert() + { + //return new SecP224R1FieldElement(ToBigInteger().ModInverse(Q)); + uint[] z = Nat224.Create(); + Mod.Invert(SecP224R1Field.P, x, z); + return new SecP224R1FieldElement(z); + } + + /** + * return a sqrt root - the routine verifies that the calculation returns the right value - if + * none exists it returns null. + */ + public override ECFieldElement Sqrt() + { + uint[] c = this.x; + if (Nat224.IsZero(c) || Nat224.IsOne(c)) + return this; + + uint[] nc = Nat224.Create(); + SecP224R1Field.Negate(c, nc); + + uint[] r = Mod.Random(SecP224R1Field.P); + uint[] t = Nat224.Create(); + + if (!IsSquare(c)) + return null; + + while (!TrySqrt(nc, r, t)) + { + SecP224R1Field.AddOne(r, r); + } + + SecP224R1Field.Square(t, r); + + return Nat224.Eq(c, r) ? new SecP224R1FieldElement(t) : null; + } + + public override bool Equals(object obj) + { + return Equals(obj as SecP224R1FieldElement); + } + + public override bool Equals(ECFieldElement other) + { + return Equals(other as SecP224R1FieldElement); + } + + public virtual bool Equals(SecP224R1FieldElement other) + { + if (this == other) + return true; + if (null == other) + return false; + return Nat224.Eq(x, other.x); + } + + public override int GetHashCode() + { + return Q.GetHashCode() ^ Arrays.GetHashCode(x, 0, 7); + } + + private static bool IsSquare(uint[] x) + { + uint[] t1 = Nat224.Create(); + uint[] t2 = Nat224.Create(); + Nat224.Copy(x, t1); + + for (int i = 0; i < 7; ++i) + { + Nat224.Copy(t1, t2); + SecP224R1Field.SquareN(t1, 1 << i, t1); + SecP224R1Field.Multiply(t1, t2, t1); + } + + SecP224R1Field.SquareN(t1, 95, t1); + return Nat224.IsOne(t1); + } + + private static void RM(uint[] nc, uint[] d0, uint[] e0, uint[] d1, uint[] e1, uint[] f1, uint[] t) + { + SecP224R1Field.Multiply(e1, e0, t); + SecP224R1Field.Multiply(t, nc, t); + SecP224R1Field.Multiply(d1, d0, f1); + SecP224R1Field.Add(f1, t, f1); + SecP224R1Field.Multiply(d1, e0, t); + Nat224.Copy(f1, d1); + SecP224R1Field.Multiply(e1, d0, e1); + SecP224R1Field.Add(e1, t, e1); + SecP224R1Field.Square(e1, f1); + SecP224R1Field.Multiply(f1, nc, f1); + } + + private static void RP(uint[] nc, uint[] d1, uint[] e1, uint[] f1, uint[] t) + { + Nat224.Copy(nc, f1); + + uint[] d0 = Nat224.Create(); + uint[] e0 = Nat224.Create(); + + for (int i = 0; i < 7; ++i) + { + Nat224.Copy(d1, d0); + Nat224.Copy(e1, e0); + + int j = 1 << i; + while (--j >= 0) + { + RS(d1, e1, f1, t); + } + + RM(nc, d0, e0, d1, e1, f1, t); + } + } + + private static void RS(uint[] d, uint[] e, uint[] f, uint[] t) + { + SecP224R1Field.Multiply(e, d, e); + SecP224R1Field.Twice(e, e); + SecP224R1Field.Square(d, t); + SecP224R1Field.Add(f, t, d); + SecP224R1Field.Multiply(f, t, f); + uint c = Nat.ShiftUpBits(7, f, 2, 0); + SecP224R1Field.Reduce32(c, f); + } + + private static bool TrySqrt(uint[] nc, uint[] r, uint[] t) + { + uint[] d1 = Nat224.Create(); + Nat224.Copy(r, d1); + uint[] e1 = Nat224.Create(); + e1[0] = 1; + uint[] f1 = Nat224.Create(); + RP(nc, d1, e1, f1, t); + + uint[] d0 = Nat224.Create(); + uint[] e0 = Nat224.Create(); + + for (int k = 1; k < 96; ++k) + { + Nat224.Copy(d1, d0); + Nat224.Copy(e1, e0); + + RS(d1, e1, f1, t); + + if (Nat224.IsZero(d1)) + { + Mod.Invert(SecP224R1Field.P, e0, t); + SecP224R1Field.Multiply(t, d0, t); + return true; + } + } + + return false; + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP224R1Point.cs b/crypto/src/math/ec/custom/sec/SecP224R1Point.cs new file mode 100644
index 000000000..c3f4efb59 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP224R1Point.cs
@@ -0,0 +1,277 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP224R1Point + : AbstractFpPoint + { + /** + * 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 + * + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecP224R1Point(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 + * + * @deprecated per-point compression property will be removed, refer + * {@link #getEncoded(bool)} + */ + public SecP224R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecP224R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecP224R1Point(null, AffineXCoord, AffineYCoord); + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + if (this == b) + return Twice(); + + ECCurve curve = this.Curve; + + SecP224R1FieldElement X1 = (SecP224R1FieldElement)this.RawXCoord, Y1 = (SecP224R1FieldElement)this.RawYCoord; + SecP224R1FieldElement X2 = (SecP224R1FieldElement)b.RawXCoord, Y2 = (SecP224R1FieldElement)b.RawYCoord; + + SecP224R1FieldElement Z1 = (SecP224R1FieldElement)this.RawZCoords[0]; + SecP224R1FieldElement Z2 = (SecP224R1FieldElement)b.RawZCoords[0]; + + uint c; + uint[] tt1 = Nat224.CreateExt(); + uint[] t2 = Nat224.Create(); + uint[] t3 = Nat224.Create(); + uint[] t4 = Nat224.Create(); + + bool Z1IsOne = Z1.IsOne; + uint[] U2, S2; + if (Z1IsOne) + { + U2 = X2.x; + S2 = Y2.x; + } + else + { + S2 = t3; + SecP224R1Field.Square(Z1.x, S2); + + U2 = t2; + SecP224R1Field.Multiply(S2, X2.x, U2); + + SecP224R1Field.Multiply(S2, Z1.x, S2); + SecP224R1Field.Multiply(S2, Y2.x, S2); + } + + bool Z2IsOne = Z2.IsOne; + uint[] U1, S1; + if (Z2IsOne) + { + U1 = X1.x; + S1 = Y1.x; + } + else + { + S1 = t4; + SecP224R1Field.Square(Z2.x, S1); + + U1 = tt1; + SecP224R1Field.Multiply(S1, X1.x, U1); + + SecP224R1Field.Multiply(S1, Z2.x, S1); + SecP224R1Field.Multiply(S1, Y1.x, S1); + } + + uint[] H = Nat224.Create(); + SecP224R1Field.Subtract(U1, U2, H); + + uint[] R = t2; + SecP224R1Field.Subtract(S1, S2, R); + + // Check if b == this or b == -this + if (Nat224.IsZero(H)) + { + if (Nat224.IsZero(R)) + { + // this == b, i.e. this must be doubled + return this.Twice(); + } + + // this == -b, i.e. the result is the point at infinity + return curve.Infinity; + } + + uint[] HSquared = t3; + SecP224R1Field.Square(H, HSquared); + + uint[] G = Nat224.Create(); + SecP224R1Field.Multiply(HSquared, H, G); + + uint[] V = t3; + SecP224R1Field.Multiply(HSquared, U1, V); + + SecP224R1Field.Negate(G, G); + Nat224.Mul(S1, G, tt1); + + c = Nat224.AddBothTo(V, V, G); + SecP224R1Field.Reduce32(c, G); + + SecP224R1FieldElement X3 = new SecP224R1FieldElement(t4); + SecP224R1Field.Square(R, X3.x); + SecP224R1Field.Subtract(X3.x, G, X3.x); + + SecP224R1FieldElement Y3 = new SecP224R1FieldElement(G); + SecP224R1Field.Subtract(V, X3.x, Y3.x); + SecP224R1Field.MultiplyAddToExt(Y3.x, R, tt1); + SecP224R1Field.Reduce(tt1, Y3.x); + + SecP224R1FieldElement Z3 = new SecP224R1FieldElement(H); + if (!Z1IsOne) + { + SecP224R1Field.Multiply(Z3.x, Z1.x, Z3.x); + } + if (!Z2IsOne) + { + SecP224R1Field.Multiply(Z3.x, Z2.x, Z3.x); + } + + ECFieldElement[] zs = new ECFieldElement[] { Z3 }; + + return new SecP224R1Point(curve, X3, Y3, zs, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + SecP224R1FieldElement Y1 = (SecP224R1FieldElement)this.RawYCoord; + if (Y1.IsZero) + return curve.Infinity; + + SecP224R1FieldElement X1 = (SecP224R1FieldElement)this.RawXCoord, Z1 = (SecP224R1FieldElement)this.RawZCoords[0]; + + uint c; + uint[] t1 = Nat224.Create(); + uint[] t2 = Nat224.Create(); + + uint[] Y1Squared = Nat224.Create(); + SecP224R1Field.Square(Y1.x, Y1Squared); + + uint[] T = Nat224.Create(); + SecP224R1Field.Square(Y1Squared, T); + + bool Z1IsOne = Z1.IsOne; + + uint[] Z1Squared = Z1.x; + if (!Z1IsOne) + { + Z1Squared = t2; + SecP224R1Field.Square(Z1.x, Z1Squared); + } + + SecP224R1Field.Subtract(X1.x, Z1Squared, t1); + + uint[] M = t2; + SecP224R1Field.Add(X1.x, Z1Squared, M); + SecP224R1Field.Multiply(M, t1, M); + c = Nat224.AddBothTo(M, M, M); + SecP224R1Field.Reduce32(c, M); + + uint[] S = Y1Squared; + SecP224R1Field.Multiply(Y1Squared, X1.x, S); + c = Nat.ShiftUpBits(7, S, 2, 0); + SecP224R1Field.Reduce32(c, S); + + c = Nat.ShiftUpBits(7, T, 3, 0, t1); + SecP224R1Field.Reduce32(c, t1); + + SecP224R1FieldElement X3 = new SecP224R1FieldElement(T); + SecP224R1Field.Square(M, X3.x); + SecP224R1Field.Subtract(X3.x, S, X3.x); + SecP224R1Field.Subtract(X3.x, S, X3.x); + + SecP224R1FieldElement Y3 = new SecP224R1FieldElement(S); + SecP224R1Field.Subtract(S, X3.x, Y3.x); + SecP224R1Field.Multiply(Y3.x, M, Y3.x); + SecP224R1Field.Subtract(Y3.x, t1, Y3.x); + + SecP224R1FieldElement Z3 = new SecP224R1FieldElement(M); + SecP224R1Field.Twice(Y1.x, Z3.x); + if (!Z1IsOne) + { + SecP224R1Field.Multiply(Z3.x, Z1.x, Z3.x); + } + + return new SecP224R1Point(curve, X3, Y3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this == b) + return ThreeTimes(); + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECFieldElement Y1 = this.RawYCoord; + if (Y1.IsZero) + return b; + + return Twice().Add(b); + } + + public override ECPoint ThreeTimes() + { + if (this.IsInfinity || this.RawYCoord.IsZero) + return this; + + // NOTE: Be careful about recursions between TwicePlus and ThreeTimes + return Twice().Add(this); + } + + public override ECPoint Negate() + { + if (IsInfinity) + return this; + + return new SecP224R1Point(Curve, RawXCoord, RawYCoord.Negate(), RawZCoords, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP256K1Curve.cs b/crypto/src/math/ec/custom/sec/SecP256K1Curve.cs new file mode 100644
index 000000000..59e2cefb2 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP256K1Curve.cs
@@ -0,0 +1,75 @@ +using System; + +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP256K1Curve + : AbstractFpCurve + { + public static readonly BigInteger q = new BigInteger(1, + Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F")); + + private const int SECP256K1_DEFAULT_COORDS = COORD_JACOBIAN; + + protected readonly SecP256K1Point m_infinity; + + public SecP256K1Curve() + : base(q) + { + this.m_infinity = new SecP256K1Point(this, null, null); + + this.m_a = FromBigInteger(BigInteger.Zero); + this.m_b = FromBigInteger(BigInteger.ValueOf(7)); + this.m_order = new BigInteger(1, Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141")); + this.m_cofactor = BigInteger.One; + this.m_coord = SECP256K1_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecP256K1Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_JACOBIAN: + return true; + default: + return false; + } + } + + public virtual BigInteger Q + { + get { return q; } + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return q.BitLength; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecP256K1FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecP256K1Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecP256K1Point(this, x, y, zs, withCompression); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP256K1Field.cs b/crypto/src/math/ec/custom/sec/SecP256K1Field.cs new file mode 100644
index 000000000..ba3a070a9 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP256K1Field.cs
@@ -0,0 +1,178 @@ +using System; +using System.Diagnostics; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP256K1Field + { + // 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1 + internal static readonly uint[] P = new uint[]{ 0xFFFFFC2F, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF }; + internal static readonly uint[] PExt = new uint[]{ 0x000E90A1, 0x000007A2, 0x00000001, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0xFFFFF85E, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF }; + private static readonly uint[] PExtInv = new uint[]{ 0xFFF16F5F, 0xFFFFF85D, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x000007A1, 0x00000002 }; + private const uint P7 = 0xFFFFFFFF; + private const uint PExt15 = 0xFFFFFFFF; + private const uint PInv33 = 0x3D1; + + public static void Add(uint[] x, uint[] y, uint[] z) + { + uint c = Nat256.Add(x, y, z); + if (c != 0 || (z[7] == P7 && Nat256.Gte(z, P))) + { + Nat.Add33To(8, PInv33, z); + } + } + + public static void AddExt(uint[] xx, uint[] yy, uint[] zz) + { + uint c = Nat.Add(16, xx, yy, zz); + if (c != 0 || (zz[15] == PExt15 && Nat.Gte(16, zz, PExt))) + { + if (Nat.AddTo(PExtInv.Length, PExtInv, zz) != 0) + { + Nat.IncAt(16, zz, PExtInv.Length); + } + } + } + + public static void AddOne(uint[] x, uint[] z) + { + uint c = Nat.Inc(8, x, z); + if (c != 0 || (z[7] == P7 && Nat256.Gte(z, P))) + { + Nat.Add33To(8, PInv33, z); + } + } + + public static uint[] FromBigInteger(BigInteger x) + { + uint[] z = Nat256.FromBigInteger(x); + if (z[7] == P7 && Nat256.Gte(z, P)) + { + Nat256.SubFrom(P, z); + } + return z; + } + + public static void Half(uint[] x, uint[] z) + { + if ((x[0] & 1) == 0) + { + Nat.ShiftDownBit(8, x, 0, z); + } + else + { + uint c = Nat256.Add(x, P, z); + Nat.ShiftDownBit(8, z, c); + } + } + + public static void Multiply(uint[] x, uint[] y, uint[] z) + { + uint[] tt = Nat256.CreateExt(); + Nat256.Mul(x, y, tt); + Reduce(tt, z); + } + + public static void MultiplyAddToExt(uint[] x, uint[] y, uint[] zz) + { + uint c = Nat256.MulAddTo(x, y, zz); + if (c != 0 || (zz[15] == PExt15 && Nat.Gte(16, zz, PExt))) + { + if (Nat.AddTo(PExtInv.Length, PExtInv, zz) != 0) + { + Nat.IncAt(16, zz, PExtInv.Length); + } + } + } + + public static void Negate(uint[] x, uint[] z) + { + if (Nat256.IsZero(x)) + { + Nat256.Zero(z); + } + else + { + Nat256.Sub(P, x, z); + } + } + + public static void Reduce(uint[] xx, uint[] z) + { + ulong cc = Nat256.Mul33Add(PInv33, xx, 8, xx, 0, z, 0); + uint c = Nat256.Mul33DWordAdd(PInv33, cc, z, 0); + + Debug.Assert(c == 0 || c == 1); + + if (c != 0 || (z[7] == P7 && Nat256.Gte(z, P))) + { + Nat.Add33To(8, PInv33, z); + } + } + + public static void Reduce32(uint x, uint[] z) + { + if ((x != 0 && Nat256.Mul33WordAdd(PInv33, x, z, 0) != 0) + || (z[7] == P7 && Nat256.Gte(z, P))) + { + Nat.Add33To(8, PInv33, z); + } + } + + public static void Square(uint[] x, uint[] z) + { + uint[] tt = Nat256.CreateExt(); + Nat256.Square(x, tt); + Reduce(tt, z); + } + + public static void SquareN(uint[] x, int n, uint[] z) + { + Debug.Assert(n > 0); + + uint[] tt = Nat256.CreateExt(); + Nat256.Square(x, tt); + Reduce(tt, z); + + while (--n > 0) + { + Nat256.Square(z, tt); + Reduce(tt, z); + } + } + + public static void Subtract(uint[] x, uint[] y, uint[] z) + { + int c = Nat256.Sub(x, y, z); + if (c != 0) + { + Nat.Sub33From(8, PInv33, z); + } + } + + public static void SubtractExt(uint[] xx, uint[] yy, uint[] zz) + { + int c = Nat.Sub(16, xx, yy, zz); + if (c != 0) + { + if (Nat.SubFrom(PExtInv.Length, PExtInv, zz) != 0) + { + Nat.DecAt(16, zz, PExtInv.Length); + } + } + } + + public static void Twice(uint[] x, uint[] z) + { + uint c = Nat.ShiftUpBit(8, x, 0, z); + if (c != 0 || (z[7] == P7 && Nat256.Gte(z, P))) + { + Nat.Add33To(8, PInv33, z); + } + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP256K1FieldElement.cs b/crypto/src/math/ec/custom/sec/SecP256K1FieldElement.cs new file mode 100644
index 000000000..d9a039a4f --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP256K1FieldElement.cs
@@ -0,0 +1,213 @@ +using System; +using System.Diagnostics; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP256K1FieldElement + : ECFieldElement + { + public static readonly BigInteger Q = SecP256K1Curve.q; + + protected internal readonly uint[] x; + + public SecP256K1FieldElement(BigInteger x) + { + if (x == null || x.SignValue < 0 || x.CompareTo(Q) >= 0) + throw new ArgumentException("value invalid for SecP256K1FieldElement", "x"); + + this.x = SecP256K1Field.FromBigInteger(x); + } + + public SecP256K1FieldElement() + { + this.x = Nat256.Create(); + } + + protected internal SecP256K1FieldElement(uint[] x) + { + this.x = x; + } + + public override bool IsZero + { + get { return Nat256.IsZero(x); } + } + + public override bool IsOne + { + get { return Nat256.IsOne(x); } + } + + public override bool TestBitZero() + { + return Nat256.GetBit(x, 0) == 1; + } + + public override BigInteger ToBigInteger() + { + return Nat256.ToBigInteger(x); + } + + public override string FieldName + { + get { return "SecP256K1Field"; } + } + + public override int FieldSize + { + get { return Q.BitLength; } + } + + public override ECFieldElement Add(ECFieldElement b) + { + uint[] z = Nat256.Create(); + SecP256K1Field.Add(x, ((SecP256K1FieldElement)b).x, z); + return new SecP256K1FieldElement(z); + } + + public override ECFieldElement AddOne() + { + uint[] z = Nat256.Create(); + SecP256K1Field.AddOne(x, z); + return new SecP256K1FieldElement(z); + } + + public override ECFieldElement Subtract(ECFieldElement b) + { + uint[] z = Nat256.Create(); + SecP256K1Field.Subtract(x, ((SecP256K1FieldElement)b).x, z); + return new SecP256K1FieldElement(z); + } + + public override ECFieldElement Multiply(ECFieldElement b) + { + uint[] z = Nat256.Create(); + SecP256K1Field.Multiply(x, ((SecP256K1FieldElement)b).x, z); + return new SecP256K1FieldElement(z); + } + + public override ECFieldElement Divide(ECFieldElement b) + { + //return Multiply(b.Invert()); + uint[] z = Nat256.Create(); + Mod.Invert(SecP256K1Field.P, ((SecP256K1FieldElement)b).x, z); + SecP256K1Field.Multiply(z, x, z); + return new SecP256K1FieldElement(z); + } + + public override ECFieldElement Negate() + { + uint[] z = Nat256.Create(); + SecP256K1Field.Negate(x, z); + return new SecP256K1FieldElement(z); + } + + public override ECFieldElement Square() + { + uint[] z = Nat256.Create(); + SecP256K1Field.Square(x, z); + return new SecP256K1FieldElement(z); + } + + public override ECFieldElement Invert() + { + //return new SecP256K1FieldElement(ToBigInteger().ModInverse(Q)); + uint[] z = Nat256.Create(); + Mod.Invert(SecP256K1Field.P, x, z); + return new SecP256K1FieldElement(z); + } + + /** + * return a sqrt root - the routine verifies that the calculation returns the right value - if + * none exists it returns null. + */ + public override ECFieldElement Sqrt() + { + /* + * Raise this element to the exponent 2^254 - 2^30 - 2^7 - 2^6 - 2^5 - 2^4 - 2^2 + * + * Breaking up the exponent's binary representation into "repunits", we get: + * { 223 1s } { 1 0s } { 22 1s } { 4 0s } { 2 1s } { 2 0s} + * + * Therefore we need an addition chain containing 2, 22, 223 (the lengths of the repunits) + * We use: 1, [2], 3, 6, 9, 11, [22], 44, 88, 176, 220, [223] + */ + + uint[] x1 = this.x; + if (Nat256.IsZero(x1) || Nat256.IsOne(x1)) + return this; + + uint[] x2 = Nat256.Create(); + SecP256K1Field.Square(x1, x2); + SecP256K1Field.Multiply(x2, x1, x2); + uint[] x3 = Nat256.Create(); + SecP256K1Field.Square(x2, x3); + SecP256K1Field.Multiply(x3, x1, x3); + uint[] x6 = Nat256.Create(); + SecP256K1Field.SquareN(x3, 3, x6); + SecP256K1Field.Multiply(x6, x3, x6); + uint[] x9 = x6; + SecP256K1Field.SquareN(x6, 3, x9); + SecP256K1Field.Multiply(x9, x3, x9); + uint[] x11 = x9; + SecP256K1Field.SquareN(x9, 2, x11); + SecP256K1Field.Multiply(x11, x2, x11); + uint[] x22 = Nat256.Create(); + SecP256K1Field.SquareN(x11, 11, x22); + SecP256K1Field.Multiply(x22, x11, x22); + uint[] x44 = x11; + SecP256K1Field.SquareN(x22, 22, x44); + SecP256K1Field.Multiply(x44, x22, x44); + uint[] x88 = Nat256.Create(); + SecP256K1Field.SquareN(x44, 44, x88); + SecP256K1Field.Multiply(x88, x44, x88); + uint[] x176 = Nat256.Create(); + SecP256K1Field.SquareN(x88, 88, x176); + SecP256K1Field.Multiply(x176, x88, x176); + uint[] x220 = x88; + SecP256K1Field.SquareN(x176, 44, x220); + SecP256K1Field.Multiply(x220, x44, x220); + uint[] x223 = x44; + SecP256K1Field.SquareN(x220, 3, x223); + SecP256K1Field.Multiply(x223, x3, x223); + + uint[] t1 = x223; + SecP256K1Field.SquareN(t1, 23, t1); + SecP256K1Field.Multiply(t1, x22, t1); + SecP256K1Field.SquareN(t1, 6, t1); + SecP256K1Field.Multiply(t1, x2, t1); + SecP256K1Field.SquareN(t1, 2, t1); + + uint[] t2 = x2; + SecP256K1Field.Square(t1, t2); + + return Nat256.Eq(x1, t2) ? new SecP256K1FieldElement(t1) : null; + } + + public override bool Equals(object obj) + { + return Equals(obj as SecP256K1FieldElement); + } + + public override bool Equals(ECFieldElement other) + { + return Equals(other as SecP256K1FieldElement); + } + + public virtual bool Equals(SecP256K1FieldElement other) + { + if (this == other) + return true; + if (null == other) + return false; + return Nat256.Eq(x, other.x); + } + + public override int GetHashCode() + { + return Q.GetHashCode() ^ Arrays.GetHashCode(x, 0, 8); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP256K1Point.cs b/crypto/src/math/ec/custom/sec/SecP256K1Point.cs new file mode 100644
index 000000000..3165682fa --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP256K1Point.cs
@@ -0,0 +1,265 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP256K1Point + : AbstractFpPoint + { + /** + * 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 + * + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecP256K1Point(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 + * + * @deprecated per-point compression property will be removed, refer + * {@link #getEncoded(bool)} + */ + public SecP256K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecP256K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, + bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecP256K1Point(null, AffineXCoord, AffineYCoord); + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + if (this == b) + return Twice(); + + ECCurve curve = this.Curve; + + SecP256K1FieldElement X1 = (SecP256K1FieldElement)this.RawXCoord, Y1 = (SecP256K1FieldElement)this.RawYCoord; + SecP256K1FieldElement X2 = (SecP256K1FieldElement)b.RawXCoord, Y2 = (SecP256K1FieldElement)b.RawYCoord; + + SecP256K1FieldElement Z1 = (SecP256K1FieldElement)this.RawZCoords[0]; + SecP256K1FieldElement Z2 = (SecP256K1FieldElement)b.RawZCoords[0]; + + uint c; + uint[] tt1 = Nat256.CreateExt(); + uint[] t2 = Nat256.Create(); + uint[] t3 = Nat256.Create(); + uint[] t4 = Nat256.Create(); + + bool Z1IsOne = Z1.IsOne; + uint[] U2, S2; + if (Z1IsOne) + { + U2 = X2.x; + S2 = Y2.x; + } + else + { + S2 = t3; + SecP256K1Field.Square(Z1.x, S2); + + U2 = t2; + SecP256K1Field.Multiply(S2, X2.x, U2); + + SecP256K1Field.Multiply(S2, Z1.x, S2); + SecP256K1Field.Multiply(S2, Y2.x, S2); + } + + bool Z2IsOne = Z2.IsOne; + uint[] U1, S1; + if (Z2IsOne) + { + U1 = X1.x; + S1 = Y1.x; + } + else + { + S1 = t4; + SecP256K1Field.Square(Z2.x, S1); + + U1 = tt1; + SecP256K1Field.Multiply(S1, X1.x, U1); + + SecP256K1Field.Multiply(S1, Z2.x, S1); + SecP256K1Field.Multiply(S1, Y1.x, S1); + } + + uint[] H = Nat256.Create(); + SecP256K1Field.Subtract(U1, U2, H); + + uint[] R = t2; + SecP256K1Field.Subtract(S1, S2, R); + + // Check if b == this or b == -this + if (Nat256.IsZero(H)) + { + if (Nat256.IsZero(R)) + { + // this == b, i.e. this must be doubled + return this.Twice(); + } + + // this == -b, i.e. the result is the point at infinity + return curve.Infinity; + } + + uint[] HSquared = t3; + SecP256K1Field.Square(H, HSquared); + + uint[] G = Nat256.Create(); + SecP256K1Field.Multiply(HSquared, H, G); + + uint[] V = t3; + SecP256K1Field.Multiply(HSquared, U1, V); + + SecP256K1Field.Negate(G, G); + Nat256.Mul(S1, G, tt1); + + c = Nat256.AddBothTo(V, V, G); + SecP256K1Field.Reduce32(c, G); + + SecP256K1FieldElement X3 = new SecP256K1FieldElement(t4); + SecP256K1Field.Square(R, X3.x); + SecP256K1Field.Subtract(X3.x, G, X3.x); + + SecP256K1FieldElement Y3 = new SecP256K1FieldElement(G); + SecP256K1Field.Subtract(V, X3.x, Y3.x); + SecP256K1Field.MultiplyAddToExt(Y3.x, R, tt1); + SecP256K1Field.Reduce(tt1, Y3.x); + + SecP256K1FieldElement Z3 = new SecP256K1FieldElement(H); + if (!Z1IsOne) + { + SecP256K1Field.Multiply(Z3.x, Z1.x, Z3.x); + } + if (!Z2IsOne) + { + SecP256K1Field.Multiply(Z3.x, Z2.x, Z3.x); + } + + ECFieldElement[] zs = new ECFieldElement[] { Z3 }; + + return new SecP256K1Point(curve, X3, Y3, zs, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + SecP256K1FieldElement Y1 = (SecP256K1FieldElement)this.RawYCoord; + if (Y1.IsZero) + return curve.Infinity; + + SecP256K1FieldElement X1 = (SecP256K1FieldElement)this.RawXCoord, Z1 = (SecP256K1FieldElement)this.RawZCoords[0]; + + uint c; + + uint[] Y1Squared = Nat256.Create(); + SecP256K1Field.Square(Y1.x, Y1Squared); + + uint[] T = Nat256.Create(); + SecP256K1Field.Square(Y1Squared, T); + + uint[] M = Nat256.Create(); + SecP256K1Field.Square(X1.x, M); + c = Nat256.AddBothTo(M, M, M); + SecP256K1Field.Reduce32(c, M); + + uint[] S = Y1Squared; + SecP256K1Field.Multiply(Y1Squared, X1.x, S); + c = Nat.ShiftUpBits(8, S, 2, 0); + SecP256K1Field.Reduce32(c, S); + + uint[] t1 = Nat256.Create(); + c = Nat.ShiftUpBits(8, T, 3, 0, t1); + SecP256K1Field.Reduce32(c, t1); + + SecP256K1FieldElement X3 = new SecP256K1FieldElement(T); + SecP256K1Field.Square(M, X3.x); + SecP256K1Field.Subtract(X3.x, S, X3.x); + SecP256K1Field.Subtract(X3.x, S, X3.x); + + SecP256K1FieldElement Y3 = new SecP256K1FieldElement(S); + SecP256K1Field.Subtract(S, X3.x, Y3.x); + SecP256K1Field.Multiply(Y3.x, M, Y3.x); + SecP256K1Field.Subtract(Y3.x, t1, Y3.x); + + SecP256K1FieldElement Z3 = new SecP256K1FieldElement(M); + SecP256K1Field.Twice(Y1.x, Z3.x); + if (!Z1.IsOne) + { + SecP256K1Field.Multiply(Z3.x, Z1.x, Z3.x); + } + + return new SecP256K1Point(curve, X3, Y3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this == b) + return ThreeTimes(); + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECFieldElement Y1 = this.RawYCoord; + if (Y1.IsZero) + return b; + + return Twice().Add(b); + } + + public override ECPoint ThreeTimes() + { + if (this.IsInfinity || this.RawYCoord.IsZero) + return this; + + // NOTE: Be careful about recursions between TwicePlus and ThreeTimes + return Twice().Add(this); + } + + public override ECPoint Negate() + { + if (IsInfinity) + return this; + + return new SecP256K1Point(Curve, RawXCoord, RawYCoord.Negate(), RawZCoords, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP256R1Curve.cs b/crypto/src/math/ec/custom/sec/SecP256R1Curve.cs new file mode 100644
index 000000000..6b3448f06 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP256R1Curve.cs
@@ -0,0 +1,77 @@ +using System; + +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP256R1Curve + : AbstractFpCurve + { + public static readonly BigInteger q = new BigInteger(1, + Hex.Decode("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF")); + + private const int SecP256R1_DEFAULT_COORDS = COORD_JACOBIAN; + + protected readonly SecP256R1Point m_infinity; + + public SecP256R1Curve() + : base(q) + { + this.m_infinity = new SecP256R1Point(this, null, null); + + this.m_a = FromBigInteger(new BigInteger(1, + Hex.Decode("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC"))); + this.m_b = FromBigInteger(new BigInteger(1, + Hex.Decode("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B"))); + this.m_order = new BigInteger(1, Hex.Decode("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551")); + this.m_cofactor = BigInteger.One; + this.m_coord = SecP256R1_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecP256R1Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_JACOBIAN: + return true; + default: + return false; + } + } + + public virtual BigInteger Q + { + get { return q; } + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return q.BitLength; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecP256R1FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecP256R1Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecP256R1Point(this, x, y, zs, withCompression); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP256R1Field.cs b/crypto/src/math/ec/custom/sec/SecP256R1Field.cs new file mode 100644
index 000000000..9ed9dcd41 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP256R1Field.cs
@@ -0,0 +1,309 @@ +using System; +using System.Diagnostics; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP256R1Field + { + // 2^256 - 2^224 + 2^192 + 2^96 - 1 + internal static readonly uint[] P = new uint[]{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, + 0x00000001, 0xFFFFFFFF }; + internal static readonly uint[] PExt = new uint[]{ 0x00000001, 0x00000000, 0x00000000, 0xFFFFFFFE, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFE, 0x00000001, 0xFFFFFFFE, 0x00000001, 0xFFFFFFFE, 0x00000001, 0x00000001, 0xFFFFFFFE, + 0x00000002, 0xFFFFFFFE }; + internal const uint P7 = 0xFFFFFFFF; + internal const uint PExt15 = 0xFFFFFFFE; + + public static void Add(uint[] x, uint[] y, uint[] z) + { + uint c = Nat256.Add(x, y, z); + if (c != 0 || (z[7] == P7 && Nat256.Gte(z, P))) + { + AddPInvTo(z); + } + } + + public static void AddExt(uint[] xx, uint[] yy, uint[] zz) + { + uint c = Nat.Add(16, xx, yy, zz); + if (c != 0 || (zz[15] >= PExt15 && Nat.Gte(16, zz, PExt))) + { + Nat.SubFrom(16, PExt, zz); + } + } + + public static void AddOne(uint[] x, uint[] z) + { + uint c = Nat.Inc(8, x, z); + if (c != 0 || (z[7] == P7 && Nat256.Gte(z, P))) + { + AddPInvTo(z); + } + } + + public static uint[] FromBigInteger(BigInteger x) + { + uint[] z = Nat256.FromBigInteger(x); + if (z[7] == P7 && Nat256.Gte(z, P)) + { + Nat256.SubFrom(P, z); + } + return z; + } + + public static void Half(uint[] x, uint[] z) + { + if ((x[0] & 1) == 0) + { + Nat.ShiftDownBit(8, x, 0, z); + } + else + { + uint c = Nat256.Add(x, P, z); + Nat.ShiftDownBit(8, z, c); + } + } + + public static void Multiply(uint[] x, uint[] y, uint[] z) + { + uint[] tt = Nat256.CreateExt(); + Nat256.Mul(x, y, tt); + Reduce(tt, z); + } + + public static void MultiplyAddToExt(uint[] x, uint[] y, uint[] zz) + { + uint c = Nat256.MulAddTo(x, y, zz); + if (c != 0 || (zz[15] >= PExt15 && Nat.Gte(16, zz, PExt))) + { + Nat.SubFrom(16, PExt, zz); + } + } + + public static void Negate(uint[] x, uint[] z) + { + if (Nat256.IsZero(x)) + { + Nat256.Zero(z); + } + else + { + Nat256.Sub(P, x, z); + } + } + + public static void Reduce(uint[] xx, uint[] z) + { + long xx08 = xx[8], xx09 = xx[9], xx10 = xx[10], xx11 = xx[11]; + long xx12 = xx[12], xx13 = xx[13], xx14 = xx[14], xx15 = xx[15]; + + const long n = 6; + + xx08 -= n; + + long t0 = xx08 + xx09; + long t1 = xx09 + xx10; + long t2 = xx10 + xx11 - xx15; + long t3 = xx11 + xx12; + long t4 = xx12 + xx13; + long t5 = xx13 + xx14; + long t6 = xx14 + xx15; + + long cc = 0; + cc += (long)xx[0] + t0 - t3 - t5; + z[0] = (uint)cc; + cc >>= 32; + cc += (long)xx[1] + t1 - t4 - t6; + z[1] = (uint)cc; + cc >>= 32; + cc += (long)xx[2] + t2 - t5; + z[2] = (uint)cc; + cc >>= 32; + cc += (long)xx[3] + (t3 << 1) + xx13 - xx15 - t0; + z[3] = (uint)cc; + cc >>= 32; + cc += (long)xx[4] + (t4 << 1) + xx14 - t1; + z[4] = (uint)cc; + cc >>= 32; + cc += (long)xx[5] + (t5 << 1) - t2; + z[5] = (uint)cc; + cc >>= 32; + cc += (long)xx[6] + (t6 << 1) + t5 - t0; + z[6] = (uint)cc; + cc >>= 32; + cc += (long)xx[7] + (xx15 << 1) + xx08 - t2 - t4; + z[7] = (uint)cc; + cc >>= 32; + cc += n; + + Debug.Assert(cc >= 0); + + Reduce32((uint)cc, z); + } + + public static void Reduce32(uint x, uint[] z) + { + long cc = 0; + + if (x != 0) + { + long xx08 = x; + + cc += (long)z[0] + xx08; + z[0] = (uint)cc; + cc >>= 32; + if (cc != 0) + { + cc += (long)z[1]; + z[1] = (uint)cc; + cc >>= 32; + cc += (long)z[2]; + z[2] = (uint)cc; + cc >>= 32; + } + cc += (long)z[3] - xx08; + z[3] = (uint)cc; + cc >>= 32; + if (cc != 0) + { + cc += (long)z[4]; + z[4] = (uint)cc; + cc >>= 32; + cc += (long)z[5]; + z[5] = (uint)cc; + cc >>= 32; + } + cc += (long)z[6] - xx08; + z[6] = (uint)cc; + cc >>= 32; + cc += (long)z[7] + xx08; + z[7] = (uint)cc; + cc >>= 32; + + Debug.Assert(cc == 0 || cc == 1); + } + + if (cc != 0 || (z[7] == P7 && Nat256.Gte(z, P))) + { + AddPInvTo(z); + } + } + + public static void Square(uint[] x, uint[] z) + { + uint[] tt = Nat256.CreateExt(); + Nat256.Square(x, tt); + Reduce(tt, z); + } + + public static void SquareN(uint[] x, int n, uint[] z) + { + Debug.Assert(n > 0); + + uint[] tt = Nat256.CreateExt(); + Nat256.Square(x, tt); + Reduce(tt, z); + + while (--n > 0) + { + Nat256.Square(z, tt); + Reduce(tt, z); + } + } + + public static void Subtract(uint[] x, uint[] y, uint[] z) + { + int c = Nat256.Sub(x, y, z); + if (c != 0) + { + SubPInvFrom(z); + } + } + + public static void SubtractExt(uint[] xx, uint[] yy, uint[] zz) + { + int c = Nat.Sub(16, xx, yy, zz); + if (c != 0) + { + Nat.AddTo(16, PExt, zz); + } + } + + public static void Twice(uint[] x, uint[] z) + { + uint c = Nat.ShiftUpBit(8, x, 0, z); + if (c != 0 || (z[7] == P7 && Nat256.Gte(z, P))) + { + AddPInvTo(z); + } + } + + private static void AddPInvTo(uint[] z) + { + long c = (long)z[0] + 1; + z[0] = (uint)c; + c >>= 32; + if (c != 0) + { + c += (long)z[1]; + z[1] = (uint)c; + c >>= 32; + c += (long)z[2]; + z[2] = (uint)c; + c >>= 32; + } + c += (long)z[3] - 1; + z[3] = (uint)c; + c >>= 32; + if (c != 0) + { + c += (long)z[4]; + z[4] = (uint)c; + c >>= 32; + c += (long)z[5]; + z[5] = (uint)c; + c >>= 32; + } + c += (long)z[6] - 1; + z[6] = (uint)c; + c >>= 32; + c += (long)z[7] + 1; + z[7] = (uint)c; + //c >>= 32; + } + + private static void SubPInvFrom(uint[] z) + { + long c = (long)z[0] - 1; + z[0] = (uint)c; + c >>= 32; + if (c != 0) + { + c += (long)z[1]; + z[1] = (uint)c; + c >>= 32; + c += (long)z[2]; + z[2] = (uint)c; + c >>= 32; + } + c += (long)z[3] + 1; + z[3] = (uint)c; + c >>= 32; + if (c != 0) + { + c += (long)z[4]; + z[4] = (uint)c; + c >>= 32; + c += (long)z[5]; + z[5] = (uint)c; + c >>= 32; + } + c += (long)z[6] + 1; + z[6] = (uint)c; + c >>= 32; + c += (long)z[7] - 1; + z[7] = (uint)c; + //c >>= 32; + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP256R1FieldElement.cs b/crypto/src/math/ec/custom/sec/SecP256R1FieldElement.cs new file mode 100644
index 000000000..b22763cfa --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP256R1FieldElement.cs
@@ -0,0 +1,187 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP256R1FieldElement + : ECFieldElement + { + public static readonly BigInteger Q = SecP256R1Curve.q; + + protected internal readonly uint[] x; + + public SecP256R1FieldElement(BigInteger x) + { + if (x == null || x.SignValue < 0 || x.CompareTo(Q) >= 0) + throw new ArgumentException("value invalid for SecP256R1FieldElement", "x"); + + this.x = SecP256R1Field.FromBigInteger(x); + } + + public SecP256R1FieldElement() + { + this.x = Nat256.Create(); + } + + protected internal SecP256R1FieldElement(uint[] x) + { + this.x = x; + } + + public override bool IsZero + { + get { return Nat256.IsZero(x); } + } + + public override bool IsOne + { + get { return Nat256.IsOne(x); } + } + + public override bool TestBitZero() + { + return Nat256.GetBit(x, 0) == 1; + } + + public override BigInteger ToBigInteger() + { + return Nat256.ToBigInteger(x); + } + + public override string FieldName + { + get { return "SecP256R1Field"; } + } + + public override int FieldSize + { + get { return Q.BitLength; } + } + + public override ECFieldElement Add(ECFieldElement b) + { + uint[] z = Nat256.Create(); + SecP256R1Field.Add(x, ((SecP256R1FieldElement)b).x, z); + return new SecP256R1FieldElement(z); + } + + public override ECFieldElement AddOne() + { + uint[] z = Nat256.Create(); + SecP256R1Field.AddOne(x, z); + return new SecP256R1FieldElement(z); + } + + public override ECFieldElement Subtract(ECFieldElement b) + { + uint[] z = Nat256.Create(); + SecP256R1Field.Subtract(x, ((SecP256R1FieldElement)b).x, z); + return new SecP256R1FieldElement(z); + } + + public override ECFieldElement Multiply(ECFieldElement b) + { + uint[] z = Nat256.Create(); + SecP256R1Field.Multiply(x, ((SecP256R1FieldElement)b).x, z); + return new SecP256R1FieldElement(z); + } + + public override ECFieldElement Divide(ECFieldElement b) + { + //return Multiply(b.Invert()); + uint[] z = Nat256.Create(); + Mod.Invert(SecP256R1Field.P, ((SecP256R1FieldElement)b).x, z); + SecP256R1Field.Multiply(z, x, z); + return new SecP256R1FieldElement(z); + } + + public override ECFieldElement Negate() + { + uint[] z = Nat256.Create(); + SecP256R1Field.Negate(x, z); + return new SecP256R1FieldElement(z); + } + + public override ECFieldElement Square() + { + uint[] z = Nat256.Create(); + SecP256R1Field.Square(x, z); + return new SecP256R1FieldElement(z); + } + + public override ECFieldElement Invert() + { + //return new SecP256R1FieldElement(ToBigInteger().ModInverse(Q)); + uint[] z = Nat256.Create(); + Mod.Invert(SecP256R1Field.P, x, z); + return new SecP256R1FieldElement(z); + } + + /** + * return a sqrt root - the routine verifies that the calculation returns the right value - if + * none exists it returns null. + */ + public override ECFieldElement Sqrt() + { + // Raise this element to the exponent 2^254 - 2^222 + 2^190 + 2^94 + + uint[] x1 = this.x; + if (Nat256.IsZero(x1) || Nat256.IsOne(x1)) + return this; + + uint[] t1 = Nat256.Create(); + uint[] t2 = Nat256.Create(); + + SecP256R1Field.Square(x1, t1); + SecP256R1Field.Multiply(t1, x1, t1); + + SecP256R1Field.SquareN(t1, 2, t2); + SecP256R1Field.Multiply(t2, t1, t2); + + SecP256R1Field.SquareN(t2, 4, t1); + SecP256R1Field.Multiply(t1, t2, t1); + + SecP256R1Field.SquareN(t1, 8, t2); + SecP256R1Field.Multiply(t2, t1, t2); + + SecP256R1Field.SquareN(t2, 16, t1); + SecP256R1Field.Multiply(t1, t2, t1); + + SecP256R1Field.SquareN(t1, 32, t1); + SecP256R1Field.Multiply(t1, x1, t1); + + SecP256R1Field.SquareN(t1, 96, t1); + SecP256R1Field.Multiply(t1, x1, t1); + + SecP256R1Field.SquareN(t1, 94, t1); + SecP256R1Field.Multiply(t1, t1, t2); + + return Nat256.Eq(x1, t2) ? new SecP256R1FieldElement(t1) : null; + } + + public override bool Equals(object obj) + { + return Equals(obj as SecP256R1FieldElement); + } + + public override bool Equals(ECFieldElement other) + { + return Equals(other as SecP256R1FieldElement); + } + + public virtual bool Equals(SecP256R1FieldElement other) + { + if (this == other) + return true; + if (null == other) + return false; + return Nat256.Eq(x, other.x); + } + + public override int GetHashCode() + { + return Q.GetHashCode() ^ Arrays.GetHashCode(x, 0, 8); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP256R1Point.cs b/crypto/src/math/ec/custom/sec/SecP256R1Point.cs new file mode 100644
index 000000000..1de4a0b4a --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP256R1Point.cs
@@ -0,0 +1,277 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP256R1Point + : AbstractFpPoint + { + /** + * 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 + * + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecP256R1Point(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 + * + * @deprecated per-point compression property will be removed, refer + * {@link #getEncoded(bool)} + */ + public SecP256R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecP256R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecP256R1Point(null, AffineXCoord, AffineYCoord); + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + if (this == b) + return Twice(); + + ECCurve curve = this.Curve; + + SecP256R1FieldElement X1 = (SecP256R1FieldElement)this.RawXCoord, Y1 = (SecP256R1FieldElement)this.RawYCoord; + SecP256R1FieldElement X2 = (SecP256R1FieldElement)b.RawXCoord, Y2 = (SecP256R1FieldElement)b.RawYCoord; + + SecP256R1FieldElement Z1 = (SecP256R1FieldElement)this.RawZCoords[0]; + SecP256R1FieldElement Z2 = (SecP256R1FieldElement)b.RawZCoords[0]; + + uint c; + uint[] tt1 = Nat256.CreateExt(); + uint[] t2 = Nat256.Create(); + uint[] t3 = Nat256.Create(); + uint[] t4 = Nat256.Create(); + + bool Z1IsOne = Z1.IsOne; + uint[] U2, S2; + if (Z1IsOne) + { + U2 = X2.x; + S2 = Y2.x; + } + else + { + S2 = t3; + SecP256R1Field.Square(Z1.x, S2); + + U2 = t2; + SecP256R1Field.Multiply(S2, X2.x, U2); + + SecP256R1Field.Multiply(S2, Z1.x, S2); + SecP256R1Field.Multiply(S2, Y2.x, S2); + } + + bool Z2IsOne = Z2.IsOne; + uint[] U1, S1; + if (Z2IsOne) + { + U1 = X1.x; + S1 = Y1.x; + } + else + { + S1 = t4; + SecP256R1Field.Square(Z2.x, S1); + + U1 = tt1; + SecP256R1Field.Multiply(S1, X1.x, U1); + + SecP256R1Field.Multiply(S1, Z2.x, S1); + SecP256R1Field.Multiply(S1, Y1.x, S1); + } + + uint[] H = Nat256.Create(); + SecP256R1Field.Subtract(U1, U2, H); + + uint[] R = t2; + SecP256R1Field.Subtract(S1, S2, R); + + // Check if b == this or b == -this + if (Nat256.IsZero(H)) + { + if (Nat256.IsZero(R)) + { + // this == b, i.e. this must be doubled + return this.Twice(); + } + + // this == -b, i.e. the result is the point at infinity + return curve.Infinity; + } + + uint[] HSquared = t3; + SecP256R1Field.Square(H, HSquared); + + uint[] G = Nat256.Create(); + SecP256R1Field.Multiply(HSquared, H, G); + + uint[] V = t3; + SecP256R1Field.Multiply(HSquared, U1, V); + + SecP256R1Field.Negate(G, G); + Nat256.Mul(S1, G, tt1); + + c = Nat256.AddBothTo(V, V, G); + SecP256R1Field.Reduce32(c, G); + + SecP256R1FieldElement X3 = new SecP256R1FieldElement(t4); + SecP256R1Field.Square(R, X3.x); + SecP256R1Field.Subtract(X3.x, G, X3.x); + + SecP256R1FieldElement Y3 = new SecP256R1FieldElement(G); + SecP256R1Field.Subtract(V, X3.x, Y3.x); + SecP256R1Field.MultiplyAddToExt(Y3.x, R, tt1); + SecP256R1Field.Reduce(tt1, Y3.x); + + SecP256R1FieldElement Z3 = new SecP256R1FieldElement(H); + if (!Z1IsOne) + { + SecP256R1Field.Multiply(Z3.x, Z1.x, Z3.x); + } + if (!Z2IsOne) + { + SecP256R1Field.Multiply(Z3.x, Z2.x, Z3.x); + } + + ECFieldElement[] zs = new ECFieldElement[]{ Z3 }; + + return new SecP256R1Point(curve, X3, Y3, zs, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + SecP256R1FieldElement Y1 = (SecP256R1FieldElement)this.RawYCoord; + if (Y1.IsZero) + return curve.Infinity; + + SecP256R1FieldElement X1 = (SecP256R1FieldElement)this.RawXCoord, Z1 = (SecP256R1FieldElement)this.RawZCoords[0]; + + uint c; + uint[] t1 = Nat256.Create(); + uint[] t2 = Nat256.Create(); + + uint[] Y1Squared = Nat256.Create(); + SecP256R1Field.Square(Y1.x, Y1Squared); + + uint[] T = Nat256.Create(); + SecP256R1Field.Square(Y1Squared, T); + + bool Z1IsOne = Z1.IsOne; + + uint[] Z1Squared = Z1.x; + if (!Z1IsOne) + { + Z1Squared = t2; + SecP256R1Field.Square(Z1.x, Z1Squared); + } + + SecP256R1Field.Subtract(X1.x, Z1Squared, t1); + + uint[] M = t2; + SecP256R1Field.Add(X1.x, Z1Squared, M); + SecP256R1Field.Multiply(M, t1, M); + c = Nat256.AddBothTo(M, M, M); + SecP256R1Field.Reduce32(c, M); + + uint[] S = Y1Squared; + SecP256R1Field.Multiply(Y1Squared, X1.x, S); + c = Nat.ShiftUpBits(8, S, 2, 0); + SecP256R1Field.Reduce32(c, S); + + c = Nat.ShiftUpBits(8, T, 3, 0, t1); + SecP256R1Field.Reduce32(c, t1); + + SecP256R1FieldElement X3 = new SecP256R1FieldElement(T); + SecP256R1Field.Square(M, X3.x); + SecP256R1Field.Subtract(X3.x, S, X3.x); + SecP256R1Field.Subtract(X3.x, S, X3.x); + + SecP256R1FieldElement Y3 = new SecP256R1FieldElement(S); + SecP256R1Field.Subtract(S, X3.x, Y3.x); + SecP256R1Field.Multiply(Y3.x, M, Y3.x); + SecP256R1Field.Subtract(Y3.x, t1, Y3.x); + + SecP256R1FieldElement Z3 = new SecP256R1FieldElement(M); + SecP256R1Field.Twice(Y1.x, Z3.x); + if (!Z1IsOne) + { + SecP256R1Field.Multiply(Z3.x, Z1.x, Z3.x); + } + + return new SecP256R1Point(curve, X3, Y3, new ECFieldElement[]{ Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this == b) + return ThreeTimes(); + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECFieldElement Y1 = this.RawYCoord; + if (Y1.IsZero) + return b; + + return Twice().Add(b); + } + + public override ECPoint ThreeTimes() + { + if (this.IsInfinity || this.RawYCoord.IsZero) + return this; + + // NOTE: Be careful about recursions between TwicePlus and ThreeTimes + return Twice().Add(this); + } + + public override ECPoint Negate() + { + if (IsInfinity) + return this; + + return new SecP256R1Point(Curve, RawXCoord, RawYCoord.Negate(), RawZCoords, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP384R1Curve.cs b/crypto/src/math/ec/custom/sec/SecP384R1Curve.cs new file mode 100644
index 000000000..7fd58276a --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP384R1Curve.cs
@@ -0,0 +1,77 @@ +using System; + +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP384R1Curve + : AbstractFpCurve + { + public static readonly BigInteger q = new BigInteger(1, + Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF")); + + private const int SecP384R1_DEFAULT_COORDS = COORD_JACOBIAN; + + protected readonly SecP384R1Point m_infinity; + + public SecP384R1Curve() + : base(q) + { + this.m_infinity = new SecP384R1Point(this, null, null); + + this.m_a = FromBigInteger(new BigInteger(1, + Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC"))); + this.m_b = FromBigInteger(new BigInteger(1, + Hex.Decode("B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF"))); + this.m_order = new BigInteger(1, Hex.Decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973")); + this.m_cofactor = BigInteger.One; + this.m_coord = SecP384R1_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecP384R1Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_JACOBIAN: + return true; + default: + return false; + } + } + + public virtual BigInteger Q + { + get { return q; } + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return q.BitLength; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecP384R1FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecP384R1Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecP384R1Point(this, x, y, zs, withCompression); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP384R1Field.cs b/crypto/src/math/ec/custom/sec/SecP384R1Field.cs new file mode 100644
index 000000000..508b01e3c --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP384R1Field.cs
@@ -0,0 +1,292 @@ +using System; +using System.Diagnostics; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP384R1Field + { + // 2^384 - 2^128 - 2^96 + 2^32 - 1 + internal static readonly uint[] P = new uint[]{ 0xFFFFFFFF, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + internal static readonly uint[] PExt = new uint[]{ 0x00000001, 0xFFFFFFFE, 0x00000000, 0x00000002, 0x00000000, 0xFFFFFFFE, + 0x00000000, 0x00000002, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFE, 0x00000001, 0x00000000, + 0xFFFFFFFE, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + private static readonly uint[] PExtInv = new uint[]{ 0xFFFFFFFF, 0x00000001, 0xFFFFFFFF, 0xFFFFFFFD, 0xFFFFFFFF, 0x00000001, + 0xFFFFFFFF, 0xFFFFFFFD, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000001, 0xFFFFFFFE, 0xFFFFFFFF, + 0x00000001, 0x00000002 }; + private const uint P11 = 0xFFFFFFFF; + private const uint PExt23 = 0xFFFFFFFF; + + public static void Add(uint[] x, uint[] y, uint[] z) + { + uint c = Nat.Add(12, x, y, z); + if (c != 0 || (z[11] == P11 && Nat.Gte(12, z, P))) + { + AddPInvTo(z); + } + } + + public static void AddExt(uint[] xx, uint[] yy, uint[] zz) + { + uint c = Nat.Add(24, xx, yy, zz); + if (c != 0 || (zz[23] == PExt23 && Nat.Gte(24, zz, PExt))) + { + if (Nat.AddTo(PExtInv.Length, PExtInv, zz) != 0) + { + Nat.IncAt(24, zz, PExtInv.Length); + } + } + } + + public static void AddOne(uint[] x, uint[] z) + { + uint c = Nat.Inc(12, x, z); + if (c != 0 || (z[11] == P11 && Nat.Gte(12, z, P))) + { + AddPInvTo(z); + } + } + + public static uint[] FromBigInteger(BigInteger x) + { + uint[] z = Nat.FromBigInteger(384, x); + if (z[11] == P11 && Nat.Gte(12, z, P)) + { + Nat.SubFrom(12, P, z); + } + return z; + } + + public static void Half(uint[] x, uint[] z) + { + if ((x[0] & 1) == 0) + { + Nat.ShiftDownBit(12, x, 0, z); + } + else + { + uint c = Nat.Add(12, x, P, z); + Nat.ShiftDownBit(12, z, c); + } + } + + public static void Multiply(uint[] x, uint[] y, uint[] z) + { + uint[] tt = Nat.Create(24); + Nat384.Mul(x, y, tt); + Reduce(tt, z); + } + + public static void Negate(uint[] x, uint[] z) + { + if (Nat.IsZero(12, x)) + { + Nat.Zero(12, z); + } + else + { + Nat.Sub(12, P, x, z); + } + } + + public static void Reduce(uint[] xx, uint[] z) + { + long xx16 = xx[16], xx17 = xx[17], xx18 = xx[18], xx19 = xx[19]; + long xx20 = xx[20], xx21 = xx[21], xx22 = xx[22], xx23 = xx[23]; + + const long n = 1; + + long t0 = (long)xx[12] + xx20 - n; + long t1 = (long)xx[13] + xx22; + long t2 = (long)xx[14] + xx22 + xx23; + long t3 = (long)xx[15] + xx23; + long t4 = xx17 + xx21; + long t5 = xx21 - xx23; + long t6 = xx22 - xx23; + + long cc = 0; + cc += (long)xx[0] + t0 + t5; + z[0] = (uint)cc; + cc >>= 32; + cc += (long)xx[1] + xx23 - t0 + t1; + z[1] = (uint)cc; + cc >>= 32; + cc += (long)xx[2] - xx21 - t1 + t2; + z[2] = (uint)cc; + cc >>= 32; + cc += (long)xx[3] + t0 - t2 + t3 + t5; + z[3] = (uint)cc; + cc >>= 32; + cc += (long)xx[4] + xx16 + xx21 + t0 + t1 - t3 + t5; + z[4] = (uint)cc; + cc >>= 32; + cc += (long)xx[5] - xx16 + t1 + t2 + t4; + z[5] = (uint)cc; + cc >>= 32; + cc += (long)xx[6] + xx18 - xx17 + t2 + t3; + z[6] = (uint)cc; + cc >>= 32; + cc += (long)xx[7] + xx16 + xx19 - xx18 + t3; + z[7] = (uint)cc; + cc >>= 32; + cc += (long)xx[8] + xx16 + xx17 + xx20 - xx19; + z[8] = (uint)cc; + cc >>= 32; + cc += (long)xx[9] + xx18 - xx20 + t4; + z[9] = (uint)cc; + cc >>= 32; + cc += (long)xx[10] + xx18 + xx19 - t5 + t6; + z[10] = (uint)cc; + cc >>= 32; + cc += (long)xx[11] + xx19 + xx20 - t6; + z[11] = (uint)cc; + cc >>= 32; + cc += n; + + Debug.Assert(cc >= 0); + + Reduce32((uint)cc, z); + } + + public static void Reduce32(uint x, uint[] z) + { + long cc = 0; + + if (x != 0) + { + long xx12 = x; + + cc += (long)z[0] + xx12; + z[0] = (uint)cc; + cc >>= 32; + cc += (long)z[1] - xx12; + z[1] = (uint)cc; + cc >>= 32; + if (cc != 0) + { + cc += (long)z[2]; + z[2] = (uint)cc; + cc >>= 32; + } + cc += (long)z[3] + xx12; + z[3] = (uint)cc; + cc >>= 32; + cc += (long)z[4] + xx12; + z[4] = (uint)cc; + cc >>= 32; + + Debug.Assert(cc == 0 || cc == 1); + } + + if ((cc != 0 && Nat.IncAt(12, z, 5) != 0) + || (z[11] == P11 && Nat.Gte(12, z, P))) + { + AddPInvTo(z); + } + } + + public static void Square(uint[] x, uint[] z) + { + uint[] tt = Nat.Create(24); + Nat384.Square(x, tt); + Reduce(tt, z); + } + + public static void SquareN(uint[] x, int n, uint[] z) + { + Debug.Assert(n > 0); + + uint[] tt = Nat.Create(24); + Nat384.Square(x, tt); + Reduce(tt, z); + + while (--n > 0) + { + Nat384.Square(z, tt); + Reduce(tt, z); + } + } + + public static void Subtract(uint[] x, uint[] y, uint[] z) + { + int c = Nat.Sub(12, x, y, z); + if (c != 0) + { + SubPInvFrom(z); + } + } + + public static void SubtractExt(uint[] xx, uint[] yy, uint[] zz) + { + int c = Nat.Sub(24, xx, yy, zz); + if (c != 0) + { + if (Nat.SubFrom(PExtInv.Length, PExtInv, zz) != 0) + { + Nat.DecAt(24, zz, PExtInv.Length); + } + } + } + + public static void Twice(uint[] x, uint[] z) + { + uint c = Nat.ShiftUpBit(12, x, 0, z); + if (c != 0 || (z[11] == P11 && Nat.Gte(12, z, P))) + { + AddPInvTo(z); + } + } + + private static void AddPInvTo(uint[] z) + { + long c = (long)z[0] + 1; + z[0] = (uint)c; + c >>= 32; + c += (long)z[1] - 1; + z[1] = (uint)c; + c >>= 32; + if (c != 0) + { + c += (long)z[2]; + z[2] = (uint)c; + c >>= 32; + } + c += (long)z[3] + 1; + z[3] = (uint)c; + c >>= 32; + c += (long)z[4] + 1; + z[4] = (uint)c; + c >>= 32; + if (c != 0) + { + Nat.IncAt(12, z, 5); + } + } + + private static void SubPInvFrom(uint[] z) + { + long c = (long)z[0] - 1; + z[0] = (uint)c; + c >>= 32; + c += (long)z[1] + 1; + z[1] = (uint)c; + c >>= 32; + if (c != 0) + { + c += (long)z[2]; + z[2] = (uint)c; + c >>= 32; + } + c += (long)z[3] - 1; + z[3] = (uint)c; + c >>= 32; + c += (long)z[4] - 1; + z[4] = (uint)c; + c >>= 32; + if (c != 0) + { + Nat.DecAt(12, z, 5); + } + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP384R1FieldElement.cs b/crypto/src/math/ec/custom/sec/SecP384R1FieldElement.cs new file mode 100644
index 000000000..40086978d --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP384R1FieldElement.cs
@@ -0,0 +1,209 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP384R1FieldElement + : ECFieldElement + { + public static readonly BigInteger Q = SecP384R1Curve.q; + + protected internal readonly uint[] x; + + public SecP384R1FieldElement(BigInteger x) + { + if (x == null || x.SignValue < 0 || x.CompareTo(Q) >= 0) + throw new ArgumentException("value invalid for SecP384R1FieldElement", "x"); + + this.x = SecP384R1Field.FromBigInteger(x); + } + + public SecP384R1FieldElement() + { + this.x = Nat.Create(12); + } + + protected internal SecP384R1FieldElement(uint[] x) + { + this.x = x; + } + + public override bool IsZero + { + get { return Nat.IsZero(12, x); } + } + + public override bool IsOne + { + get { return Nat.IsOne(12, x); } + } + + public override bool TestBitZero() + { + return Nat.GetBit(x, 0) == 1; + } + + public override BigInteger ToBigInteger() + { + return Nat.ToBigInteger(12, x); + } + + public override string FieldName + { + get { return "SecP384R1Field"; } + } + + public override int FieldSize + { + get { return Q.BitLength; } + } + + public override ECFieldElement Add(ECFieldElement b) + { + uint[] z = Nat.Create(12); + SecP384R1Field.Add(x, ((SecP384R1FieldElement)b).x, z); + return new SecP384R1FieldElement(z); + } + + public override ECFieldElement AddOne() + { + uint[] z = Nat.Create(12); + SecP384R1Field.AddOne(x, z); + return new SecP384R1FieldElement(z); + } + + public override ECFieldElement Subtract(ECFieldElement b) + { + uint[] z = Nat.Create(12); + SecP384R1Field.Subtract(x, ((SecP384R1FieldElement)b).x, z); + return new SecP384R1FieldElement(z); + } + + public override ECFieldElement Multiply(ECFieldElement b) + { + uint[] z = Nat.Create(12); + SecP384R1Field.Multiply(x, ((SecP384R1FieldElement)b).x, z); + return new SecP384R1FieldElement(z); + } + + public override ECFieldElement Divide(ECFieldElement b) + { + //return Multiply(b.Invert()); + uint[] z = Nat.Create(12); + Mod.Invert(SecP384R1Field.P, ((SecP384R1FieldElement)b).x, z); + SecP384R1Field.Multiply(z, x, z); + return new SecP384R1FieldElement(z); + } + + public override ECFieldElement Negate() + { + uint[] z = Nat.Create(12); + SecP384R1Field.Negate(x, z); + return new SecP384R1FieldElement(z); + } + + public override ECFieldElement Square() + { + uint[] z = Nat.Create(12); + SecP384R1Field.Square(x, z); + return new SecP384R1FieldElement(z); + } + + public override ECFieldElement Invert() + { + //return new SecP384R1FieldElement(ToBigInteger().ModInverse(Q)); + uint[] z = Nat.Create(12); + Mod.Invert(SecP384R1Field.P, x, z); + return new SecP384R1FieldElement(z); + } + + /** + * return a sqrt root - the routine verifies that the calculation returns the right value - if + * none exists it returns null. + */ + public override ECFieldElement Sqrt() + { + // Raise this element to the exponent 2^382 - 2^126 - 2^94 + 2^30 + + uint[] x1 = this.x; + if (Nat.IsZero(12, x1) || Nat.IsOne(12, x1)) + return this; + + uint[] t1 = Nat.Create(12); + uint[] t2 = Nat.Create(12); + uint[] t3 = Nat.Create(12); + uint[] t4 = Nat.Create(12); + + SecP384R1Field.Square(x1, t1); + SecP384R1Field.Multiply(t1, x1, t1); + + SecP384R1Field.SquareN(t1, 2, t2); + SecP384R1Field.Multiply(t2, t1, t2); + + SecP384R1Field.Square(t2, t2); + SecP384R1Field.Multiply(t2, x1, t2); + + SecP384R1Field.SquareN(t2, 5, t3); + SecP384R1Field.Multiply(t3, t2, t3); + + SecP384R1Field.SquareN(t3, 5, t4); + SecP384R1Field.Multiply(t4, t2, t4); + + SecP384R1Field.SquareN(t4, 15, t2); + SecP384R1Field.Multiply(t2, t4, t2); + + SecP384R1Field.SquareN(t2, 2, t3); + SecP384R1Field.Multiply(t1, t3, t1); + + SecP384R1Field.SquareN(t3, 28, t3); + SecP384R1Field.Multiply(t2, t3, t2); + + SecP384R1Field.SquareN(t2, 60, t3); + SecP384R1Field.Multiply(t3, t2, t3); + + uint[] r = t2; + + SecP384R1Field.SquareN(t3, 120, r); + SecP384R1Field.Multiply(r, t3, r); + + SecP384R1Field.SquareN(r, 15, r); + SecP384R1Field.Multiply(r, t4, r); + + SecP384R1Field.SquareN(r, 33, r); + SecP384R1Field.Multiply(r, t1, r); + + SecP384R1Field.SquareN(r, 64, r); + SecP384R1Field.Multiply(r, x1, r); + + SecP384R1Field.SquareN(r, 30, t1); + SecP384R1Field.Square(t1, t2); + + return Nat.Eq(12, x1, t2) ? new SecP384R1FieldElement(t1) : null; + } + + public override bool Equals(object obj) + { + return Equals(obj as SecP384R1FieldElement); + } + + public override bool Equals(ECFieldElement other) + { + return Equals(other as SecP384R1FieldElement); + } + + public virtual bool Equals(SecP384R1FieldElement other) + { + if (this == other) + return true; + if (null == other) + return false; + return Nat.Eq(12, x, other.x); + } + + public override int GetHashCode() + { + return Q.GetHashCode() ^ Arrays.GetHashCode(x, 0, 12); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP384R1Point.cs b/crypto/src/math/ec/custom/sec/SecP384R1Point.cs new file mode 100644
index 000000000..68c601611 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP384R1Point.cs
@@ -0,0 +1,278 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP384R1Point + : AbstractFpPoint + { + /** + * 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 + * + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecP384R1Point(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 + * + * @deprecated per-point compression property will be removed, refer + * {@link #getEncoded(bool)} + */ + public SecP384R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecP384R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecP384R1Point(null, AffineXCoord, AffineYCoord); + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + if (this == b) + return Twice(); + + ECCurve curve = this.Curve; + + SecP384R1FieldElement X1 = (SecP384R1FieldElement)this.RawXCoord, Y1 = (SecP384R1FieldElement)this.RawYCoord; + SecP384R1FieldElement X2 = (SecP384R1FieldElement)b.RawXCoord, Y2 = (SecP384R1FieldElement)b.RawYCoord; + + SecP384R1FieldElement Z1 = (SecP384R1FieldElement)this.RawZCoords[0]; + SecP384R1FieldElement Z2 = (SecP384R1FieldElement)b.RawZCoords[0]; + + uint c; + uint[] tt1 = Nat.Create(24); + uint[] tt2 = Nat.Create(24); + uint[] t3 = Nat.Create(12); + uint[] t4 = Nat.Create(12); + + bool Z1IsOne = Z1.IsOne; + uint[] U2, S2; + if (Z1IsOne) + { + U2 = X2.x; + S2 = Y2.x; + } + else + { + S2 = t3; + SecP384R1Field.Square(Z1.x, S2); + + U2 = tt2; + SecP384R1Field.Multiply(S2, X2.x, U2); + + SecP384R1Field.Multiply(S2, Z1.x, S2); + SecP384R1Field.Multiply(S2, Y2.x, S2); + } + + bool Z2IsOne = Z2.IsOne; + uint[] U1, S1; + if (Z2IsOne) + { + U1 = X1.x; + S1 = Y1.x; + } + else + { + S1 = t4; + SecP384R1Field.Square(Z2.x, S1); + + U1 = tt1; + SecP384R1Field.Multiply(S1, X1.x, U1); + + SecP384R1Field.Multiply(S1, Z2.x, S1); + SecP384R1Field.Multiply(S1, Y1.x, S1); + } + + uint[] H = Nat.Create(12); + SecP384R1Field.Subtract(U1, U2, H); + + uint[] R = Nat.Create(12); + SecP384R1Field.Subtract(S1, S2, R); + + // Check if b == this or b == -this + if (Nat.IsZero(12, H)) + { + if (Nat.IsZero(12, R)) + { + // this == b, i.e. this must be doubled + return this.Twice(); + } + + // this == -b, i.e. the result is the point at infinity + return curve.Infinity; + } + + uint[] HSquared = t3; + SecP384R1Field.Square(H, HSquared); + + uint[] G = Nat.Create(12); + SecP384R1Field.Multiply(HSquared, H, G); + + uint[] V = t3; + SecP384R1Field.Multiply(HSquared, U1, V); + + SecP384R1Field.Negate(G, G); + Nat384.Mul(S1, G, tt1); + + c = Nat.AddBothTo(12, V, V, G); + SecP384R1Field.Reduce32(c, G); + + SecP384R1FieldElement X3 = new SecP384R1FieldElement(t4); + SecP384R1Field.Square(R, X3.x); + SecP384R1Field.Subtract(X3.x, G, X3.x); + + SecP384R1FieldElement Y3 = new SecP384R1FieldElement(G); + SecP384R1Field.Subtract(V, X3.x, Y3.x); + Nat384.Mul(Y3.x, R, tt2); + SecP384R1Field.AddExt(tt1, tt2, tt1); + SecP384R1Field.Reduce(tt1, Y3.x); + + SecP384R1FieldElement Z3 = new SecP384R1FieldElement(H); + if (!Z1IsOne) + { + SecP384R1Field.Multiply(Z3.x, Z1.x, Z3.x); + } + if (!Z2IsOne) + { + SecP384R1Field.Multiply(Z3.x, Z2.x, Z3.x); + } + + ECFieldElement[] zs = new ECFieldElement[] { Z3 }; + + return new SecP384R1Point(curve, X3, Y3, zs, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + SecP384R1FieldElement Y1 = (SecP384R1FieldElement)this.RawYCoord; + if (Y1.IsZero) + return curve.Infinity; + + SecP384R1FieldElement X1 = (SecP384R1FieldElement)this.RawXCoord, Z1 = (SecP384R1FieldElement)this.RawZCoords[0]; + + uint c; + uint[] t1 = Nat.Create(12); + uint[] t2 = Nat.Create(12); + + uint[] Y1Squared = Nat.Create(12); + SecP384R1Field.Square(Y1.x, Y1Squared); + + uint[] T = Nat.Create(12); + SecP384R1Field.Square(Y1Squared, T); + + bool Z1IsOne = Z1.IsOne; + + uint[] Z1Squared = Z1.x; + if (!Z1IsOne) + { + Z1Squared = t2; + SecP384R1Field.Square(Z1.x, Z1Squared); + } + + SecP384R1Field.Subtract(X1.x, Z1Squared, t1); + + uint[] M = t2; + SecP384R1Field.Add(X1.x, Z1Squared, M); + SecP384R1Field.Multiply(M, t1, M); + c = Nat.AddBothTo(12, M, M, M); + SecP384R1Field.Reduce32(c, M); + + uint[] S = Y1Squared; + SecP384R1Field.Multiply(Y1Squared, X1.x, S); + c = Nat.ShiftUpBits(12, S, 2, 0); + SecP384R1Field.Reduce32(c, S); + + c = Nat.ShiftUpBits(12, T, 3, 0, t1); + SecP384R1Field.Reduce32(c, t1); + + SecP384R1FieldElement X3 = new SecP384R1FieldElement(T); + SecP384R1Field.Square(M, X3.x); + SecP384R1Field.Subtract(X3.x, S, X3.x); + SecP384R1Field.Subtract(X3.x, S, X3.x); + + SecP384R1FieldElement Y3 = new SecP384R1FieldElement(S); + SecP384R1Field.Subtract(S, X3.x, Y3.x); + SecP384R1Field.Multiply(Y3.x, M, Y3.x); + SecP384R1Field.Subtract(Y3.x, t1, Y3.x); + + SecP384R1FieldElement Z3 = new SecP384R1FieldElement(M); + SecP384R1Field.Twice(Y1.x, Z3.x); + if (!Z1IsOne) + { + SecP384R1Field.Multiply(Z3.x, Z1.x, Z3.x); + } + + return new SecP384R1Point(curve, X3, Y3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this == b) + return ThreeTimes(); + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECFieldElement Y1 = this.RawYCoord; + if (Y1.IsZero) + return b; + + return Twice().Add(b); + } + + public override ECPoint ThreeTimes() + { + if (this.IsInfinity || this.RawYCoord.IsZero) + return this; + + // NOTE: Be careful about recursions between TwicePlus and ThreeTimes + return Twice().Add(this); + } + + public override ECPoint Negate() + { + if (IsInfinity) + return this; + + return new SecP384R1Point(Curve, RawXCoord, RawYCoord.Negate(), RawZCoords, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP521R1Curve.cs b/crypto/src/math/ec/custom/sec/SecP521R1Curve.cs new file mode 100644
index 000000000..e5083c7f0 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP521R1Curve.cs
@@ -0,0 +1,77 @@ +using System; + +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP521R1Curve + : AbstractFpCurve + { + public static readonly BigInteger q = new BigInteger(1, + Hex.Decode("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")); + + private const int SecP521R1_DEFAULT_COORDS = COORD_JACOBIAN; + + protected readonly SecP521R1Point m_infinity; + + public SecP521R1Curve() + : base(q) + { + this.m_infinity = new SecP521R1Point(this, null, null); + + this.m_a = FromBigInteger(new BigInteger(1, + Hex.Decode("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC"))); + this.m_b = FromBigInteger(new BigInteger(1, + Hex.Decode("0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00"))); + this.m_order = new BigInteger(1, Hex.Decode("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409")); + this.m_cofactor = BigInteger.One; + this.m_coord = SecP521R1_DEFAULT_COORDS; + } + + protected override ECCurve CloneCurve() + { + return new SecP521R1Curve(); + } + + public override bool SupportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_JACOBIAN: + return true; + default: + return false; + } + } + + public virtual BigInteger Q + { + get { return q; } + } + + public override ECPoint Infinity + { + get { return m_infinity; } + } + + public override int FieldSize + { + get { return q.BitLength; } + } + + public override ECFieldElement FromBigInteger(BigInteger x) + { + return new SecP521R1FieldElement(x); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression) + { + return new SecP521R1Point(this, x, y, withCompression); + } + + protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + { + return new SecP521R1Point(this, x, y, zs, withCompression); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP521R1Field.cs b/crypto/src/math/ec/custom/sec/SecP521R1Field.cs new file mode 100644
index 000000000..3568156d8 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP521R1Field.cs
@@ -0,0 +1,153 @@ +using System; +using System.Diagnostics; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP521R1Field + { + // 2^521 - 1 + internal static readonly uint[] P = new uint[]{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x1FF }; + private const int P16 = 0x1FF; + + public static void Add(uint[] x, uint[] y, uint[] z) + { + uint c = Nat.Add(16, x, y, z) + x[16] + y[16]; + if (c > P16 || (c == P16 && Nat.Eq(16, z, P))) + { + c += Nat.Inc(16, z); + c &= P16; + } + z[16] = c; + } + + public static void AddOne(uint[] x, uint[] z) + { + uint c = Nat.Inc(16, x, z) + x[16]; + if (c > P16 || (c == P16 && Nat.Eq(16, z, P))) + { + c += Nat.Inc(16, z); + c &= P16; + } + z[16] = c; + } + + public static uint[] FromBigInteger(BigInteger x) + { + uint[] z = Nat.FromBigInteger(521, x); + if (Nat.Eq(17, z, P)) + { + Nat.Zero(17, z); + } + return z; + } + + public static void Half(uint[] x, uint[] z) + { + uint x16 = x[16]; + uint c = Nat.ShiftDownBit(16, x, x16, z); + z[16] = (x16 >> 1) | (c >> 23); + } + + public static void Multiply(uint[] x, uint[] y, uint[] z) + { + uint[] tt = Nat.Create(33); + ImplMultiply(x, y, tt); + Reduce(tt, z); + } + + public static void Negate(uint[] x, uint[] z) + { + if (Nat.IsZero(17, x)) + { + Nat.Zero(17, z); + } + else + { + Nat.Sub(17, P, x, z); + } + } + + public static void Reduce(uint[] xx, uint[] z) + { + Debug.Assert(xx[32] >> 18 == 0); + uint xx32 = xx[32]; + uint c = Nat.ShiftDownBits(16, xx, 16, 9, xx32, z, 0) >> 23; + c += xx32 >> 9; + c += Nat.AddTo(16, xx, z); + if (c > P16 || (c == P16 && Nat.Eq(16, z, P))) + { + c += Nat.Inc(16, z); + c &= P16; + } + z[16] = c; + } + + public static void Reduce23(uint[] z) + { + uint z16 = z[16]; + uint c = Nat.AddWordTo(16, z16 >> 9, z) + (z16 & P16); + if (c > P16 || (c == P16 && Nat.Eq(16, z, P))) + { + c += Nat.Inc(16, z); + c &= P16; + } + z[16] = c; + } + + public static void Square(uint[] x, uint[] z) + { + uint[] tt = Nat.Create(33); + ImplSquare(x, tt); + Reduce(tt, z); + } + + public static void SquareN(uint[] x, int n, uint[] z) + { + Debug.Assert(n > 0); + uint[] tt = Nat.Create(33); + ImplSquare(x, tt); + Reduce(tt, z); + + while (--n > 0) + { + ImplSquare(z, tt); + Reduce(tt, z); + } + } + + public static void Subtract(uint[] x, uint[] y, uint[] z) + { + int c = Nat.Sub(16, x, y, z) + (int)(x[16] - y[16]); + if (c < 0) + { + c += Nat.Dec(16, z); + c &= P16; + } + z[16] = (uint)c; + } + + public static void Twice(uint[] x, uint[] z) + { + uint x16 = x[16]; + uint c = Nat.ShiftUpBit(16, x, x16 << 23, z) | (x16 << 1); + z[16] = c & P16; + } + + protected static void ImplMultiply(uint[] x, uint[] y, uint[] zz) + { + Nat512.Mul(x, y, zz); + + uint x16 = x[16], y16 = y[16]; + zz[32] = Nat.Mul31BothAdd(16, x16, y, y16, x, zz, 16) + (x16 * y16); + } + + protected static void ImplSquare(uint[] x, uint[] zz) + { + Nat512.Square(x, zz); + + uint x16 = x[16]; + zz[32] = Nat.MulWordAddTo(16, x16 << 1, x, 0, zz, 16) + (x16 * x16); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP521R1FieldElement.cs b/crypto/src/math/ec/custom/sec/SecP521R1FieldElement.cs new file mode 100644
index 000000000..83a615928 --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP521R1FieldElement.cs
@@ -0,0 +1,166 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP521R1FieldElement + : ECFieldElement + { + public static readonly BigInteger Q = SecP521R1Curve.q; + + protected internal readonly uint[] x; + + public SecP521R1FieldElement(BigInteger x) + { + if (x == null || x.SignValue < 0 || x.CompareTo(Q) >= 0) + throw new ArgumentException("value invalid for SecP521R1FieldElement", "x"); + + this.x = SecP521R1Field.FromBigInteger(x); + } + + public SecP521R1FieldElement() + { + this.x = Nat.Create(17); + } + + protected internal SecP521R1FieldElement(uint[] x) + { + this.x = x; + } + + public override bool IsZero + { + get { return Nat.IsZero(17, x); } + } + + public override bool IsOne + { + get { return Nat.IsOne(17, x); } + } + + public override bool TestBitZero() + { + return Nat.GetBit(x, 0) == 1; + } + + public override BigInteger ToBigInteger() + { + return Nat.ToBigInteger(17, x); + } + + public override string FieldName + { + get { return "SecP521R1Field"; } + } + + public override int FieldSize + { + get { return Q.BitLength; } + } + + public override ECFieldElement Add(ECFieldElement b) + { + uint[] z = Nat.Create(17); + SecP521R1Field.Add(x, ((SecP521R1FieldElement)b).x, z); + return new SecP521R1FieldElement(z); + } + + public override ECFieldElement AddOne() + { + uint[] z = Nat.Create(17); + SecP521R1Field.AddOne(x, z); + return new SecP521R1FieldElement(z); + } + + public override ECFieldElement Subtract(ECFieldElement b) + { + uint[] z = Nat.Create(17); + SecP521R1Field.Subtract(x, ((SecP521R1FieldElement)b).x, z); + return new SecP521R1FieldElement(z); + } + + public override ECFieldElement Multiply(ECFieldElement b) + { + uint[] z = Nat.Create(17); + SecP521R1Field.Multiply(x, ((SecP521R1FieldElement)b).x, z); + return new SecP521R1FieldElement(z); + } + + public override ECFieldElement Divide(ECFieldElement b) + { + //return Multiply(b.Invert()); + uint[] z = Nat.Create(17); + Mod.Invert(SecP521R1Field.P, ((SecP521R1FieldElement)b).x, z); + SecP521R1Field.Multiply(z, x, z); + return new SecP521R1FieldElement(z); + } + + public override ECFieldElement Negate() + { + uint[] z = Nat.Create(17); + SecP521R1Field.Negate(x, z); + return new SecP521R1FieldElement(z); + } + + public override ECFieldElement Square() + { + uint[] z = Nat.Create(17); + SecP521R1Field.Square(x, z); + return new SecP521R1FieldElement(z); + } + + public override ECFieldElement Invert() + { + //return new SecP521R1FieldElement(ToBigInteger().ModInverse(Q)); + uint[] z = Nat.Create(17); + Mod.Invert(SecP521R1Field.P, x, z); + return new SecP521R1FieldElement(z); + } + + /** + * return a sqrt root - the routine verifies that the calculation returns the right value - if + * none exists it returns null. + */ + public override ECFieldElement Sqrt() + { + // Raise this element to the exponent 2^519 + + uint[] x1 = this.x; + if (Nat.IsZero(17, x1) || Nat.IsOne(17, x1)) + return this; + + uint[] t1 = Nat.Create(17); + uint[] t2 = Nat.Create(17); + + SecP521R1Field.SquareN(x1, 519, t1); + SecP521R1Field.Square(t1, t2); + + return Nat.Eq(17, x1, t2) ? new SecP521R1FieldElement(t1) : null; + } + + public override bool Equals(object obj) + { + return Equals(obj as SecP521R1FieldElement); + } + + public override bool Equals(ECFieldElement other) + { + return Equals(other as SecP521R1FieldElement); + } + + public virtual bool Equals(SecP521R1FieldElement other) + { + if (this == other) + return true; + if (null == other) + return false; + return Nat.Eq(17, x, other.x); + } + + public override int GetHashCode() + { + return Q.GetHashCode() ^ Arrays.GetHashCode(x, 0, 17); + } + } +} diff --git a/crypto/src/math/ec/custom/sec/SecP521R1Point.cs b/crypto/src/math/ec/custom/sec/SecP521R1Point.cs new file mode 100644
index 000000000..fb1996cfd --- /dev/null +++ b/crypto/src/math/ec/custom/sec/SecP521R1Point.cs
@@ -0,0 +1,273 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Custom.Sec +{ + internal class SecP521R1Point + : AbstractFpPoint + { + /** + * 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 + * + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecP521R1Point(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 + * + * @deprecated per-point compression property will be removed, refer + * {@link #getEncoded(bool)} + */ + public SecP521R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression) + : base(curve, x, y, withCompression) + { + if ((x == null) != (y == null)) + throw new ArgumentException("Exactly one of the field elements is null"); + } + + internal SecP521R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression) + : base(curve, x, y, zs, withCompression) + { + } + + protected override ECPoint Detach() + { + return new SecP521R1Point(null, AffineXCoord, AffineYCoord); + } + + public override ECPoint Add(ECPoint b) + { + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return this; + if (this == b) + return Twice(); + + ECCurve curve = this.Curve; + + SecP521R1FieldElement X1 = (SecP521R1FieldElement)this.RawXCoord, Y1 = (SecP521R1FieldElement)this.RawYCoord; + SecP521R1FieldElement X2 = (SecP521R1FieldElement)b.RawXCoord, Y2 = (SecP521R1FieldElement)b.RawYCoord; + + SecP521R1FieldElement Z1 = (SecP521R1FieldElement)this.RawZCoords[0]; + SecP521R1FieldElement Z2 = (SecP521R1FieldElement)b.RawZCoords[0]; + + uint[] t1 = Nat.Create(17); + uint[] t2 = Nat.Create(17); + uint[] t3 = Nat.Create(17); + uint[] t4 = Nat.Create(17); + + bool Z1IsOne = Z1.IsOne; + uint[] U2, S2; + if (Z1IsOne) + { + U2 = X2.x; + S2 = Y2.x; + } + else + { + S2 = t3; + SecP521R1Field.Square(Z1.x, S2); + + U2 = t2; + SecP521R1Field.Multiply(S2, X2.x, U2); + + SecP521R1Field.Multiply(S2, Z1.x, S2); + SecP521R1Field.Multiply(S2, Y2.x, S2); + } + + bool Z2IsOne = Z2.IsOne; + uint[] U1, S1; + if (Z2IsOne) + { + U1 = X1.x; + S1 = Y1.x; + } + else + { + S1 = t4; + SecP521R1Field.Square(Z2.x, S1); + + U1 = t1; + SecP521R1Field.Multiply(S1, X1.x, U1); + + SecP521R1Field.Multiply(S1, Z2.x, S1); + SecP521R1Field.Multiply(S1, Y1.x, S1); + } + + uint[] H = Nat.Create(17); + SecP521R1Field.Subtract(U1, U2, H); + + uint[] R = t2; + SecP521R1Field.Subtract(S1, S2, R); + + // Check if b == this or b == -this + if (Nat.IsZero(17, H)) + { + if (Nat.IsZero(17, R)) + { + // this == b, i.e. this must be doubled + return this.Twice(); + } + + // this == -b, i.e. the result is the point at infinity + return curve.Infinity; + } + + uint[] HSquared = t3; + SecP521R1Field.Square(H, HSquared); + + uint[] G = Nat.Create(17); + SecP521R1Field.Multiply(HSquared, H, G); + + uint[] V = t3; + SecP521R1Field.Multiply(HSquared, U1, V); + + SecP521R1Field.Multiply(S1, G, t1); + + SecP521R1FieldElement X3 = new SecP521R1FieldElement(t4); + SecP521R1Field.Square(R, X3.x); + SecP521R1Field.Add(X3.x, G, X3.x); + SecP521R1Field.Subtract(X3.x, V, X3.x); + SecP521R1Field.Subtract(X3.x, V, X3.x); + + SecP521R1FieldElement Y3 = new SecP521R1FieldElement(G); + SecP521R1Field.Subtract(V, X3.x, Y3.x); + SecP521R1Field.Multiply(Y3.x, R, t2); + SecP521R1Field.Subtract(t2, t1, Y3.x); + + SecP521R1FieldElement Z3 = new SecP521R1FieldElement(H); + if (!Z1IsOne) + { + SecP521R1Field.Multiply(Z3.x, Z1.x, Z3.x); + } + if (!Z2IsOne) + { + SecP521R1Field.Multiply(Z3.x, Z2.x, Z3.x); + } + + ECFieldElement[] zs = new ECFieldElement[] { Z3 }; + + return new SecP521R1Point(curve, X3, Y3, zs, IsCompressed); + } + + public override ECPoint Twice() + { + if (this.IsInfinity) + return this; + + ECCurve curve = this.Curve; + + SecP521R1FieldElement Y1 = (SecP521R1FieldElement)this.RawYCoord; + if (Y1.IsZero) + return curve.Infinity; + + SecP521R1FieldElement X1 = (SecP521R1FieldElement)this.RawXCoord, Z1 = (SecP521R1FieldElement)this.RawZCoords[0]; + + uint[] t1 = Nat.Create(17); + uint[] t2 = Nat.Create(17); + + uint[] Y1Squared = Nat.Create(17); + SecP521R1Field.Square(Y1.x, Y1Squared); + + uint[] T = Nat.Create(17); + SecP521R1Field.Square(Y1Squared, T); + + bool Z1IsOne = Z1.IsOne; + + uint[] Z1Squared = Z1.x; + if (!Z1IsOne) + { + Z1Squared = t2; + SecP521R1Field.Square(Z1.x, Z1Squared); + } + + SecP521R1Field.Subtract(X1.x, Z1Squared, t1); + + uint[] M = t2; + SecP521R1Field.Add(X1.x, Z1Squared, M); + SecP521R1Field.Multiply(M, t1, M); + Nat.AddBothTo(17, M, M, M); + SecP521R1Field.Reduce23(M); + + uint[] S = Y1Squared; + SecP521R1Field.Multiply(Y1Squared, X1.x, S); + Nat.ShiftUpBits(17, S, 2, 0); + SecP521R1Field.Reduce23(S); + + Nat.ShiftUpBits(17, T, 3, 0, t1); + SecP521R1Field.Reduce23(t1); + + SecP521R1FieldElement X3 = new SecP521R1FieldElement(T); + SecP521R1Field.Square(M, X3.x); + SecP521R1Field.Subtract(X3.x, S, X3.x); + SecP521R1Field.Subtract(X3.x, S, X3.x); + + SecP521R1FieldElement Y3 = new SecP521R1FieldElement(S); + SecP521R1Field.Subtract(S, X3.x, Y3.x); + SecP521R1Field.Multiply(Y3.x, M, Y3.x); + SecP521R1Field.Subtract(Y3.x, t1, Y3.x); + + SecP521R1FieldElement Z3 = new SecP521R1FieldElement(M); + SecP521R1Field.Twice(Y1.x, Z3.x); + if (!Z1IsOne) + { + SecP521R1Field.Multiply(Z3.x, Z1.x, Z3.x); + } + + return new SecP521R1Point(curve, X3, Y3, new ECFieldElement[] { Z3 }, IsCompressed); + } + + public override ECPoint TwicePlus(ECPoint b) + { + if (this == b) + return ThreeTimes(); + if (this.IsInfinity) + return b; + if (b.IsInfinity) + return Twice(); + + ECFieldElement Y1 = this.RawYCoord; + if (Y1.IsZero) + return b; + + return Twice().Add(b); + } + + public override ECPoint ThreeTimes() + { + if (this.IsInfinity || this.RawYCoord.IsZero) + return this; + + // NOTE: Be careful about recursions between TwicePlus and ThreeTimes + return Twice().Add(this); + } + + public override ECPoint Negate() + { + if (IsInfinity) + return this; + + return new SecP521R1Point(Curve, RawXCoord, RawYCoord.Negate(), RawZCoords, IsCompressed); + } + } +} diff --git a/crypto/src/math/ec/endo/ECEndomorphism.cs b/crypto/src/math/ec/endo/ECEndomorphism.cs new file mode 100644
index 000000000..dfb321368 --- /dev/null +++ b/crypto/src/math/ec/endo/ECEndomorphism.cs
@@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Endo +{ + public interface ECEndomorphism + { + ECPointMap PointMap { get; } + + bool HasEfficientPointMap { get; } + } +} diff --git a/crypto/src/math/ec/endo/GlvEndomorphism.cs b/crypto/src/math/ec/endo/GlvEndomorphism.cs new file mode 100644
index 000000000..f65bdd613 --- /dev/null +++ b/crypto/src/math/ec/endo/GlvEndomorphism.cs
@@ -0,0 +1,10 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Endo +{ + public interface GlvEndomorphism + : ECEndomorphism + { + BigInteger[] DecomposeScalar(BigInteger k); + } +} diff --git a/crypto/src/math/ec/endo/GlvTypeBEndomorphism.cs b/crypto/src/math/ec/endo/GlvTypeBEndomorphism.cs new file mode 100644
index 000000000..d234d88bf --- /dev/null +++ b/crypto/src/math/ec/endo/GlvTypeBEndomorphism.cs
@@ -0,0 +1,55 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Endo +{ + public class GlvTypeBEndomorphism + : GlvEndomorphism + { + protected readonly ECCurve m_curve; + protected readonly GlvTypeBParameters m_parameters; + protected readonly ECPointMap m_pointMap; + + public GlvTypeBEndomorphism(ECCurve curve, GlvTypeBParameters parameters) + { + this.m_curve = curve; + this.m_parameters = parameters; + this.m_pointMap = new ScaleXPointMap(curve.FromBigInteger(parameters.Beta)); + } + + public virtual BigInteger[] DecomposeScalar(BigInteger k) + { + int bits = m_parameters.Bits; + BigInteger b1 = CalculateB(k, m_parameters.G1, bits); + BigInteger b2 = CalculateB(k, m_parameters.G2, bits); + + BigInteger[] v1 = m_parameters.V1, v2 = m_parameters.V2; + BigInteger a = k.Subtract((b1.Multiply(v1[0])).Add(b2.Multiply(v2[0]))); + BigInteger b = (b1.Multiply(v1[1])).Add(b2.Multiply(v2[1])).Negate(); + + return new BigInteger[]{ a, b }; + } + + public virtual ECPointMap PointMap + { + get { return m_pointMap; } + } + + public virtual bool HasEfficientPointMap + { + get { return true; } + } + + protected virtual BigInteger CalculateB(BigInteger k, BigInteger g, int t) + { + bool negative = (g.SignValue < 0); + BigInteger b = k.Multiply(g.Abs()); + bool extra = b.TestBit(t - 1); + b = b.ShiftRight(t); + if (extra) + { + b = b.Add(BigInteger.One); + } + return negative ? b.Negate() : b; + } + } +} diff --git a/crypto/src/math/ec/endo/GlvTypeBParameters.cs b/crypto/src/math/ec/endo/GlvTypeBParameters.cs new file mode 100644
index 000000000..f93dfaf2b --- /dev/null +++ b/crypto/src/math/ec/endo/GlvTypeBParameters.cs
@@ -0,0 +1,60 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Endo +{ + public class GlvTypeBParameters + { + protected readonly BigInteger m_beta; + protected readonly BigInteger m_lambda; + protected readonly BigInteger[] m_v1, m_v2; + protected readonly BigInteger m_g1, m_g2; + protected readonly int m_bits; + + public GlvTypeBParameters(BigInteger beta, BigInteger lambda, BigInteger[] v1, BigInteger[] v2, + BigInteger g1, BigInteger g2, int bits) + { + this.m_beta = beta; + this.m_lambda = lambda; + this.m_v1 = v1; + this.m_v2 = v2; + this.m_g1 = g1; + this.m_g2 = g2; + this.m_bits = bits; + } + + public virtual BigInteger Beta + { + get { return m_beta; } + } + + public virtual BigInteger Lambda + { + get { return m_lambda; } + } + + public virtual BigInteger[] V1 + { + get { return m_v1; } + } + + public virtual BigInteger[] V2 + { + get { return m_v2; } + } + + public virtual BigInteger G1 + { + get { return m_g1; } + } + + public virtual BigInteger G2 + { + get { return m_g2; } + } + + public virtual int Bits + { + get { return m_bits; } + } + } +} diff --git a/crypto/src/math/ec/multiplier/AbstractECMultiplier.cs b/crypto/src/math/ec/multiplier/AbstractECMultiplier.cs new file mode 100644
index 000000000..517881323 --- /dev/null +++ b/crypto/src/math/ec/multiplier/AbstractECMultiplier.cs
@@ -0,0 +1,24 @@ +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + public abstract class AbstractECMultiplier + : ECMultiplier + { + public virtual ECPoint Multiply(ECPoint p, BigInteger k) + { + int sign = k.SignValue; + if (sign == 0 || p.IsInfinity) + return p.Curve.Infinity; + + ECPoint positive = MultiplyPositive(p, k.Abs()); + ECPoint result = sign > 0 ? positive : positive.Negate(); + + /* + * Although the various multipliers ought not to produce invalid output under normal + * circumstances, a final check here is advised to guard against fault attacks. + */ + return ECAlgorithms.ValidatePoint(result); + } + + protected abstract ECPoint MultiplyPositive(ECPoint p, BigInteger k); + } +} diff --git a/crypto/src/math/ec/multiplier/DoubleAddMultiplier.cs b/crypto/src/math/ec/multiplier/DoubleAddMultiplier.cs new file mode 100644
index 000000000..18a72c0a2 --- /dev/null +++ b/crypto/src/math/ec/multiplier/DoubleAddMultiplier.cs
@@ -0,0 +1,24 @@ +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + public class DoubleAddMultiplier + : AbstractECMultiplier + { + /** + * Joye's double-add algorithm. + */ + protected override ECPoint MultiplyPositive(ECPoint p, BigInteger k) + { + ECPoint[] R = new ECPoint[]{ p.Curve.Infinity, p }; + + int n = k.BitLength; + for (int i = 0; i < n; ++i) + { + int b = k.TestBit(i) ? 1 : 0; + int bp = 1 - b; + R[bp] = R[bp].TwicePlus(R[b]); + } + + return R[0]; + } + } +} diff --git a/crypto/src/math/ec/multiplier/ECMultiplier.cs b/crypto/src/math/ec/multiplier/ECMultiplier.cs
index c6d768ea8..8d6136b34 100644 --- a/crypto/src/math/ec/multiplier/ECMultiplier.cs +++ b/crypto/src/math/ec/multiplier/ECMultiplier.cs
@@ -1,18 +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); - } + /** + * Interface for classes encapsulating a point multiplication algorithm + * for <code>ECPoint</code>s. + */ + public 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> is multiplied. + * @return <code>p</code> multiplied by <code>k</code>. + */ + ECPoint Multiply(ECPoint p, BigInteger k); + } } diff --git a/crypto/src/math/ec/multiplier/FixedPointCombMultiplier.cs b/crypto/src/math/ec/multiplier/FixedPointCombMultiplier.cs new file mode 100644
index 000000000..a8ef5a77a --- /dev/null +++ b/crypto/src/math/ec/multiplier/FixedPointCombMultiplier.cs
@@ -0,0 +1,59 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + public class FixedPointCombMultiplier + : AbstractECMultiplier + { + protected override ECPoint MultiplyPositive(ECPoint p, BigInteger k) + { + ECCurve c = p.Curve; + int size = FixedPointUtilities.GetCombSize(c); + + if (k.BitLength > size) + { + /* + * TODO The comb works best when the scalars are less than the (possibly unknown) order. + * Still, if we want to handle larger scalars, we could allow customization of the comb + * size, or alternatively we could deal with the 'extra' bits either by running the comb + * multiple times as necessary, or by using an alternative multiplier as prelude. + */ + throw new InvalidOperationException("fixed-point comb doesn't support scalars larger than the curve order"); + } + + int minWidth = GetWidthForCombSize(size); + + FixedPointPreCompInfo info = FixedPointUtilities.Precompute(p, minWidth); + ECPoint[] lookupTable = info.PreComp; + int width = info.Width; + + int d = (size + width - 1) / width; + + ECPoint R = c.Infinity; + + int top = d * width - 1; + for (int i = 0; i < d; ++i) + { + int index = 0; + + for (int j = top - i; j >= 0; j -= d) + { + index <<= 1; + if (k.TestBit(j)) + { + index |= 1; + } + } + + R = R.TwicePlus(lookupTable[index]); + } + + return R; + } + + protected virtual int GetWidthForCombSize(int combSize) + { + return combSize > 257 ? 6 : 5; + } + } +} diff --git a/crypto/src/math/ec/multiplier/FixedPointPreCompInfo.cs b/crypto/src/math/ec/multiplier/FixedPointPreCompInfo.cs new file mode 100644
index 000000000..56a6326a1 --- /dev/null +++ b/crypto/src/math/ec/multiplier/FixedPointPreCompInfo.cs
@@ -0,0 +1,34 @@ +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + /** + * Class holding precomputation data for fixed-point multiplications. + */ + public class FixedPointPreCompInfo + : PreCompInfo + { + /** + * Array holding the precomputed <code>ECPoint</code>s used for a fixed + * point multiplication. + */ + protected ECPoint[] m_preComp = null; + + /** + * The width used for the precomputation. If a larger width precomputation + * is already available this may be larger than was requested, so calling + * code should refer to the actual width. + */ + protected int m_width = -1; + + public virtual ECPoint[] PreComp + { + get { return m_preComp; } + set { this.m_preComp = value; } + } + + public virtual int Width + { + get { return m_width; } + set { this.m_width = value; } + } + } +} diff --git a/crypto/src/math/ec/multiplier/FixedPointUtilities.cs b/crypto/src/math/ec/multiplier/FixedPointUtilities.cs new file mode 100644
index 000000000..d927d010b --- /dev/null +++ b/crypto/src/math/ec/multiplier/FixedPointUtilities.cs
@@ -0,0 +1,72 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + public class FixedPointUtilities + { + public static readonly string PRECOMP_NAME = "bc_fixed_point"; + + public static int GetCombSize(ECCurve c) + { + BigInteger order = c.Order; + return order == null ? c.FieldSize + 1 : order.BitLength; + } + + public static FixedPointPreCompInfo GetFixedPointPreCompInfo(PreCompInfo preCompInfo) + { + if ((preCompInfo != null) && (preCompInfo is FixedPointPreCompInfo)) + { + return (FixedPointPreCompInfo)preCompInfo; + } + + return new FixedPointPreCompInfo(); + } + + public static FixedPointPreCompInfo Precompute(ECPoint p, int minWidth) + { + ECCurve c = p.Curve; + + int n = 1 << minWidth; + FixedPointPreCompInfo info = GetFixedPointPreCompInfo(c.GetPreCompInfo(p, PRECOMP_NAME)); + ECPoint[] lookupTable = info.PreComp; + + if (lookupTable == null || lookupTable.Length < n) + { + int bits = GetCombSize(c); + int d = (bits + minWidth - 1) / minWidth; + + ECPoint[] pow2Table = new ECPoint[minWidth]; + pow2Table[0] = p; + for (int i = 1; i < minWidth; ++i) + { + pow2Table[i] = pow2Table[i - 1].TimesPow2(d); + } + + c.NormalizeAll(pow2Table); + + lookupTable = new ECPoint[n]; + lookupTable[0] = c.Infinity; + + for (int bit = minWidth - 1; bit >= 0; --bit) + { + ECPoint pow2 = pow2Table[bit]; + + int step = 1 << bit; + for (int i = step; i < n; i += (step << 1)) + { + lookupTable[i] = lookupTable[i - step].Add(pow2); + } + } + + c.NormalizeAll(lookupTable); + + info.PreComp = lookupTable; + info.Width = minWidth; + + c.SetPreCompInfo(p, PRECOMP_NAME, info); + } + + return info; + } + } +} diff --git a/crypto/src/math/ec/multiplier/GlvMultiplier.cs b/crypto/src/math/ec/multiplier/GlvMultiplier.cs new file mode 100644
index 000000000..f19049474 --- /dev/null +++ b/crypto/src/math/ec/multiplier/GlvMultiplier.cs
@@ -0,0 +1,40 @@ +using System; + +using Org.BouncyCastle.Math.EC.Endo; + +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + public class GlvMultiplier + : AbstractECMultiplier + { + protected readonly ECCurve curve; + protected readonly GlvEndomorphism glvEndomorphism; + + public GlvMultiplier(ECCurve curve, GlvEndomorphism glvEndomorphism) + { + if (curve == null || curve.Order == null) + throw new ArgumentException("Need curve with known group order", "curve"); + + this.curve = curve; + this.glvEndomorphism = glvEndomorphism; + } + + protected override ECPoint MultiplyPositive(ECPoint p, BigInteger k) + { + if (!curve.Equals(p.Curve)) + throw new InvalidOperationException(); + + BigInteger n = p.Curve.Order; + BigInteger[] ab = glvEndomorphism.DecomposeScalar(k.Mod(n)); + BigInteger a = ab[0], b = ab[1]; + + ECPointMap pointMap = glvEndomorphism.PointMap; + if (glvEndomorphism.HasEfficientPointMap) + { + return ECAlgorithms.ImplShamirsTrickWNaf(p, a, pointMap, b); + } + + return ECAlgorithms.ImplShamirsTrickWNaf(p, a, pointMap.Map(p), b); + } + } +} diff --git a/crypto/src/math/ec/multiplier/MixedNafR2LMultiplier.cs b/crypto/src/math/ec/multiplier/MixedNafR2LMultiplier.cs new file mode 100644
index 000000000..a4c201832 --- /dev/null +++ b/crypto/src/math/ec/multiplier/MixedNafR2LMultiplier.cs
@@ -0,0 +1,75 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + /** + * Class implementing the NAF (Non-Adjacent Form) multiplication algorithm (right-to-left) using + * mixed coordinates. + */ + public class MixedNafR2LMultiplier + : AbstractECMultiplier + { + protected readonly int additionCoord, doublingCoord; + + /** + * By default, addition will be done in Jacobian coordinates, and doubling will be done in + * Modified Jacobian coordinates (independent of the original coordinate system of each point). + */ + public MixedNafR2LMultiplier() + : this(ECCurve.COORD_JACOBIAN, ECCurve.COORD_JACOBIAN_MODIFIED) + { + } + + public MixedNafR2LMultiplier(int additionCoord, int doublingCoord) + { + this.additionCoord = additionCoord; + this.doublingCoord = doublingCoord; + } + + protected override ECPoint MultiplyPositive(ECPoint p, BigInteger k) + { + ECCurve curveOrig = p.Curve; + + ECCurve curveAdd = ConfigureCurve(curveOrig, additionCoord); + ECCurve curveDouble = ConfigureCurve(curveOrig, doublingCoord); + + int[] naf = WNafUtilities.GenerateCompactNaf(k); + + ECPoint Ra = curveAdd.Infinity; + ECPoint Td = curveDouble.ImportPoint(p); + + int zeroes = 0; + for (int i = 0; i < naf.Length; ++i) + { + int ni = naf[i]; + int digit = ni >> 16; + zeroes += ni & 0xFFFF; + + Td = Td.TimesPow2(zeroes); + + ECPoint Tj = curveAdd.ImportPoint(Td); + if (digit < 0) + { + Tj = Tj.Negate(); + } + + Ra = Ra.Add(Tj); + + zeroes = 1; + } + + return curveOrig.ImportPoint(Ra); + } + + protected virtual ECCurve ConfigureCurve(ECCurve c, int coord) + { + if (c.CoordinateSystem == coord) + return c; + + if (!c.SupportsCoordinateSystem(coord)) + throw new ArgumentException("Coordinate system " + coord + " not supported by this curve", "coord"); + + return c.Configure().SetCoordinateSystem(coord).Create(); + } + } +} diff --git a/crypto/src/math/ec/multiplier/MontgomeryLadderMultiplier.cs b/crypto/src/math/ec/multiplier/MontgomeryLadderMultiplier.cs new file mode 100644
index 000000000..e2470a383 --- /dev/null +++ b/crypto/src/math/ec/multiplier/MontgomeryLadderMultiplier.cs
@@ -0,0 +1,25 @@ +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + public class MontgomeryLadderMultiplier + : AbstractECMultiplier + { + /** + * Montgomery ladder. + */ + protected override ECPoint MultiplyPositive(ECPoint p, BigInteger k) + { + ECPoint[] R = new ECPoint[]{ p.Curve.Infinity, p }; + + int n = k.BitLength; + int i = n; + while (--i >= 0) + { + int b = k.TestBit(i) ? 1 : 0; + int bp = 1 - b; + R[bp] = R[bp].Add(R[b]); + R[b] = R[b].Twice(); + } + return R[0]; + } + } +} diff --git a/crypto/src/math/ec/multiplier/NafL2RMultiplier.cs b/crypto/src/math/ec/multiplier/NafL2RMultiplier.cs new file mode 100644
index 000000000..ac80cf905 --- /dev/null +++ b/crypto/src/math/ec/multiplier/NafL2RMultiplier.cs
@@ -0,0 +1,30 @@ +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + /** + * Class implementing the NAF (Non-Adjacent Form) multiplication algorithm (left-to-right). + */ + public class NafL2RMultiplier + : AbstractECMultiplier + { + protected override ECPoint MultiplyPositive(ECPoint p, BigInteger k) + { + int[] naf = WNafUtilities.GenerateCompactNaf(k); + + ECPoint addP = p.Normalize(), subP = addP.Negate(); + + ECPoint R = p.Curve.Infinity; + + int i = naf.Length; + while (--i >= 0) + { + int ni = naf[i]; + int digit = ni >> 16, zeroes = ni & 0xFFFF; + + R = R.TwicePlus(digit < 0 ? subP : addP); + R = R.TimesPow2(zeroes); + } + + return R; + } + } +} diff --git a/crypto/src/math/ec/multiplier/NafR2LMultiplier.cs b/crypto/src/math/ec/multiplier/NafR2LMultiplier.cs new file mode 100644
index 000000000..1fa69fae8 --- /dev/null +++ b/crypto/src/math/ec/multiplier/NafR2LMultiplier.cs
@@ -0,0 +1,31 @@ +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + /** + * Class implementing the NAF (Non-Adjacent Form) multiplication algorithm (right-to-left). + */ + public class NafR2LMultiplier + : AbstractECMultiplier + { + protected override ECPoint MultiplyPositive(ECPoint p, BigInteger k) + { + int[] naf = WNafUtilities.GenerateCompactNaf(k); + + ECPoint R0 = p.Curve.Infinity, R1 = p; + + int zeroes = 0; + for (int i = 0; i < naf.Length; ++i) + { + int ni = naf[i]; + int digit = ni >> 16; + zeroes += ni & 0xFFFF; + + R1 = R1.TimesPow2(zeroes); + R0 = R0.Add(digit < 0 ? R1.Negate() : R1); + + zeroes = 1; + } + + return R0; + } + } +} diff --git a/crypto/src/math/ec/multiplier/PreCompInfo.cs b/crypto/src/math/ec/multiplier/PreCompInfo.cs
index d379508c8..5c3289286 100644 --- a/crypto/src/math/ec/multiplier/PreCompInfo.cs +++ b/crypto/src/math/ec/multiplier/PreCompInfo.cs
@@ -5,7 +5,7 @@ namespace Org.BouncyCastle.Math.EC.Multiplier * algorithms. Used as a Memento (see GOF patterns) for * <code>WNafMultiplier</code>. */ - internal interface PreCompInfo + public interface PreCompInfo { } } diff --git a/crypto/src/math/ec/multiplier/ReferenceMultiplier.cs b/crypto/src/math/ec/multiplier/ReferenceMultiplier.cs
index cdccffc2d..4848ada39 100644 --- a/crypto/src/math/ec/multiplier/ReferenceMultiplier.cs +++ b/crypto/src/math/ec/multiplier/ReferenceMultiplier.cs
@@ -1,30 +1,11 @@ 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; - } - } + public class ReferenceMultiplier + : AbstractECMultiplier + { + protected override ECPoint MultiplyPositive(ECPoint p, BigInteger k) + { + return ECAlgorithms.ReferenceMultiply(p, k); + } + } } diff --git a/crypto/src/math/ec/multiplier/WNafL2RMultiplier.cs b/crypto/src/math/ec/multiplier/WNafL2RMultiplier.cs new file mode 100644
index 000000000..f671f6a5c --- /dev/null +++ b/crypto/src/math/ec/multiplier/WNafL2RMultiplier.cs
@@ -0,0 +1,98 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + /** + * Class implementing the WNAF (Window Non-Adjacent Form) multiplication + * algorithm. + */ + public class WNafL2RMultiplier + : AbstractECMultiplier + { + /** + * 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>. + */ + protected override ECPoint MultiplyPositive(ECPoint p, BigInteger k) + { + // Clamp the window width in the range [2, 16] + int width = System.Math.Max(2, System.Math.Min(16, GetWindowSize(k.BitLength))); + + WNafPreCompInfo wnafPreCompInfo = WNafUtilities.Precompute(p, width, true); + ECPoint[] preComp = wnafPreCompInfo.PreComp; + ECPoint[] preCompNeg = wnafPreCompInfo.PreCompNeg; + + int[] wnaf = WNafUtilities.GenerateCompactWindowNaf(width, k); + + ECPoint R = p.Curve.Infinity; + + int i = wnaf.Length; + + /* + * NOTE: We try to optimize the first window using the precomputed points to substitute an + * addition for 2 or more doublings. + */ + if (i > 1) + { + int wi = wnaf[--i]; + int digit = wi >> 16, zeroes = wi & 0xFFFF; + + int n = System.Math.Abs(digit); + ECPoint[] table = digit < 0 ? preCompNeg : preComp; + + // Optimization can only be used for values in the lower half of the table + if ((n << 2) < (1 << width)) + { + int highest = LongArray.BitLengths[n]; + + // TODO Get addition/doubling cost ratio from curve and compare to 'scale' to see if worth substituting? + int scale = width - highest; + int lowBits = n ^ (1 << (highest - 1)); + + int i1 = ((1 << (width - 1)) - 1); + int i2 = (lowBits << scale) + 1; + R = table[i1 >> 1].Add(table[i2 >> 1]); + + zeroes -= scale; + + //Console.WriteLine("Optimized: 2^" + scale + " * " + n + " = " + i1 + " + " + i2); + } + else + { + R = table[n >> 1]; + } + + R = R.TimesPow2(zeroes); + } + + while (i > 0) + { + int wi = wnaf[--i]; + int digit = wi >> 16, zeroes = wi & 0xFFFF; + + int n = System.Math.Abs(digit); + ECPoint[] table = digit < 0 ? preCompNeg : preComp; + ECPoint r = table[n >> 1]; + + R = R.TwicePlus(r); + R = R.TimesPow2(zeroes); + } + + return R; + } + + /** + * Determine window width to use for a scalar multiplication of the given size. + * + * @param bits the bit-length of the scalar to multiply by + * @return the window size to use + */ + protected virtual int GetWindowSize(int bits) + { + return WNafUtilities.GetWindowSize(bits); + } + } +} diff --git a/crypto/src/math/ec/multiplier/WNafPreCompInfo.cs b/crypto/src/math/ec/multiplier/WNafPreCompInfo.cs
index d9305dace..7e0a73154 100644 --- a/crypto/src/math/ec/multiplier/WNafPreCompInfo.cs +++ b/crypto/src/math/ec/multiplier/WNafPreCompInfo.cs
@@ -1,46 +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; + /** + * Class holding precomputation data for the WNAF (Window Non-Adjacent Form) + * algorithm. + */ + public class WNafPreCompInfo + : PreCompInfo + { + /** + * Array holding the precomputed <code>ECPoint</code>s used for a Window + * NAF multiplication. + */ + protected ECPoint[] m_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; + /** + * Array holding the negations of the precomputed <code>ECPoint</code>s used + * for a Window NAF multiplication. + */ + protected ECPoint[] m_preCompNeg = null; - internal ECPoint[] GetPreComp() - { - return preComp; - } + /** + * Holds an <code>ECPoint</code> representing Twice(this). Used for the + * Window NAF multiplication to create or extend the precomputed values. + */ + protected ECPoint m_twice = null; - internal void SetPreComp(ECPoint[] preComp) - { - this.preComp = preComp; - } + public virtual ECPoint[] PreComp + { + get { return m_preComp; } + set { this.m_preComp = value; } + } - internal ECPoint GetTwiceP() - { - return twiceP; - } + public virtual ECPoint[] PreCompNeg + { + get { return m_preCompNeg; } + set { this.m_preCompNeg = value; } + } - internal void SetTwiceP(ECPoint twiceThis) - { - this.twiceP = twiceThis; - } - } + public virtual ECPoint Twice + { + get { return m_twice; } + set { this.m_twice = value; } + } + } } diff --git a/crypto/src/math/ec/multiplier/WNafUtilities.cs b/crypto/src/math/ec/multiplier/WNafUtilities.cs new file mode 100644
index 000000000..865b9073e --- /dev/null +++ b/crypto/src/math/ec/multiplier/WNafUtilities.cs
@@ -0,0 +1,469 @@ +using System; + +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + public abstract class WNafUtilities + { + public static readonly string PRECOMP_NAME = "bc_wnaf"; + + private static readonly int[] DEFAULT_WINDOW_SIZE_CUTOFFS = new int[]{ 13, 41, 121, 337, 897, 2305 }; + + private static readonly byte[] EMPTY_BYTES = new byte[0]; + private static readonly int[] EMPTY_INTS = new int[0]; + + public static int[] GenerateCompactNaf(BigInteger k) + { + if ((k.BitLength >> 16) != 0) + throw new ArgumentException("must have bitlength < 2^16", "k"); + if (k.SignValue == 0) + return EMPTY_INTS; + + BigInteger _3k = k.ShiftLeft(1).Add(k); + + int bits = _3k.BitLength; + int[] naf = new int[bits >> 1]; + + BigInteger diff = _3k.Xor(k); + + int highBit = bits - 1, length = 0, zeroes = 0; + for (int i = 1; i < highBit; ++i) + { + if (!diff.TestBit(i)) + { + ++zeroes; + continue; + } + + int digit = k.TestBit(i) ? -1 : 1; + naf[length++] = (digit << 16) | zeroes; + zeroes = 1; + ++i; + } + + naf[length++] = (1 << 16) | zeroes; + + if (naf.Length > length) + { + naf = Trim(naf, length); + } + + return naf; + } + + public static int[] GenerateCompactWindowNaf(int width, BigInteger k) + { + if (width == 2) + { + return GenerateCompactNaf(k); + } + + if (width < 2 || width > 16) + throw new ArgumentException("must be in the range [2, 16]", "width"); + if ((k.BitLength >> 16) != 0) + throw new ArgumentException("must have bitlength < 2^16", "k"); + if (k.SignValue == 0) + return EMPTY_INTS; + + int[] wnaf = new int[k.BitLength / width + 1]; + + // 2^width and a mask and sign bit set accordingly + int pow2 = 1 << width; + int mask = pow2 - 1; + int sign = pow2 >> 1; + + bool carry = false; + int length = 0, pos = 0; + + while (pos <= k.BitLength) + { + if (k.TestBit(pos) == carry) + { + ++pos; + continue; + } + + k = k.ShiftRight(pos); + + int digit = k.IntValue & mask; + if (carry) + { + ++digit; + } + + carry = (digit & sign) != 0; + if (carry) + { + digit -= pow2; + } + + int zeroes = length > 0 ? pos - 1 : pos; + wnaf[length++] = (digit << 16) | zeroes; + pos = width; + } + + // Reduce the WNAF array to its actual length + if (wnaf.Length > length) + { + wnaf = Trim(wnaf, length); + } + + return wnaf; + } + + public static byte[] GenerateJsf(BigInteger g, BigInteger h) + { + int digits = System.Math.Max(g.BitLength, h.BitLength) + 1; + byte[] jsf = new byte[digits]; + + BigInteger k0 = g, k1 = h; + int j = 0, d0 = 0, d1 = 0; + + int offset = 0; + while ((d0 | d1) != 0 || k0.BitLength > offset || k1.BitLength > offset) + { + int n0 = ((int)((uint)k0.IntValue >> offset) + d0) & 7; + int n1 = ((int)((uint)k1.IntValue >> offset) + d1) & 7; + + int u0 = n0 & 1; + if (u0 != 0) + { + u0 -= (n0 & 2); + if ((n0 + u0) == 4 && (n1 & 3) == 2) + { + u0 = -u0; + } + } + + int u1 = n1 & 1; + if (u1 != 0) + { + u1 -= (n1 & 2); + if ((n1 + u1) == 4 && (n0 & 3) == 2) + { + u1 = -u1; + } + } + + if ((d0 << 1) == 1 + u0) + { + d0 ^= 1; + } + if ((d1 << 1) == 1 + u1) + { + d1 ^= 1; + } + + if (++offset == 30) + { + offset = 0; + k0 = k0.ShiftRight(30); + k1 = k1.ShiftRight(30); + } + + jsf[j++] = (byte)((u0 << 4) | (u1 & 0xF)); + } + + // Reduce the JSF array to its actual length + if (jsf.Length > j) + { + jsf = Trim(jsf, j); + } + + return jsf; + } + + public static byte[] GenerateNaf(BigInteger k) + { + if (k.SignValue == 0) + return EMPTY_BYTES; + + BigInteger _3k = k.ShiftLeft(1).Add(k); + + int digits = _3k.BitLength - 1; + byte[] naf = new byte[digits]; + + BigInteger diff = _3k.Xor(k); + + for (int i = 1; i < digits; ++i) + { + if (diff.TestBit(i)) + { + naf[i - 1] = (byte)(k.TestBit(i) ? -1 : 1); + ++i; + } + } + + naf[digits - 1] = 1; + + return naf; + } + + /** + * 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 = &amp;sum;<sub>i=0</sub><sup>l-1</sup> k<sub>i</sub>2<sup>i</sup> + * </code>, where the <code>k<sub>i</sub></code> denote the elements of the + * returned <code>byte[]</code>. + */ + public static byte[] GenerateWindowNaf(int width, BigInteger k) + { + if (width == 2) + { + return GenerateNaf(k); + } + + if (width < 2 || width > 8) + throw new ArgumentException("must be in the range [2, 8]", "width"); + if (k.SignValue == 0) + return EMPTY_BYTES; + + byte[] wnaf = new byte[k.BitLength + 1]; + + // 2^width and a mask and sign bit set accordingly + int pow2 = 1 << width; + int mask = pow2 - 1; + int sign = pow2 >> 1; + + bool carry = false; + int length = 0, pos = 0; + + while (pos <= k.BitLength) + { + if (k.TestBit(pos) == carry) + { + ++pos; + continue; + } + + k = k.ShiftRight(pos); + + int digit = k.IntValue & mask; + if (carry) + { + ++digit; + } + + carry = (digit & sign) != 0; + if (carry) + { + digit -= pow2; + } + + length += (length > 0) ? pos - 1 : pos; + wnaf[length++] = (byte)digit; + pos = width; + } + + // Reduce the WNAF array to its actual length + if (wnaf.Length > length) + { + wnaf = Trim(wnaf, length); + } + + return wnaf; + } + + public static int GetNafWeight(BigInteger k) + { + if (k.SignValue == 0) + return 0; + + BigInteger _3k = k.ShiftLeft(1).Add(k); + BigInteger diff = _3k.Xor(k); + + return diff.BitCount; + } + + public static WNafPreCompInfo GetWNafPreCompInfo(ECPoint p) + { + return GetWNafPreCompInfo(p.Curve.GetPreCompInfo(p, PRECOMP_NAME)); + } + + public static WNafPreCompInfo GetWNafPreCompInfo(PreCompInfo preCompInfo) + { + if ((preCompInfo != null) && (preCompInfo is WNafPreCompInfo)) + { + return (WNafPreCompInfo)preCompInfo; + } + + return new WNafPreCompInfo(); + } + + /** + * Determine window width to use for a scalar multiplication of the given size. + * + * @param bits the bit-length of the scalar to multiply by + * @return the window size to use + */ + public static int GetWindowSize(int bits) + { + return GetWindowSize(bits, DEFAULT_WINDOW_SIZE_CUTOFFS); + } + + /** + * Determine window width to use for a scalar multiplication of the given size. + * + * @param bits the bit-length of the scalar to multiply by + * @param windowSizeCutoffs a monotonically increasing list of bit sizes at which to increment the window width + * @return the window size to use + */ + public static int GetWindowSize(int bits, int[] windowSizeCutoffs) + { + int w = 0; + for (; w < windowSizeCutoffs.Length; ++w) + { + if (bits < windowSizeCutoffs[w]) + { + break; + } + } + return w + 2; + } + + public static ECPoint MapPointWithPrecomp(ECPoint p, int width, bool includeNegated, + ECPointMap pointMap) + { + ECCurve c = p.Curve; + WNafPreCompInfo wnafPreCompP = Precompute(p, width, includeNegated); + + ECPoint q = pointMap.Map(p); + WNafPreCompInfo wnafPreCompQ = GetWNafPreCompInfo(c.GetPreCompInfo(q, PRECOMP_NAME)); + + ECPoint twiceP = wnafPreCompP.Twice; + if (twiceP != null) + { + ECPoint twiceQ = pointMap.Map(twiceP); + wnafPreCompQ.Twice = twiceQ; + } + + ECPoint[] preCompP = wnafPreCompP.PreComp; + ECPoint[] preCompQ = new ECPoint[preCompP.Length]; + for (int i = 0; i < preCompP.Length; ++i) + { + preCompQ[i] = pointMap.Map(preCompP[i]); + } + wnafPreCompQ.PreComp = preCompQ; + + if (includeNegated) + { + ECPoint[] preCompNegQ = new ECPoint[preCompQ.Length]; + for (int i = 0; i < preCompNegQ.Length; ++i) + { + preCompNegQ[i] = preCompQ[i].Negate(); + } + wnafPreCompQ.PreCompNeg = preCompNegQ; + } + + c.SetPreCompInfo(q, PRECOMP_NAME, wnafPreCompQ); + + return q; + } + + public static WNafPreCompInfo Precompute(ECPoint p, int width, bool includeNegated) + { + ECCurve c = p.Curve; + WNafPreCompInfo wnafPreCompInfo = GetWNafPreCompInfo(c.GetPreCompInfo(p, PRECOMP_NAME)); + + ECPoint[] preComp = wnafPreCompInfo.PreComp; + if (preComp == null) + { + preComp = new ECPoint[]{ p }; + } + + int preCompLen = preComp.Length; + int reqPreCompLen = 1 << System.Math.Max(0, width - 2); + + if (preCompLen < reqPreCompLen) + { + preComp = ResizeTable(preComp, reqPreCompLen); + if (reqPreCompLen == 2) + { + preComp[1] = preComp[0].ThreeTimes(); + } + else + { + ECPoint twiceP = wnafPreCompInfo.Twice; + if (twiceP == null) + { + twiceP = preComp[0].Twice(); + wnafPreCompInfo.Twice = twiceP; + } + + 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]); + } + } + + /* + * Having oft-used operands in affine form makes operations faster. + */ + c.NormalizeAll(preComp); + } + + wnafPreCompInfo.PreComp = preComp; + + if (includeNegated) + { + ECPoint[] preCompNeg = wnafPreCompInfo.PreCompNeg; + + int pos; + if (preCompNeg == null) + { + pos = 0; + preCompNeg = new ECPoint[reqPreCompLen]; + } + else + { + pos = preCompNeg.Length; + if (pos < reqPreCompLen) + { + preCompNeg = ResizeTable(preCompNeg, reqPreCompLen); + } + } + + while (pos < reqPreCompLen) + { + preCompNeg[pos] = preComp[pos].Negate(); + ++pos; + } + + wnafPreCompInfo.PreCompNeg = preCompNeg; + } + + c.SetPreCompInfo(p, PRECOMP_NAME, wnafPreCompInfo); + + return wnafPreCompInfo; + } + + private static byte[] Trim(byte[] a, int length) + { + byte[] result = new byte[length]; + Array.Copy(a, 0, result, 0, result.Length); + return result; + } + + private static int[] Trim(int[] a, int length) + { + int[] result = new int[length]; + Array.Copy(a, 0, result, 0, result.Length); + return result; + } + + private static ECPoint[] ResizeTable(ECPoint[] a, int length) + { + ECPoint[] result = new ECPoint[length]; + Array.Copy(a, 0, result, 0, a.Length); + return result; + } + } +} diff --git a/crypto/src/math/ec/multiplier/WTauNafMultiplier.cs b/crypto/src/math/ec/multiplier/WTauNafMultiplier.cs
index f1a605770..dda778eea 100644 --- a/crypto/src/math/ec/multiplier/WTauNafMultiplier.cs +++ b/crypto/src/math/ec/multiplier/WTauNafMultiplier.cs
@@ -4,117 +4,113 @@ 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"); + /** + * Class implementing the WTNAF (Window + * <code>&#964;</code>-adic Non-Adjacent Form) algorithm. + */ + public class WTauNafMultiplier + : AbstractECMultiplier + { + // TODO Create WTauNafUtilities class and move various functionality into it + internal static readonly string PRECOMP_NAME = "bc_wtnaf"; - F2mPoint p = (F2mPoint)point; + /** + * 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>. + */ + protected override ECPoint MultiplyPositive(ECPoint point, BigInteger k) + { + if (!(point is F2mPoint)) + throw new ArgumentException("Only F2mPoint can be used in WTauNafMultiplier"); - F2mCurve curve = (F2mCurve) p.Curve; - int m = curve.M; - sbyte a = (sbyte) curve.A.ToBigInteger().IntValue; - sbyte mu = curve.GetMu(); - BigInteger[] s = curve.GetSi(); + 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); + ZTauElement rho = Tnaf.PartModReduction(k, m, a, s, mu, (sbyte)10); - return MultiplyWTnaf(p, rho, preCompInfo, a, mu); - } + return MultiplyWTnaf(p, rho, curve.GetPreCompInfo(p, PRECOMP_NAME), 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; - } + /** + * 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 = (a == 0) ? Tnaf.Alpha0 : Tnaf.Alpha1; - BigInteger tw = Tnaf.GetTw(mu, Tnaf.Width); + BigInteger tw = Tnaf.GetTw(mu, Tnaf.Width); - sbyte[]u = Tnaf.TauAdicWNaf(mu, lambda, Tnaf.Width, - BigInteger.ValueOf(Tnaf.Pow2Width), tw, alpha); + 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; + 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(); - } + F2mPoint[] pu; + if ((preCompInfo == null) || !(preCompInfo is WTauNafPreCompInfo)) + { + pu = Tnaf.GetPreComp(p, a); - // 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]]); - } - } - } + WTauNafPreCompInfo pre = new WTauNafPreCompInfo(); + pre.PreComp = pu; + curve.SetPreCompInfo(p, PRECOMP_NAME, pre); + } + else + { + pu = ((WTauNafPreCompInfo)preCompInfo).PreComp; + } - return q; - } - } + // q = infinity + F2mPoint q = (F2mPoint)curve.Infinity; + for (int i = u.Length - 1; i >= 0; i--) + { + q = Tnaf.Tau(q); + sbyte ui = u[i]; + if (ui != 0) + { + if (ui > 0) + { + q = q.AddSimple(pu[ui]); + } + else + { + // u[i] < 0 + q = q.SubtractSimple(pu[-ui]); + } + } + } + + return q; + } + } } diff --git a/crypto/src/math/ec/multiplier/WTauNafPreCompInfo.cs b/crypto/src/math/ec/multiplier/WTauNafPreCompInfo.cs
index cede4a05d..3c18404c0 100644 --- a/crypto/src/math/ec/multiplier/WTauNafPreCompInfo.cs +++ b/crypto/src/math/ec/multiplier/WTauNafPreCompInfo.cs
@@ -1,41 +1,24 @@ 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; + /** + * Class holding precomputation data for the WTNAF (Window + * <code>&#964;</code>-adic Non-Adjacent Form) algorithm. + */ + public 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>. + */ + protected F2mPoint[] m_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; - } - } + public virtual F2mPoint[] PreComp + { + get { return m_preComp; } + set { this.m_preComp = value; } + } + } } diff --git a/crypto/src/math/ec/multiplier/ZSignedDigitL2RMultiplier.cs b/crypto/src/math/ec/multiplier/ZSignedDigitL2RMultiplier.cs new file mode 100644
index 000000000..554ac61b3 --- /dev/null +++ b/crypto/src/math/ec/multiplier/ZSignedDigitL2RMultiplier.cs
@@ -0,0 +1,29 @@ +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + public class ZSignedDigitL2RMultiplier + : AbstractECMultiplier + { + /** + * 'Zeroless' Signed Digit Left-to-Right. + */ + protected override ECPoint MultiplyPositive(ECPoint p, BigInteger k) + { + ECPoint addP = p.Normalize(), subP = addP.Negate(); + + ECPoint R0 = addP; + + int n = k.BitLength; + int s = k.GetLowestSetBit(); + + int i = n; + while (--i > s) + { + R0 = R0.TwicePlus(k.TestBit(i) ? addP : subP); + } + + R0 = R0.TimesPow2(s); + + return R0; + } + } +} diff --git a/crypto/src/math/ec/multiplier/ZSignedDigitR2LMultiplier.cs b/crypto/src/math/ec/multiplier/ZSignedDigitR2LMultiplier.cs new file mode 100644
index 000000000..91c06cbb8 --- /dev/null +++ b/crypto/src/math/ec/multiplier/ZSignedDigitR2LMultiplier.cs
@@ -0,0 +1,30 @@ +namespace Org.BouncyCastle.Math.EC.Multiplier +{ + public class ZSignedDigitR2LMultiplier + : AbstractECMultiplier + { + /** + * 'Zeroless' Signed Digit Right-to-Left. + */ + protected override ECPoint MultiplyPositive(ECPoint p, BigInteger k) + { + ECPoint R0 = p.Curve.Infinity, R1 = p; + + int n = k.BitLength; + int s = k.GetLowestSetBit(); + + R1 = R1.TimesPow2(s); + + int i = s; + while (++i < n) + { + R0 = R0.Add(k.TestBit(i) ? R1 : R1.Negate()); + R1 = R1.Twice(); + } + + R0 = R0.Add(R1); + + return R0; + } + } +} diff --git a/crypto/src/math/field/FiniteFields.cs b/crypto/src/math/field/FiniteFields.cs new file mode 100644
index 000000000..7b84569fe --- /dev/null +++ b/crypto/src/math/field/FiniteFields.cs
@@ -0,0 +1,54 @@ +using System; + +namespace Org.BouncyCastle.Math.Field +{ + public abstract class FiniteFields + { + internal static readonly IFiniteField GF_2 = new PrimeField(BigInteger.ValueOf(2)); + internal static readonly IFiniteField GF_3 = new PrimeField(BigInteger.ValueOf(3)); + + public static IPolynomialExtensionField GetBinaryExtensionField(int[] exponents) + { + if (exponents[0] != 0) + { + throw new ArgumentException("Irreducible polynomials in GF(2) must have constant term", "exponents"); + } + for (int i = 1; i < exponents.Length; ++i) + { + if (exponents[i] <= exponents[i - 1]) + { + throw new ArgumentException("Polynomial exponents must be montonically increasing", "exponents"); + } + } + + return new GenericPolynomialExtensionField(GF_2, new GF2Polynomial(exponents)); + } + + // public static IPolynomialExtensionField GetTernaryExtensionField(Term[] terms) + // { + // return new GenericPolynomialExtensionField(GF_3, new GF3Polynomial(terms)); + // } + + public static IFiniteField GetPrimeField(BigInteger characteristic) + { + int bitLength = characteristic.BitLength; + if (characteristic.SignValue <= 0 || bitLength < 2) + { + throw new ArgumentException("Must be >= 2", "characteristic"); + } + + if (bitLength < 3) + { + switch (characteristic.IntValue) + { + case 2: + return GF_2; + case 3: + return GF_3; + } + } + + return new PrimeField(characteristic); + } + } +} diff --git a/crypto/src/math/field/GF2Polynomial.cs b/crypto/src/math/field/GF2Polynomial.cs new file mode 100644
index 000000000..c062d508a --- /dev/null +++ b/crypto/src/math/field/GF2Polynomial.cs
@@ -0,0 +1,46 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Math.Field +{ + internal class GF2Polynomial + : IPolynomial + { + protected readonly int[] exponents; + + internal GF2Polynomial(int[] exponents) + { + this.exponents = Arrays.Clone(exponents); + } + + public virtual int Degree + { + get { return exponents[exponents.Length - 1]; } + } + + public virtual int[] GetExponentsPresent() + { + return Arrays.Clone(exponents); + } + + public override bool Equals(object obj) + { + if (this == obj) + { + return true; + } + GF2Polynomial other = obj as GF2Polynomial; + if (null == other) + { + return false; + } + return Arrays.AreEqual(exponents, other.exponents); + } + + public override int GetHashCode() + { + return Arrays.GetHashCode(exponents); + } + } +} diff --git a/crypto/src/math/field/GenericPolynomialExtensionField.cs b/crypto/src/math/field/GenericPolynomialExtensionField.cs new file mode 100644
index 000000000..13ef57165 --- /dev/null +++ b/crypto/src/math/field/GenericPolynomialExtensionField.cs
@@ -0,0 +1,63 @@ +using System; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Math.Field +{ + internal class GenericPolynomialExtensionField + : IPolynomialExtensionField + { + protected readonly IFiniteField subfield; + protected readonly IPolynomial minimalPolynomial; + + internal GenericPolynomialExtensionField(IFiniteField subfield, IPolynomial polynomial) + { + this.subfield = subfield; + this.minimalPolynomial = polynomial; + } + + public virtual BigInteger Characteristic + { + get { return subfield.Characteristic; } + } + + public virtual int Dimension + { + get { return subfield.Dimension * minimalPolynomial.Degree; } + } + + public virtual IFiniteField Subfield + { + get { return subfield; } + } + + public virtual int Degree + { + get { return minimalPolynomial.Degree; } + } + + public virtual IPolynomial MinimalPolynomial + { + get { return minimalPolynomial; } + } + + public override bool Equals(object obj) + { + if (this == obj) + { + return true; + } + GenericPolynomialExtensionField other = obj as GenericPolynomialExtensionField; + if (null == other) + { + return false; + } + return subfield.Equals(other.subfield) && minimalPolynomial.Equals(other.minimalPolynomial); + } + + public override int GetHashCode() + { + return subfield.GetHashCode() ^ Integers.RotateLeft(minimalPolynomial.GetHashCode(), 16); + } + } +} diff --git a/crypto/src/math/field/IExtensionField.cs b/crypto/src/math/field/IExtensionField.cs new file mode 100644
index 000000000..17f45c153 --- /dev/null +++ b/crypto/src/math/field/IExtensionField.cs
@@ -0,0 +1,12 @@ +using System; + +namespace Org.BouncyCastle.Math.Field +{ + public interface IExtensionField + : IFiniteField + { + IFiniteField Subfield { get; } + + int Degree { get; } + } +} diff --git a/crypto/src/math/field/IFiniteField.cs b/crypto/src/math/field/IFiniteField.cs new file mode 100644
index 000000000..b618be74b --- /dev/null +++ b/crypto/src/math/field/IFiniteField.cs
@@ -0,0 +1,11 @@ +using System; + +namespace Org.BouncyCastle.Math.Field +{ + public interface IFiniteField + { + BigInteger Characteristic { get; } + + int Dimension { get; } + } +} diff --git a/crypto/src/math/field/IPolynomial.cs b/crypto/src/math/field/IPolynomial.cs new file mode 100644
index 000000000..ad6dfb662 --- /dev/null +++ b/crypto/src/math/field/IPolynomial.cs
@@ -0,0 +1,15 @@ +using System; + +namespace Org.BouncyCastle.Math.Field +{ + public interface IPolynomial + { + int Degree { get; } + + //BigInteger[] GetCoefficients(); + + int[] GetExponentsPresent(); + + //Term[] GetNonZeroTerms(); + } +} diff --git a/crypto/src/math/field/IPolynomialExtensionField.cs b/crypto/src/math/field/IPolynomialExtensionField.cs new file mode 100644
index 000000000..3818c1855 --- /dev/null +++ b/crypto/src/math/field/IPolynomialExtensionField.cs
@@ -0,0 +1,10 @@ +using System; + +namespace Org.BouncyCastle.Math.Field +{ + public interface IPolynomialExtensionField + : IExtensionField + { + IPolynomial MinimalPolynomial { get; } + } +} diff --git a/crypto/src/math/field/PrimeField.cs b/crypto/src/math/field/PrimeField.cs new file mode 100644
index 000000000..f6ba629d5 --- /dev/null +++ b/crypto/src/math/field/PrimeField.cs
@@ -0,0 +1,44 @@ +using System; + +namespace Org.BouncyCastle.Math.Field +{ + internal class PrimeField + : IFiniteField + { + protected readonly BigInteger characteristic; + + internal PrimeField(BigInteger characteristic) + { + this.characteristic = characteristic; + } + + public virtual BigInteger Characteristic + { + get { return characteristic; } + } + + public virtual int Dimension + { + get { return 1; } + } + + public override bool Equals(object obj) + { + if (this == obj) + { + return true; + } + PrimeField other = obj as PrimeField; + if (null == other) + { + return false; + } + return characteristic.Equals(other.characteristic); + } + + public override int GetHashCode() + { + return characteristic.GetHashCode(); + } + } +} 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
index 44eed9f32..db53e559a 100644 --- a/crypto/src/ocsp/OCSPException.cs +++ b/crypto/src/ocsp/OCSPException.cs
@@ -2,7 +2,10 @@ using System; namespace Org.BouncyCastle.Ocsp { - public class OcspException +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class OcspException : Exception { public OcspException() 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
index dad56dc39..cbc1e95f5 100644 --- a/crypto/src/ocsp/OCSPUtil.cs +++ b/crypto/src/ocsp/OCSPUtil.cs
@@ -1,6 +1,5 @@ using System; using System.Collections; -using System.Globalization; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.CryptoPro; @@ -92,9 +91,9 @@ namespace Org.BouncyCastle.Ocsp internal static DerObjectIdentifier GetAlgorithmOid( string algorithmName) { - algorithmName = algorithmName.ToUpperInvariant(); + algorithmName = Platform.ToUpperInvariant(algorithmName); - if (algorithms.Contains(algorithmName)) + if (algorithms.Contains(algorithmName)) { return (DerObjectIdentifier)algorithms[algorithmName]; } 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
index c758ecb05..7f4ec8e53 100644 --- a/crypto/src/openpgp/PgpCompressedDataGenerator.cs +++ b/crypto/src/openpgp/PgpCompressedDataGenerator.cs
@@ -155,7 +155,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp { if (dOut != pkOut) { - dOut.Dispose(); + dOut.Close(); dOut.Flush(); } @@ -174,13 +174,10 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp { } - protected override void Dispose(bool disposing) - { - if (disposing) - { - Finish(); - } - } + public override void Close() + { + Finish(); + } } private class SafeZOutputStream : ZOutputStream @@ -190,14 +187,11 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp { } - protected override void Dispose(bool disposing) - { - if (disposing) - { - Finish(); - End(); - } - } + public override void Close() + { + Finish(); + End(); + } } } } diff --git a/crypto/src/openpgp/PgpDataValidationException.cs b/crypto/src/openpgp/PgpDataValidationException.cs
index 74674da59..aab5165b2 100644 --- a/crypto/src/openpgp/PgpDataValidationException.cs +++ b/crypto/src/openpgp/PgpDataValidationException.cs
@@ -5,6 +5,9 @@ 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> +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif public class PgpDataValidationException : PgpException { 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
index 3048116fa..378b16a56 100644 --- a/crypto/src/openpgp/PgpException.cs +++ b/crypto/src/openpgp/PgpException.cs
@@ -3,7 +3,10 @@ using System; namespace Org.BouncyCastle.Bcpg.OpenPgp { /// <remarks>Generic exception class for PGP encoding/decoding problems.</remarks> - public class PgpException +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class PgpException : Exception { public PgpException() : base() {} 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
index da07f400f..d6419b27b 100644 --- a/crypto/src/openpgp/PgpKeyValidationException.cs +++ b/crypto/src/openpgp/PgpKeyValidationException.cs
@@ -5,7 +5,10 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp /// <remarks> /// Thrown if the key checksum is invalid. /// </remarks> - public class PgpKeyValidationException +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class PgpKeyValidationException : PgpException { public PgpKeyValidationException() : base() {} 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
index b0337c80d..3b1f2fe74 100644 --- a/crypto/src/openpgp/PgpLiteralDataGenerator.cs +++ b/crypto/src/openpgp/PgpLiteralDataGenerator.cs
@@ -39,11 +39,9 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp private void WriteHeader( BcpgOutputStream outStr, char format, - string name, + byte[] encName, long modificationTime) { - byte[] encName = Strings.ToUtf8ByteArray(name); - outStr.Write( (byte) format, (byte) encName.Length); @@ -89,15 +87,17 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp // 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); + byte[] encName = Strings.ToUtf8ByteArray(name); + + pkOut = new BcpgOutputStream(outStr, PacketTag.LiteralData, + length + 2 + encName.Length + 4, oldFormat); - WriteHeader(pkOut, format, name, unixMs); + WriteHeader(pkOut, format, encName, unixMs); return new WrappedGeneratorStream(this, pkOut); } - /// <summary> + /// <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 @@ -132,14 +132,15 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp // Do this first, since it might throw an exception long unixMs = DateTimeUtilities.DateTimeToUnixMs(modificationTime); + byte[] encName = Strings.ToUtf8ByteArray(name); + pkOut = new BcpgOutputStream(outStr, PacketTag.LiteralData, buffer); - WriteHeader(pkOut, format, name, unixMs); + WriteHeader(pkOut, format, encName, unixMs); return new WrappedGeneratorStream(this, pkOut); } -#if !PORTABLE /// <summary> /// <p> /// Open a literal data packet for the passed in <c>FileInfo</c> object, returning @@ -161,7 +162,6 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp { return Open(outStr, format, file.Name, file.Length, file.LastWriteTime); } -#endif /// <summary> /// Close the literal data packet - this is equivalent to calling Close() 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
index ecb935e4b..7b1ac93bf 100644 --- a/crypto/src/openpgp/PgpPublicKeyRing.cs +++ b/crypto/src/openpgp/PgpPublicKeyRing.cs
@@ -7,164 +7,164 @@ 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 + /// <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( + public PgpPublicKeyRing( byte[] encoding) : this(new MemoryStream(encoding, false)) { } - internal PgpPublicKeyRing( + internal PgpPublicKeyRing( IList pubKeys) { this.keys = pubKeys; } - public PgpPublicKeyRing( + public PgpPublicKeyRing( Stream inputStream) { - this.keys = Platform.CreateArrayList(); + this.keys = Platform.CreateArrayList(); BcpgInputStream bcpgInput = BcpgInputStream.Wrap(inputStream); - PacketTag initialTag = bcpgInput.NextPacketTag(); + 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")); + + "tag 0x" + ((int)initialTag).ToString("X")); } - PublicKeyPacket pubPk = (PublicKeyPacket) bcpgInput.ReadPacket();; - TrustPacket trustPk = ReadOptionalTrustPacket(bcpgInput); + PublicKeyPacket pubPk = (PublicKeyPacket) bcpgInput.ReadPacket();; + TrustPacket trustPk = ReadOptionalTrustPacket(bcpgInput); // direct signatures and revocations - IList keySigs = ReadSignaturesAndTrust(bcpgInput); + IList keySigs = ReadSignaturesAndTrust(bcpgInput); - IList ids, idTrusts, idSigs; - ReadUserIDs(bcpgInput, out ids, out idTrusts, out idSigs); + IList ids, idTrusts, idSigs; + ReadUserIDs(bcpgInput, out ids, out idTrusts, out idSigs); - keys.Add(new PgpPublicKey(pubPk, trustPk, keySigs, ids, idTrusts, idSigs)); + keys.Add(new PgpPublicKey(pubPk, trustPk, keySigs, ids, idTrusts, idSigs)); - // Read subkeys - while (bcpgInput.NextPacketTag() == PacketTag.PublicSubkey) + // Read subkeys + while (bcpgInput.NextPacketTag() == PacketTag.PublicSubkey) { - keys.Add(ReadSubkey(bcpgInput)); + keys.Add(ReadSubkey(bcpgInput)); } } - /// <summary>Return the first public key in the ring.</summary> - public PgpPublicKey GetPublicKey() + /// <summary>Return the first public key in the ring.</summary> + public virtual 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( + /// <summary>Return the public key referred to by the passed in key ID if it is present.</summary> + public virtual PgpPublicKey GetPublicKey( long keyId) { - foreach (PgpPublicKey k in keys) - { - if (keyId == k.KeyId) + foreach (PgpPublicKey k in keys) + { + if (keyId == k.KeyId) { return k; } } - return null; + 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() + /// <summary>Allows enumeration of all the public keys.</summary> + /// <returns>An <c>IEnumerable</c> of <c>PgpPublicKey</c> objects.</returns> + public virtual IEnumerable GetPublicKeys() { return new EnumerableProxy(keys); } - public byte[] GetEncoded() + public virtual byte[] GetEncoded() { MemoryStream bOut = new MemoryStream(); - Encode(bOut); + Encode(bOut); - return bOut.ToArray(); + return bOut.ToArray(); } - public void Encode( + public virtual void Encode( Stream outStr) { - if (outStr == null) - throw new ArgumentNullException("outStr"); + if (outStr == null) + throw new ArgumentNullException("outStr"); - foreach (PgpPublicKey k in keys) - { - k.Encode(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> + /// <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; + bool masterFound = false; - for (int i = 0; i != keys.Count; i++) + for (int i = 0; i != keys.Count; i++) { PgpPublicKey key = (PgpPublicKey) keys[i]; - if (key.KeyId == pubKey.KeyId) + if (key.KeyId == pubKey.KeyId) { found = true; keys[i] = pubKey; } - if (key.IsMasterKey) - { - masterFound = true; - } - } + if (key.IsMasterKey) + { + masterFound = true; + } + } - if (!found) + 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); + 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> + /// <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) @@ -172,29 +172,29 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp IList keys = Platform.CreateArrayList(pubRing.keys); bool found = false; - for (int i = 0; i < keys.Count; i++) + for (int i = 0; i < keys.Count; i++) { PgpPublicKey key = (PgpPublicKey) keys[i]; - if (key.KeyId == pubKey.KeyId) + if (key.KeyId == pubKey.KeyId) { found = true; keys.RemoveAt(i); } } - return found ? new PgpPublicKeyRing(keys) : null; + return found ? new PgpPublicKeyRing(keys) : null; } - internal static PgpPublicKey ReadSubkey(BcpgInputStream bcpgInput) - { + internal static PgpPublicKey ReadSubkey(BcpgInputStream bcpgInput) + { PublicKeyPacket pk = (PublicKeyPacket) bcpgInput.ReadPacket(); - TrustPacket kTrust = ReadOptionalTrustPacket(bcpgInput); + TrustPacket kTrust = ReadOptionalTrustPacket(bcpgInput); - // PGP 8 actually leaves out the signature. - IList sigList = ReadSignaturesAndTrust(bcpgInput); + // PGP 8 actually leaves out the signature. + IList sigList = ReadSignaturesAndTrust(bcpgInput); - return new PgpPublicKey(pk, kTrust, sigList); - } + return new PgpPublicKey(pk, kTrust, sigList); + } } } diff --git a/crypto/src/openpgp/PgpPublicKeyRingBundle.cs b/crypto/src/openpgp/PgpPublicKeyRingBundle.cs
index 77cede4a1..519a2f884 100644 --- a/crypto/src/openpgp/PgpPublicKeyRingBundle.cs +++ b/crypto/src/openpgp/PgpPublicKeyRingBundle.cs
@@ -1,6 +1,5 @@ using System; using System.Collections; -using System.Globalization; using System.IO; using Org.BouncyCastle.Utilities; @@ -114,7 +113,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp if (ignoreCase) { - userId = userId.ToLowerInvariant(); + userId = Platform.ToLowerInvariant(userId); } foreach (PgpPublicKeyRing pubRing in GetKeyRings()) @@ -124,8 +123,8 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp string next = nextUserID; if (ignoreCase) { - next = next.ToLowerInvariant(); - } + next = Platform.ToLowerInvariant(next); + } if (matchPartial) { diff --git a/crypto/src/openpgp/PgpSecretKey.cs b/crypto/src/openpgp/PgpSecretKey.cs
index 9d87f49c8..872316dd7 100644 --- a/crypto/src/openpgp/PgpSecretKey.cs +++ b/crypto/src/openpgp/PgpSecretKey.cs
@@ -5,112 +5,121 @@ using System.IO; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Bcpg.OpenPgp { - /// <remarks>General class to handle a PGP secret key object.</remarks> + /// <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, + 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, - bool isMasterKey) + bool useSha1, + SecureRandom rand) + : this(privKey, pubKey, encAlgorithm, passPhrase, useSha1, rand, false) { - BcpgObject secKey; + } - this.pub = pubKey; + internal PgpSecretKey( + PgpPrivateKey privKey, + PgpPublicKey pubKey, + SymmetricKeyAlgorithmTag encAlgorithm, + char[] passPhrase, + bool useSha1, + SecureRandom rand, + bool isMasterKey) + { + BcpgObject secKey; - switch (pubKey.Algorithm) + 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"); + 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 + 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.WriteObject(secKey); - pOut.Write(checksumBytes); + byte[] keyData = bOut.ToArray(); + byte[] checksumData = Checksum(useSha1, keyData, keyData.Length); - byte[] bOutData = bOut.ToArray(); + keyData = Arrays.Concatenate(keyData, checksumData); - 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 + if (encAlgorithm == SymmetricKeyAlgorithmTag.Null) { - 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); - } - } + if (isMasterKey) + { + this.secret = new SecretKeyPacket(pub.publicPk, encAlgorithm, null, null, keyData); + } + else + { + this.secret = new SecretSubkeyPacket(pub.publicPk, encAlgorithm, null, null, keyData); + } + } + else + { + S2k s2k; + byte[] iv; + + byte[] encData; + if (pub.Version >= 4) + { + encData = EncryptKeyData(keyData, encAlgorithm, passPhrase, rand, out s2k, out iv); + } + else + { + // TODO v3 RSA key encryption + throw Platform.CreateNotImplementedException("v3 RSA"); + } + + 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) { @@ -122,7 +131,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp } } - public PgpSecretKey( + public PgpSecretKey( int certificationLevel, PgpKeyPair keyPair, string id, @@ -131,61 +140,61 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp 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 + : 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 { - PgpSignature certification = sGen.GenerateCertification(id, keyPair.PublicKey); + 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); - } + throw new PgpException("Exception doing certification: " + e.Message, e); + } } - public PgpSecretKey( + public PgpSecretKey( int certificationLevel, PublicKeyAlgorithmTag algorithm, AsymmetricKeyParameter pubKey, @@ -203,181 +212,199 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp { } - 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 + 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) { - 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> + /// <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; } + { + get { return pub.IsMasterKey; } + } + + /// <summary>Detect if the Secret Key's Private Key is empty or not</summary> + public bool IsPrivateKeyEmpty + { + get + { + byte[] secKeyData = secret.GetSecretKeyData(); + + return secKeyData == null || secKeyData.Length < 1; + } } - /// <summary>The algorithm the key is encrypted with.</summary> + /// <summary>The algorithm the key is encrypted with.</summary> public SymmetricKeyAlgorithmTag KeyEncryptionAlgorithm { - get { return secret.EncAlgorithm; } + get { return secret.EncAlgorithm; } } - /// <summary>The key ID of the public key associated with this key.</summary> + /// <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> + /// <summary>The public key associated with this key.</summary> public PgpPublicKey PublicKey { - get { return pub; } + 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> + /// <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(); } + 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> + /// <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(); } + get { return pub.GetUserAttributes(); } } - private byte[] ExtractKeyData( + 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 + byte[] encData = secret.GetSecretKeyData(); + + if (alg == SymmetricKeyAlgorithmTag.Null) + // TODO Check checksum here? + return encData; + + 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 'decryptData' + try { - KeyParameter key = PgpUtilities.MakeKeyFromPassPhrase(secret.EncAlgorithm, secret.S2k, passPhrase); - byte[] iv = secret.GetIV(); + KeyParameter key = PgpUtilities.MakeKeyFromPassPhrase(secret.EncAlgorithm, secret.S2k, passPhrase); + byte[] iv = secret.GetIV(); + byte[] data; - if (secret.PublicKeyPacket.Version == 4) + if (secret.PublicKeyPacket.Version >= 4) { - c.Init(false, new ParametersWithIV(key, iv)); + c.Init(false, new ParametersWithIV(key, iv)); - data = c.DoFinal(encData); + data = c.DoFinal(encData); - bool useSha1 = secret.S2kUsage == SecretKeyPacket.UsageSha1; - byte[] check = Checksum(useSha1, data, (useSha1) ? data.Length - 20 : data.Length - 2); + 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); - } - } - } + 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]; + data = new byte[encData.Length]; + + iv = Arrays.Clone(iv); - // + // // read in the four numbers // int pos = 0; - for (int i = 0; i != 4; i++) + 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; + int encLen = (((encData[pos] << 8) | (encData[pos + 1] & 0xff)) + 7) / 8; - data[pos] = encData[pos]; - data[pos + 1] = encData[pos + 1]; - pos += 2; + data[pos] = encData[pos]; + data[pos + 1] = encData[pos + 1]; + pos += 2; - c.DoFinal(encData, pos, encLen, data, pos); - pos += encLen; + c.DoFinal(encData, pos, encLen, data, pos); + pos += encLen; - if (i != 3) + 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); + // verify and copy checksum + // + + data[pos] = encData[pos]; + data[pos + 1] = encData[pos + 1]; + + int cs = ((encData[pos] << 8) & 0xff00) | (encData[pos + 1] & 0xff); int calcCs = 0; - for (int j=0; j < data.Length-2; j++) + for (int j = 0; j < pos; j++) { calcCs += data[j] & 0xff; } - calcCs &= 0xffff; + calcCs &= 0xffff; if (calcCs != cs) { throw new PgpException("Checksum mismatch: passphrase wrong, expected " - + cs.ToString("X") - + " found " + calcCs.ToString("X")); + + cs.ToString("X") + + " found " + calcCs.ToString("X")); } } - return data; + return data; } catch (PgpException e) { @@ -389,15 +416,14 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp } } - /// <summary>Extract a <c>PgpPrivateKey</c> from this secret key's encrypted contents.</summary> + /// <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) + if (IsPrivateKeyEmpty) return null; - PublicKeyPacket pubPk = secret.PublicKeyPacket; + PublicKeyPacket pubPk = secret.PublicKeyPacket; try { byte[] data = ExtractKeyData(passPhrase); @@ -438,7 +464,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp throw new PgpException("unknown public key algorithm encountered"); } - return new PgpPrivateKey(privateKey, KeyId); + return new PgpPrivateKey(privateKey, KeyId); } catch (PgpException e) { @@ -450,65 +476,65 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp } } - 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() + 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( + public void Encode( Stream outStr) { BcpgOutputStream bcpgOut = BcpgOutputStream.Wrap(outStr); - bcpgOut.WritePacket(secret); + bcpgOut.WritePacket(secret); if (pub.trustPk != null) { bcpgOut.WritePacket(pub.trustPk); } - if (pub.subSigs == null) // is not a sub key + if (pub.subSigs == null) // is not a sub key { - foreach (PgpSignature keySig in pub.keySigs) - { - keySig.Encode(bcpgOut); + foreach (PgpSignature keySig in pub.keySigs) + { + keySig.Encode(bcpgOut); } - for (int i = 0; i != pub.ids.Count; i++) + for (int i = 0; i != pub.ids.Count; i++) { - object pubID = pub.ids[i]; + object pubID = pub.ids[i]; if (pubID is string) { string id = (string) pubID; @@ -520,38 +546,38 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp bcpgOut.WritePacket(new UserAttributePacket(v.ToSubpacketArray())); } - if (pub.idTrusts[i] != null) + if (pub.idTrusts[i] != null) { bcpgOut.WritePacket((ContainedPacket)pub.idTrusts[i]); } - foreach (PgpSignature sig in (IList) pub.idSigs[i]) - { - sig.Encode(bcpgOut); + foreach (PgpSignature sig in (IList) pub.idSigs[i]) + { + sig.Encode(bcpgOut); } } } else { - foreach (PgpSignature subSig in pub.subSigs) - { - subSig.Encode(bcpgOut); + foreach (PgpSignature subSig in pub.subSigs) + { + subSig.Encode(bcpgOut); } } - // TODO Check that this is right/necessary - //bcpgOut.Finish(); + // 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> + /// <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, @@ -559,36 +585,48 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp SymmetricKeyAlgorithmTag newEncAlgorithm, SecureRandom rand) { + if (key.IsPrivateKeyEmpty) + throw new PgpException("no private key in this SecretKey - public key present only."); + byte[] rawKeyData = key.ExtractKeyData(oldPassPhrase); - int s2kUsage = key.secret.S2kUsage; - byte[] iv = null; + int s2kUsage = key.secret.S2kUsage; + byte[] iv = null; S2k s2k = null; byte[] keyData; + PublicKeyPacket pubKeyPacket = key.secret.PublicKeyPacket; - if (newEncAlgorithm == SymmetricKeyAlgorithmTag.Null) + 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; - } - } + 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); + if (pubKeyPacket.Version >= 4) + { + keyData = EncryptKeyData(rawKeyData, newEncAlgorithm, newPassPhrase, rand, out s2k, out iv); + } + else + { + // TODO v3 RSA key encryption + throw Platform.CreateNotImplementedException("v3 RSA"); + } } catch (PgpException e) { @@ -600,67 +638,65 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp } } - SecretKeyPacket secret; + SecretKeyPacket secret; if (key.secret is SecretSubkeyPacket) { - secret = new SecretSubkeyPacket(key.secret.PublicKeyPacket, - newEncAlgorithm, s2kUsage, s2k, iv, keyData); + secret = new SecretSubkeyPacket(pubKeyPacket, newEncAlgorithm, s2kUsage, s2k, iv, keyData); } else { - secret = new SecretKeyPacket(key.secret.PublicKeyPacket, - newEncAlgorithm, s2kUsage, s2k, iv, keyData); + secret = new SecretKeyPacket(pubKeyPacket, newEncAlgorithm, s2kUsage, s2k, iv, keyData); } - return new PgpSecretKey(secret, key.pub); + 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); } - /// <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); - } + 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
index 3e646eaa1..70cd7217c 100644 --- a/crypto/src/openpgp/PgpSecretKeyRing.cs +++ b/crypto/src/openpgp/PgpSecretKeyRing.cs
@@ -8,57 +8,57 @@ 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 + /// <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( + 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( + public PgpSecretKeyRing( Stream inputStream) { - this.keys = Platform.CreateArrayList(); + this.keys = Platform.CreateArrayList(); this.extraPubKeys = Platform.CreateArrayList(); - BcpgInputStream bcpgInput = BcpgInputStream.Wrap(inputStream); + BcpgInputStream bcpgInput = BcpgInputStream.Wrap(inputStream); - PacketTag initialTag = bcpgInput.NextPacketTag(); - if (initialTag != PacketTag.SecretKey && initialTag != PacketTag.SecretSubkey) + 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")); + + "tag 0x" + ((int)initialTag).ToString("X")); } - SecretKeyPacket secret = (SecretKeyPacket) bcpgInput.ReadPacket(); + SecretKeyPacket secret = (SecretKeyPacket) bcpgInput.ReadPacket(); - // + // // ignore GPG comment packets if found. // while (bcpgInput.NextPacketTag() == PacketTag.Experimental2) @@ -66,65 +66,65 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp bcpgInput.ReadPacket(); } - TrustPacket trust = ReadOptionalTrustPacket(bcpgInput); + TrustPacket trust = ReadOptionalTrustPacket(bcpgInput); - // revocation and direct signatures - IList keySigs = ReadSignaturesAndTrust(bcpgInput); + // revocation and direct signatures + IList keySigs = ReadSignaturesAndTrust(bcpgInput); - IList ids, idTrusts, idSigs; - ReadUserIDs(bcpgInput, out ids, out idTrusts, out idSigs); + 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))); + 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) + // 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)); - } + 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> + /// <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> + /// <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() + /// <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); } @@ -132,29 +132,29 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp public PgpSecretKey GetSecretKey( long keyId) { - foreach (PgpSecretKey k in keys) - { - if (keyId == k.KeyId) - { - return k; - } - } - - return null; + 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); } - /// <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() + public byte[] GetEncoded() { MemoryStream bOut = new MemoryStream(); @@ -166,117 +166,124 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp 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); - } + 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) - { + /// <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) - { + 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( + foreach (PgpSecretKey secretKey in ring.GetSecretKeys()) + { + if (secretKey.IsPrivateKeyEmpty) + { + newKeys.Add(secretKey); + } + else + { + 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; + bool masterFound = false; - for (int i = 0; i != keys.Count; i++) + for (int i = 0; i != keys.Count; i++) { PgpSecretKey key = (PgpSecretKey) keys[i]; - if (key.KeyId == secKey.KeyId) + if (key.KeyId == secKey.KeyId) { found = true; keys[i] = secKey; } - if (key.IsMasterKey) - { - masterFound = true; - } - } + 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); - } + 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); - } + 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> + /// <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) @@ -284,18 +291,18 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp IList keys = Platform.CreateArrayList(secRing.keys); bool found = false; - for (int i = 0; i < keys.Count; i++) + for (int i = 0; i < keys.Count; i++) { PgpSecretKey key = (PgpSecretKey)keys[i]; - if (key.KeyId == secKey.KeyId) + if (key.KeyId == secKey.KeyId) { found = true; keys.RemoveAt(i); } } - return found ? new PgpSecretKeyRing(keys, secRing.extraPubKeys) : null; + return found ? new PgpSecretKeyRing(keys, secRing.extraPubKeys) : null; } } } diff --git a/crypto/src/openpgp/PgpSecretKeyRingBundle.cs b/crypto/src/openpgp/PgpSecretKeyRingBundle.cs
index 18636dd65..12c7c098c 100644 --- a/crypto/src/openpgp/PgpSecretKeyRingBundle.cs +++ b/crypto/src/openpgp/PgpSecretKeyRingBundle.cs
@@ -114,8 +114,8 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp if (ignoreCase) { - userId = userId.ToLowerInvariant(); - } + userId = Platform.ToLowerInvariant(userId); + } foreach (PgpSecretKeyRing secRing in GetKeyRings()) { @@ -124,8 +124,8 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp string next = nextUserID; if (ignoreCase) { - next = next.ToLowerInvariant(); - } + next = Platform.ToLowerInvariant(next); + } if (matchPartial) { diff --git a/crypto/src/openpgp/PgpSignature.cs b/crypto/src/openpgp/PgpSignature.cs
index cbe0d83b4..3bb6f2f0e 100644 --- a/crypto/src/openpgp/PgpSignature.cs +++ b/crypto/src/openpgp/PgpSignature.cs
@@ -253,7 +253,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp // // hash in the id // - UpdateWithIdData(0xb4, Strings.ToByteArray(id)); + UpdateWithIdData(0xb4, Strings.ToUtf8ByteArray(id)); Update(sigPck.GetSignatureTrailer()); diff --git a/crypto/src/openpgp/PgpSignatureGenerator.cs b/crypto/src/openpgp/PgpSignatureGenerator.cs
index 891397267..c5309689f 100644 --- a/crypto/src/openpgp/PgpSignatureGenerator.cs +++ b/crypto/src/openpgp/PgpSignatureGenerator.cs
@@ -267,9 +267,9 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp // // hash in the id // - UpdateWithIdData(0xb4, Strings.ToByteArray(id)); + UpdateWithIdData(0xb4, Strings.ToUtf8ByteArray(id)); - return Generate(); + return Generate(); } /// <summary>Generate a certification for the passed in userAttributes.</summary> 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
index e22381bb1..32e37b819 100644 --- a/crypto/src/openpgp/PgpUtilities.cs +++ b/crypto/src/openpgp/PgpUtilities.cs
@@ -97,7 +97,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp return GetDigestName(hashAlgorithm) + "with" + encAlg; } - public static string GetSymmetricCipherName( + public static string GetSymmetricCipherName( SymmetricKeyAlgorithmTag algorithm) { switch (algorithm) @@ -124,12 +124,18 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp return "AES"; case SymmetricKeyAlgorithmTag.Twofish: return "Twofish"; + case SymmetricKeyAlgorithmTag.Camellia128: + return "Camellia"; + case SymmetricKeyAlgorithmTag.Camellia192: + return "Camellia"; + case SymmetricKeyAlgorithmTag.Camellia256: + return "Camellia"; default: throw new PgpException("unknown symmetric algorithm: " + algorithm); } } - public static int GetKeySize(SymmetricKeyAlgorithmTag algorithm) + public static int GetKeySize(SymmetricKeyAlgorithmTag algorithm) { int keySize; switch (algorithm) @@ -142,14 +148,17 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp case SymmetricKeyAlgorithmTag.Blowfish: case SymmetricKeyAlgorithmTag.Safer: case SymmetricKeyAlgorithmTag.Aes128: + case SymmetricKeyAlgorithmTag.Camellia128: keySize = 128; break; case SymmetricKeyAlgorithmTag.TripleDes: case SymmetricKeyAlgorithmTag.Aes192: + case SymmetricKeyAlgorithmTag.Camellia192: keySize = 192; break; case SymmetricKeyAlgorithmTag.Aes256: case SymmetricKeyAlgorithmTag.Twofish: + case SymmetricKeyAlgorithmTag.Camellia256: keySize = 256; break; default: @@ -298,7 +307,6 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp 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, @@ -336,7 +344,6 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp pOut.Close(); inputStream.Close(); } -#endif private const int ReadAhead = 60; 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
index baad0d429..6fc7329af 100644 --- a/crypto/src/openpgp/WrappedGeneratorStream.cs +++ b/crypto/src/openpgp/WrappedGeneratorStream.cs
@@ -4,25 +4,22 @@ using Org.BouncyCastle.Asn1.Utilities; namespace Org.BouncyCastle.Bcpg.OpenPgp { - public class WrappedGeneratorStream - : FilterStream - { - private readonly IStreamGenerator gen; + public class WrappedGeneratorStream + : FilterStream + { + private readonly IStreamGenerator gen; - public WrappedGeneratorStream( - IStreamGenerator gen, - Stream str) - : base(str) - { - this.gen = gen; - } + public WrappedGeneratorStream( + IStreamGenerator gen, + Stream str) + : base(str) + { + this.gen = gen; + } - protected override void Dispose(bool disposing) - { - if (disposing) - { - gen.Close(); - } - } - } + public override void Close() + { + gen.Close(); + } + } } diff --git a/crypto/src/openssl/EncryptionException.cs b/crypto/src/openssl/EncryptionException.cs
index 043e8f899..c4a6ec02f 100644 --- a/crypto/src/openssl/EncryptionException.cs +++ b/crypto/src/openssl/EncryptionException.cs
@@ -3,7 +3,10 @@ using System.IO; namespace Org.BouncyCastle.Security { - public class EncryptionException +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class EncryptionException : IOException { public EncryptionException( 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
index a1f64498d..6b91e8b1c 100644 --- a/crypto/src/openssl/MiscPemGenerator.cs +++ b/crypto/src/openssl/MiscPemGenerator.cs
@@ -1,7 +1,6 @@ using System; using System.Collections; using System.IO; -using System.Globalization; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.Cms; @@ -22,117 +21,117 @@ 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); - } + /** + * 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) // { @@ -148,130 +147,130 @@ namespace Org.BouncyCastle.OpenSsl // 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); - } - } - } + 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 = Platform.ToUpperInvariant(algorithm); + + // 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); + AlgorithmIdentifier algID = info.PrivateKeyAlgorithm; + DerObjectIdentifier oid = algID.ObjectID; + + if (oid.Equals(X9ObjectIdentifiers.IdDsa)) + { + keyType = "DSA"; + + DsaParameter p = DsaParameter.GetInstance(algID.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.ParsePrivateKey().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
index df0eebc69..4d33a2a1f 100644 --- a/crypto/src/openssl/PEMException.cs +++ b/crypto/src/openssl/PEMException.cs
@@ -3,7 +3,10 @@ using System.IO; namespace Org.BouncyCastle.OpenSsl { - public class PemException +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class PemException : IOException { public PemException( diff --git a/crypto/src/openssl/PEMReader.cs b/crypto/src/openssl/PEMReader.cs
index a2fedab96..9d3560838 100644 --- a/crypto/src/openssl/PEMReader.cs +++ b/crypto/src/openssl/PEMReader.cs
@@ -12,6 +12,7 @@ using Org.BouncyCastle.Asn1.TeleTrust; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Asn1.X9; using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.EC; using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Pkcs; @@ -23,20 +24,20 @@ 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 - { + /** + * 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() - { + static PemReader() + { // parsers.Add("CERTIFICATE REQUEST", new PKCS10CertificationRequestParser()); // parsers.Add("NEW CERTIFICATE REQUEST", new PKCS10CertificationRequestParser()); // parsers.Add("CERTIFICATE", new X509CertificateParser(provider)); @@ -52,323 +53,323 @@ namespace Org.BouncyCastle.OpenSsl // 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? + } + + 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( + 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.GetInstance(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); @@ -377,31 +378,23 @@ namespace Org.BouncyCastle.OpenSsl // 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; - } - } + //private static ECDomainParameters GetCurveParameters( + private static X9ECParameters GetCurveParameters( + string name) + { + // TODO ECGost3410NamedCurves support (returns ECDomainParameters though) + + X9ECParameters ecP = CustomNamedCurves.GetByName(name); + if (ecP == null) + { + ecP = ECNamedCurveTable.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
index b21dee231..fba958aa0 100644 --- a/crypto/src/openssl/PasswordException.cs +++ b/crypto/src/openssl/PasswordException.cs
@@ -3,7 +3,10 @@ using System.IO; namespace Org.BouncyCastle.Security { - public class PasswordException +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class PasswordException : IOException { public PasswordException( 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
index 6da3ade3e..1c37631d5 100644 --- a/crypto/src/pkcs/AsymmetricKeyEntry.cs +++ b/crypto/src/pkcs/AsymmetricKeyEntry.cs
@@ -18,7 +18,7 @@ namespace Org.BouncyCastle.Pkcs this.key = key; } -#if !(SILVERLIGHT || PORTABLE) +#if !SILVERLIGHT [Obsolete] public AsymmetricKeyEntry( AsymmetricKeyParameter key, diff --git a/crypto/src/pkcs/EncryptedPrivateKeyInfoFactory.cs b/crypto/src/pkcs/EncryptedPrivateKeyInfoFactory.cs
index b69693490..b6b7bac65 100644 --- a/crypto/src/pkcs/EncryptedPrivateKeyInfoFactory.cs +++ b/crypto/src/pkcs/EncryptedPrivateKeyInfoFactory.cs
@@ -22,54 +22,43 @@ namespace Org.BouncyCastle.Pkcs AsymmetricKeyParameter key) { return CreateEncryptedPrivateKeyInfo( - algorithm.Id, passPhrase, salt, iterationCount, - PrivateKeyInfoFactory.CreatePrivateKeyInfo(key)); + 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, + AsymmetricKeyParameter key) + { + return CreateEncryptedPrivateKeyInfo( + algorithm, passPhrase, salt, iterationCount, + PrivateKeyInfoFactory.CreatePrivateKeyInfo(key)); + } - public static EncryptedPrivateKeyInfo CreateEncryptedPrivateKeyInfo( + 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); + IBufferedCipher cipher = PbeUtilities.CreateEngine(algorithm) as IBufferedCipher; + if (cipher == null) + throw new Exception("Unknown encryption algorithm: " + algorithm); - DerObjectIdentifier oid = PbeUtilities.GetObjectIdentifier(algorithm); - AlgorithmIdentifier algID = new AlgorithmIdentifier(oid, parameters); + Asn1Encodable pbeParameters = PbeUtilities.GenerateAlgorithmParameters( + algorithm, salt, iterationCount); + ICipherParameters cipherParameters = PbeUtilities.GenerateCipherParameters( + algorithm, passPhrase, pbeParameters); + cipher.Init(true, cipherParameters); + byte[] encoding = cipher.DoFinal(keyInfo.GetEncoded()); - return new EncryptedPrivateKeyInfo(algID, encoding); + DerObjectIdentifier oid = PbeUtilities.GetObjectIdentifier(algorithm); + AlgorithmIdentifier algID = new AlgorithmIdentifier(oid, pbeParameters); + 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
index 2c343c0b4..9f24eb18a 100644 --- a/crypto/src/pkcs/Pkcs10CertificationRequest.cs +++ b/crypto/src/pkcs/Pkcs10CertificationRequest.cs
@@ -1,6 +1,5 @@ using System; using System.Collections; -using System.Globalization; using System.IO; using Org.BouncyCastle.Asn1; @@ -228,7 +227,7 @@ namespace Org.BouncyCastle.Pkcs throw new ArgumentException("key for signing must be private", "signingKey"); // DerObjectIdentifier sigOid = SignerUtilities.GetObjectIdentifier(signatureAlgorithm); - string algorithmName = signatureAlgorithm.ToUpperInvariant(); + string algorithmName = Platform.ToUpperInvariant(signatureAlgorithm); DerObjectIdentifier sigOid = (DerObjectIdentifier) algorithms[algorithmName]; if (sigOid == null) diff --git a/crypto/src/pkcs/Pkcs10CertificationRequestDelaySigned.cs b/crypto/src/pkcs/Pkcs10CertificationRequestDelaySigned.cs
index f649b47a2..ecbb4ab62 100644 --- a/crypto/src/pkcs/Pkcs10CertificationRequestDelaySigned.cs +++ b/crypto/src/pkcs/Pkcs10CertificationRequestDelaySigned.cs
@@ -104,7 +104,7 @@ namespace Org.BouncyCastle.Pkcs if (publicKey.IsPrivate) throw new ArgumentException("expected public key", "publicKey"); // DerObjectIdentifier sigOid = SignerUtilities.GetObjectIdentifier(signatureAlgorithm); - string algorithmName = signatureAlgorithm.ToUpperInvariant(); + string algorithmName = Platform.ToUpperInvariant(signatureAlgorithm); DerObjectIdentifier sigOid = (DerObjectIdentifier) algorithms[algorithmName]; if (sigOid == null) { 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
index 2e7f2295e..40364eec7 100644 --- a/crypto/src/pkcs/Pkcs12Store.cs +++ b/crypto/src/pkcs/Pkcs12Store.cs
@@ -1,6 +1,5 @@ using System; using System.Collections; -using System.Globalization; using System.IO; using System.Text; @@ -18,704 +17,704 @@ 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(); + 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 + 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) - { - } + public Pkcs12Store() + : this(PkcsObjectIdentifiers.PbeWithShaAnd3KeyTripleDesCbc, + PkcsObjectIdentifiers.PbewithShaAnd40BitRC2Cbc, false) + { + } - // TODO Consider making obsolete + // 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(); + 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 - // + 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 + { + Console.WriteLine("extra " + b.BagID); + Console.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.GetInstance(sq[0]); + Asn1Set attrSet = Asn1Set.GetInstance(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 + { + Console.WriteLine("extra " + b.BagID); + Console.WriteLine("extra " + Asn1Dump.DumpAsString(b)); + } + } + } + else + { + Console.WriteLine("extra " + oid); + Console.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() - { + Asn1OctetString localId = null; + string alias = null; + + if (b.BagAttributes != null) + { + foreach (Asn1Sequence sq in b.BagAttributes) + { + DerObjectIdentifier aOid = DerObjectIdentifier.GetInstance(sq[0]); + Asn1Set attrSet = Asn1Set.GetInstance(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; - } - } + 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) @@ -723,509 +722,506 @@ namespace Org.BouncyCastle.Pkcs 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(); + } + + 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); + return MacUtilities.DoFinal(mac, data); + } + + private static byte[] CryptPbeData( + bool forEncryption, + AlgorithmIdentifier algId, + char[] password, + bool wrongPkcs12Zero, + byte[] data) + { + IBufferedCipher cipher = PbeUtilities.CreateEngine(algId.ObjectID) as IBufferedCipher; + + if (cipher == null) + throw new Exception("Unknown encryption algorithm: " + algId.ObjectID); + + Pkcs12PbeParams pbeParameters = Pkcs12PbeParams.GetInstance(algId.Parameters); + ICipherParameters cipherParams = PbeUtilities.GenerateCipherParameters( + algId.ObjectID, password, wrongPkcs12Zero, pbeParameters); + 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; } - } - } - } + 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 = Platform.ToLowerInvariant(alias); + 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 = Platform.ToLowerInvariant(alias); + string k = (string)keys[lower]; + + if (k == null) + return null; + + return orig[k]; + } + set + { + string lower = Platform.ToLowerInvariant(alias); + 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
index ed566cae9..723d50f08 100644 --- a/crypto/src/pkcs/PrivateKeyInfoFactory.cs +++ b/crypto/src/pkcs/PrivateKeyInfoFactory.cs
@@ -15,200 +15,194 @@ 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); - } - } + 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) + throw new Exception("Unknown encryption algorithm: " + algID.ObjectID); + + ICipherParameters cipherParameters = PbeUtilities.GenerateCipherParameters( + algID, passPhrase, wrongPkcs12Zero); + cipher.Init(false, cipherParameters); + byte[] keyBytes = cipher.DoFinal(encInfo.GetEncryptedData()); + + return PrivateKeyInfo.GetInstance(keyBytes); + } + } } diff --git a/crypto/src/pkcs/X509CertificateEntry.cs b/crypto/src/pkcs/X509CertificateEntry.cs
index 2f81dd87b..a621619fb 100644 --- a/crypto/src/pkcs/X509CertificateEntry.cs +++ b/crypto/src/pkcs/X509CertificateEntry.cs
@@ -18,7 +18,7 @@ namespace Org.BouncyCastle.Pkcs this.cert = cert; } -#if !(SILVERLIGHT || PORTABLE) +#if !SILVERLIGHT [Obsolete] public X509CertificateEntry( X509Certificate cert, 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
index e13e50995..e3d3ea7fe 100644 --- a/crypto/src/pkix/PkixCertPath.cs +++ b/crypto/src/pkix/PkixCertPath.cs
@@ -401,7 +401,7 @@ namespace Org.BouncyCastle.Pkix pWrt.WriteObject(certificates[i]); } - pWrt.Writer.Dispose(); + pWrt.Writer.Close(); } catch (Exception) { 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
index 106075a63..5a4944dd8 100644 --- a/crypto/src/pkix/PkixCertPathBuilderException.cs +++ b/crypto/src/pkix/PkixCertPathBuilderException.cs
@@ -7,7 +7,10 @@ namespace Org.BouncyCastle.Pkix /// <summary> /// Summary description for PkixCertPathBuilderException. /// </summary> - public class PkixCertPathBuilderException : GeneralSecurityException +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class PkixCertPathBuilderException : GeneralSecurityException { public PkixCertPathBuilderException() : base() { } 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
index 504a3c646..35522c6f8 100644 --- a/crypto/src/pkix/PkixCertPathValidatorException.cs +++ b/crypto/src/pkix/PkixCertPathValidatorException.cs
@@ -27,8 +27,11 @@ namespace Org.BouncyCastle.Pkix * * @see CertPathValidator **/ - - public class PkixCertPathValidatorException : GeneralSecurityException +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class PkixCertPathValidatorException + : GeneralSecurityException { private Exception cause; private PkixCertPath certPath; 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
index 305b2de35..acea77856 100644 --- a/crypto/src/pkix/PkixCertPathValidatorUtilities.cs +++ b/crypto/src/pkix/PkixCertPathValidatorUtilities.cs
@@ -528,7 +528,7 @@ namespace Org.BouncyCastle.Pkix } catch (Exception e) { - new Exception( + throw new Exception( "Reason code CRL entry extension could not be decoded.", e); } @@ -683,8 +683,8 @@ namespace Org.BouncyCastle.Pkix /// 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. + /// <returns>a Collection of all found <see cref="X509Certificate"/> or + /// <see cref="Org.BouncyCastle.X509.IX509AttributeCertificate"/> objects. /// May be empty but never <code>null</code>.</returns> /// <exception cref="Exception"></exception> internal static ICollection FindCertificates( @@ -864,7 +864,7 @@ namespace Org.BouncyCastle.Pkix } catch (Exception e) { - new Exception("Could not get issuer information from distribution point.", e); + throw new Exception("Could not get issuer information from distribution point.", e); } if (cert is X509Certificate) @@ -924,7 +924,7 @@ namespace Org.BouncyCastle.Pkix } catch (IOException e) { - new Exception("Cannot extract issuer from CRL.", e); + throw new Exception("Cannot extract issuer from CRL.", e); } BigInteger completeCRLNumber = null; 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
index 9f01e8765..432d7bd6b 100644 --- a/crypto/src/pkix/PkixNameConstraintValidatorException.cs +++ b/crypto/src/pkix/PkixNameConstraintValidatorException.cs
@@ -2,7 +2,11 @@ using System; namespace Org.BouncyCastle.Pkix { - public class PkixNameConstraintValidatorException : Exception +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + 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
index bae657d90..c6f3fbff9 100644 --- a/crypto/src/pkix/Rfc3280CertPathUtilities.cs +++ b/crypto/src/pkix/Rfc3280CertPathUtilities.cs
@@ -1197,10 +1197,10 @@ namespace Org.BouncyCastle.Pkix } 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; + // This format is enforced by the NistCertPath tests + string formattedDate = certStatus.RevocationDate.Value.ToString( + "ddd MMM dd HH:mm:ss K yyyy"); + string message = "Certificate revocation after " + formattedDate; message += ", reason: " + CrlReasons[certStatus.Status]; throw new Exception(message); } diff --git a/crypto/src/pkix/Rfc3281CertPathUtilities.cs b/crypto/src/pkix/Rfc3281CertPathUtilities.cs
index bda2aa737..101ef5e11 100644 --- a/crypto/src/pkix/Rfc3281CertPathUtilities.cs +++ b/crypto/src/pkix/Rfc3281CertPathUtilities.cs
@@ -195,10 +195,10 @@ namespace Org.BouncyCastle.Pkix } 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 " + // This format is enforced by the NistCertPath tests + string formattedDate = certStatus.RevocationDate.Value.ToString( + "ddd MMM dd HH:mm:ss K yyyy"); + string message = "Attribute certificate revocation after " + formattedDate; message += ", reason: " + Rfc3280CertPathUtilities.CrlReasons[certStatus.Status]; 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
index d74ec7368..4c61ac354 100644 --- a/crypto/src/security/AgreementUtilities.cs +++ b/crypto/src/security/AgreementUtilities.cs
@@ -1,5 +1,4 @@ using System.Collections; -using System.Globalization; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.X9; @@ -39,7 +38,7 @@ namespace Org.BouncyCastle.Security public static IBasicAgreement GetBasicAgreement( string algorithm) { - string upper = algorithm.ToUpperInvariant(); + string upper = Platform.ToUpperInvariant(algorithm); string mechanism = (string) algorithms[upper]; if (mechanism == null) @@ -73,7 +72,7 @@ namespace Org.BouncyCastle.Security string agreeAlgorithm, string wrapAlgorithm) { - string upper = agreeAlgorithm.ToUpperInvariant(); + string upper = Platform.ToUpperInvariant(agreeAlgorithm); string mechanism = (string) algorithms[upper]; if (mechanism == null) diff --git a/crypto/src/security/CipherUtilities.cs b/crypto/src/security/CipherUtilities.cs
index ffd7d1373..cdb711f69 100644 --- a/crypto/src/security/CipherUtilities.cs +++ b/crypto/src/security/CipherUtilities.cs
@@ -1,6 +1,5 @@ using System; using System.Collections; -using System.Globalization; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.CryptoPro; @@ -22,675 +21,715 @@ 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(); + /// <remarks> + /// Cipher Utility class contains methods that can not be specifically grouped into other classes. + /// </remarks> + public sealed class CipherUtilities + { + private 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, + }; + + private enum CipherMode { ECB, NONE, CBC, CCM, CFB, CTR, CTS, EAX, GCM, GOFB, OCB, OFB, OPENPGPCFB, SIC }; + private 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(); + 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 + // 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.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.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.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[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["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[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["TDEA"] = "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["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; - } - } + 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 = Platform.ToUpperInvariant(mechanism); + 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 = Platform.ToUpperInvariant(algorithm); + + { + 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]; + + { + string aliased = (string)algorithms[algorithmName]; + + if (aliased != null) + algorithmName = aliased; + } + + 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; + case CipherAlgorithm.IDEA: + blockCipher = new IdeaEngine(); + break; + 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.OCB: + aeadBlockCipher = new OcbBlockCipher(blockCipher, CreateBlockCipher(cipherAlgorithm)); + 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; + } + + private static IBlockCipher CreateBlockCipher(CipherAlgorithm cipherAlgorithm) + { + switch (cipherAlgorithm) + { + case CipherAlgorithm.AES: return new AesFastEngine(); + case CipherAlgorithm.BLOWFISH: return new BlowfishEngine(); + case CipherAlgorithm.CAMELLIA: return new CamelliaEngine(); + case CipherAlgorithm.CAST5: return new Cast5Engine(); + case CipherAlgorithm.CAST6: return new Cast6Engine(); + case CipherAlgorithm.DES: return new DesEngine(); + case CipherAlgorithm.DESEDE: return new DesEdeEngine(); + case CipherAlgorithm.GOST28147: return new Gost28147Engine(); + case CipherAlgorithm.IDEA: return new IdeaEngine(); + case CipherAlgorithm.NOEKEON: return new NoekeonEngine(); + case CipherAlgorithm.RC2: return new RC2Engine(); + case CipherAlgorithm.RC5: return new RC532Engine(); + case CipherAlgorithm.RC5_64: return new RC564Engine(); + case CipherAlgorithm.RC6: return new RC6Engine(); + case CipherAlgorithm.RIJNDAEL: return new RijndaelEngine(); + case CipherAlgorithm.SEED: return new SeedEngine(); + case CipherAlgorithm.SERPENT: return new SerpentEngine(); + case CipherAlgorithm.SKIPJACK: return new SkipjackEngine(); + case CipherAlgorithm.TEA: return new TeaEngine(); + case CipherAlgorithm.TWOFISH: return new TwofishEngine(); + case CipherAlgorithm.XTEA: return new XteaEngine(); + default: + throw new SecurityUtilityException("Cipher " + cipherAlgorithm + " not recognised or not a block cipher"); + } + } + } } diff --git a/crypto/src/security/DigestUtilities.cs b/crypto/src/security/DigestUtilities.cs
index a507b4e9b..ec3f63940 100644 --- a/crypto/src/security/DigestUtilities.cs +++ b/crypto/src/security/DigestUtilities.cs
@@ -1,6 +1,5 @@ using System; using System.Collections; -using System.Globalization; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.CryptoPro; @@ -20,27 +19,29 @@ namespace Org.BouncyCastle.Security /// </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 enum DigestAlgorithm { + GOST3411, + MD2, MD4, MD5, + RIPEMD128, RIPEMD160, RIPEMD256, RIPEMD320, + SHA_1, SHA_224, SHA_256, SHA_384, SHA_512, + SHA_512_224, SHA_512_256, + SHA3_224, SHA3_256, SHA3_384, SHA3_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(); - + // 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"; @@ -55,6 +56,10 @@ namespace Org.BouncyCastle.Security algorithms[NistObjectIdentifiers.IdSha384.Id] = "SHA-384"; algorithms["SHA512"] = "SHA-512"; algorithms[NistObjectIdentifiers.IdSha512.Id] = "SHA-512"; + algorithms["SHA512/224"] = "SHA-512/224"; + algorithms[NistObjectIdentifiers.IdSha512_224.Id] = "SHA-512/224"; + algorithms["SHA512/256"] = "SHA-512/256"; + algorithms[NistObjectIdentifiers.IdSha512_256.Id] = "SHA-512/256"; algorithms["RIPEMD-128"] = "RIPEMD128"; algorithms[TeleTrusTObjectIdentifiers.RipeMD128.Id] = "RIPEMD128"; @@ -62,10 +67,10 @@ namespace Org.BouncyCastle.Security algorithms[TeleTrusTObjectIdentifiers.RipeMD160.Id] = "RIPEMD160"; algorithms["RIPEMD-256"] = "RIPEMD256"; algorithms[TeleTrusTObjectIdentifiers.RipeMD256.Id] = "RIPEMD256"; - algorithms["RIPEMD-320"] = "RIPEMD320"; + algorithms["RIPEMD-320"] = "RIPEMD320"; // algorithms[TeleTrusTObjectIdentifiers.RipeMD320.Id] = "RIPEMD320"; - algorithms[CryptoProObjectIdentifiers.GostR3411.Id] = "GOST3411"; + algorithms[CryptoProObjectIdentifiers.GostR3411.Id] = "GOST3411"; @@ -77,113 +82,121 @@ namespace Org.BouncyCastle.Security oids["SHA-256"] = NistObjectIdentifiers.IdSha256; oids["SHA-384"] = NistObjectIdentifiers.IdSha384; oids["SHA-512"] = NistObjectIdentifiers.IdSha512; + oids["SHA-512/224"] = NistObjectIdentifiers.IdSha512_224; + oids["SHA-512/256"] = NistObjectIdentifiers.IdSha512_256; oids["RIPEMD128"] = TeleTrusTObjectIdentifiers.RipeMD128; oids["RIPEMD160"] = TeleTrusTObjectIdentifiers.RipeMD160; oids["RIPEMD256"] = TeleTrusTObjectIdentifiers.RipeMD256; - oids["GOST3411"] = CryptoProObjectIdentifiers.GostR3411; + oids["GOST3411"] = CryptoProObjectIdentifiers.GostR3411; } - /// <summary> + /// <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) + string mechanism) { - if (mechanism == null) - throw new System.ArgumentNullException("mechanism"); + if (mechanism == null) + throw new System.ArgumentNullException("mechanism"); - mechanism = mechanism.ToUpperInvariant(); - string aliased = (string) algorithms[mechanism]; + mechanism = Platform.ToUpperInvariant(mechanism); + string aliased = (string) algorithms[mechanism]; - if (aliased != null) - mechanism = aliased; + if (aliased != null) + mechanism = aliased; - return (DerObjectIdentifier) oids[mechanism]; + return (DerObjectIdentifier) oids[mechanism]; } public static ICollection Algorithms { - get { return oids.Keys; } + get { return oids.Keys; } } public static IDigest GetDigest( - DerObjectIdentifier id) + DerObjectIdentifier id) { return GetDigest(id.Id); } public static IDigest GetDigest( - string algorithm) + string algorithm) { - string upper = algorithm.ToUpperInvariant(); + string upper = Platform.ToUpperInvariant(algorithm); 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."); + 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.SHA_512_224: return new Sha512tDigest(224); + case DigestAlgorithm.SHA_512_256: return new Sha512tDigest(256); + case DigestAlgorithm.SHA3_224: return new Sha3Digest(224); + case DigestAlgorithm.SHA3_256: return new Sha3Digest(256); + case DigestAlgorithm.SHA3_384: return new Sha3Digest(384); + case DigestAlgorithm.SHA3_512: return new Sha3Digest(512); + 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) + 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); - } + 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
index 4df1ae739..d50e17d39 100644 --- a/crypto/src/security/DotNetUtilities.cs +++ b/crypto/src/security/DotNetUtilities.cs
@@ -1,9 +1,10 @@ -#if !(NETCF_1_0 || SILVERLIGHT || PORTABLE) +#if !(NETCF_1_0 || SILVERLIGHT) using System; using System.Security.Cryptography; using SystemX509 = System.Security.Cryptography.X509Certificates; +using Org.BouncyCastle.Asn1.Pkcs; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; @@ -161,22 +162,21 @@ namespace Org.BouncyCastle.Security 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; - } + // TODO This appears to not work for private keys (when no CRT info) + return CreateRSAProvider(ToRSAParameters(rsaKey)); + } public static RSA ToRSA(RsaPrivateCrtKeyParameters privKey) { - RSAParameters rp = ToRSAParameters(privKey); - RSACryptoServiceProvider rsaCsp = new RSACryptoServiceProvider(); - rsaCsp.ImportParameters(rp); - return rsaCsp; - } + return CreateRSAProvider(ToRSAParameters(privKey)); + } - public static RSAParameters ToRSAParameters(RsaKeyParameters rsaKey) + public static RSA ToRSA(RsaPrivateKeyStructure privKey) + { + return CreateRSAProvider(ToRSAParameters(privKey)); + } + + public static RSAParameters ToRSAParameters(RsaKeyParameters rsaKey) { RSAParameters rp = new RSAParameters(); rp.Modulus = rsaKey.Modulus.ToByteArrayUnsigned(); @@ -201,7 +201,21 @@ namespace Org.BouncyCastle.Security return rp; } - // TODO Move functionality to more general class + public static RSAParameters ToRSAParameters(RsaPrivateKeyStructure privKey) + { + RSAParameters rp = new RSAParameters(); + rp.Modulus = privKey.Modulus.ToByteArrayUnsigned(); + rp.Exponent = privKey.PublicExponent.ToByteArrayUnsigned(); + rp.P = privKey.Prime1.ToByteArrayUnsigned(); + rp.Q = privKey.Prime2.ToByteArrayUnsigned(); + rp.D = ConvertRSAParametersField(privKey.PrivateExponent, rp.Modulus.Length); + rp.DP = ConvertRSAParametersField(privKey.Exponent1, rp.P.Length); + rp.DQ = ConvertRSAParametersField(privKey.Exponent2, rp.Q.Length); + rp.InverseQ = ConvertRSAParametersField(privKey.Coefficient, rp.Q.Length); + return rp; + } + + // TODO Move functionality to more general class private static byte[] ConvertRSAParametersField(BigInteger n, int size) { byte[] bs = n.ToByteArrayUnsigned(); @@ -216,6 +230,13 @@ namespace Org.BouncyCastle.Security Array.Copy(bs, 0, padded, size - bs.Length, bs.Length); return padded; } + + private static RSA CreateRSAProvider(RSAParameters rp) + { + RSACryptoServiceProvider rsaCsp = new RSACryptoServiceProvider(); + rsaCsp.ImportParameters(rp); + return rsaCsp; + } } } diff --git a/crypto/src/security/GeneralSecurityException.cs b/crypto/src/security/GeneralSecurityException.cs
index c0e412f98..2c3f2a555 100644 --- a/crypto/src/security/GeneralSecurityException.cs +++ b/crypto/src/security/GeneralSecurityException.cs
@@ -2,7 +2,10 @@ using System; namespace Org.BouncyCastle.Security { - public class GeneralSecurityException +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class GeneralSecurityException : Exception { public GeneralSecurityException() diff --git a/crypto/src/security/GeneratorUtilities.cs b/crypto/src/security/GeneratorUtilities.cs
index 8e9290222..45fbc9425 100644 --- a/crypto/src/security/GeneratorUtilities.cs +++ b/crypto/src/security/GeneratorUtilities.cs
@@ -1,5 +1,4 @@ using System.Collections; -using System.Globalization; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.CryptoPro; @@ -17,325 +16,334 @@ using Org.BouncyCastle.Utilities.Collections; namespace Org.BouncyCastle.Security { - public sealed class GeneratorUtilities - { - private GeneratorUtilities() - { - } + public sealed class GeneratorUtilities + { + private GeneratorUtilities() + { + } - private static readonly IDictionary kgAlgorithms = Platform.CreateHashtable(); + 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", + 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", + "TDEA", + 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("SHA512/224"); + AddHMacKeyGenerator("SHA512/256"); + AddHMacKeyGenerator("SHA3-224"); + AddHMacKeyGenerator("SHA3-256"); + AddHMacKeyGenerator("SHA3-384"); + AddHMacKeyGenerator("SHA3-512"); + 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]; - } - } + "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"); + AddDefaultKeySizeEntries(224, "HMACSHA512/224"); + AddDefaultKeySizeEntries(256, "HMACSHA512/256"); + } + + 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[Platform.ToUpperInvariant(algorithm)]; + } + + // TODO Consider making this public + internal static string GetCanonicalKeyPairGeneratorAlgorithm( + string algorithm) + { + return (string)kpgAlgorithms[Platform.ToUpperInvariant(algorithm)]; + } + + 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
index 97d48e091..4d4488ef2 100644 --- a/crypto/src/security/InvalidKeyException.cs +++ b/crypto/src/security/InvalidKeyException.cs
@@ -2,7 +2,10 @@ using System; namespace Org.BouncyCastle.Security { - public class InvalidKeyException : KeyException +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class InvalidKeyException : KeyException { public InvalidKeyException() : base() { } public InvalidKeyException(string message) : base(message) { } diff --git a/crypto/src/security/InvalidParameterException.cs b/crypto/src/security/InvalidParameterException.cs
index 08ca4a076..57a912d03 100644 --- a/crypto/src/security/InvalidParameterException.cs +++ b/crypto/src/security/InvalidParameterException.cs
@@ -2,7 +2,10 @@ using System; namespace Org.BouncyCastle.Security { - public class InvalidParameterException : KeyException +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class InvalidParameterException : KeyException { public InvalidParameterException() : base() { } public InvalidParameterException(string message) : base(message) { } diff --git a/crypto/src/security/KeyException.cs b/crypto/src/security/KeyException.cs
index f19264e5f..9304c1c4d 100644 --- a/crypto/src/security/KeyException.cs +++ b/crypto/src/security/KeyException.cs
@@ -2,7 +2,10 @@ using System; namespace Org.BouncyCastle.Security { - public class KeyException : GeneralSecurityException +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class KeyException : GeneralSecurityException { public KeyException() : base() { } public KeyException(string message) : base(message) { } diff --git a/crypto/src/security/MacUtilities.cs b/crypto/src/security/MacUtilities.cs
index 8550d7550..d1f8c89b4 100644 --- a/crypto/src/security/MacUtilities.cs +++ b/crypto/src/security/MacUtilities.cs
@@ -1,5 +1,4 @@ using System.Collections; -using System.Globalization; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.Iana; @@ -12,64 +11,65 @@ 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(); + /// <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"; - } + 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["SIPHASH"] = "SIPHASH-2-4"; + + algorithms["PBEWITHHMACSHA"] = "PBEWITHHMACSHA1"; + algorithms["1.3.14.3.2.26"] = "PBEWITHHMACSHA1"; + } // /// <summary> // /// Returns a ObjectIdentifier for a given digest mechanism. @@ -79,7 +79,7 @@ namespace Org.BouncyCastle.Security // public static DerObjectIdentifier GetObjectIdentifier( // string mechanism) // { -// mechanism = (string) algorithms[mechanism.ToUpperInvariant()]; +// mechanism = (string) algorithms[Platform.ToUpperInvariant(mechanism)]; // // if (mechanism != null) // { @@ -94,146 +94,153 @@ namespace Org.BouncyCastle.Security // 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; - } - } + public static IMac GetMac( + DerObjectIdentifier id) + { + return GetMac(id.Id); + } + + public static IMac GetMac( + string algorithm) + { + string upper = Platform.ToUpperInvariant(algorithm); + + 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 (mechanism == "IDEAMAC") + { + return new CbcBlockCipherMac(new IdeaEngine()); + } + if (mechanism == "IDEAMAC/CFB8") + { + return new CfbBlockCipherMac(new IdeaEngine()); + } + 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(); + } + if (mechanism == "SIPHASH-2-4") + { + return new SipHash(); + } + 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; + } + + public static byte[] DoFinal(IMac mac, byte[] input) + { + mac.BlockUpdate(input, 0, input.Length); + return DoFinal(mac); + } + } } diff --git a/crypto/src/security/NoSuchAlgorithmException.cs b/crypto/src/security/NoSuchAlgorithmException.cs
index 25c0ab04f..d120c5f77 100644 --- a/crypto/src/security/NoSuchAlgorithmException.cs +++ b/crypto/src/security/NoSuchAlgorithmException.cs
@@ -3,7 +3,10 @@ using System; namespace Org.BouncyCastle.Security { [Obsolete("Never thrown")] - public class NoSuchAlgorithmException : GeneralSecurityException +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class NoSuchAlgorithmException : GeneralSecurityException { public NoSuchAlgorithmException() : base() {} public NoSuchAlgorithmException(string message) : base(message) {} diff --git a/crypto/src/security/ParameterUtilities.cs b/crypto/src/security/ParameterUtilities.cs
index b44fbda73..b2d7c0dff 100644 --- a/crypto/src/security/ParameterUtilities.cs +++ b/crypto/src/security/ParameterUtilities.cs
@@ -1,6 +1,5 @@ using System; using System.Collections; -using System.Globalization; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.CryptoPro; @@ -16,312 +15,307 @@ 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]; - } - } + 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", + "TDEA", + OiwObjectIdentifiers.DesEde, + PkcsObjectIdentifiers.IdAlgCms3DesWrap); + AddAlgorithm("DESEDE3", + PkcsObjectIdentifiers.DesEde3Cbc); + AddAlgorithm("GOST28147", + "GOST", + "GOST-28147", + CryptoProObjectIdentifiers.GostR28147Cbc); + AddAlgorithm("HC128"); + AddAlgorithm("HC256"); + AddAlgorithm("IDEA", + "1.3.6.1.4.1.188.7.1.1.2"); + 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[Platform.ToUpperInvariant(algorithm)]; + } + + 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(); + } + else if (canonical == "IDEA") + { + iv = IdeaCbcPar.GetInstance(asn1Params).GetIV(); + } + 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 (canonical == "IDEA") + return new IdeaCbcPar(CreateIV(random, 8)); + + 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
index f59ab104b..56d68ba0a 100644 --- a/crypto/src/security/PbeUtilities.cs +++ b/crypto/src/security/PbeUtilities.cs
@@ -1,6 +1,5 @@ using System; using System.Collections; -using System.Globalization; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.BC; @@ -21,638 +20,644 @@ 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(); + /// <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"; + 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) + // 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); - } - } + 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] = "PBEwithSHA-1and128bitAES-CBC-BC"; + algorithms[BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes192_cbc.Id] = "PBEwithSHA-1and192bitAES-CBC-BC"; + algorithms[BCObjectIdentifiers.bc_pbe_sha1_pkcs12_aes256_cbc.Id] = "PBEwithSHA-1and256bitAES-CBC-BC"; + algorithms[BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes128_cbc.Id] = "PBEwithSHA-256and128bitAES-CBC-BC"; + algorithms[BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes192_cbc.Id] = "PBEwithSHA-256and192bitAES-CBC-BC"; + algorithms[BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes256_cbc.Id] = "PBEwithSHA-256and256bitAES-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[Platform.ToUpperInvariant(mechanism)]; + 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[Platform.ToUpperInvariant(algorithm)]; + + return mechanism != null && Pkcs12.Equals(algorithmType[mechanism]); + } + + public static bool IsPkcs5Scheme1( + string algorithm) + { + string mechanism = (string)algorithms[Platform.ToUpperInvariant(algorithm)]; + + return mechanism != null && Pkcs5S1.Equals(algorithmType[mechanism]); + } + + public static bool IsPkcs5Scheme2( + string algorithm) + { + string mechanism = (string)algorithms[Platform.ToUpperInvariant(algorithm)]; + + return mechanism != null && Pkcs5S2.Equals(algorithmType[mechanism]); + } + + public static bool IsOpenSsl( + string algorithm) + { + string mechanism = (string)algorithms[Platform.ToUpperInvariant(algorithm)]; + + return mechanism != null && OpenSsl.Equals(algorithmType[mechanism]); + } + + public static bool IsPbeAlgorithm( + string algorithm) + { + string mechanism = (string)algorithms[Platform.ToUpperInvariant(algorithm)]; + + 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[Platform.ToUpperInvariant(algorithm)]; + + 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-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.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.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[Platform.ToUpperInvariant(algorithm)]; + + 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") + || mechanism.StartsWith("PBEwithSHA-256")) + { + if (mechanism.EndsWith("AES-CBC-BC") || mechanism.EndsWith("AES-CBC-OPENSSL")) + { + return CipherUtilities.GetCipher("AES/CBC"); + } + + if (mechanism.EndsWith("DES-CBC")) + { + return CipherUtilities.GetCipher("DES/CBC"); + } + + if (mechanism.EndsWith("DESEDE-CBC")) + { + return CipherUtilities.GetCipher("DESEDE/CBC"); + } + + if (mechanism.EndsWith("RC2-CBC")) + { + return CipherUtilities.GetCipher("RC2/CBC"); + } + + if (mechanism.EndsWith("RC4")) + { + return CipherUtilities.GetCipher("RC4"); + } + } + + 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
index 5ebebd55a..1cfa37afe 100644 --- a/crypto/src/security/PrivateKeyFactory.cs +++ b/crypto/src/security/PrivateKeyFactory.cs
@@ -15,6 +15,7 @@ using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; using Org.BouncyCastle.Pkcs; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Security { @@ -24,101 +25,101 @@ namespace Org.BouncyCastle.Security { } - 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) + public static AsymmetricKeyParameter CreateKey( + byte[] privateKeyInfoData) { - 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? + 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.PrivateKeyAlgorithm; + 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.ParsePrivateKey())); + + 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) - { + else if (algOid.Equals(PkcsObjectIdentifiers.DhKeyAgreement)) + { + DHParameter para = new DHParameter( + Asn1Sequence.GetInstance(algID.Parameters.ToAsn1Object())); + DerInteger derX = (DerInteger)keyInfo.ParsePrivateKey(); + + 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.ParsePrivateKey(); + + return new ElGamalPrivateKeyParameters( + derX.Value, + new ElGamalParameters(para.P, para.G)); + } + else if (algOid.Equals(X9ObjectIdentifiers.IdDsa)) + { + DerInteger derX = (DerInteger)keyInfo.ParsePrivateKey(); + 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 - { + } + else + { x9 = new X9ECParameters((Asn1Sequence)para.Parameters); - } + } ECPrivateKeyStructure ec = new ECPrivateKeyStructure( - Asn1Sequence.GetInstance(keyInfo.PrivateKey)); + Asn1Sequence.GetInstance(keyInfo.ParsePrivateKey())); BigInteger d = ec.GetKey(); if (para.IsNamedCurve) @@ -127,74 +128,76 @@ namespace Org.BouncyCastle.Security } 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"); - } + return new ECPrivateKeyParameters(d, dParams); + } + else if (algOid.Equals(CryptoProObjectIdentifiers.GostR3410x2001)) + { + Gost3410PublicKeyAlgParameters gostParams = new Gost3410PublicKeyAlgParameters( + Asn1Sequence.GetInstance(algID.Parameters.ToAsn1Object())); + + Asn1Object privKey = keyInfo.ParsePrivateKey(); + ECPrivateKeyStructure ec; + + if (privKey is DerInteger) + { + // TODO Do we need to pass any parameters here? + ec = new ECPrivateKeyStructure(((DerInteger)privKey).Value); + } + else + { + ec = ECPrivateKeyStructure.GetInstance(privKey); + } + + ECDomainParameters ecP = ECGost3410NamedCurves.GetByOid(gostParams.PublicKeyParamSet); + + if (ecP == null) + throw new ArgumentException("Unrecognized curve OID for GostR3410x2001 private key"); + + 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.ParsePrivateKey(); + BigInteger x = new BigInteger(1, Arrays.Reverse(derX.GetOctets())); + + 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 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, @@ -203,19 +206,19 @@ namespace Org.BouncyCastle.Security int iterationCount, AsymmetricKeyParameter key) { - return EncryptedPrivateKeyInfoFactory.CreateEncryptedPrivateKeyInfo( - algorithm, passPhrase, salt, iterationCount, key).GetEncoded(); + return EncryptedPrivateKeyInfoFactory.CreateEncryptedPrivateKeyInfo( + algorithm, passPhrase, salt, iterationCount, key).GetEncoded(); } - public static byte[] EncryptKey( - string algorithm, + public static byte[] EncryptKey( + string algorithm, char[] passPhrase, byte[] salt, int iterationCount, AsymmetricKeyParameter key) { - return EncryptedPrivateKeyInfoFactory.CreateEncryptedPrivateKeyInfo( - algorithm, passPhrase, salt, iterationCount, key).GetEncoded(); + return EncryptedPrivateKeyInfoFactory.CreateEncryptedPrivateKeyInfo( + algorithm, passPhrase, salt, iterationCount, key).GetEncoded(); } - } + } } diff --git a/crypto/src/security/PublicKeyFactory.cs b/crypto/src/security/PublicKeyFactory.cs
index c15e24a41..8c0be4f70 100644 --- a/crypto/src/security/PublicKeyFactory.cs +++ b/crypto/src/security/PublicKeyFactory.cs
@@ -24,122 +24,122 @@ namespace Org.BouncyCastle.Security { } - 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) + 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()); + 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); - } + 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); @@ -151,103 +151,103 @@ namespace Org.BouncyCastle.Security } 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); - } + 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)); + + 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; + private static bool IsPkcsDHParam(Asn1Sequence seq) + { + if (seq.Count == 2) + return true; - if (seq.Count > 3) - return false; + if (seq.Count > 3) + return false; - DerInteger l = DerInteger.GetInstance(seq[2]); - DerInteger p = DerInteger.GetInstance(seq[0]); + DerInteger l = DerInteger.GetInstance(seq[2]); + DerInteger p = DerInteger.GetInstance(seq[0]); - return l.Value.CompareTo(BigInteger.ValueOf(p.Value.BitLength)) <= 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); + 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); + 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); - } - } + return new DHPublicKeyParameters(y, dhParams, algOid); + } + } } diff --git a/crypto/src/security/SecureRandom.cs b/crypto/src/security/SecureRandom.cs
index 866a2cf0d..ac9d98158 100644 --- a/crypto/src/security/SecureRandom.cs +++ b/crypto/src/security/SecureRandom.cs
@@ -1,9 +1,9 @@ using System; -using System.Globalization; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Prng; +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Security { @@ -41,7 +41,7 @@ namespace Org.BouncyCastle.Security // 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(); + string drgName = Platform.ToUpperInvariant(algorithm); IRandomGenerator drg = null; if (drgName == "SHA1PRNG") @@ -136,7 +136,7 @@ namespace Org.BouncyCastle.Security if (maxValue < 2) { if (maxValue < 0) - throw new ArgumentOutOfRangeException("maxValue < 0"); + throw new ArgumentOutOfRangeException("maxValue", "cannot be negative"); return 0; } diff --git a/crypto/src/security/SecurityUtilityException.cs b/crypto/src/security/SecurityUtilityException.cs
index 8a5ea68d0..02a3e806e 100644 --- a/crypto/src/security/SecurityUtilityException.cs +++ b/crypto/src/security/SecurityUtilityException.cs
@@ -2,6 +2,9 @@ using System; namespace Org.BouncyCastle.Security { +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif public class SecurityUtilityException : Exception { diff --git a/crypto/src/security/SignatureException.cs b/crypto/src/security/SignatureException.cs
index 5ae4cbcf4..cea3c59cd 100644 --- a/crypto/src/security/SignatureException.cs +++ b/crypto/src/security/SignatureException.cs
@@ -2,7 +2,10 @@ using System; namespace Org.BouncyCastle.Security { - public class SignatureException : GeneralSecurityException +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class SignatureException : GeneralSecurityException { public SignatureException() : base() { } public SignatureException(string message) : base(message) { } diff --git a/crypto/src/security/SignerUtilities.cs b/crypto/src/security/SignerUtilities.cs
index ab23ee5d5..0cf113f65 100644 --- a/crypto/src/security/SignerUtilities.cs +++ b/crypto/src/security/SignerUtilities.cs
@@ -1,6 +1,5 @@ using System; using System.Collections; -using System.Globalization; using System.IO; using Org.BouncyCastle.Asn1; @@ -24,16 +23,16 @@ namespace Org.BouncyCastle.Security /// </summary> public sealed class SignerUtilities { - private SignerUtilities() - { - } + private SignerUtilities() + { + } - internal static readonly IDictionary algorithms = Platform.CreateHashtable(); + internal static readonly IDictionary algorithms = Platform.CreateHashtable(); internal static readonly IDictionary oids = Platform.CreateHashtable(); - static SignerUtilities() + static SignerUtilities() { - algorithms["MD2WITHRSA"] = "MD2withRSA"; + algorithms["MD2WITHRSA"] = "MD2withRSA"; algorithms["MD2WITHRSAENCRYPTION"] = "MD2withRSA"; algorithms[PkcsObjectIdentifiers.MD2WithRsaEncryption.Id] = "MD2withRSA"; @@ -70,41 +69,41 @@ namespace Org.BouncyCastle.Security 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["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["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["SHA224WITHRSAANDMGF1"] = "SHA-224withRSAandMGF1"; + algorithms["SHA-224WITHRSAANDMGF1"] = "SHA-224withRSAandMGF1"; + algorithms["SHA224WITHRSA/PSS"] = "SHA-224withRSAandMGF1"; + algorithms["SHA-224WITHRSA/PSS"] = "SHA-224withRSAandMGF1"; - algorithms["SHA256WITHRSAANDMGF1"] = "SHA-256withRSAandMGF1"; + algorithms["SHA256WITHRSAANDMGF1"] = "SHA-256withRSAandMGF1"; algorithms["SHA-256WITHRSAANDMGF1"] = "SHA-256withRSAandMGF1"; - algorithms["SHA256WITHRSA/PSS"] = "SHA-256withRSAandMGF1"; - algorithms["SHA-256WITHRSA/PSS"] = "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["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["SHA512WITHRSA/PSS"] = "SHA-512withRSAandMGF1"; + algorithms["SHA-512WITHRSA/PSS"] = "SHA-512withRSAandMGF1"; - algorithms["RIPEMD128WITHRSA"] = "RIPEMD128withRSA"; + algorithms["RIPEMD128WITHRSA"] = "RIPEMD128withRSA"; algorithms["RIPEMD128WITHRSAENCRYPTION"] = "RIPEMD128withRSA"; algorithms[TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128.Id] = "RIPEMD128withRSA"; - algorithms["RIPEMD160WITHRSA"] = "RIPEMD160withRSA"; + algorithms["RIPEMD160WITHRSA"] = "RIPEMD160withRSA"; algorithms["RIPEMD160WITHRSAENCRYPTION"] = "RIPEMD160withRSA"; algorithms[TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160.Id] = "RIPEMD160withRSA"; @@ -112,126 +111,123 @@ namespace Org.BouncyCastle.Security 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["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["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; + 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["ECDSAWITHRIPEMD160"] = "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; @@ -241,129 +237,129 @@ namespace Org.BouncyCastle.Security 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["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["RIPEMD128withRSA"] = TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128; + oids["RIPEMD160withRSA"] = TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160; + oids["RIPEMD256withRSA"] = TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256; - oids["SHA-1withDSA"] = X9ObjectIdentifiers.IdDsaWithSha1; + 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["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; - } + oids["GOST3410"] = CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94; + oids["ECGOST3410"] = CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001; + } - /// <summary> + /// <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 + // TODO Don't really want to support this public static DerObjectIdentifier GetObjectIdentifier( - string mechanism) + string mechanism) { - if (mechanism == null) - throw new ArgumentNullException("mechanism"); + if (mechanism == null) + throw new ArgumentNullException("mechanism"); - mechanism = mechanism.ToUpperInvariant(); - string aliased = (string) algorithms[mechanism]; + mechanism = Platform.ToUpperInvariant(mechanism); + string aliased = (string) algorithms[mechanism]; - if (aliased != null) - mechanism = aliased; + if (aliased != null) + mechanism = aliased; - return (DerObjectIdentifier) oids[mechanism]; - } + return (DerObjectIdentifier) oids[mechanism]; + } - public static ICollection Algorithms + 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) + public static Asn1Encodable GetDefaultX509Parameters( + DerObjectIdentifier id) + { + return GetDefaultX509Parameters(id.Id); + } + + public static Asn1Encodable GetDefaultX509Parameters( + string algorithm) + { + if (algorithm == null) + throw new ArgumentNullException("algorithm"); + + algorithm = Platform.ToUpperInvariant(algorithm); + + 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) + public static ISigner GetSigner( + string algorithm) { - if (algorithm == null) - throw new ArgumentNullException("algorithm"); + if (algorithm == null) + throw new ArgumentNullException("algorithm"); - algorithm = algorithm.ToUpperInvariant(); + algorithm = Platform.ToUpperInvariant(algorithm); - string mechanism = (string) algorithms[algorithm]; + string mechanism = (string) algorithms[algorithm]; - if (mechanism == null) - mechanism = algorithm; + if (mechanism == null) + mechanism = algorithm; - if (mechanism.Equals("RSA")) - { - return (new RsaDigestSigner(new NullDigest())); - } - if (mechanism.Equals("MD2withRSA")) + if (mechanism.Equals("RSA")) + { + return (new RsaDigestSigner(new NullDigest(), (AlgorithmIdentifier)null)); + } + if (mechanism.Equals("MD2withRSA")) { return (new RsaDigestSigner(new MD2Digest())); } @@ -395,7 +391,7 @@ namespace Org.BouncyCastle.Security { return (new RsaDigestSigner(new Sha512Digest())); } - if (mechanism.Equals("RIPEMD128withRSA")) + if (mechanism.Equals("RIPEMD128withRSA")) { return (new RsaDigestSigner(new RipeMD128Digest())); } @@ -408,141 +404,141 @@ namespace Org.BouncyCastle.Security 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")) + 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")) + 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."); + 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) + DerObjectIdentifier oid) { return (string) algorithms[oid.Id]; } diff --git a/crypto/src/security/WrapperUtilities.cs b/crypto/src/security/WrapperUtilities.cs
index 33567c51b..ce31ea519 100644 --- a/crypto/src/security/WrapperUtilities.cs +++ b/crypto/src/security/WrapperUtilities.cs
@@ -1,6 +1,5 @@ using System; using System.Collections; -using System.Globalization; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.Kisa; @@ -13,141 +12,142 @@ 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(); + /// <remarks> + /// Utility class for creating IWrapper objects from their names/Oids + /// </remarks> + public sealed class WrapperUtilities + { + private 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); - } - } - } + 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["TDEAWRAP"] = "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 = Platform.ToUpperInvariant(algorithm); + 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
index ae6a56653..a2909b0d5 100644 --- a/crypto/src/security/cert/CertificateEncodingException.cs +++ b/crypto/src/security/cert/CertificateEncodingException.cs
@@ -2,7 +2,10 @@ using System; namespace Org.BouncyCastle.Security.Certificates { - public class CertificateEncodingException : CertificateException +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class CertificateEncodingException : CertificateException { public CertificateEncodingException() : base() { } public CertificateEncodingException(string msg) : base(msg) { } diff --git a/crypto/src/security/cert/CertificateException.cs b/crypto/src/security/cert/CertificateException.cs
index fa403c7f5..441c598e4 100644 --- a/crypto/src/security/cert/CertificateException.cs +++ b/crypto/src/security/cert/CertificateException.cs
@@ -2,7 +2,10 @@ using System; namespace Org.BouncyCastle.Security.Certificates { - public class CertificateException : GeneralSecurityException +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class CertificateException : GeneralSecurityException { public CertificateException() : base() { } public CertificateException(string message) : base(message) { } diff --git a/crypto/src/security/cert/CertificateExpiredException.cs b/crypto/src/security/cert/CertificateExpiredException.cs
index 33a54806f..c893c07ee 100644 --- a/crypto/src/security/cert/CertificateExpiredException.cs +++ b/crypto/src/security/cert/CertificateExpiredException.cs
@@ -2,7 +2,10 @@ using System; namespace Org.BouncyCastle.Security.Certificates { - public class CertificateExpiredException : CertificateException +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class CertificateExpiredException : CertificateException { public CertificateExpiredException() : base() { } public CertificateExpiredException(string message) : base(message) { } diff --git a/crypto/src/security/cert/CertificateNotYetValidException.cs b/crypto/src/security/cert/CertificateNotYetValidException.cs
index b9210e57c..a0081ce23 100644 --- a/crypto/src/security/cert/CertificateNotYetValidException.cs +++ b/crypto/src/security/cert/CertificateNotYetValidException.cs
@@ -2,7 +2,10 @@ using System; namespace Org.BouncyCastle.Security.Certificates { - public class CertificateNotYetValidException : CertificateException +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class CertificateNotYetValidException : CertificateException { public CertificateNotYetValidException() : base() { } public CertificateNotYetValidException(string message) : base(message) { } diff --git a/crypto/src/security/cert/CertificateParsingException.cs b/crypto/src/security/cert/CertificateParsingException.cs
index 9a1d0111b..8d8ed1e92 100644 --- a/crypto/src/security/cert/CertificateParsingException.cs +++ b/crypto/src/security/cert/CertificateParsingException.cs
@@ -2,7 +2,10 @@ using System; namespace Org.BouncyCastle.Security.Certificates { - public class CertificateParsingException : CertificateException +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class CertificateParsingException : CertificateException { public CertificateParsingException() : base() { } public CertificateParsingException(string message) : base(message) { } diff --git a/crypto/src/security/cert/CrlException.cs b/crypto/src/security/cert/CrlException.cs
index 59e4f20ea..0df007b1e 100644 --- a/crypto/src/security/cert/CrlException.cs +++ b/crypto/src/security/cert/CrlException.cs
@@ -2,7 +2,10 @@ using System; namespace Org.BouncyCastle.Security.Certificates { - public class CrlException : GeneralSecurityException +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class CrlException : GeneralSecurityException { public CrlException() : base() { } public CrlException(string msg) : base(msg) {} 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
index a5e24163d..3917e96a7 100644 --- a/crypto/src/tsp/TSPException.cs +++ b/crypto/src/tsp/TSPException.cs
@@ -2,7 +2,10 @@ using System; namespace Org.BouncyCastle.Tsp { - public class TspException +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class TspException : Exception { public TspException() 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
index d8243e65f..8ef2ec6cf 100644 --- a/crypto/src/tsp/TSPValidationException.cs +++ b/crypto/src/tsp/TSPValidationException.cs
@@ -1,3 +1,5 @@ +using System; + namespace Org.BouncyCastle.Tsp { /** @@ -6,7 +8,10 @@ namespace Org.BouncyCastle.Tsp * If a failure code is associated with the exception it can be retrieved using * the getFailureCode() method.</p> */ - public class TspValidationException +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class TspValidationException : TspException { private int 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
index 7fa05ebee..27fd18d6d 100644 --- a/crypto/src/util/Arrays.cs +++ b/crypto/src/util/Arrays.cs
@@ -1,28 +1,25 @@ using System; using System.Text; +using Org.BouncyCastle.Math; + namespace Org.BouncyCastle.Utilities { - /// <summary> General array utilities.</summary> - public sealed class Arrays + public abstract class Arrays { - private Arrays() + public static bool AreEqual( + bool[] a, + bool[] b) { - } - - public static bool AreEqual( - bool[] a, - bool[] b) - { - if (a == b) - return true; + if (a == b) + return true; - if (a == null || b == null) - return false; + if (a == null || b == null) + return false; return HaveSameContents(a, b); - } + } public static bool AreEqual( char[] a, @@ -44,61 +41,73 @@ namespace Org.BouncyCastle.Utilities /// <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); - } + 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); + } + + [CLSCompliantAttribute(false)] + public static bool AreEqual(uint[] a, uint[] b) + { + if (a == b) + return true; + + if (a == null || b == null) + return false; + + return HaveSameContents(a, b); + } private static bool HaveSameContents( bool[] a, @@ -133,94 +142,471 @@ namespace Org.BouncyCastle.Utilities } 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; - } + 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; + } + + private static bool HaveSameContents(uint[] a, uint[] 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; - } - } - } + 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 int GetHashCode(byte[] data, int off, int len) + { + if (data == null) + { + return 0; + } + + int i = len; + int hc = i + 1; + + while (--i >= 0) + { + hc *= 257; + hc ^= data[off + i]; + } + + return hc; + } + + public static int GetHashCode(int[] data) + { + if (data == null) + return 0; + + int i = data.Length; + int hc = i + 1; + + while (--i >= 0) + { + hc *= 257; + hc ^= data[i]; + } + + return hc; + } + + public static int GetHashCode(int[] data, int off, int len) + { + if (data == null) + return 0; + + int i = len; + int hc = i + 1; + + while (--i >= 0) + { + hc *= 257; + hc ^= data[off + i]; + } + + return hc; + } + + [CLSCompliantAttribute(false)] + public static int GetHashCode(uint[] data) + { + if (data == null) + return 0; + + int i = data.Length; + int hc = i + 1; + + while (--i >= 0) + { + hc *= 257; + hc ^= (int)data[i]; + } + + return hc; + } + + [CLSCompliantAttribute(false)] + public static int GetHashCode(uint[] data, int off, int len) + { + if (data == null) + return 0; + + int i = len; + int hc = i + 1; + + while (--i >= 0) + { + hc *= 257; + hc ^= (int)data[off + i]; + } + + return hc; + } + + public static byte[] Clone( + byte[] data) + { + return data == null ? null : (byte[])data.Clone(); + } + + public static byte[] Clone( + byte[] data, + byte[] existing) + { + if (data == null) + { + return null; + } + if ((existing == null) || (existing.Length != data.Length)) + { + return Clone(data); + } + Array.Copy(data, 0, existing, 0, existing.Length); + return existing; + } + + public static int[] Clone( + int[] data) + { + return data == null ? null : (int[])data.Clone(); + } + + public static long[] Clone(long[] data) + { + return data == null ? null : (long[])data.Clone(); + } + + [CLSCompliantAttribute(false)] + public static ulong[] Clone( + ulong[] data) + { + return data == null ? null : (ulong[]) data.Clone(); + } + + [CLSCompliantAttribute(false)] + public static ulong[] Clone( + ulong[] data, + ulong[] existing) + { + if (data == null) + { + return null; + } + if ((existing == null) || (existing.Length != data.Length)) + { + return Clone(data); + } + Array.Copy(data, 0, existing, 0, existing.Length); + return existing; + } + + public static bool Contains(byte[] a, byte n) + { + for (int i = 0; i < a.Length; ++i) + { + if (a[i] == n) + return true; + } + return false; + } + + public static bool Contains(short[] a, short n) + { + for (int i = 0; i < a.Length; ++i) + { + if (a[i] == n) + return true; + } + return false; + } + + public static bool Contains(int[] a, int n) + { + for (int i = 0; i < a.Length; ++i) + { + if (a[i] == n) + return true; + } + return false; + } + + public static void Fill( + byte[] buf, + byte b) + { + int i = buf.Length; + while (i > 0) + { + buf[--i] = b; + } + } + + public static byte[] CopyOf(byte[] data, int newLength) + { + byte[] tmp = new byte[newLength]; + Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length)); + return tmp; + } + + public static char[] CopyOf(char[] data, int newLength) + { + char[] tmp = new char[newLength]; + Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length)); + return tmp; + } + + public static int[] CopyOf(int[] data, int newLength) + { + int[] tmp = new int[newLength]; + Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length)); + return tmp; + } + + public static long[] CopyOf(long[] data, int newLength) + { + long[] tmp = new long[newLength]; + Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length)); + return tmp; + } + + public static BigInteger[] CopyOf(BigInteger[] data, int newLength) + { + BigInteger[] tmp = new BigInteger[newLength]; + Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length)); + return tmp; + } + + /** + * Make a copy of a range of bytes from the passed in data array. The range can + * extend beyond the end of the input array, in which case the return array will + * be padded with zeroes. + * + * @param data the array from which the data is to be copied. + * @param from the start index at which the copying should take place. + * @param to the final index of the range (exclusive). + * + * @return a new byte array containing the range given. + */ + public static byte[] CopyOfRange(byte[] data, int from, int to) + { + int newLength = GetLength(from, to); + byte[] tmp = new byte[newLength]; + Array.Copy(data, from, tmp, 0, System.Math.Min(newLength, data.Length - from)); + return tmp; + } + + public static int[] CopyOfRange(int[] data, int from, int to) + { + int newLength = GetLength(from, to); + int[] tmp = new int[newLength]; + Array.Copy(data, from, tmp, 0, System.Math.Min(newLength, data.Length - from)); + return tmp; + } + + public static long[] CopyOfRange(long[] data, int from, int to) + { + int newLength = GetLength(from, to); + long[] tmp = new long[newLength]; + Array.Copy(data, from, tmp, 0, System.Math.Min(newLength, data.Length - from)); + return tmp; + } + + public static BigInteger[] CopyOfRange(BigInteger[] data, int from, int to) + { + int newLength = GetLength(from, to); + BigInteger[] tmp = new BigInteger[newLength]; + Array.Copy(data, from, tmp, 0, System.Math.Min(newLength, data.Length - from)); + return tmp; + } + + private static int GetLength(int from, int to) + { + int newLength = to - from; + if (newLength < 0) + throw new ArgumentException(from + " > " + to); + return newLength; + } + + public static byte[] Append(byte[] a, byte b) + { + if (a == null) + return new byte[] { b }; + + int length = a.Length; + byte[] result = new byte[length + 1]; + Array.Copy(a, 0, result, 0, length); + result[length] = b; + return result; + } + + public static short[] Append(short[] a, short b) + { + if (a == null) + return new short[] { b }; + + int length = a.Length; + short[] result = new short[length + 1]; + Array.Copy(a, 0, result, 0, length); + result[length] = b; + return result; + } + + public static int[] Append(int[] a, int b) + { + if (a == null) + return new int[] { b }; + + int length = a.Length; + int[] result = new int[length + 1]; + Array.Copy(a, 0, result, 0, length); + result[length] = b; + return result; + } + + public static byte[] Concatenate(byte[] a, byte[] b) + { + if (a == null) + return Clone(b); + if (b == null) + return Clone(a); + + byte[] rv = new byte[a.Length + b.Length]; + Array.Copy(a, 0, rv, 0, a.Length); + Array.Copy(b, 0, rv, a.Length, b.Length); + return rv; + } + + public static int[] Concatenate(int[] a, int[] b) + { + if (a == null) + return Clone(b); + if (b == null) + return Clone(a); + + int[] rv = new int[a.Length + b.Length]; + Array.Copy(a, 0, rv, 0, a.Length); + Array.Copy(b, 0, rv, a.Length, b.Length); + return rv; + } + + public static byte[] Prepend(byte[] a, byte b) + { + if (a == null) + return new byte[] { b }; + + int length = a.Length; + byte[] result = new byte[length + 1]; + Array.Copy(a, 0, result, 1, length); + result[0] = b; + return result; + } + + public static short[] Prepend(short[] a, short b) + { + if (a == null) + return new short[] { b }; + + int length = a.Length; + short[] result = new short[length + 1]; + Array.Copy(a, 0, result, 1, length); + result[0] = b; + return result; + } + + public static int[] Prepend(int[] a, int b) + { + if (a == null) + return new int[] { b }; + + int length = a.Length; + int[] result = new int[length + 1]; + Array.Copy(a, 0, result, 1, length); + result[0] = b; + return result; + } + + public static byte[] Reverse(byte[] a) + { + if (a == null) + return null; + + int p1 = 0, p2 = a.Length; + byte[] result = new byte[p2]; + + while (--p2 >= 0) + { + result[p2] = a[p1++]; + } + + return result; + } + } } diff --git a/crypto/src/util/BigIntegers.cs b/crypto/src/util/BigIntegers.cs
index df82e1f22..f2d0425cc 100644 --- a/crypto/src/util/BigIntegers.cs +++ b/crypto/src/util/BigIntegers.cs
@@ -5,68 +5,86 @@ using Org.BouncyCastle.Security; namespace Org.BouncyCastle.Utilities { - /** - * BigInteger utilities. - */ - public sealed class BigIntegers - { - private const int MaxIterations = 1000; + /** + * BigInteger utilities. + */ + public abstract 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 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 the passed in value as an unsigned byte array of specified length, zero-extended as necessary. + * + * @param length desired length of result array. + * @param n value to be converted. + * @return a byte array of specified length, with leading zeroes as necessary given the size of n. + */ + public static byte[] AsUnsignedByteArray(int length, BigInteger n) + { + byte[] bytes = 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'"); + if (bytes.Length > length) + throw new ArgumentException("standard length exceeded", "n"); - return min; - } + if (bytes.Length == length) + return bytes; - if (min.BitLength > max.BitLength / 2) - { - return CreateRandomInRange(BigInteger.Zero, max.Subtract(min), random).Add(min); - } + byte[] tmp = new byte[length]; + Array.Copy(bytes, 0, tmp, tmp.Length - bytes.Length, bytes.Length); + return tmp; + } - 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; - } - } + /** + * 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'"); - // fall back to a faster (restricted) method - return new BigInteger(max.Subtract(min).BitLength - 1, random).Add(min); - } - } + 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
index abab5266b..8bd9c4053 100644 --- a/crypto/src/util/Enums.cs +++ b/crypto/src/util/Enums.cs
@@ -1,7 +1,7 @@ using System; using System.Text; -#if NETCF_1_0 || NETCF_2_0 || SILVERLIGHT || PORTABLE +#if NETCF_1_0 || NETCF_2_0 || SILVERLIGHT using System.Collections; using System.Reflection; #endif @@ -10,63 +10,60 @@ using Org.BouncyCastle.Utilities.Date; namespace Org.BouncyCastle.Utilities { - internal sealed class Enums + internal abstract class Enums { - private Enums() + internal static Enum GetEnumValue(System.Type enumType, string s) { - } - - internal static Enum GetEnumValue(System.Type enumType, string s) - { - if (!enumType.IsEnum) - throw new ArgumentException("Not an enumeration type", "enumType"); + 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('-', '_'); + // We only want to parse single named constants + if (s.Length > 0 && char.IsLetter(s[0]) && s.IndexOf(',') < 0) + { + s = s.Replace('-', '_'); + s = s.Replace('/', '_'); #if NETCF_1_0 - FieldInfo field = enumType.GetField(s, BindingFlags.Static | BindingFlags.Public); - if (field != null) - { - return (Enum)field.GetValue(null); - } + 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); + return (Enum)Enum.Parse(enumType, s, false); #endif - } + } - throw new ArgumentException(); - } + throw new ArgumentException(); + } - internal static Array GetEnumValues(System.Type enumType) - { - if (!enumType.IsEnum) - throw new ArgumentException("Not an enumeration type", "enumType"); + 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 +#if NETCF_1_0 || NETCF_2_0 || SILVERLIGHT IList result = Platform.CreateArrayList(); - FieldInfo[] fields = enumType.GetFields(BindingFlags.Static | BindingFlags.Public); - foreach (FieldInfo field in fields) - { + 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)); - } + result.Add(field.GetValue(enumType)); + } object[] arr = new object[result.Count]; result.CopyTo(arr, 0); return arr; #else - return Enum.GetValues(enumType); + 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); - } - } + 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/IMemoable.cs b/crypto/src/util/IMemoable.cs new file mode 100644
index 000000000..cc8a2e55b --- /dev/null +++ b/crypto/src/util/IMemoable.cs
@@ -0,0 +1,29 @@ +using System; + +namespace Org.BouncyCastle.Utilities +{ + public interface IMemoable + { + /// <summary> + /// Produce a copy of this object with its configuration and in its current state. + /// </summary> + /// <remarks> + /// The returned object may be used simply to store the state, or may be used as a similar object + /// starting from the copied state. + /// </remarks> + IMemoable Copy(); + + /// <summary> + /// Restore a copied object state into this object. + /// </summary> + /// <remarks> + /// Implementations of this method <em>should</em> try to avoid or minimise memory allocation to perform the reset. + /// </remarks> + /// <param name="other">an object originally {@link #copy() copied} from an object of the same type as this instance.</param> + /// <exception cref="InvalidCastException">if the provided object is not of the correct type.</exception> + /// <exception cref="MemoableResetException">if the <b>other</b> parameter is in some other way invalid.</exception> + void Reset(IMemoable other); + } + +} + diff --git a/crypto/src/util/Integers.cs b/crypto/src/util/Integers.cs new file mode 100644
index 000000000..ccbf872c4 --- /dev/null +++ b/crypto/src/util/Integers.cs
@@ -0,0 +1,17 @@ +using System; + +namespace Org.BouncyCastle.Utilities +{ + public abstract class Integers + { + public static int RotateLeft(int i, int distance) + { + return (i << distance) ^ (int)((uint)i >> -distance); + } + + public static int RotateRight(int i, int distance) + { + return (int)((uint)i >> distance) ^ (i << -distance); + } + } +} diff --git a/crypto/src/util/MemoableResetException.cs b/crypto/src/util/MemoableResetException.cs new file mode 100644
index 000000000..99554f6c2 --- /dev/null +++ b/crypto/src/util/MemoableResetException.cs
@@ -0,0 +1,27 @@ +using System; + +namespace Org.BouncyCastle.Utilities +{ + /** + * Exception to be thrown on a failure to reset an object implementing Memoable. + * <p> + * The exception extends InvalidCastException to enable users to have a single handling case, + * only introducing specific handling of this one if required. + * </p> + */ + public class MemoableResetException + : InvalidCastException + { + /** + * Basic Constructor. + * + * @param msg message to be associated with this exception. + */ + public MemoableResetException(string msg) + : base(msg) + { + } + } + +} + diff --git a/crypto/src/util/Platform.cs b/crypto/src/util/Platform.cs
index 52ace89e5..99d3982ea 100644 --- a/crypto/src/util/Platform.cs +++ b/crypto/src/util/Platform.cs
@@ -1,8 +1,9 @@ using System; +using System.Globalization; using System.IO; using System.Text; -#if SILVERLIGHT || PORTABLE +#if SILVERLIGHT using System.Collections.Generic; #else using System.Collections; @@ -10,22 +11,18 @@ using System.Collections; namespace Org.BouncyCastle.Utilities { - internal sealed class Platform - { - private Platform() - { - } - + internal abstract class 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(); + 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() { @@ -35,58 +32,58 @@ namespace Org.BouncyCastle.Utilities internal static int CompareIgnoreCase(string a, string b) { -#if (SILVERLIGHT || PORTABLE) - return String.Compare(a, b, StringComparison.OrdinalIgnoreCase); +#if SILVERLIGHT + return String.Compare(a, b, StringComparison.InvariantCultureIgnoreCase); #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; - } +#if NETCF_1_0 || NETCF_2_0 || SILVERLIGHT + 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; - } - } + 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 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)); - } + 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); - } + internal static Exception CreateNotImplementedException( + string message) + { + return new NotImplementedException(message); + } #endif -#if SILVERLIGHT || PORTABLE +#if SILVERLIGHT internal static System.Collections.IList CreateArrayList() { return new List<object>(); @@ -166,6 +163,16 @@ namespace Org.BouncyCastle.Utilities } #endif + internal static string ToLowerInvariant(string s) + { + return s.ToLower(CultureInfo.InvariantCulture); + } + + internal static string ToUpperInvariant(string s) + { + return s.ToUpper(CultureInfo.InvariantCulture); + } + internal static readonly string NewLine = GetNewLine(); - } + } } diff --git a/crypto/src/util/Strings.cs b/crypto/src/util/Strings.cs
index 1d35920c9..a6080f427 100644 --- a/crypto/src/util/Strings.cs +++ b/crypto/src/util/Strings.cs
@@ -3,33 +3,29 @@ 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; - } + /// <summary> General string utilities.</summary> + public abstract class 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 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) @@ -43,20 +39,20 @@ namespace Org.BouncyCastle.Utilities } 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; - } + 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) +#if SILVERLIGHT // TODO Check for non-ASCII bytes in input? return Encoding.UTF8.GetString(bytes, 0, bytes.Length); #else @@ -67,7 +63,7 @@ namespace Org.BouncyCastle.Utilities public static byte[] ToAsciiByteArray( char[] cs) { -#if SILVERLIGHT || PORTABLE +#if SILVERLIGHT // TODO Check for non-ASCII characters in input? return Encoding.UTF8.GetBytes(cs); #else @@ -78,7 +74,7 @@ namespace Org.BouncyCastle.Utilities public static byte[] ToAsciiByteArray( string s) { -#if SILVERLIGHT || PORTABLE +#if SILVERLIGHT // TODO Check for non-ASCII characters in input? return Encoding.UTF8.GetBytes(s); #else @@ -87,10 +83,10 @@ namespace Org.BouncyCastle.Utilities } public static string FromUtf8ByteArray( - byte[] bytes) - { - return Encoding.UTF8.GetString(bytes, 0, bytes.Length); - } + byte[] bytes) + { + return Encoding.UTF8.GetString(bytes, 0, bytes.Length); + } public static byte[] ToUtf8ByteArray( char[] cs) @@ -98,10 +94,10 @@ namespace Org.BouncyCastle.Utilities return Encoding.UTF8.GetBytes(cs); } - public static byte[] ToUtf8ByteArray( - string s) - { - return Encoding.UTF8.GetBytes(s); - } - } + public static byte[] ToUtf8ByteArray( + string s) + { + return Encoding.UTF8.GetBytes(s); + } + } } diff --git a/crypto/src/util/Times.cs b/crypto/src/util/Times.cs new file mode 100644
index 000000000..99a78d21a --- /dev/null +++ b/crypto/src/util/Times.cs
@@ -0,0 +1,14 @@ +using System; + +namespace Org.BouncyCastle.Utilities +{ + public sealed class Times + { + private static long NanosecondsPerTick = 100L; + + public static long NanoTime() + { + return DateTime.UtcNow.Ticks * NanosecondsPerTick; + } + } +} diff --git a/crypto/src/util/collections/CollectionUtilities.cs b/crypto/src/util/collections/CollectionUtilities.cs
index fd0bdcc7a..18fcb6774 100644 --- a/crypto/src/util/collections/CollectionUtilities.cs +++ b/crypto/src/util/collections/CollectionUtilities.cs
@@ -4,13 +4,9 @@ using System.Text; namespace Org.BouncyCastle.Utilities.Collections { - public sealed class CollectionUtilities - { - private CollectionUtilities() - { - } - - public static void AddRange(IList to, ICollection range) + public abstract class CollectionUtilities + { + public static void AddRange(IList to, IEnumerable range) { foreach (object o in range) { @@ -18,17 +14,15 @@ namespace Org.BouncyCastle.Utilities.Collections } } - public static bool CheckElementsAreOfType( - IEnumerable e, - Type t) - { - foreach (object o in e) - { - if (!t.IsInstanceOfType(o)) - return false; - } - return true; - } + 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) { @@ -45,27 +39,26 @@ namespace Org.BouncyCastle.Utilities.Collections return new UnmodifiableSetProxy(s); } - public static string ToString( - IEnumerable c) - { - StringBuilder sb = new StringBuilder("["); + public static string ToString(IEnumerable c) + { + StringBuilder sb = new StringBuilder("["); - IEnumerator e = c.GetEnumerator(); + IEnumerator e = c.GetEnumerator(); - if (e.MoveNext()) - { - sb.Append(e.Current.ToString()); + if (e.MoveNext()) + { + sb.Append(e.Current.ToString()); - while (e.MoveNext()) - { - sb.Append(", "); - sb.Append(e.Current.ToString()); - } - } + while (e.MoveNext()) + { + sb.Append(", "); + sb.Append(e.Current.ToString()); + } + } - sb.Append(']'); + sb.Append(']'); - return sb.ToString(); - } - } + 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
index 5dc9fae63..ccecd8dc2 100644 --- a/crypto/src/util/encoders/Base64.cs +++ b/crypto/src/util/encoders/Base64.cs
@@ -4,92 +4,117 @@ using System.Text; namespace Org.BouncyCastle.Utilities.Encoders { - public sealed class Base64 - { - private Base64() - { - } + 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); + public static string ToBase64String( + byte[] data) + { + return Convert.ToBase64String(data, 0, data.Length); + } + + public static string ToBase64String( + byte[] data, + int off, + int length) + { + return Convert.ToBase64String(data, off, length); + } + + /** + * encode the input data producing a base 64 encoded byte array. + * + * @return a byte array containing the base 64 encoded data. + */ + public static byte[] Encode( + byte[] data) + { + return Encode(data, 0, data.Length); + } + + /** + * encode the input data producing a base 64 encoded byte array. + * + * @return a byte array containing the base 64 encoded data. + */ + public static byte[] Encode( + byte[] data, + int off, + int length) + { + string s = Convert.ToBase64String(data, off, 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, + Stream outStream) + { + byte[] encoded = Encode(data); + 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; - } + /** + * 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) + { + byte[] encoded = Encode(data, off, length); + 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) - { + /** + * 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); - } + 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 - 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; - } - } + /** + * 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
index c94ce9d3c..7b53df25a 100644 --- a/crypto/src/util/encoders/Base64Encoder.cs +++ b/crypto/src/util/encoders/Base64Encoder.cs
@@ -3,305 +3,322 @@ 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 + 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() + { + Arrays.Fill(decodingTable, (byte)0xff); + + for (int i = 0; i < encodingTable.Length; i++) + { + decodingTable[encodingTable[i]] = (byte)i; + } + } + + public Base64Encoder() + { + InitialiseDecodingTable(); + } + + /** + * encode the input data producing a base 64 output stream. + * + * @return the number of bytes produced. + */ + public int Encode( + byte[] data, + int off, + int length, + 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++]]; + + if ((b1 | b2 | b3 | b4) >= 0x80) + throw new IOException("invalid characters encountered in base64 data"); + + 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; + byte b1, b2, b3, b4; + int length = 0; - int end = data.Length; + int end = data.Length; - while (end > 0) - { - if (!ignore(data[end - 1])) - { - break; - } + while (end > 0) + { + if (!ignore(data[end - 1])) + { + break; + } - end--; - } + end--; + } - int i = 0; - int finish = end - 4; + int i = 0; + int finish = end - 4; - i = nextI(data, i, finish); + i = nextI(data, i, finish); - while (i < finish) - { - b1 = decodingTable[data[i++]]; + while (i < finish) + { + b1 = decodingTable[data[i++]]; - i = nextI(data, i, finish); + i = nextI(data, i, finish); - b2 = decodingTable[data[i++]]; + b2 = decodingTable[data[i++]]; - i = nextI(data, i, finish); + i = nextI(data, i, finish); - b3 = decodingTable[data[i++]]; + b3 = decodingTable[data[i++]]; - i = nextI(data, i, finish); + i = nextI(data, i, finish); - b4 = decodingTable[data[i++]]; + b4 = decodingTable[data[i++]]; - outStream.WriteByte((byte)((b1 << 2) | (b2 >> 4))); - outStream.WriteByte((byte)((b2 << 4) | (b3 >> 2))); - outStream.WriteByte((byte)((b3 << 6) | b4)); + if ((b1 | b2 | b3 | b4) >= 0x80) + throw new IOException("invalid characters encountered in base64 data"); - length += 3; + outStream.WriteByte((byte)((b1 << 2) | (b2 >> 4))); + outStream.WriteByte((byte)((b2 << 4) | (b3 >> 2))); + outStream.WriteByte((byte)((b3 << 6) | b4)); - i = nextI(data, i, finish); - } + length += 3; - length += decodeLastBlock(outStream, data[end - 4], data[end - 3], data[end - 2], data[end - 1]); + i = nextI(data, i, finish); + } - return length; - } + length += decodeLastBlock(outStream, data[end - 4], data[end - 3], data[end - 2], data[end - 1]); - private int decodeLastBlock( - Stream outStream, - char c1, - char c2, - char c3, - char c4) - { - if (c3 == padding) - { - byte b1 = decodingTable[c1]; - byte b2 = decodingTable[c2]; + return length; + } - outStream.WriteByte((byte)((b1 << 2) | (b2 >> 4))); + private int decodeLastBlock( + Stream outStream, + char c1, + char c2, + char c3, + char c4) + { + if (c3 == padding) + { + byte b1 = decodingTable[c1]; + byte b2 = decodingTable[c2]; - return 1; - } + if ((b1 | b2) >= 0x80) + throw new IOException("invalid characters encountered at end of base64 data"); - 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)((b1 << 2) | (b2 >> 4))); - outStream.WriteByte((byte)((b2 << 4) | (b3 >> 2))); + return 1; + } - return 2; - } + if (c4 == padding) + { + byte b1 = decodingTable[c1]; + byte b2 = decodingTable[c2]; + byte b3 = decodingTable[c3]; - { - byte b1 = decodingTable[c1]; - byte b2 = decodingTable[c2]; - byte b3 = decodingTable[c3]; - byte b4 = decodingTable[c4]; + if ((b1 | b2 | b3) >= 0x80) + throw new IOException("invalid characters encountered at end of base64 data"); - outStream.WriteByte((byte)((b1 << 2) | (b2 >> 4))); - outStream.WriteByte((byte)((b2 << 4) | (b3 >> 2))); - outStream.WriteByte((byte)((b3 << 6) | b4)); + outStream.WriteByte((byte)((b1 << 2) | (b2 >> 4))); + outStream.WriteByte((byte)((b2 << 4) | (b3 >> 2))); - return 3; - } - } + return 2; + } - private int nextI(string data, int i, int finish) - { - while ((i < finish) && ignore(data[i])) - { - i++; - } - return i; - } - } + { + byte b1 = decodingTable[c1]; + byte b2 = decodingTable[c2]; + byte b3 = decodingTable[c3]; + byte b4 = decodingTable[c4]; + + if ((b1 | b2 | b3 | b4) >= 0x80) + throw new IOException("invalid characters encountered at end of base64 data"); + + 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
index fbe475991..3540a9d1e 100644 --- a/crypto/src/util/encoders/Hex.cs +++ b/crypto/src/util/encoders/Hex.cs
@@ -4,128 +4,127 @@ 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); + /// <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) + { + return ToHexString(data, 0, data.Length); + } + + 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); - } - } + /** + * 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
index c47d6219b..af526e0da 100644 --- a/crypto/src/util/encoders/HexEncoder.cs +++ b/crypto/src/util/encoders/HexEncoder.cs
@@ -3,162 +3,174 @@ 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; - } - } + public class HexEncoder + : IEncoder + { + protected 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. + */ + protected readonly byte[] decodingTable = new byte[128]; + + protected void InitialiseDecodingTable() + { + Arrays.Fill(decodingTable, (byte)0xff); + + for (int i = 0; i < encodingTable.Length; i++) + { + decodingTable[encodingTable[i]] = (byte)i; + } + + decodingTable['A'] = decodingTable['a']; + decodingTable['B'] = decodingTable['b']; + decodingTable['C'] = decodingTable['c']; + decodingTable['D'] = decodingTable['d']; + decodingTable['E'] = decodingTable['e']; + decodingTable['F'] = decodingTable['f']; + } + + public HexEncoder() + { + InitialiseDecodingTable(); + } + + /** + * encode the input data producing a Hex output stream. + * + * @return the number of bytes produced. + */ + public int Encode( + byte[] data, + int off, + int length, + 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 static 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++]]; + + if ((b1 | b2) >= 0x80) + throw new IOException("invalid characters encountered in Hex data"); + + 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++]]; + + if ((b1 | b2) >= 0x80) + throw new IOException("invalid characters encountered in Hex data"); + + 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
index 08eedb160..3ff4a1957 100644 --- a/crypto/src/util/io/BaseInputStream.cs +++ b/crypto/src/util/io/BaseInputStream.cs
@@ -11,15 +11,8 @@ namespace Org.BouncyCastle.Utilities.IO 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 override void Close() { closed = true; } + public sealed override void Flush() {} public sealed override long Length { get { throw new NotSupportedException(); } } public sealed override long Position { diff --git a/crypto/src/util/io/BaseOutputStream.cs b/crypto/src/util/io/BaseOutputStream.cs
index 77233f68c..6e6c6d346 100644 --- a/crypto/src/util/io/BaseOutputStream.cs +++ b/crypto/src/util/io/BaseOutputStream.cs
@@ -11,14 +11,7 @@ namespace Org.BouncyCastle.Utilities.IO 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 Close() { closed = true; } public override void Flush() {} public sealed override long Length { get { throw new NotSupportedException(); } } public sealed override long Position diff --git a/crypto/src/util/io/NullOutputStream.cs b/crypto/src/util/io/NullOutputStream.cs new file mode 100644
index 000000000..13877fa13 --- /dev/null +++ b/crypto/src/util/io/NullOutputStream.cs
@@ -0,0 +1,18 @@ +using System; + +namespace Org.BouncyCastle.Utilities.IO +{ + 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/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
index a8e7432fa..d8fcb558c 100644 --- a/crypto/src/util/io/StreamOverflowException.cs +++ b/crypto/src/util/io/StreamOverflowException.cs
@@ -3,7 +3,10 @@ using System.IO; namespace Org.BouncyCastle.Utilities.IO { - public class StreamOverflowException +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class StreamOverflowException : IOException { public StreamOverflowException() 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
index fed9823f0..373df4502 100644 --- a/crypto/src/util/io/TeeInputStream.cs +++ b/crypto/src/util/io/TeeInputStream.cs
@@ -18,14 +18,11 @@ namespace Org.BouncyCastle.Utilities.IO this.tee = tee; } - protected override void Dispose(bool disposing) - { - if (disposing) - { - input.Dispose(); - tee.Dispose(); - } - } + public override void Close() + { + input.Close(); + tee.Close(); + } public override int Read(byte[] buf, int off, int len) { diff --git a/crypto/src/util/io/TeeOutputStream.cs b/crypto/src/util/io/TeeOutputStream.cs
index 965ef23c8..fe3a7586a 100644 --- a/crypto/src/util/io/TeeOutputStream.cs +++ b/crypto/src/util/io/TeeOutputStream.cs
@@ -18,14 +18,11 @@ namespace Org.BouncyCastle.Utilities.IO this.tee = tee; } - protected override void Dispose(bool disposing) - { - if (disposing) - { - output.Dispose(); - tee.Dispose(); - } - } + public override void Close() + { + output.Close(); + tee.Close(); + } public override void Write(byte[] buffer, int offset, int count) { diff --git a/crypto/src/util/io/pem/PemGenerationException.cs b/crypto/src/util/io/pem/PemGenerationException.cs
index 22e83ac2d..b8edc622b 100644 --- a/crypto/src/util/io/pem/PemGenerationException.cs +++ b/crypto/src/util/io/pem/PemGenerationException.cs
@@ -2,7 +2,10 @@ using System; namespace Org.BouncyCastle.Utilities.IO.Pem { - public class PemGenerationException +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class PemGenerationException : Exception { public PemGenerationException() 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
index 6dea74223..1d88847bd 100644 --- a/crypto/src/util/zlib/ZDeflaterOutputStream.cs +++ b/crypto/src/util/zlib/ZDeflaterOutputStream.cs
@@ -135,20 +135,16 @@ namespace Org.BouncyCastle.Utilities.Zlib { z.free(); z=null; } - - protected override void Dispose(bool disposing) - { - if (disposing) - { - try{ - try { Finish(); } - catch (IOException) { } - } - finally{ - End(); - outp.Dispose(); - outp = null; - } + + public override void Close() { + try{ + try{Finish();} + catch (IOException) {} + } + finally{ + End(); + outp.Close(); + outp=null; } } } diff --git a/crypto/src/util/zlib/ZInflaterInputStream.cs b/crypto/src/util/zlib/ZInflaterInputStream.cs
index d2d70ebd1..5a3ff5aa6 100644 --- a/crypto/src/util/zlib/ZInflaterInputStream.cs +++ b/crypto/src/util/zlib/ZInflaterInputStream.cs
@@ -114,12 +114,8 @@ namespace Org.BouncyCastle.Utilities.Zlib { public override void WriteByte(byte b) { } - protected override void Dispose(bool disposing) - { - if (disposing) - { - inp.Dispose(); - } + public override void Close() { + inp.Close(); } public override int ReadByte() { diff --git a/crypto/src/util/zlib/ZInputStream.cs b/crypto/src/util/zlib/ZInputStream.cs
index 179c133bf..d1e1ba160 100644 --- a/crypto/src/util/zlib/ZInputStream.cs +++ b/crypto/src/util/zlib/ZInputStream.cs
@@ -93,16 +93,13 @@ namespace Org.BouncyCastle.Utilities.Zlib 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 override void Close() + { + if (!closed) + { + closed = true; + input.Close(); + } } public sealed override void Flush() {} diff --git a/crypto/src/util/zlib/ZOutputStream.cs b/crypto/src/util/zlib/ZOutputStream.cs
index 7ea7cbb33..1d2ead7b3 100644 --- a/crypto/src/util/zlib/ZOutputStream.cs +++ b/crypto/src/util/zlib/ZOutputStream.cs
@@ -44,7 +44,7 @@ namespace Org.BouncyCastle.Utilities.Zlib { private const int BufferSize = 512; - protected ZStream z = new ZStream(); + protected ZStream z; protected int flushLevel = JZlib.Z_NO_FLUSH; // TODO Allow custom buf protected byte[] buf = new byte[BufferSize]; @@ -54,17 +54,28 @@ namespace Org.BouncyCastle.Utilities.Zlib protected Stream output; protected bool closed; - public ZOutputStream(Stream output) + public ZOutputStream(Stream output) + : this(output, null) + { + } + + public ZOutputStream(Stream output, ZStream z) : base() { Debug.Assert(output.CanWrite); - this.output = output; - this.z.inflateInit(); + if (z == null) + { + z = new ZStream(); + z.inflateInit(); + } + + this.output = output; + this.z = z; this.compress = false; } - public ZOutputStream(Stream output, int level) + public ZOutputStream(Stream output, int level) : this(output, level, false) { } @@ -75,6 +86,7 @@ namespace Org.BouncyCastle.Utilities.Zlib Debug.Assert(output.CanWrite); this.output = output; + this.z = new ZStream(); this.z.deflateInit(level, nowrap); this.compress = true; } @@ -83,32 +95,29 @@ namespace Org.BouncyCastle.Utilities.Zlib 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 override void Close() + { + if (this.closed) + return; + + try + { + try + { + Finish(); + } + catch (IOException) + { + // Ignore + } + } + finally + { + this.closed = true; + End(); + output.Close(); + output = null; + } } public virtual void End() 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
index 54ca78090..bb6f37831 100644 --- a/crypto/src/x509/SubjectPublicKeyInfoFactory.cs +++ b/crypto/src/x509/SubjectPublicKeyInfoFactory.cs
@@ -26,162 +26,162 @@ namespace Org.BouncyCastle.X509 { } - /// <summary> + /// <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) + AsymmetricKeyParameter key) { - if (key == null) - throw new ArgumentNullException("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) + if (key is ElGamalPublicKeyParameters) { - ElGamalPublicKeyParameters _key = (ElGamalPublicKeyParameters)key; - ElGamalParameters kp = _key.Parameters; + 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)); + SubjectPublicKeyInfo info = new SubjectPublicKeyInfo( + new AlgorithmIdentifier( + OiwObjectIdentifiers.ElGamalAlgorithm, + new ElGamalParameter(kp.P, kp.G).ToAsn1Object()), + new DerInteger(_key.Y)); - return info; + return info; } - if (key is DsaPublicKeyParameters) + 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(); + DsaParameters kp = _key.Parameters; + Asn1Encodable ae = kp == null + ? null + : new DsaParameter(kp.P, kp.Q, kp.G).ToAsn1Object(); - return new SubjectPublicKeyInfo( + return new SubjectPublicKeyInfo( new AlgorithmIdentifier(X9ObjectIdentifiers.IdDsa, ae), - new DerInteger(_key.Y)); + new DerInteger(_key.Y)); } - if (key is DHPublicKeyParameters) + if (key is DHPublicKeyParameters) { DHPublicKeyParameters _key = (DHPublicKeyParameters) key; - DHParameters kp = _key.Parameters; + DHParameters kp = _key.Parameters; - SubjectPublicKeyInfo info = new SubjectPublicKeyInfo( + SubjectPublicKeyInfo info = new SubjectPublicKeyInfo( new AlgorithmIdentifier( - _key.AlgorithmOid, - new DHParameter(kp.P, kp.G, kp.L).ToAsn1Object()), - new DerInteger(_key.Y)); + _key.AlgorithmOid, + new DHParameter(kp.P, kp.G, kp.L).ToAsn1Object()), + new DerInteger(_key.Y)); - return info; + 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()); + SubjectPublicKeyInfo info = new SubjectPublicKeyInfo( + new AlgorithmIdentifier(PkcsObjectIdentifiers.RsaEncryption, DerNull.Instance), + new RsaPublicKeyStructure(_key.Modulus, _key.Exponent).ToAsn1Object()); - return info; + return info; } // End of RSA. - if (key is ECPublicKeyParameters) + 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]; - } - } - } + if (_key.AlgorithmName == "ECGOST3410") + { + if (_key.PublicKeyParamSet == null) + throw Platform.CreateNotImplementedException("Not a CryptoPro parameter set"); + + ECPoint q = _key.Q.Normalize(); + BigInteger bX = q.AffineXCoord.ToBigInteger(); + BigInteger bY = q.AffineYCoord.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
index 000958340..52a122c21 100644 --- a/crypto/src/x509/X509Utilities.cs +++ b/crypto/src/x509/X509Utilities.cs
@@ -1,6 +1,5 @@ using System; using System.Collections; -using System.Globalization; using System.IO; using Org.BouncyCastle.Asn1; @@ -124,9 +123,9 @@ namespace Org.BouncyCastle.X509 internal static DerObjectIdentifier GetAlgorithmOid( string algorithmName) { - algorithmName = algorithmName.ToUpperInvariant(); + algorithmName = Platform.ToUpperInvariant(algorithmName); - if (algorithms.Contains(algorithmName)) + if (algorithms.Contains(algorithmName)) { return (DerObjectIdentifier) algorithms[algorithmName]; } @@ -143,7 +142,7 @@ namespace Org.BouncyCastle.X509 return new AlgorithmIdentifier(sigOid); } - algorithmName = algorithmName.ToUpperInvariant(); + algorithmName = Platform.ToUpperInvariant(algorithmName); if (exParams.Contains(algorithmName)) { 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
index 3f37fcd66..09f6f1849 100644 --- a/crypto/src/x509/store/IX509Selector.cs +++ b/crypto/src/x509/store/IX509Selector.cs
@@ -3,11 +3,11 @@ using System; namespace Org.BouncyCastle.X509.Store { public interface IX509Selector -#if !(SILVERLIGHT || PORTABLE) +#if !SILVERLIGHT : ICloneable #endif - { -#if SILVERLIGHT || PORTABLE + { +#if SILVERLIGHT 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
index 3df26de79..02c593245 100644 --- a/crypto/src/x509/store/NoSuchStoreException.cs +++ b/crypto/src/x509/store/NoSuchStoreException.cs
@@ -2,7 +2,10 @@ using System; namespace Org.BouncyCastle.X509.Store { - public class NoSuchStoreException +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class NoSuchStoreException : X509StoreException { public NoSuchStoreException() 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
index f6a5e235f..f781291e2 100644 --- a/crypto/src/x509/store/X509StoreException.cs +++ b/crypto/src/x509/store/X509StoreException.cs
@@ -2,7 +2,10 @@ using System; namespace Org.BouncyCastle.X509.Store { - public class X509StoreException +#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT) + [Serializable] +#endif + public class X509StoreException : Exception { public X509StoreException() diff --git a/crypto/src/x509/store/X509StoreFactory.cs b/crypto/src/x509/store/X509StoreFactory.cs
index 322639991..96f22be3f 100644 --- a/crypto/src/x509/store/X509StoreFactory.cs +++ b/crypto/src/x509/store/X509StoreFactory.cs
@@ -1,6 +1,7 @@ using System; using System.Collections; -using System.Globalization; + +using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.X509.Store { @@ -17,9 +18,9 @@ namespace Org.BouncyCastle.X509.Store if (type == null) throw new ArgumentNullException("type"); - string[] parts = type.ToUpperInvariant().Split('/'); + string[] parts = Platform.ToUpperInvariant(type).Split('/'); - if (parts.Length < 2) + if (parts.Length < 2) throw new ArgumentException("type"); if (parts[1] != "COLLECTION")