diff options
author | Sergio Giro <sgiro@google.com> | 2016-02-02 15:27:40 +0000 |
---|---|---|
committer | Sergio Giro <sgiro@google.com> | 2016-02-02 15:30:05 +0000 |
commit | 11975162f2da08e65157d37cd272721485f2b34b (patch) | |
tree | e2d9c4866d82c0e93854d5e9a6200cfce47e6232 | |
parent | bdb7b3d37025690a0434040b4e0d0623d9fa74af (diff) | |
download | bouncycastle-11975162f2da08e65157d37cd272721485f2b34b.tar.gz |
bouncycastle: Android tree with upstream code for version 1.54.
Adding missing files
Change-Id: Ife77e8b1df7ec05555b29fb48a984f4c0da2e562
177 files changed, 31372 insertions, 0 deletions
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/path/test/AllTests.java b/bcpkix/src/main/java/org/bouncycastle/cert/path/test/AllTests.java new file mode 100644 index 00000000..8644cb1d --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cert/path/test/AllTests.java @@ -0,0 +1,67 @@ +package org.bouncycastle.cert.path.test; + +import java.security.Security; + +import junit.extensions.TestSetup; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.test.SimpleTestResult; + +public class AllTests + extends TestCase +{ + public void testSimpleTests() + { + org.bouncycastle.util.test.Test[] tests = new org.bouncycastle.util.test.Test[] { new CertPathTest(), new CertPathValidationTest() }; + + for (int i = 0; i != tests.length; i++) + { + SimpleTestResult result = (SimpleTestResult)tests[i].perform(); + + if (!result.isSuccessful()) + { + if (result.getException() != null) + { + result.getException().printStackTrace(); + } + fail(result.toString()); + } + } + } + + public static void main (String[] args) + { + junit.textui.TestRunner.run(suite()); + } + + public static Test suite() + { + TestSuite suite = new TestSuite("Cert Path Tests"); + + suite.addTestSuite(AllTests.class); + + return new BCTestSetup(suite); + } + + static class BCTestSetup + extends TestSetup + { + public BCTestSetup(Test test) + { + super(test); + } + + protected void setUp() + { + Security.addProvider(new BouncyCastleProvider()); + } + + protected void tearDown() + { + Security.removeProvider("BC"); + } + } + +}
\ No newline at end of file diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/PKCS7ProcessableObject.java b/bcpkix/src/main/java/org/bouncycastle/cms/PKCS7ProcessableObject.java new file mode 100644 index 00000000..077b2dc6 --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cms/PKCS7ProcessableObject.java @@ -0,0 +1,65 @@ +package org.bouncycastle.cms; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Iterator; + +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1Sequence; + +public class PKCS7ProcessableObject + implements CMSTypedData +{ + private final ASN1ObjectIdentifier type; + private final ASN1Encodable structure; + + public PKCS7ProcessableObject( + ASN1ObjectIdentifier type, + ASN1Encodable structure) + { + this.type = type; + this.structure = structure; + } + + public ASN1ObjectIdentifier getContentType() + { + return type; + } + + public void write(OutputStream cOut) + throws IOException, CMSException + { + if (structure instanceof ASN1Sequence) + { + ASN1Sequence s = ASN1Sequence.getInstance(structure); + + for (Iterator it = s.iterator(); it.hasNext();) + { + ASN1Encodable enc = (ASN1Encodable)it.next(); + + cOut.write(enc.toASN1Primitive().getEncoded(ASN1Encoding.DER)); + } + } + else + { + byte[] encoded = structure.toASN1Primitive().getEncoded(ASN1Encoding.DER); + int index = 1; + + while ((encoded[index] & 0xff) > 127) + { + index++; + } + + index++; + + cOut.write(encoded, index, encoded.length - index); + } + } + + public Object getContent() + { + return structure; + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/PKCS7TypedStream.java b/bcpkix/src/main/java/org/bouncycastle/cms/PKCS7TypedStream.java new file mode 100644 index 00000000..4128e542 --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cms/PKCS7TypedStream.java @@ -0,0 +1,62 @@ +package org.bouncycastle.cms; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; + +public class PKCS7TypedStream + extends CMSTypedStream +{ + private final ASN1Encodable content; + + public PKCS7TypedStream(ASN1ObjectIdentifier oid, ASN1Encodable encodable) + throws IOException + { + super(oid); + + content = encodable; + } + + public ASN1Encodable getContent() + { + return content; + } + + public InputStream getContentStream() + { + try + { + return getContentStream(content); + } + catch (IOException e) + { + throw new CMSRuntimeException("unable to convert content to stream: " + e.getMessage(), e); + } + } + + public void drain() + throws IOException + { + getContentStream(content); // this will parse in the data + } + + private InputStream getContentStream(ASN1Encodable encodable) + throws IOException + { + byte[] encoded = encodable.toASN1Primitive().getEncoded(ASN1Encoding.DER); + int index = 1; + + while ((encoded[index] & 0xff) > 127) + { + index++; + } + + index++; + + return new ByteArrayInputStream(encoded, index, encoded.length - index); + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransAuthenticatedRecipient.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransAuthenticatedRecipient.java new file mode 100644 index 00000000..1d1d2a9a --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransAuthenticatedRecipient.java @@ -0,0 +1,64 @@ +package org.bouncycastle.cms.jcajce; + +import java.io.IOException; +import java.io.OutputStream; +import java.security.Key; +import java.security.PrivateKey; + +import javax.crypto.Mac; + +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.cms.CMSException; +import org.bouncycastle.cms.KeyTransRecipientId; +import org.bouncycastle.cms.RecipientOperator; +import org.bouncycastle.jcajce.io.MacOutputStream; +import org.bouncycastle.operator.GenericKey; +import org.bouncycastle.operator.MacCalculator; +import org.bouncycastle.operator.jcajce.JceGenericKey; + + +/** + * the KeyTransRecipient class for a recipient who has been sent secret + * key material encrypted using their public key that needs to be used to + * derive a key and authenticate a message. + */ +public class JceKTSKeyTransAuthenticatedRecipient + extends JceKTSKeyTransRecipient +{ + public JceKTSKeyTransAuthenticatedRecipient(PrivateKey recipientKey, KeyTransRecipientId recipientId) + throws IOException + { + super(recipientKey, getPartyVInfoFromRID(recipientId)); + } + + public RecipientOperator getRecipientOperator(AlgorithmIdentifier keyEncryptionAlgorithm, final AlgorithmIdentifier contentMacAlgorithm, byte[] encryptedContentEncryptionKey) + throws CMSException + { + final Key secretKey = extractSecretKey(keyEncryptionAlgorithm, contentMacAlgorithm, encryptedContentEncryptionKey); + + final Mac dataMac = contentHelper.createContentMac(secretKey, contentMacAlgorithm); + + return new RecipientOperator(new MacCalculator() + { + public AlgorithmIdentifier getAlgorithmIdentifier() + { + return contentMacAlgorithm; + } + + public GenericKey getKey() + { + return new JceGenericKey(contentMacAlgorithm, secretKey); + } + + public OutputStream getOutputStream() + { + return new MacOutputStream(dataMac); + } + + public byte[] getMac() + { + return dataMac.doFinal(); + } + }); + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransEnvelopedRecipient.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransEnvelopedRecipient.java new file mode 100644 index 00000000..8568f820 --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransEnvelopedRecipient.java @@ -0,0 +1,51 @@ +package org.bouncycastle.cms.jcajce; + +import java.io.IOException; +import java.io.InputStream; +import java.security.Key; +import java.security.PrivateKey; + +import javax.crypto.Cipher; + +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.cms.CMSException; +import org.bouncycastle.cms.KeyTransRecipientId; +import org.bouncycastle.cms.RecipientOperator; +import org.bouncycastle.jcajce.io.CipherInputStream; +import org.bouncycastle.operator.InputDecryptor; + +/** + * the KeyTransRecipient class for a recipient who has been sent secret + * key material encrypted using their public key that needs to be used to + * derive a key and extract a message. + */ +public class JceKTSKeyTransEnvelopedRecipient + extends JceKTSKeyTransRecipient +{ + public JceKTSKeyTransEnvelopedRecipient(PrivateKey recipientKey, KeyTransRecipientId recipientId) + throws IOException + { + super(recipientKey, getPartyVInfoFromRID(recipientId)); + } + + public RecipientOperator getRecipientOperator(AlgorithmIdentifier keyEncryptionAlgorithm, final AlgorithmIdentifier contentEncryptionAlgorithm, byte[] encryptedContentEncryptionKey) + throws CMSException + { + Key secretKey = extractSecretKey(keyEncryptionAlgorithm, contentEncryptionAlgorithm, encryptedContentEncryptionKey); + + final Cipher dataCipher = contentHelper.createContentCipher(secretKey, contentEncryptionAlgorithm); + + return new RecipientOperator(new InputDecryptor() + { + public AlgorithmIdentifier getAlgorithmIdentifier() + { + return contentEncryptionAlgorithm; + } + + public InputStream getInputStream(InputStream dataIn) + { + return new CipherInputStream(dataIn, dataCipher); + } + }); + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransRecipient.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransRecipient.java new file mode 100644 index 00000000..01c57912 --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransRecipient.java @@ -0,0 +1,169 @@ +package org.bouncycastle.cms.jcajce; + +import java.io.IOException; +import java.security.Key; +import java.security.PrivateKey; +import java.security.Provider; +import java.util.HashMap; +import java.util.Map; + +import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.cms.IssuerAndSerialNumber; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.cms.CMSException; +import org.bouncycastle.cms.KeyTransRecipient; +import org.bouncycastle.cms.KeyTransRecipientId; +import org.bouncycastle.operator.OperatorException; +import org.bouncycastle.operator.jcajce.JceKTSKeyUnwrapper; +import org.bouncycastle.util.encoders.Hex; + +public abstract class JceKTSKeyTransRecipient + implements KeyTransRecipient +{ + private static final byte[] ANONYMOUS_SENDER = Hex.decode("0c14416e6f6e796d6f75732053656e64657220202020"); // "Anonymous Sender " + private final byte[] partyVInfo; + + private PrivateKey recipientKey; + + protected EnvelopedDataHelper helper = new EnvelopedDataHelper(new DefaultJcaJceExtHelper()); + protected EnvelopedDataHelper contentHelper = helper; + protected Map extraMappings = new HashMap(); + protected boolean validateKeySize = false; + protected boolean unwrappedKeyMustBeEncodable; + + public JceKTSKeyTransRecipient(PrivateKey recipientKey, byte[] partyVInfo) + { + this.recipientKey = recipientKey; + this.partyVInfo = partyVInfo; + } + + /** + * Set the provider to use for key recovery and content processing. + * + * @param provider provider to use. + * @return this recipient. + */ + public JceKTSKeyTransRecipient setProvider(Provider provider) + { + this.helper = new EnvelopedDataHelper(new ProviderJcaJceExtHelper(provider)); + this.contentHelper = helper; + + return this; + } + + /** + * Set the provider to use for key recovery and content processing. + * + * @param providerName the name of the provider to use. + * @return this recipient. + */ + public JceKTSKeyTransRecipient setProvider(String providerName) + { + this.helper = new EnvelopedDataHelper(new NamedJcaJceExtHelper(providerName)); + this.contentHelper = helper; + + return this; + } + + /** + * Internally algorithm ids are converted into cipher names using a lookup table. For some providers + * the standard lookup table won't work. Use this method to establish a specific mapping from an + * algorithm identifier to a specific algorithm. + * <p> + * For example: + * <pre> + * unwrapper.setAlgorithmMapping(PKCSObjectIdentifiers.rsaEncryption, "RSA"); + * </pre> + * </p> + * @param algorithm OID of algorithm in recipient. + * @param algorithmName JCE algorithm name to use. + * @return the current Recipient. + */ + public JceKTSKeyTransRecipient setAlgorithmMapping(ASN1ObjectIdentifier algorithm, String algorithmName) + { + extraMappings.put(algorithm, algorithmName); + + return this; + } + + /** + * Set the provider to use for content processing. If providerName is null a "no provider" search will be + * used to satisfy getInstance calls. + * + * @param provider the provider to use. + * @return this recipient. + */ + public JceKTSKeyTransRecipient setContentProvider(Provider provider) + { + this.contentHelper = CMSUtils.createContentHelper(provider); + + return this; + } + + /** + * Set the provider to use for content processing. If providerName is null a "no provider" search will be + * used to satisfy getInstance calls. + * + * @param providerName the name of the provider to use. + * @return this recipient. + */ + public JceKTSKeyTransRecipient setContentProvider(String providerName) + { + this.contentHelper = CMSUtils.createContentHelper(providerName); + + return this; + } + + /** + * Set validation of retrieved key sizes against the algorithm parameters for the encrypted key where possible - default is off. + * <p> + * This setting will not have any affect if the encryption algorithm in the recipient does not specify a particular key size, or + * if the unwrapper is a HSM and the byte encoding of the unwrapped secret key is not available. + * </p> + * @param doValidate true if unwrapped key's should be validated against the content encryption algorithm, false otherwise. + * @return this recipient. + */ + public JceKTSKeyTransRecipient setKeySizeValidation(boolean doValidate) + { + this.validateKeySize = doValidate; + + return this; + } + + protected Key extractSecretKey(AlgorithmIdentifier keyEncryptionAlgorithm, AlgorithmIdentifier encryptedKeyAlgorithm, byte[] encryptedEncryptionKey) + throws CMSException + { + JceKTSKeyUnwrapper unwrapper = helper.createAsymmetricUnwrapper(keyEncryptionAlgorithm, recipientKey, ANONYMOUS_SENDER, partyVInfo); + + try + { + Key key = helper.getJceKey(encryptedKeyAlgorithm.getAlgorithm(), unwrapper.generateUnwrappedKey(encryptedKeyAlgorithm, encryptedEncryptionKey)); + + if (validateKeySize) + { + helper.keySizeCheck(encryptedKeyAlgorithm, key); + } + + return key; + } + catch (OperatorException e) + { + throw new CMSException("exception unwrapping key: " + e.getMessage(), e); + } + } + + protected static byte[] getPartyVInfoFromRID(KeyTransRecipientId recipientId) + throws IOException + { + if (recipientId.getSerialNumber() != null) + { + return new IssuerAndSerialNumber(recipientId.getIssuer(), recipientId.getSerialNumber()).getEncoded(ASN1Encoding.DER); + } + else + { + return new DEROctetString(recipientId.getSubjectKeyIdentifier()).getEncoded(); + } + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransRecipientInfoGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransRecipientInfoGenerator.java new file mode 100644 index 00000000..7a440b4f --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransRecipientInfoGenerator.java @@ -0,0 +1,115 @@ +package org.bouncycastle.cms.jcajce; + +import java.io.IOException; +import java.security.Provider; +import java.security.PublicKey; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; + +import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.cms.IssuerAndSerialNumber; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; +import org.bouncycastle.cms.KeyTransRecipientInfoGenerator; +import org.bouncycastle.operator.jcajce.JceAsymmetricKeyWrapper; +import org.bouncycastle.operator.jcajce.JceKTSKeyWrapper; +import org.bouncycastle.util.encoders.Hex; + +public class JceKTSKeyTransRecipientInfoGenerator + extends KeyTransRecipientInfoGenerator +{ + private static final byte[] ANONYMOUS_SENDER = Hex.decode("0c14416e6f6e796d6f75732053656e64657220202020"); // "Anonymous Sender " + + private JceKTSKeyTransRecipientInfoGenerator(X509Certificate recipientCert, IssuerAndSerialNumber recipientID, String symmetricWrappingAlg, int keySizeInBits) + throws CertificateEncodingException + { + super(recipientID, new JceKTSKeyWrapper(recipientCert, symmetricWrappingAlg, keySizeInBits, ANONYMOUS_SENDER, getEncodedRecipID(recipientID))); + } + + public JceKTSKeyTransRecipientInfoGenerator(X509Certificate recipientCert, String symmetricWrappingAlg, int keySizeInBits) + throws CertificateEncodingException + { + this(recipientCert, new IssuerAndSerialNumber(new JcaX509CertificateHolder(recipientCert).toASN1Structure()), symmetricWrappingAlg, keySizeInBits); + } + + public JceKTSKeyTransRecipientInfoGenerator(byte[] subjectKeyIdentifier, PublicKey publicKey, String symmetricWrappingAlg, int keySizeInBits) + { + super(subjectKeyIdentifier, new JceKTSKeyWrapper(publicKey, symmetricWrappingAlg, keySizeInBits, ANONYMOUS_SENDER, getEncodedSubKeyId(subjectKeyIdentifier))); + } + + private static byte[] getEncodedRecipID(IssuerAndSerialNumber recipientID) + throws CertificateEncodingException + { + try + { + return recipientID.getEncoded(ASN1Encoding.DER); + } + catch (final IOException e) + { + throw new CertificateEncodingException("Cannot process extracted IssuerAndSerialNumber: " + e.getMessage()) + { + public Throwable getCause() + { + return e; + } + }; + } + } + + private static byte[] getEncodedSubKeyId(byte[] subjectKeyIdentifier) + { + try + { + return new DEROctetString(subjectKeyIdentifier).getEncoded(); + } + catch (final IOException e) + { + throw new IllegalArgumentException("Cannot process subject key identifier: " + e.getMessage()) + { + public Throwable getCause() + { + return e; + } + }; + } + } + + /** + * Create a generator overriding the algorithm type implied by the public key in the certificate passed in. + * + * @param recipientCert certificate carrying the public key. + * @param algorithmIdentifier the identifier and parameters for the encryption algorithm to be used. + */ + public JceKTSKeyTransRecipientInfoGenerator(X509Certificate recipientCert, AlgorithmIdentifier algorithmIdentifier) + throws CertificateEncodingException + { + super(new IssuerAndSerialNumber(new JcaX509CertificateHolder(recipientCert).toASN1Structure()), new JceAsymmetricKeyWrapper(algorithmIdentifier, recipientCert.getPublicKey())); + } + + /** + * Create a generator overriding the algorithm type implied by the public key passed in. + * + * @param subjectKeyIdentifier the subject key identifier value to associate with the public key. + * @param algorithmIdentifier the identifier and parameters for the encryption algorithm to be used. + * @param publicKey the public key to use. + */ + public JceKTSKeyTransRecipientInfoGenerator(byte[] subjectKeyIdentifier, AlgorithmIdentifier algorithmIdentifier, PublicKey publicKey) + { + super(subjectKeyIdentifier, new JceAsymmetricKeyWrapper(algorithmIdentifier, publicKey)); + } + + public JceKTSKeyTransRecipientInfoGenerator setProvider(String providerName) + { + ((JceKTSKeyWrapper)this.wrapper).setProvider(providerName); + + return this; + } + + public JceKTSKeyTransRecipientInfoGenerator setProvider(Provider provider) + { + ((JceKTSKeyWrapper)this.wrapper).setProvider(provider); + + return this; + } +}
\ No newline at end of file diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/KeyMaterialGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/KeyMaterialGenerator.java new file mode 100644 index 00000000..b5170f2f --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/KeyMaterialGenerator.java @@ -0,0 +1,8 @@ +package org.bouncycastle.cms.jcajce; + +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; + +interface KeyMaterialGenerator +{ + byte[] generateKDFMaterial(AlgorithmIdentifier keyAlgorithm, int keySize, byte[] userKeyMaterialParameters); +} diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/RFC5753KeyMaterialGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/RFC5753KeyMaterialGenerator.java new file mode 100644 index 00000000..a07eedb1 --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/RFC5753KeyMaterialGenerator.java @@ -0,0 +1,26 @@ +package org.bouncycastle.cms.jcajce; + +import java.io.IOException; + +import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.cms.ecc.ECCCMSSharedInfo; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.util.Pack; + +class RFC5753KeyMaterialGenerator + implements KeyMaterialGenerator +{ + public byte[] generateKDFMaterial(AlgorithmIdentifier keyAlgorithm, int keySize, byte[] userKeyMaterialParameters) + { + ECCCMSSharedInfo eccInfo = new ECCCMSSharedInfo(keyAlgorithm, userKeyMaterialParameters, Pack.intToBigEndian(keySize)); + + try + { + return eccInfo.getEncoded(ASN1Encoding.DER); + } + catch (IOException e) + { + throw new IllegalStateException("Unable to create KDF material: " + e); + } + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/mozilla/jcajce/JcaSignedPublicKeyAndChallenge.java b/bcpkix/src/main/java/org/bouncycastle/mozilla/jcajce/JcaSignedPublicKeyAndChallenge.java new file mode 100644 index 00000000..83440fff --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/mozilla/jcajce/JcaSignedPublicKeyAndChallenge.java @@ -0,0 +1,81 @@ +package org.bouncycastle.mozilla.jcajce; + +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Provider; +import java.security.PublicKey; +import java.security.spec.X509EncodedKeySpec; + +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.jcajce.util.DefaultJcaJceHelper; +import org.bouncycastle.jcajce.util.JcaJceHelper; +import org.bouncycastle.jcajce.util.NamedJcaJceHelper; +import org.bouncycastle.jcajce.util.ProviderJcaJceHelper; +import org.bouncycastle.mozilla.SignedPublicKeyAndChallenge; + +/** + * This is designed to parse the SignedPublicKeyAndChallenge created by the + * KEYGEN tag included by Mozilla based browsers. + * <pre> + * PublicKeyAndChallenge ::= SEQUENCE { + * spki SubjectPublicKeyInfo, + * challenge IA5STRING + * } + * + * SignedPublicKeyAndChallenge ::= SEQUENCE { + * publicKeyAndChallenge PublicKeyAndChallenge, + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING + * } + * </pre> + */ +public class JcaSignedPublicKeyAndChallenge + extends SignedPublicKeyAndChallenge +{ + JcaJceHelper helper = new DefaultJcaJceHelper(); + + private JcaSignedPublicKeyAndChallenge(org.bouncycastle.asn1.mozilla.SignedPublicKeyAndChallenge struct, JcaJceHelper helper) + { + super(struct); + this.helper = helper; + } + + public JcaSignedPublicKeyAndChallenge(byte[] bytes) + { + super(bytes); + } + + public JcaSignedPublicKeyAndChallenge setProvider(String providerName) + { + return new JcaSignedPublicKeyAndChallenge(this.spkacSeq, new NamedJcaJceHelper(providerName)); + } + + public JcaSignedPublicKeyAndChallenge setProvider(Provider provider) + { + return new JcaSignedPublicKeyAndChallenge(this.spkacSeq, new ProviderJcaJceHelper(provider)); + } + + public PublicKey getPublicKey() + throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException + { + try + { + SubjectPublicKeyInfo subjectPublicKeyInfo = spkacSeq.getPublicKeyAndChallenge().getSubjectPublicKeyInfo(); + X509EncodedKeySpec xspec = new X509EncodedKeySpec(subjectPublicKeyInfo.getEncoded()); + + + AlgorithmIdentifier keyAlg = subjectPublicKeyInfo.getAlgorithm(); + + KeyFactory factory = helper.createKeyFactory(keyAlg.getAlgorithm().getId()); + + return factory.generatePublic(xspec); + } + catch (Exception e) + { + throw new InvalidKeyException("error encoding public key"); + } + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/CertificateTrustBlock.java b/bcpkix/src/main/java/org/bouncycastle/openssl/CertificateTrustBlock.java new file mode 100644 index 00000000..9a76084b --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/openssl/CertificateTrustBlock.java @@ -0,0 +1,132 @@ +package org.bouncycastle.openssl; + +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1TaggedObject; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.DERTaggedObject; +import org.bouncycastle.asn1.DERUTF8String; + +public class CertificateTrustBlock +{ + private ASN1Sequence uses; + private ASN1Sequence prohibitions; + private String alias; + + public CertificateTrustBlock(Set<ASN1ObjectIdentifier> uses) + { + this(null, uses, null); + } + + public CertificateTrustBlock(String alias, Set<ASN1ObjectIdentifier> uses) + { + this(alias, uses, null); + } + + public CertificateTrustBlock(String alias, Set<ASN1ObjectIdentifier> uses, Set<ASN1ObjectIdentifier> prohibitions) + { + this.alias = alias; + this.uses = toSequence(uses); + this.prohibitions = toSequence(prohibitions); + } + + CertificateTrustBlock(byte[] encoded) + { + ASN1Sequence seq = ASN1Sequence.getInstance(encoded); + + for (Enumeration en = seq.getObjects(); en.hasMoreElements();) + { + ASN1Encodable obj = (ASN1Encodable)en.nextElement(); + + if (obj instanceof ASN1Sequence) + { + this.uses = ASN1Sequence.getInstance(obj); + } + else if (obj instanceof ASN1TaggedObject) + { + this.prohibitions = ASN1Sequence.getInstance((ASN1TaggedObject)obj, false); + } + else if (obj instanceof DERUTF8String) + { + this.alias = DERUTF8String.getInstance(obj).getString(); + } + } + } + + public String getAlias() + { + return alias; + } + + public Set<ASN1ObjectIdentifier> getUses() + { + return toSet(uses); + } + + public Set<ASN1ObjectIdentifier> getProhibitions() + { + return toSet(prohibitions); + } + + private Set<ASN1ObjectIdentifier> toSet(ASN1Sequence seq) + { + if (seq != null) + { + Set<ASN1ObjectIdentifier> oids = new HashSet<ASN1ObjectIdentifier>(seq.size()); + + for (Enumeration en = seq.getObjects(); en.hasMoreElements(); ) + { + oids.add(ASN1ObjectIdentifier.getInstance(en.nextElement())); + } + + return oids; + } + + return Collections.EMPTY_SET; + } + + private ASN1Sequence toSequence(Set<ASN1ObjectIdentifier> oids) + { + if (oids == null || oids.isEmpty()) + { + return null; + } + + ASN1EncodableVector v = new ASN1EncodableVector(); + + for (Iterator it = oids.iterator(); it.hasNext();) + { + v.add((ASN1Encodable)it.next()); + } + + return new DERSequence(v); + } + + ASN1Sequence toASN1Sequence() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (uses != null) + { + v.add(uses); + } + if (prohibitions != null) + { + v.add(new DERTaggedObject(false, 0, prohibitions)); + } + if (alias != null) + { + v.add(new DERUTF8String(alias)); + } + + return new DERSequence(v); + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/X509TrustedCertificateBlock.java b/bcpkix/src/main/java/org/bouncycastle/openssl/X509TrustedCertificateBlock.java new file mode 100644 index 00000000..709af31a --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/openssl/X509TrustedCertificateBlock.java @@ -0,0 +1,57 @@ +package org.bouncycastle.openssl; + +import java.io.IOException; + +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.util.Arrays; + +/** + * Holder for an OpenSSL trusted certificate block. + */ +public class X509TrustedCertificateBlock +{ + private final X509CertificateHolder certificateHolder; + private final CertificateTrustBlock trustBlock; + + public X509TrustedCertificateBlock(X509CertificateHolder certificateHolder, CertificateTrustBlock trustBlock) + { + this.certificateHolder = certificateHolder; + this.trustBlock = trustBlock; + } + + public X509TrustedCertificateBlock(byte[] encoding) + throws IOException + { + ASN1InputStream aIn = new ASN1InputStream(encoding); + + this.certificateHolder = new X509CertificateHolder(aIn.readObject().getEncoded()); + this.trustBlock = new CertificateTrustBlock(aIn.readObject().getEncoded()); + } + + public byte[] getEncoded() + throws IOException + { + return Arrays.concatenate(certificateHolder.getEncoded(), trustBlock.toASN1Sequence().getEncoded()); + } + + /** + * Return the certificate associated with this Trusted Certificate + * + * @return the certificate holder. + */ + public X509CertificateHolder getCertificateHolder() + { + return certificateHolder; + } + + /** + * Return the trust block associated with this Trusted Certificate + * + * @return the trust block. + */ + public CertificateTrustBlock getTrustBlock() + { + return trustBlock; + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/bc/BcPEMDecryptorProvider.java b/bcpkix/src/main/java/org/bouncycastle/openssl/bc/BcPEMDecryptorProvider.java new file mode 100644 index 00000000..d387da8f --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/openssl/bc/BcPEMDecryptorProvider.java @@ -0,0 +1,34 @@ +package org.bouncycastle.openssl.bc; + +import org.bouncycastle.openssl.PEMDecryptor; +import org.bouncycastle.openssl.PEMDecryptorProvider; +import org.bouncycastle.openssl.PEMException; +import org.bouncycastle.openssl.PasswordException; + +public class BcPEMDecryptorProvider + implements PEMDecryptorProvider +{ + private final char[] password; + + public BcPEMDecryptorProvider(char[] password) + { + this.password = password; + } + + public PEMDecryptor get(final String dekAlgName) + { + return new PEMDecryptor() + { + public byte[] decrypt(byte[] keyBytes, byte[] iv) + throws PEMException + { + if (password == null) + { + throw new PasswordException("Password is null, but a password is required"); + } + + return PEMUtilities.crypt(false, keyBytes, password, dekAlgName, iv); + } + }; + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/bc/PEMUtilities.java b/bcpkix/src/main/java/org/bouncycastle/openssl/bc/PEMUtilities.java new file mode 100644 index 00000000..e5829011 --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/openssl/bc/PEMUtilities.java @@ -0,0 +1,297 @@ +package org.bouncycastle.openssl.bc; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.BufferedBlockCipher; +import org.bouncycastle.crypto.PBEParametersGenerator; +import org.bouncycastle.crypto.digests.SHA1Digest; +import org.bouncycastle.crypto.engines.AESFastEngine; +import org.bouncycastle.crypto.engines.BlowfishEngine; +import org.bouncycastle.crypto.engines.DESEngine; +import org.bouncycastle.crypto.engines.DESedeEngine; +import org.bouncycastle.crypto.engines.RC2Engine; +import org.bouncycastle.crypto.generators.OpenSSLPBEParametersGenerator; +import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator; +import org.bouncycastle.crypto.modes.CBCBlockCipher; +import org.bouncycastle.crypto.modes.CFBBlockCipher; +import org.bouncycastle.crypto.modes.OFBBlockCipher; +import org.bouncycastle.crypto.paddings.BlockCipherPadding; +import org.bouncycastle.crypto.paddings.PKCS7Padding; +import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.crypto.params.RC2Parameters; +import org.bouncycastle.openssl.EncryptionException; +import org.bouncycastle.openssl.PEMException; +import org.bouncycastle.util.Integers; + +class PEMUtilities +{ + private static final Map KEYSIZES = new HashMap(); + private static final Set PKCS5_SCHEME_1 = new HashSet(); + private static final Set PKCS5_SCHEME_2 = new HashSet(); + + static + { + PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD2AndDES_CBC); + PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD2AndRC2_CBC); + PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD5AndDES_CBC); + PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithMD5AndRC2_CBC); + PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithSHA1AndDES_CBC); + PKCS5_SCHEME_1.add(PKCSObjectIdentifiers.pbeWithSHA1AndRC2_CBC); + + PKCS5_SCHEME_2.add(PKCSObjectIdentifiers.id_PBES2); + PKCS5_SCHEME_2.add(PKCSObjectIdentifiers.des_EDE3_CBC); + PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes128_CBC); + PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes192_CBC); + PKCS5_SCHEME_2.add(NISTObjectIdentifiers.id_aes256_CBC); + + KEYSIZES.put(PKCSObjectIdentifiers.des_EDE3_CBC.getId(), Integers.valueOf(192)); + KEYSIZES.put(NISTObjectIdentifiers.id_aes128_CBC.getId(), Integers.valueOf(128)); + KEYSIZES.put(NISTObjectIdentifiers.id_aes192_CBC.getId(), Integers.valueOf(192)); + KEYSIZES.put(NISTObjectIdentifiers.id_aes256_CBC.getId(), Integers.valueOf(256)); + KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC4.getId(), Integers.valueOf(128)); + KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC4, Integers.valueOf(40)); + KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd2_KeyTripleDES_CBC, Integers.valueOf(128)); + KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd3_KeyTripleDES_CBC, Integers.valueOf(192)); + KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC2_CBC, Integers.valueOf(128)); + KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC2_CBC, Integers.valueOf(40)); + } + + static int getKeySize(String algorithm) + { + if (!KEYSIZES.containsKey(algorithm)) + { + throw new IllegalStateException("no key size for algorithm: " + algorithm); + } + + return ((Integer)KEYSIZES.get(algorithm)).intValue(); + } + + static boolean isPKCS5Scheme1(ASN1ObjectIdentifier algOid) + { + return PKCS5_SCHEME_1.contains(algOid); + } + + static boolean isPKCS5Scheme2(ASN1ObjectIdentifier algOid) + { + return PKCS5_SCHEME_2.contains(algOid); + } + + public static boolean isPKCS12(ASN1ObjectIdentifier algOid) + { + return algOid.getId().startsWith(PKCSObjectIdentifiers.pkcs_12PbeIds.getId()); + } + + public static KeyParameter generateSecretKeyForPKCS5Scheme2(String algorithm, char[] password, byte[] salt, int iterationCount) + { + PBEParametersGenerator paramsGen = new PKCS5S2ParametersGenerator(new SHA1Digest()); + + paramsGen.init(PBEParametersGenerator.PKCS5PasswordToBytes(password), salt, iterationCount); + + return (KeyParameter)paramsGen.generateDerivedParameters(PEMUtilities.getKeySize(algorithm)); + } + + static byte[] crypt( + boolean encrypt, + byte[] bytes, + char[] password, + String dekAlgName, + byte[] iv) + throws PEMException + { + byte[] ivValue = iv; + String blockMode = "CBC"; + BlockCipher engine; + BlockCipherPadding padding = new PKCS7Padding(); + KeyParameter sKey; + + // Figure out block mode and padding. + if (dekAlgName.endsWith("-CFB")) + { + blockMode = "CFB"; + padding = null; + } + if (dekAlgName.endsWith("-ECB") || + "DES-EDE".equals(dekAlgName) || + "DES-EDE3".equals(dekAlgName)) + { + // ECB is actually the default (though seldom used) when OpenSSL + // uses DES-EDE (des2) or DES-EDE3 (des3). + blockMode = "ECB"; + ivValue = null; + } + if (dekAlgName.endsWith("-OFB")) + { + blockMode = "OFB"; + padding = null; + } + + // Figure out algorithm and key size. + if (dekAlgName.startsWith("DES-EDE")) + { + // "DES-EDE" is actually des2 in OpenSSL-speak! + // "DES-EDE3" is des3. + boolean des2 = !dekAlgName.startsWith("DES-EDE3"); + sKey = getKey(password, 24, iv, des2); + engine = new DESedeEngine(); + } + else if (dekAlgName.startsWith("DES-")) + { + sKey = getKey(password, 8, iv); + engine = new DESEngine(); + } + else if (dekAlgName.startsWith("BF-")) + { + sKey = getKey(password, 16, iv); + engine = new BlowfishEngine(); + } + else if (dekAlgName.startsWith("RC2-")) + { + int keyBits = 128; + if (dekAlgName.startsWith("RC2-40-")) + { + keyBits = 40; + } + else if (dekAlgName.startsWith("RC2-64-")) + { + keyBits = 64; + } + sKey = new RC2Parameters(getKey(password, keyBits / 8, iv).getKey(), keyBits);; + engine = new RC2Engine(); + } + else if (dekAlgName.startsWith("AES-")) + { + byte[] salt = iv; + if (salt.length > 8) + { + salt = new byte[8]; + System.arraycopy(iv, 0, salt, 0, 8); + } + + int keyBits; + if (dekAlgName.startsWith("AES-128-")) + { + keyBits = 128; + } + else if (dekAlgName.startsWith("AES-192-")) + { + keyBits = 192; + } + else if (dekAlgName.startsWith("AES-256-")) + { + keyBits = 256; + } + else + { + throw new EncryptionException("unknown AES encryption with private key: " + dekAlgName); + } + sKey = getKey(password, keyBits / 8, salt); + engine = new AESFastEngine(); + } + else + { + throw new EncryptionException("unknown encryption with private key: " + dekAlgName); + } + + if (blockMode.equals("CBC")) + { + engine = new CBCBlockCipher(engine); + } + else if (blockMode.equals("CFB")) + { + engine = new CFBBlockCipher(engine, engine.getBlockSize() * 8); + } + else if (blockMode.equals("OFB")) + { + engine = new OFBBlockCipher(engine, engine.getBlockSize() * 8); + } + + try + { + BufferedBlockCipher c; + if (padding == null) + { + c = new BufferedBlockCipher(engine); + } + else + { + c = new PaddedBufferedBlockCipher(engine, padding); + } + + if (ivValue == null) // ECB block mode + { + c.init(encrypt, sKey); + } + else + { + c.init(encrypt, new ParametersWithIV(sKey, ivValue)); + } + + byte[] out = new byte[c.getOutputSize(bytes.length)]; + + int procLen = c.processBytes(bytes, 0, bytes.length, out, 0); + + procLen += c.doFinal(out, procLen); + + if (procLen == out.length) + { + return out; + } + else + { + byte[] rv = new byte[procLen]; + + System.arraycopy(out, 0, rv, 0, procLen); + + return rv; + } + } + catch (Exception e) + { + throw new EncryptionException("exception using cipher - please check password and data.", e); + } + } + + private static KeyParameter getKey( + char[] password, + int keyLength, + byte[] salt) + throws PEMException + { + return getKey(password, keyLength, salt, false); + } + + private static KeyParameter getKey( + char[] password, + int keyLength, + byte[] salt, + boolean des2) + throws PEMException + { + PBEParametersGenerator paramsGen = new OpenSSLPBEParametersGenerator(); + + paramsGen.init(PBEParametersGenerator.PKCS5PasswordToBytes(password), salt, 1); + + KeyParameter kp = (KeyParameter)paramsGen.generateDerivedParameters(keyLength * 8); + + if (des2 && kp.getKey().length == 24) + { + // For DES2, we must copy first 8 bytes into the last 8 bytes. + byte[] key = kp.getKey(); + + System.arraycopy(key, 0, key, 16, 8); + + return new KeyParameter(key); + } + + return kp; + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcECContentSignerBuilder.java b/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcECContentSignerBuilder.java new file mode 100644 index 00000000..baf866b5 --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcECContentSignerBuilder.java @@ -0,0 +1,25 @@ +package org.bouncycastle.operator.bc; + +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.Signer; +import org.bouncycastle.crypto.signers.DSADigestSigner; +import org.bouncycastle.crypto.signers.ECDSASigner; +import org.bouncycastle.operator.OperatorCreationException; + +public class BcECContentSignerBuilder + extends BcContentSignerBuilder +{ + public BcECContentSignerBuilder(AlgorithmIdentifier sigAlgId, AlgorithmIdentifier digAlgId) + { + super(sigAlgId, digAlgId); + } + + protected Signer createSigner(AlgorithmIdentifier sigAlgId, AlgorithmIdentifier digAlgId) + throws OperatorCreationException + { + Digest dig = digestProvider.get(digAlgId); + + return new DSADigestSigner(new ECDSASigner(), dig); + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcECContentVerifierProviderBuilder.java b/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcECContentVerifierProviderBuilder.java new file mode 100644 index 00000000..0d0d5625 --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcECContentVerifierProviderBuilder.java @@ -0,0 +1,40 @@ +package org.bouncycastle.operator.bc; + +import java.io.IOException; + +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.Signer; +import org.bouncycastle.crypto.params.AsymmetricKeyParameter; +import org.bouncycastle.crypto.signers.DSADigestSigner; +import org.bouncycastle.crypto.signers.ECDSASigner; +import org.bouncycastle.crypto.util.PublicKeyFactory; +import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder; +import org.bouncycastle.operator.OperatorCreationException; + +public class BcECContentVerifierProviderBuilder + extends BcContentVerifierProviderBuilder +{ + private DigestAlgorithmIdentifierFinder digestAlgorithmFinder; + + public BcECContentVerifierProviderBuilder(DigestAlgorithmIdentifierFinder digestAlgorithmFinder) + { + this.digestAlgorithmFinder = digestAlgorithmFinder; + } + + protected Signer createSigner(AlgorithmIdentifier sigAlgId) + throws OperatorCreationException + { + AlgorithmIdentifier digAlg = digestAlgorithmFinder.find(sigAlgId); + Digest dig = digestProvider.get(digAlg); + + return new DSADigestSigner(new ECDSASigner(), dig); + } + + protected AsymmetricKeyParameter extractKeyParameters(SubjectPublicKeyInfo publicKeyInfo) + throws IOException + { + return PublicKeyFactory.createKey(publicKeyInfo); + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceKTSKeyUnwrapper.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceKTSKeyUnwrapper.java new file mode 100644 index 00000000..3e190839 --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceKTSKeyUnwrapper.java @@ -0,0 +1,82 @@ +package org.bouncycastle.operator.jcajce; + +import java.security.Key; +import java.security.PrivateKey; +import java.security.Provider; +import java.util.HashMap; +import java.util.Map; + +import javax.crypto.Cipher; + +import org.bouncycastle.asn1.cms.GenericHybridParameters; +import org.bouncycastle.asn1.cms.RsaKemParameters; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.crypto.util.DEROtherInfo; +import org.bouncycastle.jcajce.spec.KTSParameterSpec; +import org.bouncycastle.jcajce.util.DefaultJcaJceHelper; +import org.bouncycastle.jcajce.util.NamedJcaJceHelper; +import org.bouncycastle.jcajce.util.ProviderJcaJceHelper; +import org.bouncycastle.operator.AsymmetricKeyUnwrapper; +import org.bouncycastle.operator.GenericKey; +import org.bouncycastle.operator.OperatorException; +import org.bouncycastle.util.Arrays; + +public class JceKTSKeyUnwrapper + extends AsymmetricKeyUnwrapper +{ + private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper()); + private Map extraMappings = new HashMap(); + private PrivateKey privKey; + private byte[] partyUInfo; + private byte[] partyVInfo; + + public JceKTSKeyUnwrapper(AlgorithmIdentifier algorithmIdentifier, PrivateKey privKey, byte[] partyUInfo, byte[] partyVInfo) + { + super(algorithmIdentifier); + + this.privKey = privKey; + this.partyUInfo = Arrays.clone(partyUInfo); + this.partyVInfo = Arrays.clone(partyVInfo); + } + + public JceKTSKeyUnwrapper setProvider(Provider provider) + { + this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider)); + + return this; + } + + public JceKTSKeyUnwrapper setProvider(String providerName) + { + this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName)); + + return this; + } + + public GenericKey generateUnwrappedKey(AlgorithmIdentifier encryptedKeyAlgorithm, byte[] encryptedKey) + throws OperatorException + { + GenericHybridParameters params = GenericHybridParameters.getInstance(this.getAlgorithmIdentifier().getParameters()); + Cipher keyCipher = helper.createAsymmetricWrapper(this.getAlgorithmIdentifier().getAlgorithm(), extraMappings); + String symmetricWrappingAlg = helper.getWrappingAlgorithmName(params.getDem().getAlgorithm()); + RsaKemParameters kemParameters = RsaKemParameters.getInstance(params.getKem().getParameters()); + int keySizeInBits = kemParameters.getKeyLength().intValue() * 8; + Key sKey; + + try + { + DEROtherInfo otherInfo = new DEROtherInfo.Builder(params.getDem(), partyUInfo, partyVInfo).build(); + KTSParameterSpec ktsSpec = new KTSParameterSpec.Builder(symmetricWrappingAlg, keySizeInBits, otherInfo.getEncoded()).withKdfAlgorithm(kemParameters.getKeyDerivationFunction()).build(); + + keyCipher.init(Cipher.UNWRAP_MODE, privKey, ktsSpec); + + sKey = keyCipher.unwrap(encryptedKey, helper.getKeyAlgorithmName(encryptedKeyAlgorithm.getAlgorithm()), Cipher.SECRET_KEY); + } + catch (Exception e) + { + throw new OperatorException("Unable to unwrap contents key: " + e.getMessage(), e); + } + + return new JceGenericKey(encryptedKeyAlgorithm, sKey); + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceKTSKeyWrapper.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceKTSKeyWrapper.java new file mode 100644 index 00000000..63194404 --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceKTSKeyWrapper.java @@ -0,0 +1,96 @@ +package org.bouncycastle.operator.jcajce; + +import java.security.Provider; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; +import java.util.HashMap; + +import javax.crypto.Cipher; + +import org.bouncycastle.asn1.cms.GenericHybridParameters; +import org.bouncycastle.asn1.cms.RsaKemParameters; +import org.bouncycastle.asn1.iso.ISOIECObjectIdentifiers; +import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; +import org.bouncycastle.crypto.util.DEROtherInfo; +import org.bouncycastle.jcajce.spec.KTSParameterSpec; +import org.bouncycastle.jcajce.util.DefaultJcaJceHelper; +import org.bouncycastle.jcajce.util.NamedJcaJceHelper; +import org.bouncycastle.jcajce.util.ProviderJcaJceHelper; +import org.bouncycastle.operator.AsymmetricKeyWrapper; +import org.bouncycastle.operator.GenericKey; +import org.bouncycastle.operator.OperatorException; +import org.bouncycastle.util.Arrays; + +public class JceKTSKeyWrapper + extends AsymmetricKeyWrapper +{ + private final String symmetricWrappingAlg; + private final int keySizeInBits; + private final byte[] partyUInfo; + private final byte[] partyVInfo; + + private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper()); + private PublicKey publicKey; + private SecureRandom random; + + public JceKTSKeyWrapper(PublicKey publicKey, String symmetricWrappingAlg, int keySizeInBits, byte[] partyUInfo, byte[] partyVInfo) + { + super(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_rsa_KEM, new GenericHybridParameters(new AlgorithmIdentifier(ISOIECObjectIdentifiers.id_kem_rsa, new RsaKemParameters(new AlgorithmIdentifier(X9ObjectIdentifiers.id_kdf_kdf3, new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256)), (keySizeInBits + 7) / 8)), JceSymmetricKeyWrapper.determineKeyEncAlg(symmetricWrappingAlg, keySizeInBits)))); + + this.publicKey = publicKey; + this.symmetricWrappingAlg = symmetricWrappingAlg; + this.keySizeInBits = keySizeInBits; + this.partyUInfo = Arrays.clone(partyUInfo); + this.partyVInfo = Arrays.clone(partyVInfo); + } + + public JceKTSKeyWrapper(X509Certificate certificate, String symmetricWrappingAlg, int keySizeInBits, byte[] partyUInfo, byte[] partyVInfo) + { + this(certificate.getPublicKey(), symmetricWrappingAlg, keySizeInBits, partyUInfo, partyVInfo); + } + + public JceKTSKeyWrapper setProvider(Provider provider) + { + this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider)); + + return this; + } + + public JceKTSKeyWrapper setProvider(String providerName) + { + this.helper = new OperatorHelper(new NamedJcaJceHelper(providerName)); + + return this; + } + + public JceKTSKeyWrapper setSecureRandom(SecureRandom random) + { + this.random = random; + + return this; + } + + public byte[] generateWrappedKey(GenericKey encryptionKey) + throws OperatorException + { + Cipher keyEncryptionCipher = helper.createAsymmetricWrapper(getAlgorithmIdentifier().getAlgorithm(), new HashMap()); + + try + { + DEROtherInfo otherInfo = new DEROtherInfo.Builder(JceSymmetricKeyWrapper.determineKeyEncAlg(symmetricWrappingAlg, keySizeInBits), partyUInfo, partyVInfo).build(); + KTSParameterSpec ktsSpec = new KTSParameterSpec.Builder(symmetricWrappingAlg, keySizeInBits, otherInfo.getEncoded()).build(); + + keyEncryptionCipher.init(Cipher.WRAP_MODE, publicKey, ktsSpec, random); + + return keyEncryptionCipher.wrap(OperatorUtils.getJceKey(encryptionKey)); + } + catch (Exception e) + { + throw new OperatorException("Unable to wrap contents key: " + e.getMessage(), e); + } + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/tsp/test/GenTimeAccuracyUnitTest.java b/bcpkix/src/main/java/org/bouncycastle/tsp/test/GenTimeAccuracyUnitTest.java new file mode 100644 index 00000000..a809f071 --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/tsp/test/GenTimeAccuracyUnitTest.java @@ -0,0 +1,106 @@ +package org.bouncycastle.tsp.test; + +import junit.framework.TestCase; +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.tsp.Accuracy; +import org.bouncycastle.tsp.GenTimeAccuracy; + +public class GenTimeAccuracyUnitTest + extends TestCase +{ + private static final ASN1Integer ZERO_VALUE = new ASN1Integer(0); + private static final ASN1Integer ONE_VALUE = new ASN1Integer(1); + private static final ASN1Integer TWO_VALUE = new ASN1Integer(2); + private static final ASN1Integer THREE_VALUE = new ASN1Integer(3); + + public void testOneTwoThree() + { + GenTimeAccuracy accuracy = new GenTimeAccuracy(new Accuracy(ONE_VALUE, TWO_VALUE, THREE_VALUE)); + + checkValues(accuracy, ONE_VALUE, TWO_VALUE, THREE_VALUE); + + checkToString(accuracy, "1.002003"); + } + + public void testThreeTwoOne() + { + GenTimeAccuracy accuracy = new GenTimeAccuracy(new Accuracy(THREE_VALUE, TWO_VALUE, ONE_VALUE)); + + checkValues(accuracy, THREE_VALUE, TWO_VALUE, ONE_VALUE); + + checkToString(accuracy, "3.002001"); + } + + public void testTwoThreeTwo() + { + GenTimeAccuracy accuracy = new GenTimeAccuracy(new Accuracy(TWO_VALUE, THREE_VALUE, TWO_VALUE)); + + checkValues(accuracy, TWO_VALUE, THREE_VALUE, TWO_VALUE); + + checkToString(accuracy, "2.003002"); + } + + + public void testZeroTwoThree() + { + GenTimeAccuracy accuracy = new GenTimeAccuracy(new Accuracy(ZERO_VALUE, TWO_VALUE, THREE_VALUE)); + + checkValues(accuracy, ZERO_VALUE, TWO_VALUE, THREE_VALUE); + + checkToString(accuracy, "0.002003"); + } + + public void testThreeTwoNull() + { + GenTimeAccuracy accuracy = new GenTimeAccuracy(new Accuracy(THREE_VALUE, TWO_VALUE, null)); + + checkValues(accuracy, THREE_VALUE, TWO_VALUE, ZERO_VALUE); + + checkToString(accuracy, "3.002000"); + } + + public void testOneNullOne() + { + GenTimeAccuracy accuracy = new GenTimeAccuracy(new Accuracy(ONE_VALUE, null, ONE_VALUE)); + + checkValues(accuracy, ONE_VALUE, ZERO_VALUE, ONE_VALUE); + + checkToString(accuracy, "1.000001"); + } + + public void testZeroNullNull() + { + GenTimeAccuracy accuracy = new GenTimeAccuracy(new Accuracy(ZERO_VALUE, null, null)); + + checkValues(accuracy, ZERO_VALUE, ZERO_VALUE, ZERO_VALUE); + + checkToString(accuracy, "0.000000"); + } + + public void testNullNullNull() + { + GenTimeAccuracy accuracy = new GenTimeAccuracy(new Accuracy(null, null, null)); + + checkValues(accuracy, ZERO_VALUE, ZERO_VALUE, ZERO_VALUE); + + checkToString(accuracy, "0.000000"); + } + + private void checkValues( + GenTimeAccuracy accuracy, + ASN1Integer secs, + ASN1Integer millis, + ASN1Integer micros) + { + assertEquals(secs.getValue().intValue(), accuracy.getSeconds()); + assertEquals(millis.getValue().intValue(), accuracy.getMillis()); + assertEquals(micros.getValue().intValue(), accuracy.getMicros()); + } + + private void checkToString( + GenTimeAccuracy accuracy, + String expected) + { + assertEquals(expected, accuracy.toString()); + } +} diff --git a/bcpkix/src/main/java/org/bouncycastle/tsp/test/TimeStampTokenInfoUnitTest.java b/bcpkix/src/main/java/org/bouncycastle/tsp/test/TimeStampTokenInfoUnitTest.java new file mode 100644 index 00000000..61102830 --- /dev/null +++ b/bcpkix/src/main/java/org/bouncycastle/tsp/test/TimeStampTokenInfoUnitTest.java @@ -0,0 +1,161 @@ +package org.bouncycastle.tsp.test; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.math.BigInteger; + +import junit.framework.Assert; +import junit.framework.TestCase; +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.tsp.TSTInfo; +import org.bouncycastle.tsp.GenTimeAccuracy; +import org.bouncycastle.tsp.TSPAlgorithms; +import org.bouncycastle.tsp.TSPException; +import org.bouncycastle.tsp.TimeStampTokenInfo; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.encoders.Hex; + +public class TimeStampTokenInfoUnitTest + extends TestCase +{ + private static final byte[] tstInfo1 = Hex.decode( + "303e02010106022a033021300906052b0e03021a050004140000000000000000000000000000000000000000" + + "020118180f32303035313130313038313732315a"); + + private static final byte[] tstInfo2 = Hex.decode( + "304c02010106022a033021300906052b0e03021a05000414ffffffffffffffffffffffffffffffffffffffff" + + "020117180f32303035313130313038323934355a3009020103800101810102020164"); + + private static final byte[] tstInfo3 = Hex.decode( + "304f02010106022a033021300906052b0e03021a050004140000000000000000000000000000000000000000" + + "020117180f32303035313130313038343733355a30090201038001018101020101ff020164"); + + private static final byte[] tstInfoDudDate = Hex.decode( + "303e02010106022a033021300906052b0e03021a050004140000000000000000000000000000000000000000" + + "020118180f32303056313130313038313732315a"); + + public void testTstInfo1() + throws Exception + { + TimeStampTokenInfo tstInfo = getTimeStampTokenInfo(tstInfo1); + + // + // verify + // + GenTimeAccuracy accuracy = tstInfo.getGenTimeAccuracy(); + + assertNull(accuracy); + + assertEquals(new BigInteger("24"), tstInfo.getSerialNumber()); + + assertEquals(1130833041000L, tstInfo.getGenTime().getTime()); + + assertEquals("1.2.3", tstInfo.getPolicy().getId()); + + assertEquals(false, tstInfo.isOrdered()); + + assertNull(tstInfo.getNonce()); + + Assert.assertEquals(TSPAlgorithms.SHA1, tstInfo.getMessageImprintAlgOID()); + + assertTrue(Arrays.areEqual(new byte[20], tstInfo.getMessageImprintDigest())); + + assertTrue(Arrays.areEqual(tstInfo1, tstInfo.getEncoded())); + } + + public void testTstInfo2() + throws Exception + { + TimeStampTokenInfo tstInfo = getTimeStampTokenInfo(tstInfo2); + + // + // verify + // + GenTimeAccuracy accuracy = tstInfo.getGenTimeAccuracy(); + + assertEquals(3, accuracy.getSeconds()); + assertEquals(1, accuracy.getMillis()); + assertEquals(2, accuracy.getMicros()); + + assertEquals(new BigInteger("23"), tstInfo.getSerialNumber()); + + assertEquals(1130833785000L, tstInfo.getGenTime().getTime()); + + assertEquals("1.2.3", tstInfo.getPolicy().getId()); + + assertEquals(false, tstInfo.isOrdered()); + + assertEquals(tstInfo.getNonce(), BigInteger.valueOf(100)); + + assertTrue(Arrays.areEqual(Hex.decode("ffffffffffffffffffffffffffffffffffffffff"), tstInfo.getMessageImprintDigest())); + + assertTrue(Arrays.areEqual(tstInfo2, tstInfo.getEncoded())); + } + + public void testTstInfo3() + throws Exception + { + TimeStampTokenInfo tstInfo = getTimeStampTokenInfo(tstInfo3); + + // + // verify + // + GenTimeAccuracy accuracy = tstInfo.getGenTimeAccuracy(); + + assertEquals(3, accuracy.getSeconds()); + assertEquals(1, accuracy.getMillis()); + assertEquals(2, accuracy.getMicros()); + + assertEquals(new BigInteger("23"), tstInfo.getSerialNumber()); + + assertEquals(1130834855000L, tstInfo.getGenTime().getTime()); + + assertEquals("1.2.3", tstInfo.getPolicy().getId()); + + assertEquals(true, tstInfo.isOrdered()); + + assertEquals(tstInfo.getNonce(), BigInteger.valueOf(100)); + + assertEquals(TSPAlgorithms.SHA1, tstInfo.getMessageImprintAlgOID()); + + assertTrue(Arrays.areEqual(new byte[20], tstInfo.getMessageImprintDigest())); + + assertTrue(Arrays.areEqual(tstInfo3, tstInfo.getEncoded())); + } + + public void testTstInfoDudDate() + throws Exception + { + try + { + getTimeStampTokenInfo(tstInfoDudDate); + + fail("dud date not detected."); + } + catch (TSPException e) + { + // expected + } + } + + private TimeStampTokenInfo getTimeStampTokenInfo( + byte[] tstInfo) + throws Exception + { + ASN1InputStream aIn = new ASN1InputStream(tstInfo); + TSTInfo info = TSTInfo.getInstance(aIn.readObject()); + + final Constructor constructor = TimeStampTokenInfo.class.getDeclaredConstructor(TSTInfo.class); + + constructor.setAccessible(true); + + try + { + return (TimeStampTokenInfo)constructor.newInstance(new Object[]{info}); + } + catch (InvocationTargetException e) + { + throw (Exception)e.getTargetException(); + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecific.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecific.java new file mode 100644 index 00000000..fca4e01a --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1ApplicationSpecific.java @@ -0,0 +1,226 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +import org.bouncycastle.util.Arrays; + +/** + * Base class for an application specific object + */ +public abstract class ASN1ApplicationSpecific + extends ASN1Primitive +{ + protected final boolean isConstructed; + protected final int tag; + protected final byte[] octets; + + ASN1ApplicationSpecific( + boolean isConstructed, + int tag, + byte[] octets) + { + this.isConstructed = isConstructed; + this.tag = tag; + this.octets = octets; + } + + /** + * Return an ASN1ApplicationSpecific from the passed in object, which may be a byte array, or null. + * + * @param obj the object to be converted. + * @return obj's representation as an ASN1ApplicationSpecific object. + */ + public static ASN1ApplicationSpecific getInstance(Object obj) + { + if (obj == null || obj instanceof ASN1ApplicationSpecific) + { + return (ASN1ApplicationSpecific)obj; + } + else if (obj instanceof byte[]) + { + try + { + return ASN1ApplicationSpecific.getInstance(ASN1Primitive.fromByteArray((byte[])obj)); + } + catch (IOException e) + { + throw new IllegalArgumentException("Failed to construct object from byte[]: " + e.getMessage()); + } + } + + throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName()); + } + + protected static int getLengthOfHeader(byte[] data) + { + int length = data[1] & 0xff; // TODO: assumes 1 byte tag + + if (length == 0x80) + { + return 2; // indefinite-length encoding + } + + if (length > 127) + { + int size = length & 0x7f; + + // Note: The invalid long form "0xff" (see X.690 8.1.3.5c) will be caught here + if (size > 4) + { + throw new IllegalStateException("DER length more than 4 bytes: " + size); + } + + return size + 2; + } + + return 2; + } + + /** + * Return true if the object is marked as constructed, false otherwise. + * + * @return true if constructed, otherwise false. + */ + public boolean isConstructed() + { + return isConstructed; + } + + /** + * Return the contents of this object as a byte[] + * + * @return the encoded contents of the object. + */ + public byte[] getContents() + { + return octets; + } + + /** + * Return the tag number associated with this object, + * + * @return the application tag number. + */ + public int getApplicationTag() + { + return tag; + } + + /** + * Return the enclosed object assuming explicit tagging. + * + * @return the resulting object + * @throws IOException if reconstruction fails. + */ + public ASN1Primitive getObject() + throws IOException + { + return new ASN1InputStream(getContents()).readObject(); + } + + /** + * Return the enclosed object assuming implicit tagging. + * + * @param derTagNo the type tag that should be applied to the object's contents. + * @return the resulting object + * @throws IOException if reconstruction fails. + */ + public ASN1Primitive getObject(int derTagNo) + throws IOException + { + if (derTagNo >= 0x1f) + { + throw new IOException("unsupported tag number"); + } + + byte[] orig = this.getEncoded(); + byte[] tmp = replaceTagNumber(derTagNo, orig); + + if ((orig[0] & BERTags.CONSTRUCTED) != 0) + { + tmp[0] |= BERTags.CONSTRUCTED; + } + + return new ASN1InputStream(tmp).readObject(); + } + + int encodedLength() + throws IOException + { + return StreamUtil.calculateTagLength(tag) + StreamUtil.calculateBodyLength(octets.length) + octets.length; + } + + /* (non-Javadoc) + * @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream) + */ + void encode(ASN1OutputStream out) throws IOException + { + int classBits = BERTags.APPLICATION; + if (isConstructed) + { + classBits |= BERTags.CONSTRUCTED; + } + + out.writeEncoded(classBits, tag, octets); + } + + boolean asn1Equals( + ASN1Primitive o) + { + if (!(o instanceof ASN1ApplicationSpecific)) + { + return false; + } + + ASN1ApplicationSpecific other = (ASN1ApplicationSpecific)o; + + return isConstructed == other.isConstructed + && tag == other.tag + && Arrays.areEqual(octets, other.octets); + } + + public int hashCode() + { + return (isConstructed ? 1 : 0) ^ tag ^ Arrays.hashCode(octets); + } + + private byte[] replaceTagNumber(int newTag, byte[] input) + throws IOException + { + int tagNo = input[0] & 0x1f; + int index = 1; + // + // with tagged object tag number is bottom 5 bits, or stored at the start of the content + // + if (tagNo == 0x1f) + { + tagNo = 0; + + int b = input[index++] & 0xff; + + // X.690-0207 8.1.2.4.2 + // "c) bits 7 to 1 of the first subsequent octet shall not all be zero." + if ((b & 0x7f) == 0) // Note: -1 will pass + { + throw new ASN1ParsingException("corrupted stream - invalid high tag number found"); + } + + while ((b >= 0) && ((b & 0x80) != 0)) + { + tagNo |= (b & 0x7f); + tagNo <<= 7; + b = input[index++] & 0xff; + } + +// tagNo |= (b & 0x7f); + } + + byte[] tmp = new byte[input.length - index + 1]; + + System.arraycopy(input, index, tmp, 1, tmp.length - 1); + + tmp[0] = (byte)newTag; + + return tmp; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ASN1BitString.java b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1BitString.java new file mode 100644 index 00000000..513d4e5b --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/asn1/ASN1BitString.java @@ -0,0 +1,291 @@ +package org.bouncycastle.asn1; + +import java.io.ByteArrayOutputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; + +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.io.Streams; + +/** + * Base class for BIT STRING objects + */ +public abstract class ASN1BitString + extends ASN1Primitive + implements ASN1String +{ + private static final char[] table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + + protected final byte[] data; + protected final int padBits; + + /** + * @param bitString an int containing the BIT STRING + * @return the correct number of pad bits for a bit string defined in + * a 32 bit constant + */ + static protected int getPadBits( + int bitString) + { + int val = 0; + for (int i = 3; i >= 0; i--) + { + // + // this may look a little odd, but if it isn't done like this pre jdk1.2 + // JVM's break! + // + if (i != 0) + { + if ((bitString >> (i * 8)) != 0) + { + val = (bitString >> (i * 8)) & 0xFF; + break; + } + } + else + { + if (bitString != 0) + { + val = bitString & 0xFF; + break; + } + } + } + + if (val == 0) + { + return 0; + } + + int bits = 1; + + while (((val <<= 1) & 0xFF) != 0) + { + bits++; + } + + return 8 - bits; + } + + /** + * @param bitString an int containing the BIT STRING + * @return the correct number of bytes for a bit string defined in + * a 32 bit constant + */ + static protected byte[] getBytes(int bitString) + { + if (bitString == 0) + { + return new byte[0]; + } + + 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; + } + + /** + * Base constructor. + * + * @param data the octets making up the bit string. + * @param padBits the number of extra bits at the end of the string. + */ + public ASN1BitString( + byte[] data, + int padBits) + { + if (data == null) + { + throw new NullPointerException("data cannot be null"); + } + if (data.length == 0 && padBits != 0) + { + throw new IllegalArgumentException("zero length data with non-zero pad bits"); + } + if (padBits > 7 || padBits < 0) + { + throw new IllegalArgumentException("pad bits cannot be greater than 7 or less than 0"); + } + + this.data = Arrays.clone(data); + this.padBits = padBits; + } + + /** + * Return a String representation of this BIT STRING + * + * @return a String representation. + */ + public String getString() + { + StringBuffer buf = new StringBuffer("#"); + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ASN1OutputStream aOut = new ASN1OutputStream(bOut); + + try + { + aOut.writeObject(this); + } + catch (IOException e) + { + throw new ASN1ParsingException("Internal error encoding BitString: " + e.getMessage(), e); + } + + byte[] string = bOut.toByteArray(); + + for (int i = 0; i != string.length; i++) + { + buf.append(table[(string[i] >>> 4) & 0xf]); + buf.append(table[string[i] & 0xf]); + } + + return buf.toString(); + } + + /** + * @return the value of the bit string as an int (truncating if necessary) + */ + public int intValue() + { + int value = 0; + byte[] string = data; + + if (padBits > 0 && data.length <= 4) + { + string = derForm(data, padBits); + } + + for (int i = 0; i != string.length && i != 4; i++) + { + value |= (string[i] & 0xff) << (8 * i); + } + + return value; + } + + /** + * Return the octets contained in this BIT STRING, checking that this BIT STRING really + * does represent an octet aligned string. Only use this method when the standard you are + * following dictates that the BIT STRING will be octet aligned. + * + * @return a copy of the octet aligned data. + */ + public byte[] getOctets() + { + if (padBits != 0) + { + throw new IllegalStateException("attempt to get non-octet aligned data from BIT STRING"); + } + + return Arrays.clone(data); + } + + public byte[] getBytes() + { + return derForm(data, padBits); + } + + public int getPadBits() + { + return padBits; + } + + public String toString() + { + return getString(); + } + + public int hashCode() + { + return padBits ^ Arrays.hashCode(this.getBytes()); + } + + protected boolean asn1Equals( + ASN1Primitive o) + { + if (!(o instanceof ASN1BitString)) + { + return false; + } + + ASN1BitString other = (ASN1BitString)o; + + return this.padBits == other.padBits + && Arrays.areEqual(this.getBytes(), other.getBytes()); + } + + protected static byte[] derForm(byte[] data, int padBits) + { + byte[] rv = Arrays.clone(data); + // DER requires pad bits be zero + if (padBits > 0) + { + rv[data.length - 1] &= 0xff << padBits; + } + + return rv; + } + + static ASN1BitString fromInputStream(int length, InputStream stream) + throws IOException + { + if (length < 1) + { + throw new IllegalArgumentException("truncated BIT STRING detected"); + } + + int padBits = stream.read(); + byte[] data = new byte[length - 1]; + + if (data.length != 0) + { + if (Streams.readFully(stream, data) != data.length) + { + throw new EOFException("EOF encountered in middle of BIT STRING"); + } + + if (padBits > 0 && padBits < 8) + { + if (data[data.length - 1] != (byte)(data[data.length - 1] & (0xff << padBits))) + { + return new DLBitString(data, padBits); + } + } + } + + return new DERBitString(data, padBits); + } + + public ASN1Primitive getLoadedObject() + { + return this.toASN1Primitive(); + } + + ASN1Primitive toDERObject() + { + return new DERBitString(data, padBits); + } + + ASN1Primitive toDLObject() + { + return new DLBitString(data, padBits); + } + + abstract void encode(ASN1OutputStream out) + throws IOException; +} diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERGraphicString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERGraphicString.java new file mode 100644 index 00000000..01baf0f9 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERGraphicString.java @@ -0,0 +1,124 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Strings; + +public class DERGraphicString + extends ASN1Primitive + implements ASN1String +{ + private final byte[] string; + + /** + * return a Graphic String from the passed in object + * + * @param obj a DERGraphicString or an object that can be converted into one. + * @exception IllegalArgumentException if the object cannot be converted. + * @return a DERGraphicString instance, or null. + */ + public static DERGraphicString getInstance( + Object obj) + { + if (obj == null || obj instanceof DERGraphicString) + { + return (DERGraphicString)obj; + } + + if (obj instanceof byte[]) + { + try + { + return (DERGraphicString)fromByteArray((byte[])obj); + } + catch (Exception e) + { + throw new IllegalArgumentException("encoding error in getInstance: " + e.toString()); + } + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return a Graphic String from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + * @return a DERGraphicString instance, or null. + */ + public static DERGraphicString getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + ASN1Primitive o = obj.getObject(); + + if (explicit || o instanceof DERGraphicString) + { + return getInstance(o); + } + else + { + return new DERGraphicString(((ASN1OctetString)o).getOctets()); + } + } + + /** + * basic constructor - with bytes. + * @param string the byte encoding of the characters making up the string. + */ + public DERGraphicString( + byte[] string) + { + this.string = Arrays.clone(string); + } + + public byte[] getOctets() + { + return Arrays.clone(string); + } + + boolean isConstructed() + { + return false; + } + + int encodedLength() + { + return 1 + StreamUtil.calculateBodyLength(string.length) + string.length; + } + + void encode( + ASN1OutputStream out) + throws IOException + { + out.writeEncoded(BERTags.GRAPHIC_STRING, string); + } + + public int hashCode() + { + return Arrays.hashCode(string); + } + + boolean asn1Equals( + ASN1Primitive o) + { + if (!(o instanceof DERGraphicString)) + { + return false; + } + + DERGraphicString s = (DERGraphicString)o; + + return Arrays.areEqual(string, s.string); + } + + public String getString() + { + return Strings.fromByteArray(string); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERVideotexString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERVideotexString.java new file mode 100644 index 00000000..da231e15 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERVideotexString.java @@ -0,0 +1,124 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Strings; + +public class DERVideotexString + extends ASN1Primitive + implements ASN1String +{ + private final byte[] string; + + /** + * return a Videotex String from the passed in object + * + * @param obj a DERVideotexString or an object that can be converted into one. + * @exception IllegalArgumentException if the object cannot be converted. + * @return a DERVideotexString instance, or null. + */ + public static DERVideotexString getInstance( + Object obj) + { + if (obj == null || obj instanceof DERVideotexString) + { + return (DERVideotexString)obj; + } + + if (obj instanceof byte[]) + { + try + { + return (DERVideotexString)fromByteArray((byte[])obj); + } + catch (Exception e) + { + throw new IllegalArgumentException("encoding error in getInstance: " + e.toString()); + } + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return a Videotex String from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + * @return a DERVideotexString instance, or null. + */ + public static DERVideotexString getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + ASN1Primitive o = obj.getObject(); + + if (explicit || o instanceof DERVideotexString) + { + return getInstance(o); + } + else + { + return new DERVideotexString(((ASN1OctetString)o).getOctets()); + } + } + + /** + * basic constructor - with bytes. + * @param string the byte encoding of the characters making up the string. + */ + public DERVideotexString( + byte[] string) + { + this.string = Arrays.clone(string); + } + + public byte[] getOctets() + { + return Arrays.clone(string); + } + + boolean isConstructed() + { + return false; + } + + int encodedLength() + { + return 1 + StreamUtil.calculateBodyLength(string.length) + string.length; + } + + void encode( + ASN1OutputStream out) + throws IOException + { + out.writeEncoded(BERTags.VIDEOTEX_STRING, string); + } + + public int hashCode() + { + return Arrays.hashCode(string); + } + + boolean asn1Equals( + ASN1Primitive o) + { + if (!(o instanceof DERVideotexString)) + { + return false; + } + + DERVideotexString s = (DERVideotexString)o; + + return Arrays.areEqual(string, s.string); + } + + public String getString() + { + return Strings.fromByteArray(string); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DLBitString.java b/bcprov/src/main/java/org/bouncycastle/asn1/DLBitString.java new file mode 100644 index 00000000..c81f0ab9 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/asn1/DLBitString.java @@ -0,0 +1,145 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +/** + * A Definite length BIT STRING + */ +public class DLBitString + extends ASN1BitString +{ + /** + * return a Bit String that can be definite-length encoded from the passed in object. + * + * @param obj a DL or DER BitString or an object that can be converted into one. + * @exception IllegalArgumentException if the object cannot be converted. + * @return an ASN1BitString instance, or null. + */ + public static ASN1BitString getInstance( + Object obj) + { + if (obj == null || obj instanceof DLBitString) + { + return (DLBitString)obj; + } + if (obj instanceof DERBitString) + { + return (DERBitString)obj; + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return a Bit String from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + * @return an ASN1BitString instance, or null. + */ + public static ASN1BitString getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + ASN1Primitive o = obj.getObject(); + + if (explicit || o instanceof DLBitString) + { + return getInstance(o); + } + else + { + return fromOctetString(((ASN1OctetString)o).getOctets()); + } + } + + protected DLBitString( + byte data, + int padBits) + { + this(toByteArray(data), padBits); + } + + private static byte[] toByteArray(byte data) + { + byte[] rv = new byte[1]; + + rv[0] = data; + + return rv; + } + + /** + * @param data the octets making up the bit string. + * @param padBits the number of extra bits at the end of the string. + */ + public DLBitString( + byte[] data, + int padBits) + { + super(data, padBits); + } + + public DLBitString( + byte[] data) + { + this(data, 0); + } + + public DLBitString( + int value) + { + super(getBytes(value), getPadBits(value)); + } + + public DLBitString( + ASN1Encodable obj) + throws IOException + { + super(obj.toASN1Primitive().getEncoded(ASN1Encoding.DER), 0); + } + + boolean isConstructed() + { + return false; + } + + int encodedLength() + { + return 1 + StreamUtil.calculateBodyLength(data.length + 1) + data.length + 1; + } + + void encode( + ASN1OutputStream out) + throws IOException + { + byte[] string = data; + byte[] bytes = new byte[string.length + 1]; + + bytes[0] = (byte)getPadBits(); + System.arraycopy(string, 0, bytes, 1, bytes.length - 1); + + out.writeEncoded(BERTags.BIT_STRING, bytes); + } + + static DLBitString fromOctetString(byte[] bytes) + { + if (bytes.length < 1) + { + throw new IllegalArgumentException("truncated BIT STRING detected"); + } + + int padBits = bytes[0]; + byte[] data = new byte[bytes.length - 1]; + + if (data.length != 0) + { + System.arraycopy(bytes, 1, data, 0, bytes.length - 1); + } + + return new DLBitString(data, padBits); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/anssi/ANSSINamedCurves.java b/bcprov/src/main/java/org/bouncycastle/asn1/anssi/ANSSINamedCurves.java new file mode 100644 index 00000000..87f0a250 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/asn1/anssi/ANSSINamedCurves.java @@ -0,0 +1,120 @@ +package org.bouncycastle.asn1.anssi; + +import java.math.BigInteger; +import java.util.Enumeration; +import java.util.Hashtable; + +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.asn1.x9.X9ECParametersHolder; +import org.bouncycastle.asn1.x9.X9ECPoint; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.util.Strings; +import org.bouncycastle.util.encoders.Hex; + +/** + * ANSSI Elliptic curve table. + */ +public class ANSSINamedCurves +{ + private static ECCurve configureCurve(ECCurve curve) + { + return curve; + } + + private static BigInteger fromHex( + String hex) + { + return new BigInteger(1, Hex.decode(hex)); + } + + /* + * FRP256v1 + */ + static X9ECParametersHolder FRP256v1 = new X9ECParametersHolder() + { + protected X9ECParameters createParameters() + { + BigInteger p = fromHex("F1FD178C0B3AD58F10126DE8CE42435B3961ADBCABC8CA6DE8FCF353D86E9C03"); + BigInteger a = fromHex("F1FD178C0B3AD58F10126DE8CE42435B3961ADBCABC8CA6DE8FCF353D86E9C00"); + BigInteger b = fromHex("EE353FCA5428A9300D4ABA754A44C00FDFEC0C9AE4B1A1803075ED967B7BB73F"); + byte[] S = null; + BigInteger n = fromHex("F1FD178C0B3AD58F10126DE8CE42435B53DC67E140D2BF941FFDD459C6D655E1"); + BigInteger h = BigInteger.valueOf(1); + + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b, n, h)); + X9ECPoint G = new X9ECPoint(curve, Hex.decode("04" + + "B6B3D4C356C139EB31183D4749D423958C27D2DCAF98B70164C97A2DD98F5CFF" + + "6142E0F7C8B204911F9271F0F3ECEF8C2701C307E8E4C9E183115A1554062CFB")); + + return new X9ECParameters(curve, G, n, h, S); + } + }; + + + static final Hashtable objIds = new Hashtable(); + static final Hashtable curves = new Hashtable(); + static final Hashtable names = new Hashtable(); + + static void defineCurve(String name, ASN1ObjectIdentifier oid, X9ECParametersHolder holder) + { + objIds.put(name.toLowerCase(), oid); + names.put(oid, name); + curves.put(oid, holder); + } + + static + { + defineCurve("FRP256v1", ANSSIObjectIdentifiers.FRP256v1, FRP256v1); + } + + public static X9ECParameters getByName( + String name) + { + ASN1ObjectIdentifier oid = getOID(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( + ASN1ObjectIdentifier oid) + { + X9ECParametersHolder holder = (X9ECParametersHolder)curves.get(oid); + return holder == null ? null : holder.getParameters(); + } + + /** + * return the object identifier signified by the passed in name. Null + * if there is no object identifier associated with name. + * + * @return the object identifier associated with name, if present. + */ + public static ASN1ObjectIdentifier getOID( + String name) + { + return (ASN1ObjectIdentifier)objIds.get(Strings.toLowerCase(name)); + } + + /** + * return the named curve name represented by the given object identifier. + */ + public static String getName( + ASN1ObjectIdentifier oid) + { + return (String)names.get(oid); + } + + /** + * returns an enumeration containing the name strings for curves + * contained in this structure. + */ + public static Enumeration getNames() + { + return names.elements(); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/anssi/ANSSIObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/anssi/ANSSIObjectIdentifiers.java new file mode 100644 index 00000000..1cc8d674 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/asn1/anssi/ANSSIObjectIdentifiers.java @@ -0,0 +1,11 @@ +package org.bouncycastle.asn1.anssi; + +import org.bouncycastle.asn1.ASN1ObjectIdentifier; + +/** + * Object Identifiers belong to the French Agency, ANSSI. + */ +public interface ANSSIObjectIdentifiers +{ + static final ASN1ObjectIdentifier FRP256v1 = new ASN1ObjectIdentifier("1.2.250.1.223.101.256.1"); +} diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSAlgorithmProtection.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSAlgorithmProtection.java new file mode 100644 index 00000000..d18fe4bc --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSAlgorithmProtection.java @@ -0,0 +1,136 @@ +package org.bouncycastle.asn1.cms; + +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Object; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1TaggedObject; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.DERTaggedObject; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; + +/** + * From RFC 6211 + * <pre> + * CMSAlgorithmProtection ::= SEQUENCE { + * digestAlgorithm DigestAlgorithmIdentifier, + * signatureAlgorithm [1] SignatureAlgorithmIdentifier OPTIONAL, + * macAlgorithm [2] MessageAuthenticationCodeAlgorithm + * OPTIONAL + * } + * (WITH COMPONENTS { signatureAlgorithm PRESENT, + * macAlgorithm ABSENT } | + * WITH COMPONENTS { signatureAlgorithm ABSENT, + * macAlgorithm PRESENT }) + * </pre> + */ +public class CMSAlgorithmProtection + extends ASN1Object +{ + public static final int SIGNATURE = 1; + public static final int MAC = 2; + + private final AlgorithmIdentifier digestAlgorithm; + private final AlgorithmIdentifier signatureAlgorithm; + private final AlgorithmIdentifier macAlgorithm; + + public CMSAlgorithmProtection(AlgorithmIdentifier digestAlgorithm, int type, AlgorithmIdentifier algorithmIdentifier) + { + if (digestAlgorithm == null || algorithmIdentifier == null) + { + throw new NullPointerException("AlgorithmIdentifiers cannot be null"); + } + + this.digestAlgorithm = digestAlgorithm; + + if (type == 1) + { + this.signatureAlgorithm = algorithmIdentifier; + this.macAlgorithm = null; + } + else if (type == 2) + { + this.signatureAlgorithm = null; + this.macAlgorithm = algorithmIdentifier; + } + else + { + throw new IllegalArgumentException("Unknown type: " + type); + } + } + + private CMSAlgorithmProtection(ASN1Sequence sequence) + { + if (sequence.size() != 2) + { + throw new IllegalArgumentException("Sequence wrong size: One of signatureAlgorithm or macAlgorithm must be present"); + } + + this.digestAlgorithm = AlgorithmIdentifier.getInstance(sequence.getObjectAt(0)); + + ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(sequence.getObjectAt(1)); + if (tagged.getTagNo() == 1) + { + this.signatureAlgorithm = AlgorithmIdentifier.getInstance(tagged, false); + this.macAlgorithm = null; + } + else if (tagged.getTagNo() == 2) + { + this.signatureAlgorithm = null; + + this.macAlgorithm = AlgorithmIdentifier.getInstance(tagged, false); + } + else + { + throw new IllegalArgumentException("Unknown tag found: " + tagged.getTagNo()); + } + } + + public static CMSAlgorithmProtection getInstance( + Object obj) + { + if (obj instanceof CMSAlgorithmProtection) + { + return (CMSAlgorithmProtection)obj; + } + else if (obj != null) + { + return new CMSAlgorithmProtection(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + + public AlgorithmIdentifier getDigestAlgorithm() + { + return digestAlgorithm; + } + + public AlgorithmIdentifier getMacAlgorithm() + { + return macAlgorithm; + } + + public AlgorithmIdentifier getSignatureAlgorithm() + { + return signatureAlgorithm; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(digestAlgorithm); + if (signatureAlgorithm != null) + { + v.add(new DERTaggedObject(false, 1, signatureAlgorithm)); + } + if (macAlgorithm != null) + { + v.add(new DERTaggedObject(false, 2, macAlgorithm)); + } + + return new DERSequence(v); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/GenericHybridParameters.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/GenericHybridParameters.java new file mode 100644 index 00000000..e0157b45 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/GenericHybridParameters.java @@ -0,0 +1,79 @@ +package org.bouncycastle.asn1.cms; + +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Object; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; + +/** + * RFC 5990 GenericHybridParameters class. + * <pre> + * GenericHybridParameters ::= SEQUENCE { + * kem KeyEncapsulationMechanism, + * dem DataEncapsulationMechanism + * } + * + * KeyEncapsulationMechanism ::= AlgorithmIdentifier {{KEMAlgorithms}} + * DataEncapsulationMechanism ::= AlgorithmIdentifier {{DEMAlgorithms}} + * </pre> + */ +public class GenericHybridParameters + extends ASN1Object +{ + private final AlgorithmIdentifier kem; + private final AlgorithmIdentifier dem; + + private GenericHybridParameters(ASN1Sequence sequence) + { + if (sequence.size() != 2) + { + throw new IllegalArgumentException("ASN.1 SEQUENCE should be of length 2"); + } + + this.kem = AlgorithmIdentifier.getInstance(sequence.getObjectAt(0)); + this.dem = AlgorithmIdentifier.getInstance(sequence.getObjectAt(1)); + } + + public static GenericHybridParameters getInstance( + Object o) + { + if (o instanceof GenericHybridParameters) + { + return (GenericHybridParameters)o; + } + else if (o != null) + { + return new GenericHybridParameters(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public GenericHybridParameters(AlgorithmIdentifier kem, AlgorithmIdentifier dem) + { + this.kem = kem; + this.dem = dem; + } + + public AlgorithmIdentifier getDem() + { + return dem; + } + + public AlgorithmIdentifier getKem() + { + return kem; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(kem); + v.add(dem); + + return new DERSequence(v); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/RsaKemParameters.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/RsaKemParameters.java new file mode 100644 index 00000000..07315a61 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/RsaKemParameters.java @@ -0,0 +1,87 @@ +package org.bouncycastle.asn1.cms; + +import java.math.BigInteger; + +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.ASN1Object; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; + +/** + * RFC 5990 RSA KEM parameters class. + * <pre> + * RsaKemParameters ::= SEQUENCE { + * keyDerivationFunction KeyDerivationFunction, + * keyLength KeyLength + * } + * + * KeyDerivationFunction ::= AlgorithmIdentifier + * KeyLength ::= INTEGER (1..MAX) + * </pre> + */ +public class RsaKemParameters + extends ASN1Object +{ + private final AlgorithmIdentifier keyDerivationFunction; + private final BigInteger keyLength; + + private RsaKemParameters(ASN1Sequence sequence) + { + if (sequence.size() != 2) + { + throw new IllegalArgumentException("ASN.1 SEQUENCE should be of length 2"); + } + this.keyDerivationFunction = AlgorithmIdentifier.getInstance(sequence.getObjectAt(0)); + this.keyLength = ASN1Integer.getInstance(sequence.getObjectAt(1)).getValue(); + } + + public static RsaKemParameters getInstance( + Object o) + { + if (o instanceof RsaKemParameters) + { + return (RsaKemParameters)o; + } + else if (o != null) + { + return new RsaKemParameters(ASN1Sequence.getInstance(o)); + } + + return null; + } + + /** + * Base constructor. + * + * @param keyDerivationFunction algorithm ID describing the key derivation function. + * @param keyLength length of key to be derived (in bytes). + */ + public RsaKemParameters(AlgorithmIdentifier keyDerivationFunction, int keyLength) + { + this.keyDerivationFunction = keyDerivationFunction; + this.keyLength = BigInteger.valueOf(keyLength); + } + + public AlgorithmIdentifier getKeyDerivationFunction() + { + return keyDerivationFunction; + } + + public BigInteger getKeyLength() + { + return keyLength; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(keyDerivationFunction); + v.add(new ASN1Integer(keyLength)); + + return new DERSequence(v); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/ecc/ECCCMSSharedInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/ecc/ECCCMSSharedInfo.java new file mode 100644 index 00000000..5bfdc869 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/ecc/ECCCMSSharedInfo.java @@ -0,0 +1,117 @@ +package org.bouncycastle.asn1.cms.ecc; + +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Object; +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1TaggedObject; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.DERTaggedObject; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.util.Arrays; + +/** + * <pre> + * ECC-CMS-SharedInfo ::= SEQUENCE { + * keyInfo AlgorithmIdentifier, + * entityUInfo [0] EXPLICIT OCTET STRING OPTIONAL, + * suppPubInfo [2] EXPLICIT OCTET STRING } + * </pre> + */ +public class ECCCMSSharedInfo + extends ASN1Object +{ + + private final AlgorithmIdentifier keyInfo; + private final byte[] entityUInfo; + private final byte[] suppPubInfo; + + public ECCCMSSharedInfo( + AlgorithmIdentifier keyInfo, + byte[] entityUInfo, + byte[] suppPubInfo) + { + this.keyInfo = keyInfo; + this.entityUInfo = Arrays.clone(entityUInfo); + this.suppPubInfo = Arrays.clone(suppPubInfo); + } + + public ECCCMSSharedInfo( + AlgorithmIdentifier keyInfo, + byte[] suppPubInfo) + { + this.keyInfo = keyInfo; + this.entityUInfo = null; + this.suppPubInfo = Arrays.clone(suppPubInfo); + } + + private ECCCMSSharedInfo( + ASN1Sequence seq) + { + this.keyInfo = AlgorithmIdentifier.getInstance(seq.getObjectAt(0)); + + if (seq.size() == 2) + { + this.entityUInfo = null; + this.suppPubInfo = ASN1OctetString.getInstance((ASN1TaggedObject)seq.getObjectAt(1), true).getOctets(); + } + else + { + this.entityUInfo = ASN1OctetString.getInstance((ASN1TaggedObject)seq.getObjectAt(1), true).getOctets(); + this.suppPubInfo = ASN1OctetString.getInstance((ASN1TaggedObject)seq.getObjectAt(2), true).getOctets(); + } + } + + /** + * Return an ECC-CMS-SharedInfo object from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @throws IllegalArgumentException if the object held by the + * tagged object cannot be converted. + */ + public static ECCCMSSharedInfo getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static ECCCMSSharedInfo getInstance( + Object obj) + { + if (obj instanceof ECCCMSSharedInfo) + { + return (ECCCMSSharedInfo)obj; + } + else if (obj != null) + { + return new ECCCMSSharedInfo(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + + /** + * Produce an object suitable for an ASN1OutputStream. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(keyInfo); + + if (entityUInfo != null) + { + v.add(new DERTaggedObject(true, 0, new DEROctetString(entityUInfo))); + } + + v.add(new DERTaggedObject(true, 2, new DEROctetString(suppPubInfo))); + + return new DERSequence(v); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/iso/ISOIECObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/iso/ISOIECObjectIdentifiers.java new file mode 100644 index 00000000..a9816b10 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/asn1/iso/ISOIECObjectIdentifiers.java @@ -0,0 +1,35 @@ +package org.bouncycastle.asn1.iso; + +import org.bouncycastle.asn1.ASN1ObjectIdentifier; + +/** + * OIDS from ISO/IEC 10118-3:2004 + */ +public interface ISOIECObjectIdentifiers +{ + ASN1ObjectIdentifier iso_encryption_algorithms = new ASN1ObjectIdentifier("1.0.10118"); + + ASN1ObjectIdentifier hash_algorithms = iso_encryption_algorithms.branch("3.0"); + + ASN1ObjectIdentifier ripemd160 = hash_algorithms.branch("49"); + ASN1ObjectIdentifier ripemd128 = hash_algorithms.branch("50"); + ASN1ObjectIdentifier whirlpool = hash_algorithms.branch("55"); + + + + /** + * -- ISO/IEC 18033-2 arc + + is18033-2 OID ::= { iso(1) standard(0) is18033(18033) part2(2) } + */ + ASN1ObjectIdentifier is18033_2 = new ASN1ObjectIdentifier("1.0.18033.2"); + + ASN1ObjectIdentifier id_ac_generic_hybrid = is18033_2.branch("1.2"); + + /** + id-kem-rsa OID ::= { + is18033-2 key-encapsulation-mechanism(2) rsa(4) + } + */ + ASN1ObjectIdentifier id_kem_rsa = is18033_2.branch("2.4"); +} diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/mozilla/SignedPublicKeyAndChallenge.java b/bcprov/src/main/java/org/bouncycastle/asn1/mozilla/SignedPublicKeyAndChallenge.java new file mode 100644 index 00000000..14d6534d --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/asn1/mozilla/SignedPublicKeyAndChallenge.java @@ -0,0 +1,64 @@ +package org.bouncycastle.asn1.mozilla; + +import org.bouncycastle.asn1.ASN1Object; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERBitString; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; + +/** + * <pre> + * SignedPublicKeyAndChallenge ::= SEQUENCE { + * publicKeyAndChallenge PublicKeyAndChallenge, + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING + * } + * + * </pre> + */ +public class SignedPublicKeyAndChallenge + extends ASN1Object +{ + private final PublicKeyAndChallenge pubKeyAndChal; + private final ASN1Sequence pkacSeq; + + public static SignedPublicKeyAndChallenge getInstance(Object obj) + { + if (obj instanceof SignedPublicKeyAndChallenge) + { + return (SignedPublicKeyAndChallenge)obj; + } + else if (obj != null) + { + return new SignedPublicKeyAndChallenge(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private SignedPublicKeyAndChallenge(ASN1Sequence seq) + { + pkacSeq = seq; + pubKeyAndChal = PublicKeyAndChallenge.getInstance(seq.getObjectAt(0)); + } + + public ASN1Primitive toASN1Primitive() + { + return pkacSeq; + } + + public PublicKeyAndChallenge getPublicKeyAndChallenge() + { + return pubKeyAndChal; + } + + public AlgorithmIdentifier getSignatureAlgorithm() + { + return AlgorithmIdentifier.getInstance(pkacSeq.getObjectAt(1)); + } + + public DERBitString getSignature() + { + return DERBitString.getInstance(pkacSeq.getObjectAt(2)); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/NameConstraintValidator.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/NameConstraintValidator.java new file mode 100644 index 00000000..4596fe0c --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/NameConstraintValidator.java @@ -0,0 +1,18 @@ +package org.bouncycastle.asn1.x509; + +public interface NameConstraintValidator +{ + void checkPermitted(GeneralName name) + throws NameConstraintValidatorException; + + void checkExcluded(GeneralName name) + throws NameConstraintValidatorException; + + void intersectPermittedSubtree(GeneralSubtree permitted); + + void intersectPermittedSubtree(GeneralSubtree[] permitted); + + void intersectEmptyPermittedSubtree(int nameType); + + void addExcludedSubtree(GeneralSubtree subtree); +} diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/NameConstraintValidatorException.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/NameConstraintValidatorException.java new file mode 100644 index 00000000..517fddb0 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/NameConstraintValidatorException.java @@ -0,0 +1,10 @@ +package org.bouncycastle.asn1.x509; + +public class NameConstraintValidatorException + extends Exception +{ + public NameConstraintValidatorException(String msg) + { + super(msg); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/PKIXNameConstraintValidator.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/PKIXNameConstraintValidator.java new file mode 100644 index 00000000..0f15dae1 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/PKIXNameConstraintValidator.java @@ -0,0 +1,1920 @@ +package org.bouncycastle.asn1.x509; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERIA5String; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Integers; +import org.bouncycastle.util.Strings; + +public class PKIXNameConstraintValidator + implements NameConstraintValidator +{ + private Set excludedSubtreesDN = new HashSet(); + + private Set excludedSubtreesDNS = new HashSet(); + + private Set excludedSubtreesEmail = new HashSet(); + + private Set excludedSubtreesURI = new HashSet(); + + private Set excludedSubtreesIP = new HashSet(); + + private Set permittedSubtreesDN; + + private Set permittedSubtreesDNS; + + private Set permittedSubtreesEmail; + + private Set permittedSubtreesURI; + + private Set permittedSubtreesIP; + + public PKIXNameConstraintValidator() + { + } + + /** + * Checks if the given GeneralName is in the permitted set. + * + * @param name The GeneralName + * @throws NameConstraintValidatorException If the <code>name</code> + */ + public void checkPermitted(GeneralName name) + throws NameConstraintValidatorException + { + switch (name.getTagNo()) + { + case GeneralName.rfc822Name: + checkPermittedEmail(permittedSubtreesEmail, + extractNameAsString(name)); + break; + case GeneralName.dNSName: + checkPermittedDNS(permittedSubtreesDNS, DERIA5String.getInstance( + name.getName()).getString()); + break; + case GeneralName.directoryName: + checkPermittedDN(X500Name.getInstance(name.getName())); + break; + case GeneralName.uniformResourceIdentifier: + checkPermittedURI(permittedSubtreesURI, DERIA5String.getInstance( + name.getName()).getString()); + break; + case GeneralName.iPAddress: + byte[] ip = ASN1OctetString.getInstance(name.getName()).getOctets(); + + checkPermittedIP(permittedSubtreesIP, ip); + } + } + + /** + * Check if the given GeneralName is contained in the excluded set. + * + * @param name The GeneralName. + * @throws NameConstraintValidatorException If the <code>name</code> is + * excluded. + */ + public void checkExcluded(GeneralName name) + throws NameConstraintValidatorException + { + switch (name.getTagNo()) + { + case GeneralName.rfc822Name: + checkExcludedEmail(excludedSubtreesEmail, extractNameAsString(name)); + break; + case GeneralName.dNSName: + checkExcludedDNS(excludedSubtreesDNS, DERIA5String.getInstance( + name.getName()).getString()); + break; + case GeneralName.directoryName: + checkExcludedDN(X500Name.getInstance(name.getName())); + break; + case GeneralName.uniformResourceIdentifier: + checkExcludedURI(excludedSubtreesURI, DERIA5String.getInstance( + name.getName()).getString()); + break; + case GeneralName.iPAddress: + byte[] ip = ASN1OctetString.getInstance(name.getName()).getOctets(); + + checkExcludedIP(excludedSubtreesIP, ip); + } + } + + public void intersectPermittedSubtree(GeneralSubtree permitted) + { + intersectPermittedSubtree(new GeneralSubtree[]{permitted}); + } + + /** + * Updates the permitted set of these name constraints with the intersection + * with the given subtree. + * + * @param permitted The permitted subtrees + */ + public void intersectPermittedSubtree(GeneralSubtree[] permitted) + { + Map subtreesMap = new HashMap(); + + // group in sets in a map ordered by tag no. + for (int i = 0; i != permitted.length; i++) + { + GeneralSubtree subtree = permitted[i]; + Integer tagNo = Integers.valueOf(subtree.getBase().getTagNo()); + if (subtreesMap.get(tagNo) == null) + { + subtreesMap.put(tagNo, new HashSet()); + } + ((Set)subtreesMap.get(tagNo)).add(subtree); + } + + for (Iterator it = subtreesMap.entrySet().iterator(); it.hasNext(); ) + { + Map.Entry entry = (Map.Entry)it.next(); + + // go through all subtree groups + switch (((Integer)entry.getKey()).intValue()) + { + case GeneralName.rfc822Name: + permittedSubtreesEmail = intersectEmail(permittedSubtreesEmail, + (Set)entry.getValue()); + break; + case GeneralName.dNSName: + permittedSubtreesDNS = intersectDNS(permittedSubtreesDNS, + (Set)entry.getValue()); + break; + case GeneralName.directoryName: + permittedSubtreesDN = intersectDN(permittedSubtreesDN, + (Set)entry.getValue()); + break; + case GeneralName.uniformResourceIdentifier: + permittedSubtreesURI = intersectURI(permittedSubtreesURI, + (Set)entry.getValue()); + break; + case GeneralName.iPAddress: + permittedSubtreesIP = intersectIP(permittedSubtreesIP, + (Set)entry.getValue()); + } + } + } + + public void intersectEmptyPermittedSubtree(int nameType) + { + switch (nameType) + { + case GeneralName.rfc822Name: + permittedSubtreesEmail = new HashSet(); + break; + case GeneralName.dNSName: + permittedSubtreesDNS = new HashSet(); + break; + case GeneralName.directoryName: + permittedSubtreesDN = new HashSet(); + break; + case GeneralName.uniformResourceIdentifier: + permittedSubtreesURI = new HashSet(); + break; + case GeneralName.iPAddress: + permittedSubtreesIP = new HashSet(); + } + } + + /** + * Adds a subtree to the excluded set of these name constraints. + * + * @param subtree A subtree with an excluded GeneralName. + */ + public void addExcludedSubtree(GeneralSubtree subtree) + { + GeneralName base = subtree.getBase(); + + switch (base.getTagNo()) + { + case GeneralName.rfc822Name: + excludedSubtreesEmail = unionEmail(excludedSubtreesEmail, + extractNameAsString(base)); + break; + case GeneralName.dNSName: + excludedSubtreesDNS = unionDNS(excludedSubtreesDNS, + extractNameAsString(base)); + break; + case GeneralName.directoryName: + excludedSubtreesDN = unionDN(excludedSubtreesDN, + (ASN1Sequence)base.getName().toASN1Primitive()); + break; + case GeneralName.uniformResourceIdentifier: + excludedSubtreesURI = unionURI(excludedSubtreesURI, + extractNameAsString(base)); + break; + case GeneralName.iPAddress: + excludedSubtreesIP = unionIP(excludedSubtreesIP, ASN1OctetString + .getInstance(base.getName()).getOctets()); + break; + } + } + + public int hashCode() + { + return hashCollection(excludedSubtreesDN) + + hashCollection(excludedSubtreesDNS) + + hashCollection(excludedSubtreesEmail) + + hashCollection(excludedSubtreesIP) + + hashCollection(excludedSubtreesURI) + + hashCollection(permittedSubtreesDN) + + hashCollection(permittedSubtreesDNS) + + hashCollection(permittedSubtreesEmail) + + hashCollection(permittedSubtreesIP) + + hashCollection(permittedSubtreesURI); + } + + public boolean equals(Object o) + { + if (!(o instanceof 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); + } + + public 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; + } + + private void checkPermittedDN(X500Name dns) + throws NameConstraintValidatorException + { + checkPermittedDN(permittedSubtreesDN, ASN1Sequence.getInstance(dns.toASN1Primitive())); + } + + private void checkExcludedDN(X500Name dns) + throws NameConstraintValidatorException + { + checkExcludedDN(excludedSubtreesDN, ASN1Sequence.getInstance(dns)); + } + + private static boolean withinDNSubtree( + ASN1Sequence dns, + ASN1Sequence subtree) + { + if (subtree.size() < 1) + { + return false; + } + + if (subtree.size() > dns.size()) + { + return false; + } + + for (int j = subtree.size() - 1; j >= 0; j--) + { + if (!subtree.getObjectAt(j).equals(dns.getObjectAt(j))) + { + return false; + } + } + + return true; + } + + private void checkPermittedDN(Set permitted, ASN1Sequence dns) + throws NameConstraintValidatorException + { + if (permitted == null) + { + return; + } + + if (permitted.isEmpty() && dns.size() == 0) + { + return; + } + Iterator it = permitted.iterator(); + + while (it.hasNext()) + { + ASN1Sequence subtree = (ASN1Sequence)it.next(); + + if (withinDNSubtree(dns, subtree)) + { + return; + } + } + + throw new NameConstraintValidatorException( + "Subject distinguished name is not from a permitted subtree"); + } + + private void checkExcludedDN(Set excluded, ASN1Sequence dns) + throws NameConstraintValidatorException + { + if (excluded.isEmpty()) + { + return; + } + + Iterator it = excluded.iterator(); + + while (it.hasNext()) + { + ASN1Sequence subtree = (ASN1Sequence)it.next(); + + if (withinDNSubtree(dns, subtree)) + { + throw new NameConstraintValidatorException( + "Subject distinguished name is from an excluded subtree"); + } + } + } + + private Set intersectDN(Set permitted, Set dns) + { + Set intersect = new HashSet(); + for (Iterator it = dns.iterator(); it.hasNext(); ) + { + ASN1Sequence dn = ASN1Sequence.getInstance(((GeneralSubtree)it + .next()).getBase().getName().toASN1Primitive()); + if (permitted == null) + { + if (dn != null) + { + intersect.add(dn); + } + } + else + { + Iterator _iter = permitted.iterator(); + while (_iter.hasNext()) + { + ASN1Sequence subtree = (ASN1Sequence)_iter.next(); + + if (withinDNSubtree(dn, subtree)) + { + intersect.add(dn); + } + else if (withinDNSubtree(subtree, dn)) + { + intersect.add(subtree); + } + } + } + } + return intersect; + } + + private Set unionDN(Set excluded, ASN1Sequence dn) + { + if (excluded.isEmpty()) + { + if (dn == null) + { + return excluded; + } + excluded.add(dn); + + return excluded; + } + else + { + Set intersect = new HashSet(); + + Iterator it = excluded.iterator(); + while (it.hasNext()) + { + ASN1Sequence subtree = (ASN1Sequence)it.next(); + + 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 Set intersectEmail(Set permitted, Set emails) + { + Set intersect = new HashSet(); + for (Iterator it = emails.iterator(); it.hasNext(); ) + { + String email = extractNameAsString(((GeneralSubtree)it.next()) + .getBase()); + + if (permitted == null) + { + if (email != null) + { + intersect.add(email); + } + } + else + { + Iterator it2 = permitted.iterator(); + while (it2.hasNext()) + { + String _permitted = (String)it2.next(); + + intersectEmail(email, _permitted, intersect); + } + } + } + return intersect; + } + + private Set unionEmail(Set excluded, String email) + { + if (excluded.isEmpty()) + { + if (email == null) + { + return excluded; + } + excluded.add(email); + return excluded; + } + else + { + Set union = new HashSet(); + + Iterator it = excluded.iterator(); + while (it.hasNext()) + { + String _excluded = (String)it.next(); + + 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 Set intersectIP(Set permitted, Set ips) + { + Set intersect = new HashSet(); + for (Iterator it = ips.iterator(); it.hasNext(); ) + { + byte[] ip = ASN1OctetString.getInstance( + ((GeneralSubtree)it.next()).getBase().getName()).getOctets(); + if (permitted == null) + { + if (ip != null) + { + intersect.add(ip); + } + } + else + { + Iterator it2 = permitted.iterator(); + while (it2.hasNext()) + { + byte[] _permitted = (byte[])it2.next(); + 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 Set unionIP(Set excluded, byte[] ip) + { + if (excluded.isEmpty()) + { + if (ip == null) + { + return excluded; + } + excluded.add(ip); + + return excluded; + } + else + { + Set union = new HashSet(); + + Iterator it = excluded.iterator(); + while (it.hasNext()) + { + byte[] _excluded = (byte[])it.next(); + 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 Set unionIPRange(byte[] ipWithSubmask1, byte[] ipWithSubmask2) + { + Set set = new HashSet(); + + // difficult, adding always all IPs is not wrong + if (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 Set intersectIPRange(byte[] ipWithSubmask1, byte[] ipWithSubmask2) + { + if (ipWithSubmask1.length != ipWithSubmask2.length) + { + return Collections.EMPTY_SET; + } + 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; + } + // OR keeps all significant bits + byte[] ip = or(minMax[0], minMax[2]); + byte[] subnetmask = or(subnetmask1, subnetmask2); + return Collections.singleton(ipWithSubnetMask(ip, subnetmask)); + } + + /** + * 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]; + System.arraycopy(ip, 0, temp, 0, ipLength); + System.arraycopy(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]; + System.arraycopy(ipWithSubmask1, 0, ip1, 0, ipLength); + System.arraycopy(ipWithSubmask1, ipLength, subnetmask1, 0, ipLength); + + byte ip2[] = new byte[ipLength]; + byte subnetmask2[] = new byte[ipLength]; + System.arraycopy(ipWithSubmask2, 0, ip2, 0, ipLength); + System.arraycopy(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(Set permitted, String email) + throws NameConstraintValidatorException + { + if (permitted == null) + { + return; + } + + Iterator it = permitted.iterator(); + + while (it.hasNext()) + { + String str = ((String)it.next()); + + if (emailIsConstrained(email, str)) + { + return; + } + } + + if (email.length() == 0 && permitted.size() == 0) + { + return; + } + + throw new NameConstraintValidatorException( + "Subject email address is not from a permitted subtree."); + } + + private void checkExcludedEmail(Set excluded, String email) + throws NameConstraintValidatorException + { + if (excluded.isEmpty()) + { + return; + } + + Iterator it = excluded.iterator(); + + while (it.hasNext()) + { + String str = (String)it.next(); + + if (emailIsConstrained(email, str)) + { + throw new NameConstraintValidatorException( + "Email address is from an excluded subtree."); + } + } + } + + /** + * Checks if the IP <code>ip</code> is included in the permitted set + * <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 NameConstraintValidatorException if the IP is not permitted. + */ + private void checkPermittedIP(Set permitted, byte[] ip) + throws NameConstraintValidatorException + { + if (permitted == null) + { + return; + } + + Iterator it = permitted.iterator(); + + while (it.hasNext()) + { + byte[] ipWithSubnet = (byte[])it.next(); + + if (isIPConstrained(ip, ipWithSubnet)) + { + return; + } + } + if (ip.length == 0 && permitted.size() == 0) + { + return; + } + throw new NameConstraintValidatorException( + "IP is not from a permitted subtree."); + } + + /** + * Checks if the IP <code>ip</code> is included in the excluded set + * <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 NameConstraintValidatorException if the IP is excluded. + */ + private void checkExcludedIP(Set excluded, byte[] ip) + throws NameConstraintValidatorException + { + if (excluded.isEmpty()) + { + return; + } + + Iterator it = excluded.iterator(); + + while (it.hasNext()) + { + byte[] ipWithSubnet = (byte[])it.next(); + + if (isIPConstrained(ip, ipWithSubnet)) + { + throw new NameConstraintValidatorException( + "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 boolean isIPConstrained(byte ip[], byte[] constraint) + { + int ipLength = ip.length; + + if (ipLength != (constraint.length / 2)) + { + return false; + } + + byte[] subnetMask = new byte[ipLength]; + System.arraycopy(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 Arrays.areEqual(permittedSubnetAddress, ipSubnetAddress); + } + + private boolean emailIsConstrained(String email, String constraint) + { + String sub = email.substring(email.indexOf('@') + 1); + // a particular mailbox + if (constraint.indexOf('@') != -1) + { + if (email.equalsIgnoreCase(constraint)) + { + return true; + } + } + // on particular host + else if (!(constraint.charAt(0) == '.')) + { + if (sub.equalsIgnoreCase(constraint)) + { + return true; + } + } + // address in sub domain + else if (withinDomain(sub, constraint)) + { + return true; + } + return false; + } + + private boolean withinDomain(String testDomain, String domain) + { + String tempDomain = domain; + if (tempDomain.startsWith(".")) + { + tempDomain = tempDomain.substring(1); + } + String[] domainParts = Strings.split(tempDomain, '.'); + String[] testDomainParts = 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 (!domainParts[i].equalsIgnoreCase(testDomainParts[i + d])) + { + return false; + } + } + return true; + } + + private void checkPermittedDNS(Set permitted, String dns) + throws NameConstraintValidatorException + { + if (permitted == null) + { + return; + } + + Iterator it = permitted.iterator(); + + while (it.hasNext()) + { + String str = ((String)it.next()); + + // is sub domain + if (withinDomain(dns, str) || dns.equalsIgnoreCase(str)) + { + return; + } + } + if (dns.length() == 0 && permitted.size() == 0) + { + return; + } + throw new NameConstraintValidatorException( + "DNS is not from a permitted subtree."); + } + + private void checkExcludedDNS(Set excluded, String dns) + throws NameConstraintValidatorException + { + if (excluded.isEmpty()) + { + return; + } + + Iterator it = excluded.iterator(); + + while (it.hasNext()) + { + String str = ((String)it.next()); + + // is sub domain or the same + if (withinDomain(dns, str) || dns.equalsIgnoreCase(str)) + { + throw new NameConstraintValidatorException( + "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, Set 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 (email1.equalsIgnoreCase(email2)) + { + 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 (_sub.equalsIgnoreCase(email2)) + { + 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) + || email1.equalsIgnoreCase(email2)) + { + 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 (_sub.equalsIgnoreCase(email1)) + { + 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 (email1.equalsIgnoreCase(email2)) + { + union.add(email1); + } + else + { + union.add(email1); + union.add(email2); + } + } + } + } + + private void unionURI(String email1, String email2, Set 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 (email1.equalsIgnoreCase(email2)) + { + 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 (_sub.equalsIgnoreCase(email2)) + { + 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) + || email1.equalsIgnoreCase(email2)) + { + 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 (_sub.equalsIgnoreCase(email1)) + { + 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 (email1.equalsIgnoreCase(email2)) + { + union.add(email1); + } + else + { + union.add(email1); + union.add(email2); + } + } + } + } + + private Set intersectDNS(Set permitted, Set dnss) + { + Set intersect = new HashSet(); + for (Iterator it = dnss.iterator(); it.hasNext(); ) + { + String dns = extractNameAsString(((GeneralSubtree)it.next()) + .getBase()); + if (permitted == null) + { + if (dns != null) + { + intersect.add(dns); + } + } + else + { + Iterator _iter = permitted.iterator(); + while (_iter.hasNext()) + { + String _permitted = (String)_iter.next(); + + if (withinDomain(_permitted, dns)) + { + intersect.add(_permitted); + } + else if (withinDomain(dns, _permitted)) + { + intersect.add(dns); + } + } + } + } + + return intersect; + } + + private Set unionDNS(Set excluded, String dns) + { + if (excluded.isEmpty()) + { + if (dns == null) + { + return excluded; + } + excluded.add(dns); + + return excluded; + } + else + { + Set union = new HashSet(); + + Iterator _iter = excluded.iterator(); + while (_iter.hasNext()) + { + String _permitted = (String)_iter.next(); + + 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, Set 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 (email1.equalsIgnoreCase(email2)) + { + 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 (_sub.equalsIgnoreCase(email2)) + { + 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) + || email1.equalsIgnoreCase(email2)) + { + 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 (_sub.equalsIgnoreCase(email1)) + { + 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 (email1.equalsIgnoreCase(email2)) + { + intersect.add(email1); + } + } + } + } + + private void checkExcludedURI(Set excluded, String uri) + throws NameConstraintValidatorException + { + if (excluded.isEmpty()) + { + return; + } + + Iterator it = excluded.iterator(); + + while (it.hasNext()) + { + String str = ((String)it.next()); + + if (isUriConstrained(uri, str)) + { + throw new NameConstraintValidatorException( + "URI is from an excluded subtree."); + } + } + } + + private Set intersectURI(Set permitted, Set uris) + { + Set intersect = new HashSet(); + for (Iterator it = uris.iterator(); it.hasNext(); ) + { + String uri = extractNameAsString(((GeneralSubtree)it.next()) + .getBase()); + if (permitted == null) + { + if (uri != null) + { + intersect.add(uri); + } + } + else + { + Iterator _iter = permitted.iterator(); + while (_iter.hasNext()) + { + String _permitted = (String)_iter.next(); + intersectURI(_permitted, uri, intersect); + } + } + } + return intersect; + } + + private Set unionURI(Set excluded, String uri) + { + if (excluded.isEmpty()) + { + if (uri == null) + { + return excluded; + } + excluded.add(uri); + + return excluded; + } + else + { + Set union = new HashSet(); + + Iterator _iter = excluded.iterator(); + while (_iter.hasNext()) + { + String _excluded = (String)_iter.next(); + + unionURI(_excluded, uri, union); + } + + return union; + } + } + + private void intersectURI(String email1, String email2, Set 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 (email1.equalsIgnoreCase(email2)) + { + 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 (_sub.equalsIgnoreCase(email2)) + { + 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) + || email1.equalsIgnoreCase(email2)) + { + 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 (_sub.equalsIgnoreCase(email1)) + { + 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 (email1.equalsIgnoreCase(email2)) + { + intersect.add(email1); + } + } + } + } + + private void checkPermittedURI(Set permitted, String uri) + throws NameConstraintValidatorException + { + if (permitted == null) + { + return; + } + + Iterator it = permitted.iterator(); + + while (it.hasNext()) + { + String str = ((String)it.next()); + + if (isUriConstrained(uri, str)) + { + return; + } + } + if (uri.length() == 0 && permitted.size() == 0) + { + return; + } + throw new NameConstraintValidatorException( + "URI is not from a permitted subtree."); + } + + private boolean isUriConstrained(String uri, String constraint) + { + String host = extractHostFromURL(uri); + // a host + if (!constraint.startsWith(".")) + { + if (host.equalsIgnoreCase(constraint)) + { + 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; + } + + private String extractNameAsString(GeneralName name) + { + return DERIA5String.getInstance(name.getName()).getString(); + } + + /** + * 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 (Arrays.areEqual(ip1, ip2)) + { + return 0; + } + if (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; + } + + private int hashCollection(Collection coll) + { + if (coll == null) + { + return 0; + } + int hash = 0; + Iterator it1 = coll.iterator(); + while (it1.hasNext()) + { + Object o = it1.next(); + if (o instanceof byte[]) + { + hash += Arrays.hashCode((byte[])o); + } + else + { + hash += o.hashCode(); + } + } + return hash; + } + + private boolean collectionsAreEqual(Collection coll1, Collection coll2) + { + if (coll1 == coll2) + { + return true; + } + if (coll1 == null || coll2 == null) + { + return false; + } + if (coll1.size() != coll2.size()) + { + return false; + } + Iterator it1 = coll1.iterator(); + + while (it1.hasNext()) + { + Object a = it1.next(); + Iterator it2 = coll2.iterator(); + boolean found = false; + while (it2.hasNext()) + { + Object b = it2.next(); + if (equals(a, b)) + { + found = true; + break; + } + } + if (!found) + { + return false; + } + } + return true; + } + + private boolean equals(Object o1, Object o2) + { + if (o1 == o2) + { + return true; + } + if (o1 == null || o2 == null) + { + return false; + } + if (o1 instanceof byte[] && o2 instanceof byte[]) + { + return 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 = temp.substring(0, temp.length() - 1); + temp += "/"; + for (int i = ip.length / 2; i < ip.length; i++) + { + temp += Integer.toString(ip[i] & 0x00FF) + "."; + } + temp = temp.substring(0, temp.length() - 1); + return temp; + } + + private String stringifyIPCollection(Set ips) + { + String temp = ""; + temp += "["; + for (Iterator it = ips.iterator(); it.hasNext(); ) + { + temp += stringifyIP((byte[])it.next()) + ","; + } + if (temp.length() > 1) + { + temp = temp.substring(0, temp.length() - 1); + } + temp += "]"; + return temp; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/DomainParameters.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/DomainParameters.java new file mode 100644 index 00000000..e23f1b84 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/DomainParameters.java @@ -0,0 +1,223 @@ +package org.bouncycastle.asn1.x9; + +import java.math.BigInteger; +import java.util.Enumeration; + +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.ASN1Object; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1TaggedObject; +import org.bouncycastle.asn1.DERSequence; + +/** + * X9.44 Diffie-Hellman domain parameters. + * <pre> + * DomainParameters ::= SEQUENCE { + * p INTEGER, -- odd prime, p=jq +1 + * g INTEGER, -- generator, g + * q INTEGER, -- factor of p-1 + * j INTEGER OPTIONAL, -- subgroup factor, j>= 2 + * validationParams ValidationParams OPTIONAL + * } + * </pre> + */ +public class DomainParameters + extends ASN1Object +{ + private final ASN1Integer p, g, q, j; + private final ValidationParams validationParams; + + /** + * Return a DomainParameters object from the passed in tagged object. + * + * @param obj a tagged object. + * @param explicit true if the contents of the object is explictly tagged, false otherwise. + * @return a DomainParameters + */ + public static DomainParameters getInstance(ASN1TaggedObject obj, boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + /** + * Return a DomainParameters object from the passed in object. + * + * @param obj an object for conversion or a byte[]. + * @return a DomainParameters + */ + public static DomainParameters getInstance(Object obj) + { + if (obj instanceof DomainParameters) + { + return (DomainParameters)obj; + } + else if (obj != null) + { + return new DomainParameters(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + /** + * Base constructor - the full domain parameter set. + * + * @param p the prime p defining the Galois field. + * @param g the generator of the multiplicative subgroup of order g. + * @param q specifies the prime factor of p - 1 + * @param j optionally specifies the value that satisfies the equation p = jq+1 + * @param validationParams parameters for validating these domain parameters. + */ + public DomainParameters(BigInteger p, BigInteger g, BigInteger q, BigInteger j, + ValidationParams validationParams) + { + if (p == null) + { + throw new IllegalArgumentException("'p' cannot be null"); + } + if (g == null) + { + throw new IllegalArgumentException("'g' cannot be null"); + } + if (q == null) + { + throw new IllegalArgumentException("'q' cannot be null"); + } + + this.p = new ASN1Integer(p); + this.g = new ASN1Integer(g); + this.q = new ASN1Integer(q); + + if (j != null) + { + this.j = new ASN1Integer(j); + } + else + { + this.j = null; + } + this.validationParams = validationParams; + } + + private DomainParameters(ASN1Sequence seq) + { + if (seq.size() < 3 || seq.size() > 5) + { + throw new IllegalArgumentException("Bad sequence size: " + seq.size()); + } + + Enumeration e = seq.getObjects(); + this.p = ASN1Integer.getInstance(e.nextElement()); + this.g = ASN1Integer.getInstance(e.nextElement()); + this.q = ASN1Integer.getInstance(e.nextElement()); + + ASN1Encodable next = getNext(e); + + if (next != null && next instanceof ASN1Integer) + { + this.j = ASN1Integer.getInstance(next); + next = getNext(e); + } + else + { + this.j = null; + } + + if (next != null) + { + this.validationParams = ValidationParams.getInstance(next.toASN1Primitive()); + } + else + { + this.validationParams = null; + } + } + + private static ASN1Encodable getNext(Enumeration e) + { + return e.hasMoreElements() ? (ASN1Encodable)e.nextElement() : null; + } + + /** + * Return the prime p defining the Galois field. + * + * @return the prime p. + */ + public BigInteger getP() + { + return this.p.getPositiveValue(); + } + + /** + * Return the generator of the multiplicative subgroup of order g. + * + * @return the generator g. + */ + public BigInteger getG() + { + return this.g.getPositiveValue(); + } + + /** + * Return q, the prime factor of p - 1 + * + * @return q value + */ + public BigInteger getQ() + { + return this.q.getPositiveValue(); + } + + /** + * Return the value that satisfies the equation p = jq+1 (if present). + * + * @return j value or null. + */ + public BigInteger getJ() + { + if (this.j == null) + { + return null; + } + + return this.j.getPositiveValue(); + } + + /** + * Return the validation parameters for this set (if present). + * + * @return validation parameters, or null if absent. + */ + public ValidationParams getValidationParams() + { + return this.validationParams; + } + + /** + * Return an ASN.1 primitive representation of this object. + * + * @return a DERSequence containing the parameter values. + */ + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + v.add(this.p); + v.add(this.g); + v.add(this.q); + + if (this.j != null) + { + v.add(this.j); + } + + if (this.validationParams != null) + { + v.add(this.validationParams); + } + + return new DERSequence(v); + } +}
\ No newline at end of file diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/ValidationParams.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/ValidationParams.java new file mode 100644 index 00000000..34ad746a --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/ValidationParams.java @@ -0,0 +1,99 @@ +package org.bouncycastle.asn1.x9; + +import java.math.BigInteger; + +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.ASN1Object; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1TaggedObject; +import org.bouncycastle.asn1.DERBitString; +import org.bouncycastle.asn1.DERSequence; + +/** + * ValidationParams ::= SEQUENCE { + * seed BIT STRING, + * pgenCounter INTEGER + * } + */ +public class ValidationParams + extends ASN1Object +{ + private DERBitString seed; + private ASN1Integer pgenCounter; + + public static ValidationParams getInstance(ASN1TaggedObject obj, boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static ValidationParams getInstance(Object obj) + { + if (obj instanceof ValidationParams) + { + return (ValidationParams)obj; + } + else if (obj != null) + { + return new ValidationParams(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + public ValidationParams(byte[] seed, int pgenCounter) + { + if (seed == null) + { + throw new IllegalArgumentException("'seed' cannot be null"); + } + + this.seed = new DERBitString(seed); + this.pgenCounter = new ASN1Integer(pgenCounter); + } + + public ValidationParams(DERBitString seed, ASN1Integer pgenCounter) + { + if (seed == null) + { + throw new IllegalArgumentException("'seed' cannot be null"); + } + if (pgenCounter == null) + { + throw new IllegalArgumentException("'pgenCounter' cannot be null"); + } + + this.seed = seed; + this.pgenCounter = pgenCounter; + } + + private ValidationParams(ASN1Sequence seq) + { + if (seq.size() != 2) + { + throw new IllegalArgumentException("Bad sequence size: " + seq.size()); + } + + this.seed = DERBitString.getInstance(seq.getObjectAt(0)); + this.pgenCounter = ASN1Integer.getInstance(seq.getObjectAt(1)); + } + + public byte[] getSeed() + { + return this.seed.getBytes(); + } + + public BigInteger getPgenCounter() + { + return this.pgenCounter.getPositiveValue(); + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + v.add(this.seed); + v.add(this.pgenCounter); + return new DERSequence(v); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/CharToByteConverter.java b/bcprov/src/main/java/org/bouncycastle/crypto/CharToByteConverter.java new file mode 100644 index 00000000..8b640993 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/CharToByteConverter.java @@ -0,0 +1,22 @@ +package org.bouncycastle.crypto; + +/** + * Interface for a converter that produces a byte encoding for a char array. + */ +public interface CharToByteConverter +{ + /** + * Return the type of the conversion. + * + * @return a type name for the conversion. + */ + String getType(); + + /** + * Return a byte encoded representation of the passed in password. + * + * @param password the characters to encode. + * @return a byte encoding of password. + */ + byte[] convert(char[] password); +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/Xof.java b/bcprov/src/main/java/org/bouncycastle/crypto/Xof.java new file mode 100644 index 00000000..9183a7a2 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/Xof.java @@ -0,0 +1,19 @@ +package org.bouncycastle.crypto; + +/** + * With FIPS PUB 202 a new kind of message digest was announced which supported extendable output, or variable digest sizes. + * This interface provides the extra method required to support variable output on an extended digest implementation. + */ +public interface Xof + extends ExtendedDigest +{ + /** + * Output the results of the final calculation for this digest to outLen number of bytes. + * + * @param out output array to write the output bytes to. + * @param outOff offset to start writing the bytes at. + * @param outLen the number of output bytes requested. + * @return the number of bytes written + */ + int doFinal(byte[] out, int outOff, int outLen); +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ConcatenationKDFGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ConcatenationKDFGenerator.java new file mode 100644 index 00000000..c3353f9d --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ConcatenationKDFGenerator.java @@ -0,0 +1,124 @@ +package org.bouncycastle.crypto.agreement.kdf; + +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.DerivationFunction; +import org.bouncycastle.crypto.DerivationParameters; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.params.KDFParameters; + +/** + * Generator for Concatenation Key Derivation Function defined in NIST SP 800-56A, Sect 5.8.1 + */ +public class ConcatenationKDFGenerator + implements DerivationFunction +{ + private Digest digest; + private byte[] shared; + private byte[] otherInfo; + private int hLen; + + /** + * @param digest the digest to be used as the source of generated bytes + */ + public ConcatenationKDFGenerator( + Digest digest) + { + this.digest = digest; + this.hLen = digest.getDigestSize(); + } + + public void init( + DerivationParameters param) + { + if (param instanceof KDFParameters) + { + KDFParameters p = (KDFParameters)param; + + shared = p.getSharedSecret(); + otherInfo = p.getIV(); + } + else + { + throw new IllegalArgumentException("KDF parameters required for KDF2Generator"); + } + } + + /** + * return the underlying digest. + */ + public Digest getDigest() + { + return digest; + } + + /** + * int to octet string. + */ + private void ItoOSP( + int i, + byte[] sp) + { + sp[0] = (byte)(i >>> 24); + sp[1] = (byte)(i >>> 16); + sp[2] = (byte)(i >>> 8); + sp[3] = (byte)(i >>> 0); + } + + /** + * fill len bytes of the output buffer with bytes generated from + * the derivation function. + * + * @throws DataLengthException if the out buffer is too small. + */ + public int generateBytes( + byte[] out, + int outOff, + int len) + throws DataLengthException, IllegalArgumentException + { + if ((out.length - len) < outOff) + { + throw new DataLengthException("output buffer too small"); + } + + byte[] hashBuf = new byte[hLen]; + byte[] C = new byte[4]; + int counter = 1; + int outputLen = 0; + + digest.reset(); + + if (len > hLen) + { + do + { + ItoOSP(counter, C); + + digest.update(C, 0, C.length); + digest.update(shared, 0, shared.length); + digest.update(otherInfo, 0, otherInfo.length); + + digest.doFinal(hashBuf, 0); + + System.arraycopy(hashBuf, 0, out, outOff + outputLen, hLen); + outputLen += hLen; + } + while ((counter++) < (len / hLen)); + } + + if (outputLen < len) + { + ItoOSP(counter, C); + + digest.update(C, 0, C.length); + digest.update(shared, 0, shared.length); + digest.update(otherInfo, 0, otherInfo.length); + + digest.doFinal(hashBuf, 0); + + System.arraycopy(hashBuf, 0, out, outOff + outputLen, len - outputLen); + } + + return len; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/Blake2bDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/Blake2bDigest.java new file mode 100644 index 00000000..0fd3ebb9 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/Blake2bDigest.java @@ -0,0 +1,580 @@ +package org.bouncycastle.crypto.digests; + + +/* The BLAKE2 cryptographic hash function was designed by Jean- + Philippe Aumasson, Samuel Neves, Zooko Wilcox-O'Hearn, and Christian + Winnerlein. + + Reference Implementation and Description can be found at: https://blake2.net/ + Internet Draft: https://tools.ietf.org/html/draft-saarinen-blake2-02 + + This implementation does not support the Tree Hashing Mode. + + For unkeyed hashing, developers adapting BLAKE2 to ASN.1 - based + message formats SHOULD use the OID tree at x = 1.3.6.1.4.1.1722.12.2. + + Algorithm | Target | Collision | Hash | Hash ASN.1 | + Identifier | Arch | Security | nn | OID Suffix | + ---------------+--------+-----------+------+------------+ + id-blake2b160 | 64-bit | 2**80 | 20 | x.1.20 | + id-blake2b256 | 64-bit | 2**128 | 32 | x.1.32 | + id-blake2b384 | 64-bit | 2**192 | 48 | x.1.48 | + id-blake2b512 | 64-bit | 2**256 | 64 | x.1.64 | + ---------------+--------+-----------+------+------------+ + */ + +import org.bouncycastle.crypto.ExtendedDigest; +import org.bouncycastle.util.Arrays; + + +/** + * Implementation of the cryptographic hash function Blakbe2b. + * + * Blake2b offers a built-in keying mechanism to be used directly + * for authentication ("Prefix-MAC") rather than a HMAC construction. + * + * Blake2b offers a built-in support for a salt for randomized hashing + * and a personal string for defining a unique hash function for each application. + * + * BLAKE2b is optimized for 64-bit platforms and produces digests of any size + * between 1 and 64 bytes. + */ +public class Blake2bDigest + implements ExtendedDigest +{ + // Blake2b Initialization Vector: + private final static long blake2b_IV[] = + // Produced from the square root of primes 2, 3, 5, 7, 11, 13, 17, 19. + // The same as SHA-512 IV. + { + 0x6a09e667f3bcc908L, 0xbb67ae8584caa73bL, 0x3c6ef372fe94f82bL, + 0xa54ff53a5f1d36f1L, 0x510e527fade682d1L, 0x9b05688c2b3e6c1fL, + 0x1f83d9abfb41bd6bL, 0x5be0cd19137e2179L + }; + + // Message word permutations: + private final static byte[][] blake2b_sigma = + { + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, + { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, + { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, + { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, + { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, + { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, + { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, + { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }, + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } + }; + + private static int rOUNDS = 12; // to use for Catenas H' + private final static int BLOCK_LENGTH_BYTES = 128;// bytes + + // General parameters: + private int digestLength = 64; // 1- 64 bytes + private int keyLength = 0; // 0 - 64 bytes for keyed hashing for MAC + private byte[] salt = null;// new byte[16]; + private byte[] personalization = null;// new byte[16]; + + // the key + private byte[] key = null; + + // Tree hashing parameters: + // Because this class does not implement the Tree Hashing Mode, + // these parameters can be treated as constants (see init() function) + /* + * private int fanout = 1; // 0-255 private int depth = 1; // 1 - 255 + * private int leafLength= 0; private long nodeOffset = 0L; private int + * nodeDepth = 0; private int innerHashLength = 0; + */ + + // whenever this buffer overflows, it will be processed + // in the compress() function. + // For performance issues, long messages will not use this buffer. + private byte[] buffer = null;// new byte[BLOCK_LENGTH_BYTES]; + // Position of last inserted byte: + private int bufferPos = 0;// a value from 0 up to 128 + + private long[] internalState = new long[16]; // In the Blake2b paper it is + // called: v + private long[] chainValue = null; // state vector, in the Blake2b paper it + // is called: h + + private long t0 = 0L; // holds last significant bits, counter (counts bytes) + private long t1 = 0L; // counter: Length up to 2^128 are supported + private long f0 = 0L; // finalization flag, for last block: ~0L + + // For Tree Hashing Mode, not used here: + // private long f1 = 0L; // finalization flag, for last node: ~0L + + public Blake2bDigest() + { + this(512); + } + + public Blake2bDigest(Blake2bDigest digest) + { + this.bufferPos = digest.bufferPos; + this.buffer = Arrays.clone(digest.buffer); + this.keyLength = digest.keyLength; + this.key = Arrays.clone(digest.key); + this.digestLength = digest.digestLength; + this.chainValue = Arrays.clone(digest.chainValue); + this.personalization = Arrays.clone(personalization); + } + + public Blake2bDigest(int digestLength) + { + if (digestLength != 160 && digestLength != 256 && digestLength != 384 && digestLength != 512) + { + throw new IllegalArgumentException("Blake2b digest restricted to one of [160, 256, 384, 512]"); + } + + buffer = new byte[BLOCK_LENGTH_BYTES]; + keyLength = 0; + this.digestLength = digestLength / 8; + init(); + } + + /** + * Blake2b for authentication ("Prefix-MAC mode"). + * After calling the doFinal() method, the key will + * remain to be used for further computations of + * this instance. + * The key can be overwritten using the clearKey() method. + * + * @param key A key up to 64 bytes or null + */ + public Blake2bDigest(byte[] key) + { + buffer = new byte[BLOCK_LENGTH_BYTES]; + if (key != null) + { + this.key = new byte[key.length]; + System.arraycopy(key, 0, this.key, 0, key.length); + + if (key.length > 64) + { + throw new IllegalArgumentException( + "Keys > 64 are not supported"); + } + keyLength = key.length; + System.arraycopy(key, 0, buffer, 0, key.length); + bufferPos = BLOCK_LENGTH_BYTES; // zero padding + } + digestLength = 64; + init(); + } + + /** + * Blake2b with key, required digest length, salt and personalization. + * After calling the doFinal() method, the key, the salt and the personal string + * will remain and might be used for further computations with this instance. + * The key can be overwritten using the clearKey() method, the salt (pepper) + * can be overwritten using the clearSalt() method. + * + * @param key A key up to 64 bytes or null + * @param digestLength from 1 up to 64 bytes + * @param salt up to 16 bytes or null + * @param personalization up to 16 bytes or null + */ + public Blake2bDigest(byte[] key, int digestLength, byte[] salt, + byte[] personalization) + { + + buffer = new byte[BLOCK_LENGTH_BYTES]; + if (digestLength < 1 || digestLength > 64) + { + throw new IllegalArgumentException( + "Invalid digest length (required: 1 - 64)"); + } + this.digestLength = digestLength; + if (salt != null) + { + if (salt.length != 16) + { + throw new IllegalArgumentException( + "salt length must be exactly 16 bytes"); + } + this.salt = new byte[16]; + System.arraycopy(salt, 0, this.salt, 0, salt.length); + } + if (personalization != null) + { + if (personalization.length != 16) + { + throw new IllegalArgumentException( + "personalization length must be exactly 16 bytes"); + } + this.personalization = new byte[16]; + System.arraycopy(personalization, 0, this.personalization, 0, + personalization.length); + } + if (key != null) + { + this.key = new byte[key.length]; + System.arraycopy(key, 0, this.key, 0, key.length); + + if (key.length > 64) + { + throw new IllegalArgumentException( + "Keys > 64 are not supported"); + } + keyLength = key.length; + System.arraycopy(key, 0, buffer, 0, key.length); + bufferPos = BLOCK_LENGTH_BYTES; // zero padding + } + init(); + } + + // initialize chainValue + private void init() + { + + if (chainValue == null) + { + chainValue = new long[8]; + + chainValue[0] = blake2b_IV[0] + ^ (digestLength | (keyLength << 8) | 0x1010000); + // 0x1010000 = ((fanout << 16) | (depth << 24) | (leafLength << + // 32)); + // with fanout = 1; depth = 0; leafLength = 0; + chainValue[1] = blake2b_IV[1];// ^ nodeOffset; with nodeOffset = 0; + chainValue[2] = blake2b_IV[2];// ^ ( nodeDepth | (innerHashLength << + // 8) ); + // with nodeDepth = 0; innerHashLength = 0; + + chainValue[3] = blake2b_IV[3]; + + chainValue[4] = blake2b_IV[4]; + chainValue[5] = blake2b_IV[5]; + if (salt != null) + { + chainValue[4] ^= (bytes2long(salt, 0)); + chainValue[5] ^= (bytes2long(salt, 8)); + } + + chainValue[6] = blake2b_IV[6]; + chainValue[7] = blake2b_IV[7]; + if (personalization != null) + { + chainValue[6] ^= (bytes2long(personalization, 0)); + chainValue[7] ^= (bytes2long(personalization, 8)); + } + } + } + + private void initializeInternalState() + { + // initialize v: + System.arraycopy(chainValue, 0, internalState, 0, chainValue.length); + System.arraycopy(blake2b_IV, 0, internalState, chainValue.length, 4); + internalState[12] = t0 ^ blake2b_IV[4]; + internalState[13] = t1 ^ blake2b_IV[5]; + internalState[14] = f0 ^ blake2b_IV[6]; + internalState[15] = blake2b_IV[7];// ^ f1 with f1 = 0 + } + + /** + * update the message digest with a single byte. + * + * @param b the input byte to be entered. + */ + public void update(byte b) + { + int remainingLength = 0; // left bytes of buffer + + // process the buffer if full else add to buffer: + remainingLength = BLOCK_LENGTH_BYTES - bufferPos; + if (remainingLength == 0) + { // full buffer + t0 += BLOCK_LENGTH_BYTES; + if (t0 == 0) + { // if message > 2^64 + t1++; + } + compress(buffer, 0); + Arrays.fill(buffer, (byte)0);// clear buffer + buffer[0] = b; + bufferPos = 1; + } else + { + buffer[bufferPos] = b; + bufferPos++; + return; + } + } + + /** + * update the message digest with a block of bytes. + * + * @param message the byte array containing the data. + * @param offset the offset into the byte array where the data starts. + * @param len the length of the data. + */ + public void update(byte[] message, int offset, int len) + { + + if (message == null || len == 0) + return; + + int remainingLength = 0; // left bytes of buffer + + if (bufferPos != 0) + { // commenced, incomplete buffer + + // complete the buffer: + remainingLength = BLOCK_LENGTH_BYTES - bufferPos; + if (remainingLength < len) + { // full buffer + at least 1 byte + System.arraycopy(message, offset, buffer, bufferPos, + remainingLength); + t0 += BLOCK_LENGTH_BYTES; + if (t0 == 0) + { // if message > 2^64 + t1++; + } + compress(buffer, 0); + bufferPos = 0; + Arrays.fill(buffer, (byte) 0);// clear buffer + } else + { + System.arraycopy(message, offset, buffer, bufferPos, len); + bufferPos += len; + return; + } + } + + // process blocks except last block (also if last block is full) + int messagePos; + int blockWiseLastPos = offset + len - BLOCK_LENGTH_BYTES; + for (messagePos = offset + remainingLength; messagePos < blockWiseLastPos; messagePos += BLOCK_LENGTH_BYTES) + { // block wise 128 bytes + // without buffer: + t0 += BLOCK_LENGTH_BYTES; + if (t0 == 0) + { + t1++; + } + compress(message, messagePos); + } + + // fill the buffer with left bytes, this might be a full block + System.arraycopy(message, messagePos, buffer, 0, offset + len + - messagePos); + bufferPos += offset + len - messagePos; + } + + /** + * close the digest, producing the final digest value. The doFinal + * call leaves the digest reset. + * Key, salt and personal string remain. + * + * @param out the array the digest is to be copied into. + * @param outOffset the offset into the out array the digest is to start at. + */ + public int doFinal(byte[] out, int outOffset) + { + + f0 = 0xFFFFFFFFFFFFFFFFL; + t0 += bufferPos; + // bufferPos may be < 128, so (t0 == 0) does not work + // for 2^64 < message length > 2^64 - 127 + if ((t0 < 0) && (bufferPos > -t0)) + { + t1++; + } + compress(buffer, 0); + Arrays.fill(buffer, (byte) 0);// Holds eventually the key if input is null + Arrays.fill(internalState, 0L); + + for (int i = 0; i < chainValue.length && (i * 8 < digestLength); i++) + { + byte[] bytes = long2bytes(chainValue[i]); + + if (i * 8 < digestLength - 8) + { + System.arraycopy(bytes, 0, out, outOffset + i * 8, 8); + } + else + { + System.arraycopy(bytes, 0, out, outOffset + i * 8, digestLength - (i * 8)); + } + } + + Arrays.fill(chainValue, 0L); + + reset(); + + return digestLength; + } + + /** + * Reset the digest back to it's initial state. + * The key, the salt and the personal string will + * remain for further computations. + */ + public void reset() + { + bufferPos = 0; + f0 = 0L; + t0 = 0L; + t1 = 0L; + chainValue = null; + if (key != null) + { + System.arraycopy(key, 0, buffer, 0, key.length); + bufferPos = BLOCK_LENGTH_BYTES; // zero padding + } + init(); + } + + private void compress(byte[] message, int messagePos) + { + + initializeInternalState(); + + long[] m = new long[16]; + for (int j = 0; j < 16; j++) + { + m[j] = bytes2long(message, messagePos + j * 8); + } + + for (int round = 0; round < rOUNDS; round++) + { + + // G apply to columns of internalState:m[blake2b_sigma[round][2 * + // blockPos]] /+1 + G(m[blake2b_sigma[round][0]], m[blake2b_sigma[round][1]], 0, 4, 8, + 12); + G(m[blake2b_sigma[round][2]], m[blake2b_sigma[round][3]], 1, 5, 9, + 13); + G(m[blake2b_sigma[round][4]], m[blake2b_sigma[round][5]], 2, 6, 10, + 14); + G(m[blake2b_sigma[round][6]], m[blake2b_sigma[round][7]], 3, 7, 11, + 15); + // G apply to diagonals of internalState: + G(m[blake2b_sigma[round][8]], m[blake2b_sigma[round][9]], 0, 5, 10, + 15); + G(m[blake2b_sigma[round][10]], m[blake2b_sigma[round][11]], 1, 6, + 11, 12); + G(m[blake2b_sigma[round][12]], m[blake2b_sigma[round][13]], 2, 7, + 8, 13); + G(m[blake2b_sigma[round][14]], m[blake2b_sigma[round][15]], 3, 4, + 9, 14); + } + + // update chain values: + for (int offset = 0; offset < chainValue.length; offset++) + { + chainValue[offset] = chainValue[offset] ^ internalState[offset] + ^ internalState[offset + 8]; + } + } + + private void G(long m1, long m2, int posA, int posB, int posC, int posD) + { + + internalState[posA] = internalState[posA] + internalState[posB] + m1; + internalState[posD] = rotr64(internalState[posD] ^ internalState[posA], + 32); + internalState[posC] = internalState[posC] + internalState[posD]; + internalState[posB] = rotr64(internalState[posB] ^ internalState[posC], + 24); // replaces 25 of BLAKE + internalState[posA] = internalState[posA] + internalState[posB] + m2; + internalState[posD] = rotr64(internalState[posD] ^ internalState[posA], + 16); + internalState[posC] = internalState[posC] + internalState[posD]; + internalState[posB] = rotr64(internalState[posB] ^ internalState[posC], + 63); // replaces 11 of BLAKE + } + + private long rotr64(long x, int rot) + { + return x >>> rot | (x << (64 - rot)); + } + + // convert one long value in byte array + // little-endian byte order! + private final byte[] long2bytes(long longValue) + { + return new byte[] + { (byte) longValue, (byte) (longValue >> 8), + (byte) (longValue >> 16), (byte) (longValue >> 24), + (byte) (longValue >> 32), (byte) (longValue >> 40), + (byte) (longValue >> 48), (byte) (longValue >> 56) + }; + } + + // little-endian byte order! + private final long bytes2long(byte[] byteArray, int offset) + { + return (((long) byteArray[offset] & 0xFF) + | (((long) byteArray[offset + 1] & 0xFF) << 8) + | (((long) byteArray[offset + 2] & 0xFF) << 16) + | (((long) byteArray[offset + 3] & 0xFF) << 24) + | (((long) byteArray[offset + 4] & 0xFF) << 32) + | (((long) byteArray[offset + 5] & 0xFF) << 40) + | (((long) byteArray[offset + 6] & 0xFF) << 48) + | (((long) byteArray[offset + 7] & 0xFF) << 56)); + } + + /** + * return the algorithm name + * + * @return the algorithm name + */ + public String getAlgorithmName() + { + return "Blake2b"; + } + + /** + * return the size, in bytes, of the digest produced by this message digest. + * + * @return the size, in bytes, of the digest produced by this message digest. + */ + public int getDigestSize() + { + return digestLength; + } + + /** + * Return the size in bytes of the internal buffer the digest applies it's compression + * function to. + * + * @return byte length of the digests internal buffer. + */ + public int getByteLength() + { + return BLOCK_LENGTH_BYTES; + } + + /** + * Overwrite the key + * if it is no longer used (zeroization) + */ + public void clearKey() + { + if (key != null) + { + Arrays.fill(key, (byte) 0); + Arrays.fill(buffer, (byte) 0); + } + } + + /** + * Overwrite the salt (pepper) if it + * is secret and no longer used (zeroization) + */ + public void clearSalt() + { + if (salt != null) + { + Arrays.fill(salt, (byte) 0); + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/KeccakDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/KeccakDigest.java new file mode 100644 index 00000000..40194461 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/KeccakDigest.java @@ -0,0 +1,549 @@ +package org.bouncycastle.crypto.digests; + +import org.bouncycastle.crypto.ExtendedDigest; +import org.bouncycastle.util.Arrays; + +/** + * implementation of Keccak based on following KeccakNISTInterface.c from http://keccak.noekeon.org/ + * <p> + * Following the naming conventions used in the C source code to enable easy review of the implementation. + */ +public class KeccakDigest + implements ExtendedDigest +{ + private static long[] KeccakRoundConstants = keccakInitializeRoundConstants(); + + private static int[] KeccakRhoOffsets = keccakInitializeRhoOffsets(); + + private static long[] keccakInitializeRoundConstants() + { + long[] keccakRoundConstants = new long[24]; + byte[] LFSRstate = new byte[1]; + + LFSRstate[0] = 0x01; + int i, j, bitPosition; + + for (i = 0; i < 24; i++) + { + keccakRoundConstants[i] = 0; + for (j = 0; j < 7; j++) + { + bitPosition = (1 << j) - 1; + if (LFSR86540(LFSRstate)) + { + keccakRoundConstants[i] ^= 1L << bitPosition; + } + } + } + + return keccakRoundConstants; + } + + private static boolean LFSR86540(byte[] LFSR) + { + boolean result = (((LFSR[0]) & 0x01) != 0); + if (((LFSR[0]) & 0x80) != 0) + { + LFSR[0] = (byte)(((LFSR[0]) << 1) ^ 0x71); + } + else + { + LFSR[0] <<= 1; + } + + return result; + } + + private static int[] keccakInitializeRhoOffsets() + { + int[] keccakRhoOffsets = new int[25]; + int x, y, t, newX, newY; + + keccakRhoOffsets[(((0) % 5) + 5 * ((0) % 5))] = 0; + x = 1; + y = 0; + for (t = 0; t < 24; t++) + { + keccakRhoOffsets[(((x) % 5) + 5 * ((y) % 5))] = ((t + 1) * (t + 2) / 2) % 64; + newX = (0 * x + 1 * y) % 5; + newY = (2 * x + 3 * y) % 5; + x = newX; + y = newY; + } + + return keccakRhoOffsets; + } + + protected byte[] state = new byte[(1600 / 8)]; + protected byte[] dataQueue = new byte[(1536 / 8)]; + protected int rate; + protected int bitsInQueue; + protected int fixedOutputLength; + protected boolean squeezing; + protected int bitsAvailableForSqueezing; + protected byte[] chunk; + protected byte[] oneByte; + + private void clearDataQueueSection(int off, int len) + { + for (int i = off; i != off + len; i++) + { + dataQueue[i] = 0; + } + } + + public KeccakDigest() + { + this(288); + } + + public KeccakDigest(int bitLength) + { + init(bitLength); + } + + public KeccakDigest(KeccakDigest source) { + System.arraycopy(source.state, 0, this.state, 0, source.state.length); + System.arraycopy(source.dataQueue, 0, this.dataQueue, 0, source.dataQueue.length); + this.rate = source.rate; + this.bitsInQueue = source.bitsInQueue; + this.fixedOutputLength = source.fixedOutputLength; + this.squeezing = source.squeezing; + this.bitsAvailableForSqueezing = source.bitsAvailableForSqueezing; + this.chunk = Arrays.clone(source.chunk); + this.oneByte = Arrays.clone(source.oneByte); + } + + public String getAlgorithmName() + { + return "Keccak-" + fixedOutputLength; + } + + public int getDigestSize() + { + return fixedOutputLength / 8; + } + + public void update(byte in) + { + oneByte[0] = in; + + absorb(oneByte, 0, 8L); + } + + public void update(byte[] in, int inOff, int len) + { + absorb(in, inOff, len * 8L); + } + + public int doFinal(byte[] out, int outOff) + { + squeeze(out, outOff, fixedOutputLength); + + reset(); + + return getDigestSize(); + } + + /* + * TODO Possible API change to support partial-byte suffixes. + */ + protected int doFinal(byte[] out, int outOff, byte partialByte, int partialBits) + { + if (partialBits > 0) + { + oneByte[0] = partialByte; + absorb(oneByte, 0, partialBits); + } + + squeeze(out, outOff, fixedOutputLength); + + reset(); + + return getDigestSize(); + } + + public void reset() + { + init(fixedOutputLength); + } + + /** + * Return the size of block that the compression function is applied to in bytes. + * + * @return internal byte length of a block. + */ + public int getByteLength() + { + return rate / 8; + } + + private void init(int bitLength) + { + switch (bitLength) + { + case 288: + initSponge(1024, 576); + break; + case 128: + initSponge(1344, 256); + break; + case 224: + initSponge(1152, 448); + break; + case 256: + initSponge(1088, 512); + break; + case 384: + initSponge(832, 768); + break; + case 512: + initSponge(576, 1024); + break; + default: + throw new IllegalArgumentException("bitLength must be one of 128, 224, 256, 288, 384, or 512."); + } + } + + private void initSponge(int rate, int capacity) + { + if (rate + capacity != 1600) + { + throw new IllegalStateException("rate + capacity != 1600"); + } + if ((rate <= 0) || (rate >= 1600) || ((rate % 64) != 0)) + { + throw new IllegalStateException("invalid rate value"); + } + + this.rate = rate; + // this is never read, need to check to see why we want to save it + // this.capacity = capacity; + 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; + } + + protected void absorb(byte[] data, int off, long databitlen) + { + long i, j, wholeBlocks; + + if ((bitsInQueue % 8) != 0) + { + throw new IllegalStateException("attempt to absorb with odd length queue."); + } + if (squeezing) + { + throw new IllegalStateException("attempt to absorb while squeezing."); + } + + i = 0; + while (i < databitlen) + { + if ((bitsInQueue == 0) && (databitlen >= rate) && (i <= (databitlen - rate))) + { + wholeBlocks = (databitlen - i) / rate; + + for (j = 0; j < wholeBlocks; j++) + { + System.arraycopy(data, (int)(off + (i / 8) + (j * chunk.length)), chunk, 0, chunk.length); + +// displayIntermediateValues.displayBytes(1, "Block to be absorbed", curData, rate / 8); + + KeccakAbsorb(state, chunk, chunk.length); + } + + i += wholeBlocks * rate; + } + else + { + int partialBlock = (int)(databitlen - i); + if (partialBlock + bitsInQueue > rate) + { + partialBlock = rate - bitsInQueue; + } + int partialByte = partialBlock % 8; + partialBlock -= partialByte; + System.arraycopy(data, off + (int)(i / 8), dataQueue, bitsInQueue / 8, partialBlock / 8); + + bitsInQueue += partialBlock; + i += partialBlock; + if (bitsInQueue == rate) + { + absorbQueue(); + } + if (partialByte > 0) + { + int mask = (1 << partialByte) - 1; + dataQueue[bitsInQueue / 8] = (byte)(data[off + ((int)(i / 8))] & mask); + bitsInQueue += partialByte; + i += partialByte; + } + } + } + } + + private void padAndSwitchToSqueezingPhase() + { + if (bitsInQueue + 1 == rate) + { + dataQueue[bitsInQueue / 8] |= 1 << (bitsInQueue % 8); + absorbQueue(); + clearDataQueueSection(0, rate / 8); + } + else + { + clearDataQueueSection((bitsInQueue + 7) / 8, rate / 8 - (bitsInQueue + 7) / 8); + dataQueue[bitsInQueue / 8] |= 1 << (bitsInQueue % 8); + } + dataQueue[(rate - 1) / 8] |= 1 << ((rate - 1) % 8); + absorbQueue(); + + +// displayIntermediateValues.displayText(1, "--- Switching to squeezing phase ---"); + + + if (rate == 1024) + { + KeccakExtract1024bits(state, dataQueue); + bitsAvailableForSqueezing = 1024; + } + else + + { + KeccakExtract(state, dataQueue, rate / 64); + bitsAvailableForSqueezing = rate; + } + +// displayIntermediateValues.displayBytes(1, "Block available for squeezing", dataQueue, bitsAvailableForSqueezing / 8); + + squeezing = true; + } + + protected void squeeze(byte[] output, int offset, long outputLength) + { + long i; + int partialBlock; + + if (!squeezing) + { + padAndSwitchToSqueezingPhase(); + } + if ((outputLength % 8) != 0) + { + throw new IllegalStateException("outputLength not a multiple of 8"); + } + + i = 0; + while (i < outputLength) + { + if (bitsAvailableForSqueezing == 0) + { + keccakPermutation(state); + + if (rate == 1024) + { + KeccakExtract1024bits(state, dataQueue); + bitsAvailableForSqueezing = 1024; + } + else + + { + KeccakExtract(state, dataQueue, rate / 64); + bitsAvailableForSqueezing = rate; + } + +// displayIntermediateValues.displayBytes(1, "Block available for squeezing", dataQueue, bitsAvailableForSqueezing / 8); + + } + partialBlock = bitsAvailableForSqueezing; + if ((long)partialBlock > outputLength - i) + { + partialBlock = (int)(outputLength - i); + } + + System.arraycopy(dataQueue, (rate - bitsAvailableForSqueezing) / 8, output, offset + (int)(i / 8), partialBlock / 8); + bitsAvailableForSqueezing -= partialBlock; + i += partialBlock; + } + } + + private void fromBytesToWords(long[] stateAsWords, byte[] state) + { + for (int i = 0; i < (1600 / 64); i++) + { + stateAsWords[i] = 0; + int index = i * (64 / 8); + for (int j = 0; j < (64 / 8); j++) + { + stateAsWords[i] |= ((long)state[index + j] & 0xff) << ((8 * j)); + } + } + } + + private void fromWordsToBytes(byte[] state, long[] stateAsWords) + { + for (int i = 0; i < (1600 / 64); i++) + { + int index = i * (64 / 8); + for (int j = 0; j < (64 / 8); j++) + { + state[index + j] = (byte)((stateAsWords[i] >>> ((8 * j))) & 0xFF); + } + } + } + + private void keccakPermutation(byte[] state) + { + long[] longState = new long[state.length / 8]; + + fromBytesToWords(longState, state); + +// displayIntermediateValues.displayStateAsBytes(1, "Input of permutation", longState); + + keccakPermutationOnWords(longState); + +// displayIntermediateValues.displayStateAsBytes(1, "State after permutation", longState); + + fromWordsToBytes(state, longState); + } + + private void keccakPermutationAfterXor(byte[] state, byte[] data, int dataLengthInBytes) + { + int i; + + for (i = 0; i < dataLengthInBytes; i++) + { + state[i] ^= data[i]; + } + + keccakPermutation(state); + } + + private void keccakPermutationOnWords(long[] state) + { + int i; + +// displayIntermediateValues.displayStateAs64bitWords(3, "Same, with lanes as 64-bit words", state); + + for (i = 0; i < 24; i++) + { +// displayIntermediateValues.displayRoundNumber(3, i); + + theta(state); +// displayIntermediateValues.displayStateAs64bitWords(3, "After theta", state); + + rho(state); +// displayIntermediateValues.displayStateAs64bitWords(3, "After rho", state); + + pi(state); +// displayIntermediateValues.displayStateAs64bitWords(3, "After pi", state); + + chi(state); +// displayIntermediateValues.displayStateAs64bitWords(3, "After chi", state); + + iota(state, i); +// displayIntermediateValues.displayStateAs64bitWords(3, "After iota", state); + } + } + + long[] C = new long[5]; + + private void theta(long[] A) + { + for (int x = 0; x < 5; x++) + { + C[x] = 0; + for (int y = 0; y < 5; y++) + { + C[x] ^= A[x + 5 * y]; + } + } + for (int x = 0; x < 5; x++) + { + long dX = ((((C[(x + 1) % 5]) << 1) ^ ((C[(x + 1) % 5]) >>> (64 - 1)))) ^ C[(x + 4) % 5]; + for (int y = 0; y < 5; y++) + { + A[x + 5 * y] ^= dX; + } + } + } + + private void rho(long[] A) + { + for (int x = 0; x < 5; x++) + { + for (int y = 0; y < 5; y++) + { + int index = x + 5 * y; + A[index] = ((KeccakRhoOffsets[index] != 0) ? (((A[index]) << KeccakRhoOffsets[index]) ^ ((A[index]) >>> (64 - KeccakRhoOffsets[index]))) : A[index]); + } + } + } + + long[] tempA = new long[25]; + + private void pi(long[] A) + { + System.arraycopy(A, 0, tempA, 0, tempA.length); + + for (int x = 0; x < 5; x++) + { + for (int y = 0; y < 5; y++) + { + A[y + 5 * ((2 * x + 3 * y) % 5)] = tempA[x + 5 * y]; + } + } + } + + long[] chiC = new long[5]; + + private void chi(long[] A) + { + for (int y = 0; y < 5; y++) + { + for (int x = 0; x < 5; x++) + { + chiC[x] = A[x + 5 * y] ^ ((~A[(((x + 1) % 5) + 5 * y)]) & A[(((x + 2) % 5) + 5 * y)]); + } + for (int x = 0; x < 5; x++) + { + A[x + 5 * y] = chiC[x]; + } + } + } + + private void iota(long[] A, int indexRound) + { + A[(((0) % 5) + 5 * ((0) % 5))] ^= KeccakRoundConstants[indexRound]; + } + + private void KeccakAbsorb(byte[] byteState, byte[] data, int dataInBytes) + { + keccakPermutationAfterXor(byteState, data, dataInBytes); + } + + + private void KeccakExtract1024bits(byte[] byteState, byte[] data) + { + System.arraycopy(byteState, 0, data, 0, 128); + } + + + private void KeccakExtract(byte[] byteState, byte[] data, int laneCount) + { + System.arraycopy(byteState, 0, data, 0, laneCount * 8); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHAKEDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHAKEDigest.java new file mode 100644 index 00000000..20dc641c --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SHAKEDigest.java @@ -0,0 +1,103 @@ +package org.bouncycastle.crypto.digests; + +import org.bouncycastle.crypto.Xof; + + +/** + * implementation of SHAKE based on following KeccakNISTInterface.c from http://keccak.noekeon.org/ + * <p> + * Following the naming conventions used in the C source code to enable easy review of the implementation. + */ +public class SHAKEDigest + extends KeccakDigest + implements Xof +{ + private static int checkBitLength(int bitLength) + { + switch (bitLength) + { + case 128: + case 256: + return bitLength; + default: + throw new IllegalArgumentException("'bitLength' " + bitLength + " not supported for SHAKE"); + } + } + + public SHAKEDigest() + { + this(128); + } + + public SHAKEDigest(int bitLength) + { + super(checkBitLength(bitLength)); + } + + public SHAKEDigest(SHAKEDigest source) { + super(source); + } + + public String getAlgorithmName() + { + return "SHAKE" + fixedOutputLength; + } + + public int doFinal(byte[] out, int outOff) + { + return doFinal(out, outOff, getDigestSize()); + } + + public int doFinal(byte[] out, int outOff, int outLen) + { + absorb(new byte[]{ 0x0F }, 0, 4); + + squeeze(out, outOff, ((long)outLen) * 8); + + reset(); + + return outLen; + } + + /* + * TODO Possible API change to support partial-byte suffixes. + */ + protected int doFinal(byte[] out, int outOff, byte partialByte, int partialBits) + { + return doFinal(out, outOff, getDigestSize(), partialByte, partialBits); + } + + /* + * TODO Possible API change to support partial-byte suffixes. + */ + protected int doFinal(byte[] out, int outOff, int outLen, byte partialByte, int partialBits) + { + if (partialBits < 0 || partialBits > 7) + { + throw new IllegalArgumentException("'partialBits' must be in the range [0,7]"); + } + + int finalInput = (partialByte & ((1 << partialBits) - 1)) | (0x0F << partialBits); + int finalBits = partialBits + 4; + + if (finalBits >= 8) + { + oneByte[0] = (byte)finalInput; + absorb(oneByte, 0, 8); + finalBits -= 8; + finalInput >>>= 8; + } + + if (finalBits > 0) + { + oneByte[0] = (byte)finalInput; + absorb(oneByte, 0, finalBits); + } + + squeeze(out, outOff, ((long)outLen) * 8); + + reset(); + + return outLen; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/OldIESEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/OldIESEngine.java new file mode 100644 index 00000000..48ecbe3c --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/OldIESEngine.java @@ -0,0 +1,61 @@ +package org.bouncycastle.crypto.engines; + +import org.bouncycastle.crypto.BasicAgreement; +import org.bouncycastle.crypto.BufferedBlockCipher; +import org.bouncycastle.crypto.DerivationFunction; +import org.bouncycastle.crypto.Mac; +import org.bouncycastle.util.Pack; + +/** + * Support class for constructing integrated encryption ciphers + * for doing basic message exchanges on top of key agreement ciphers. + * Follows the description given in IEEE Std 1363a. + */ +public class OldIESEngine + extends IESEngine +{ + /** + * set up for use with stream mode, where the key derivation function + * is used to provide a stream of bytes to xor with the message. + * + * @param agree the key agreement used as the basis for the encryption + * @param kdf the key derivation function used for byte generation + * @param mac the message authentication code generator for the message + */ + public OldIESEngine( + BasicAgreement agree, + DerivationFunction kdf, + Mac mac) + { + super(agree, kdf, mac); + } + + + /** + * set up for use in conjunction with a block cipher to handle the + * message. + * + * @param agree the key agreement used as the basis for the encryption + * @param kdf the key derivation function used for byte generation + * @param mac the message authentication code generator for the message + * @param cipher the cipher to used for encrypting the message + */ + public OldIESEngine( + BasicAgreement agree, + DerivationFunction kdf, + Mac mac, + BufferedBlockCipher cipher) + { + super(agree, kdf, mac, cipher); + } + + protected byte[] getLengthTag(byte[] p2) + { + byte[] L2 = new byte[4]; + if (p2 != null) + { + Pack.intToBigEndian(p2.length * 8, L2, 0); + } + return L2; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/SM4Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/SM4Engine.java new file mode 100644 index 00000000..bf70eadb --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/SM4Engine.java @@ -0,0 +1,267 @@ +package org.bouncycastle.crypto.engines; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.OutputLengthException; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.util.Pack; + +/** + * SM4 Block Cipher - SM4 is a 128 bit block cipher with a 128 bit key. + * <p> + * The implementation here is based on the document <a href="http://eprint.iacr.org/2008/329.pdf">http://eprint.iacr.org/2008/329.pdf</a> + * by Whitfield Diffie and George Ledin, which is a translation of Prof. LU Shu-wang's original standard. + * </p> + */ +public class SM4Engine + implements BlockCipher +{ + private static final int BLOCK_SIZE = 16; + + private final static byte[] Sbox = + { + (byte)0xd6, (byte)0x90, (byte)0xe9, (byte)0xfe, (byte)0xcc, (byte)0xe1, (byte)0x3d, (byte)0xb7, (byte)0x16, (byte)0xb6, (byte)0x14, (byte)0xc2, (byte)0x28, (byte)0xfb, (byte)0x2c, (byte)0x05, + (byte)0x2b, (byte)0x67, (byte)0x9a, (byte)0x76, (byte)0x2a, (byte)0xbe, (byte)0x04, (byte)0xc3, (byte)0xaa, (byte)0x44, (byte)0x13, (byte)0x26, (byte)0x49, (byte)0x86, (byte)0x06, (byte)0x99, + (byte)0x9c, (byte)0x42, (byte)0x50, (byte)0xf4, (byte)0x91, (byte)0xef, (byte)0x98, (byte)0x7a, (byte)0x33, (byte)0x54, (byte)0x0b, (byte)0x43, (byte)0xed, (byte)0xcf, (byte)0xac, (byte)0x62, + (byte)0xe4, (byte)0xb3, (byte)0x1c, (byte)0xa9, (byte)0xc9, (byte)0x08, (byte)0xe8, (byte)0x95, (byte)0x80, (byte)0xdf, (byte)0x94, (byte)0xfa, (byte)0x75, (byte)0x8f, (byte)0x3f, (byte)0xa6, + (byte)0x47, (byte)0x07, (byte)0xa7, (byte)0xfc, (byte)0xf3, (byte)0x73, (byte)0x17, (byte)0xba, (byte)0x83, (byte)0x59, (byte)0x3c, (byte)0x19, (byte)0xe6, (byte)0x85, (byte)0x4f, (byte)0xa8, + (byte)0x68, (byte)0x6b, (byte)0x81, (byte)0xb2, (byte)0x71, (byte)0x64, (byte)0xda, (byte)0x8b, (byte)0xf8, (byte)0xeb, (byte)0x0f, (byte)0x4b, (byte)0x70, (byte)0x56, (byte)0x9d, (byte)0x35, + (byte)0x1e, (byte)0x24, (byte)0x0e, (byte)0x5e, (byte)0x63, (byte)0x58, (byte)0xd1, (byte)0xa2, (byte)0x25, (byte)0x22, (byte)0x7c, (byte)0x3b, (byte)0x01, (byte)0x21, (byte)0x78, (byte)0x87, + (byte)0xd4, (byte)0x00, (byte)0x46, (byte)0x57, (byte)0x9f, (byte)0xd3, (byte)0x27, (byte)0x52, (byte)0x4c, (byte)0x36, (byte)0x02, (byte)0xe7, (byte)0xa0, (byte)0xc4, (byte)0xc8, (byte)0x9e, + (byte)0xea, (byte)0xbf, (byte)0x8a, (byte)0xd2, (byte)0x40, (byte)0xc7, (byte)0x38, (byte)0xb5, (byte)0xa3, (byte)0xf7, (byte)0xf2, (byte)0xce, (byte)0xf9, (byte)0x61, (byte)0x15, (byte)0xa1, + (byte)0xe0, (byte)0xae, (byte)0x5d, (byte)0xa4, (byte)0x9b, (byte)0x34, (byte)0x1a, (byte)0x55, (byte)0xad, (byte)0x93, (byte)0x32, (byte)0x30, (byte)0xf5, (byte)0x8c, (byte)0xb1, (byte)0xe3, + (byte)0x1d, (byte)0xf6, (byte)0xe2, (byte)0x2e, (byte)0x82, (byte)0x66, (byte)0xca, (byte)0x60, (byte)0xc0, (byte)0x29, (byte)0x23, (byte)0xab, (byte)0x0d, (byte)0x53, (byte)0x4e, (byte)0x6f, + (byte)0xd5, (byte)0xdb, (byte)0x37, (byte)0x45, (byte)0xde, (byte)0xfd, (byte)0x8e, (byte)0x2f, (byte)0x03, (byte)0xff, (byte)0x6a, (byte)0x72, (byte)0x6d, (byte)0x6c, (byte)0x5b, (byte)0x51, + (byte)0x8d, (byte)0x1b, (byte)0xaf, (byte)0x92, (byte)0xbb, (byte)0xdd, (byte)0xbc, (byte)0x7f, (byte)0x11, (byte)0xd9, (byte)0x5c, (byte)0x41, (byte)0x1f, (byte)0x10, (byte)0x5a, (byte)0xd8, + (byte)0x0a, (byte)0xc1, (byte)0x31, (byte)0x88, (byte)0xa5, (byte)0xcd, (byte)0x7b, (byte)0xbd, (byte)0x2d, (byte)0x74, (byte)0xd0, (byte)0x12, (byte)0xb8, (byte)0xe5, (byte)0xb4, (byte)0xb0, + (byte)0x89, (byte)0x69, (byte)0x97, (byte)0x4a, (byte)0x0c, (byte)0x96, (byte)0x77, (byte)0x7e, (byte)0x65, (byte)0xb9, (byte)0xf1, (byte)0x09, (byte)0xc5, (byte)0x6e, (byte)0xc6, (byte)0x84, + (byte)0x18, (byte)0xf0, (byte)0x7d, (byte)0xec, (byte)0x3a, (byte)0xdc, (byte)0x4d, (byte)0x20, (byte)0x79, (byte)0xee, (byte)0x5f, (byte)0x3e, (byte)0xd7, (byte)0xcb, (byte)0x39, (byte)0x48 + }; + + private final static int[] CK = + { + 0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269, + 0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9, + 0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249, + 0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9, + 0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229, + 0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299, + 0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209, + 0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279 + }; + + private final static int[] FK = + { + 0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc + }; + + private final int[] X = new int[4]; + + private int[] rk; + + private int rotateLeft( + int x, + int bits) + { + return (x << bits) | (x >>> -bits); + } + + // non-linear substitution tau. + private int tau( + int A) + { + int b0 = Sbox[(A >> 24) & 0xff] & 0xff; + int b1 = Sbox[(A >> 16) & 0xff] & 0xff; + int b2 = Sbox[(A >> 8) & 0xff] & 0xff; + int b3 = Sbox[A & 0xff] & 0xff; + + return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; + } + + private int L_ap( + int B) + { + return (B ^ (rotateLeft(B, 13)) ^ (rotateLeft(B, 23))); + } + + private int T_ap( + int Z) + { + return L_ap(tau(Z)); + } + + // Key expansion + private int[] expandKey(boolean forEncryption, byte[] key) + { + int[] rk = new int[32]; + int[] MK = new int[4]; + + MK[0] = Pack.bigEndianToInt(key, 0); + MK[1] = Pack.bigEndianToInt(key, 4); + MK[2] = Pack.bigEndianToInt(key, 8); + MK[3] = Pack.bigEndianToInt(key, 12); + + int i; + int[] K = new int[4]; + K[0] = MK[0] ^ FK[0]; + K[1] = MK[1] ^ FK[1]; + K[2] = MK[2] ^ FK[2]; + K[3] = MK[3] ^ FK[3]; + + if (forEncryption) + { + rk[0] = K[0] ^ T_ap(K[1] ^ K[2] ^ K[3] ^ CK[0]); + rk[1] = K[1] ^ T_ap(K[2] ^ K[3] ^ rk[0] ^ CK[1]); + rk[2] = K[2] ^ T_ap(K[3] ^ rk[0] ^ rk[1] ^ CK[2]); + rk[3] = K[3] ^ T_ap(rk[0] ^ rk[1] ^ rk[2] ^ CK[3]); + for (i = 4; i < 32; i++) + { + rk[i] = rk[i - 4] ^ T_ap(rk[i - 3] ^ rk[i - 2] ^ rk[i - 1] ^ CK[i]); + } + } + else + { + rk[31] = K[0] ^ T_ap(K[1] ^ K[2] ^ K[3] ^ CK[0]); + rk[30] = K[1] ^ T_ap(K[2] ^ K[3] ^ rk[31] ^ CK[1]); + rk[29] = K[2] ^ T_ap(K[3] ^ rk[31] ^ rk[30] ^ CK[2]); + rk[28] = K[3] ^ T_ap(rk[31] ^ rk[30] ^ rk[29] ^ CK[3]); + for (i = 27; i >= 0; i--) + { + rk[i] = rk[i + 4] ^ T_ap(rk[i + 3] ^ rk[i + 2] ^ rk[i + 1] ^ CK[31 - i]); + } + } + + return rk; + } + + + // Linear substitution L + private int L(int B) + { + int C; + C = (B ^ (rotateLeft(B, 2)) ^ (rotateLeft(B, 10)) ^ (rotateLeft(B, + 18)) ^ (rotateLeft(B, 24))); + return C; + } + + // Mixer-substitution T + private int T(int Z) + { + return L(tau(Z)); + } + + // reverse substitution + private void R(int[] A, int off) + { + int off0 = off; + int off1 = off + 1; + int off2 = off + 2; + int off3 = off + 3; + + A[off0] = A[off0] ^ A[off3]; + A[off3] = A[off0] ^ A[off3]; + A[off0] = A[off0] ^ A[off3]; + A[off1] = A[off1] ^ A[off2]; + A[off2] = A[off1] ^ A[off2]; + A[off1] = A[off1] ^ A[off2]; + } + + // The round functions + private int F0(int[] X, int rk) + { + return (X[0] ^ T(X[1] ^ X[2] ^ X[3] ^ rk)); + } + + private int F1(int[] X, int rk) + { + return (X[1] ^ T(X[2] ^ X[3] ^ X[0] ^ rk)); + } + + private int F2(int[] X, int rk) + { + return (X[2] ^ T(X[3] ^ X[0] ^ X[1] ^ rk)); + } + + private int F3(int[] X, int rk) + { + return (X[3] ^ T(X[0] ^ X[1] ^ X[2] ^ rk)); + } + + public void init(boolean forEncryption, CipherParameters params) + throws IllegalArgumentException + { + if (params instanceof KeyParameter) + { + byte[] key = ((KeyParameter)params).getKey(); + + if (key.length != 16) + { + throw new IllegalArgumentException("SM4 requires a 128 bit key"); + } + + rk = expandKey(forEncryption, key); + } + else + { + throw new IllegalArgumentException("invalid parameter passed to SM4 init - " + params.getClass().getName()); + } + } + + public String getAlgorithmName() + { + return "SM4"; + } + + public int getBlockSize() + { + return BLOCK_SIZE; + } + + public int processBlock(byte[] in, int inOff, byte[] out, int outOff) + throws DataLengthException, IllegalStateException + { + if (rk == null) + { + throw new IllegalStateException("SM4 not initialised"); + } + + if ((inOff + BLOCK_SIZE) > in.length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + BLOCK_SIZE) > out.length) + { + throw new OutputLengthException("output buffer too short"); + } + + X[0] = Pack.bigEndianToInt(in, inOff); + X[1] = Pack.bigEndianToInt(in, inOff + 4); + X[2] = Pack.bigEndianToInt(in, inOff + 8); + X[3] = Pack.bigEndianToInt(in, inOff + 12); + + int i; + + for (i = 0; i < 32; i += 4) + { + X[0] = F0(X, rk[i]); + X[1] = F1(X, rk[i + 1]); + X[2] = F2(X, rk[i + 2]); + X[3] = F3(X, rk[i + 3]); + } + R(X, 0); + + Pack.intToBigEndian(X[0], out, outOff); + Pack.intToBigEndian(X[1], out, outOff + 4); + Pack.intToBigEndian(X[2], out, outOff + 8); + Pack.intToBigEndian(X[3], out, outOff + 12); + + return 16; + } + + public void reset() + { + + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/SerpentEngineBase.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/SerpentEngineBase.java new file mode 100644 index 00000000..c47b2c26 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/SerpentEngineBase.java @@ -0,0 +1,486 @@ +package org.bouncycastle.crypto.engines; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.OutputLengthException; +import org.bouncycastle.crypto.params.KeyParameter; + +public abstract class SerpentEngineBase + implements BlockCipher +{ + protected static final int BLOCK_SIZE = 16; + + static final int ROUNDS = 32; + static final int PHI = 0x9E3779B9; // (sqrt(5) - 1) * 2**31 + + protected boolean encrypting; + protected int[] wKey; + + protected int X0, X1, X2, X3; // registers + + SerpentEngineBase() + { + + } + + /** + * initialise a Serpent cipher. + * + * @param encrypting whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @throws IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init( + boolean encrypting, + CipherParameters params) + { + if (params instanceof KeyParameter) + { + this.encrypting = encrypting; + this.wKey = makeWorkingKey(((KeyParameter)params).getKey()); + return; + } + + throw new IllegalArgumentException("invalid parameter passed to " + getAlgorithmName() + " init - " + params.getClass().getName()); + } + + public String getAlgorithmName() + { + return "Serpent"; + } + + public int getBlockSize() + { + return BLOCK_SIZE; + } + + /** + * Process one block of input from the array in and write it to + * the out array. + * + * @param in the array containing the input data. + * @param inOff offset into the in array the data starts at. + * @param out the array the output data will be copied into. + * @param outOff the offset into the out array the output will start at. + * @return the number of bytes processed and produced. + * @throws DataLengthException if there isn't enough data in in, or + * space in out. + * @throws IllegalStateException if the cipher isn't initialised. + */ + public final int processBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + if (wKey == null) + { + throw new IllegalStateException(getAlgorithmName() + " not initialised"); + } + + if ((inOff + BLOCK_SIZE) > in.length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + BLOCK_SIZE) > out.length) + { + throw new OutputLengthException("output buffer too short"); + } + + if (encrypting) + { + encryptBlock(in, inOff, out, outOff); + } + else + { + decryptBlock(in, inOff, out, outOff); + } + + return BLOCK_SIZE; + } + + public void reset() + { + } + + protected static int rotateLeft( + int x, + int bits) + { + return (x << bits) | (x >>> -bits); + } + + protected static int rotateRight( + int x, + int bits) + { + return (x >>> bits) | (x << -bits); + } + + /** + * The sboxes below are based on the work of Brian Gladman and + * Sam Simpson, whose original notice appears below. + * <p> + * For further details see: + * http://fp.gladman.plus.com/cryptography_technology/serpent/ + */ + + /* Partially optimised Serpent S Box boolean functions derived */ + /* using a recursive descent analyser but without a full search */ + /* of all subtrees. This set of S boxes is the result of work */ + /* by Sam Simpson and Brian Gladman using the spare time on a */ + /* cluster of high capacity servers to search for S boxes with */ + /* this customised search engine. There are now an average of */ + /* 15.375 terms per S box. */ + /* */ + /* Copyright: Dr B. R Gladman (gladman@seven77.demon.co.uk) */ + /* and Sam Simpson (s.simpson@mia.co.uk) */ + /* 17th December 1998 */ + /* */ + /* We hereby give permission for information in this file to be */ + /* used freely subject only to acknowledgement of its origin. */ + + /** + * S0 - { 3, 8,15, 1,10, 6, 5,11,14,13, 4, 2, 7, 0, 9,12 } - 15 terms. + */ + protected final 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. + */ + protected final 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. + */ + protected final 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. + */ + protected final 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. + */ + protected final 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. + */ + protected final 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. + */ + protected final 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 + */ + protected final 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. + */ + protected final 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. + */ + protected final 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. + */ + protected final 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. + */ + protected final 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. + */ + protected final 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. + */ + protected final 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. + */ + protected final 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. + */ + protected final 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. + */ + protected final 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. + */ + protected final 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); + } + + protected abstract int[] makeWorkingKey(byte[] key); + + protected abstract void encryptBlock(byte[] input, int inOff, byte[] output, int outOff); + + protected abstract void decryptBlock(byte[] input, int inOff, byte[] output, int outOff); +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/TnepresEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/TnepresEngine.java new file mode 100644 index 00000000..a34e0162 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/TnepresEngine.java @@ -0,0 +1,303 @@ +package org.bouncycastle.crypto.engines; + +import org.bouncycastle.util.Pack; + +/** + * Tnepres 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> + * Tnepres is based on Serpent which was designed by Ross Anderson, Eli Biham and Lars Knudsen as a + * candidate algorithm for the NIST AES Quest. Unfortunately there was an endianness issue + * with test vectors in the AES submission and the resulting confusion lead to the Tnepres cipher + * as well, which is a byte swapped version of Serpent. + * <p> + * For full details see <a href="http://www.cl.cam.ac.uk/~rja14/serpent.html">The Serpent home page</a> + */ +public final class TnepresEngine + extends SerpentEngineBase +{ + public String getAlgorithmName() + { + return "Tnepres"; + } + + /** + * Expand a user-supplied key material into a session key. + * + * @param key The user-key bytes (multiples of 4) to use. + * @exception IllegalArgumentException + */ + protected int[] makeWorkingKey( + byte[] key) + throws IllegalArgumentException + { + // + // pad key to 256 bits + // + int[] kPad = new int[16]; + int off = 0; + int length = 0; + + for (off = key.length - 4; off > 0; off -= 4) + { + kPad[length++] = Pack.bigEndianToInt(key, off); + } + + if (off == 0) + { + kPad[length++] = Pack.bigEndianToInt(key, 0); + if (length < 8) + { + kPad[length] = 1; + } + } + else + { + throw new IllegalArgumentException("key must be a multiple of 4 bytes"); + } + + // + // expand the padded key up to 33 x 128 bits of key material + // + int amount = (ROUNDS + 1) * 4; + int[] w = new int[amount]; + + // + // compute w0 to w7 from w-8 to w-1 + // + for (int i = 8; i < 16; i++) + { + kPad[i] = rotateLeft(kPad[i - 8] ^ kPad[i - 5] ^ kPad[i - 3] ^ kPad[i - 1] ^ PHI ^ (i - 8), 11); + } + + System.arraycopy(kPad, 8, w, 0, 8); + + // + // compute w8 to w136 + // + for (int i = 8; i < amount; i++) + { + w[i] = rotateLeft(w[i - 8] ^ w[i - 5] ^ w[i - 3] ^ w[i - 1] ^ PHI ^ i, 11); + } + + // + // create the working keys by processing w with the Sbox and IP + // + sb3(w[0], w[1], w[2], w[3]); + w[0] = X0; w[1] = X1; w[2] = X2; w[3] = X3; + sb2(w[4], w[5], w[6], w[7]); + w[4] = X0; w[5] = X1; w[6] = X2; w[7] = X3; + sb1(w[8], w[9], w[10], w[11]); + w[8] = X0; w[9] = X1; w[10] = X2; w[11] = X3; + sb0(w[12], w[13], w[14], w[15]); + w[12] = X0; w[13] = X1; w[14] = X2; w[15] = X3; + sb7(w[16], w[17], w[18], w[19]); + w[16] = X0; w[17] = X1; w[18] = X2; w[19] = X3; + sb6(w[20], w[21], w[22], w[23]); + w[20] = X0; w[21] = X1; w[22] = X2; w[23] = X3; + sb5(w[24], w[25], w[26], w[27]); + w[24] = X0; w[25] = X1; w[26] = X2; w[27] = X3; + sb4(w[28], w[29], w[30], w[31]); + w[28] = X0; w[29] = X1; w[30] = X2; w[31] = X3; + sb3(w[32], w[33], w[34], w[35]); + w[32] = X0; w[33] = X1; w[34] = X2; w[35] = X3; + sb2(w[36], w[37], w[38], w[39]); + w[36] = X0; w[37] = X1; w[38] = X2; w[39] = X3; + sb1(w[40], w[41], w[42], w[43]); + w[40] = X0; w[41] = X1; w[42] = X2; w[43] = X3; + sb0(w[44], w[45], w[46], w[47]); + w[44] = X0; w[45] = X1; w[46] = X2; w[47] = X3; + sb7(w[48], w[49], w[50], w[51]); + w[48] = X0; w[49] = X1; w[50] = X2; w[51] = X3; + sb6(w[52], w[53], w[54], w[55]); + w[52] = X0; w[53] = X1; w[54] = X2; w[55] = X3; + sb5(w[56], w[57], w[58], w[59]); + w[56] = X0; w[57] = X1; w[58] = X2; w[59] = X3; + sb4(w[60], w[61], w[62], w[63]); + w[60] = X0; w[61] = X1; w[62] = X2; w[63] = X3; + sb3(w[64], w[65], w[66], w[67]); + w[64] = X0; w[65] = X1; w[66] = X2; w[67] = X3; + sb2(w[68], w[69], w[70], w[71]); + w[68] = X0; w[69] = X1; w[70] = X2; w[71] = X3; + sb1(w[72], w[73], w[74], w[75]); + w[72] = X0; w[73] = X1; w[74] = X2; w[75] = X3; + sb0(w[76], w[77], w[78], w[79]); + w[76] = X0; w[77] = X1; w[78] = X2; w[79] = X3; + sb7(w[80], w[81], w[82], w[83]); + w[80] = X0; w[81] = X1; w[82] = X2; w[83] = X3; + sb6(w[84], w[85], w[86], w[87]); + w[84] = X0; w[85] = X1; w[86] = X2; w[87] = X3; + sb5(w[88], w[89], w[90], w[91]); + w[88] = X0; w[89] = X1; w[90] = X2; w[91] = X3; + sb4(w[92], w[93], w[94], w[95]); + w[92] = X0; w[93] = X1; w[94] = X2; w[95] = X3; + sb3(w[96], w[97], w[98], w[99]); + w[96] = X0; w[97] = X1; w[98] = X2; w[99] = X3; + sb2(w[100], w[101], w[102], w[103]); + w[100] = X0; w[101] = X1; w[102] = X2; w[103] = X3; + sb1(w[104], w[105], w[106], w[107]); + w[104] = X0; w[105] = X1; w[106] = X2; w[107] = X3; + sb0(w[108], w[109], w[110], w[111]); + w[108] = X0; w[109] = X1; w[110] = X2; w[111] = X3; + sb7(w[112], w[113], w[114], w[115]); + w[112] = X0; w[113] = X1; w[114] = X2; w[115] = X3; + sb6(w[116], w[117], w[118], w[119]); + w[116] = X0; w[117] = X1; w[118] = X2; w[119] = X3; + sb5(w[120], w[121], w[122], w[123]); + w[120] = X0; w[121] = X1; w[122] = X2; w[123] = X3; + sb4(w[124], w[125], w[126], w[127]); + w[124] = X0; w[125] = X1; w[126] = X2; w[127] = X3; + sb3(w[128], w[129], w[130], w[131]); + w[128] = X0; w[129] = X1; w[130] = X2; w[131] = X3; + + return w; + } + + /** + * Encrypt one block of plaintext. + * + * @param input the array containing the input data. + * @param inOff offset into the in array the data starts at. + * @param output the array the output data will be copied into. + * @param outOff the offset into the out array the output will start at. + */ + protected void encryptBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + X3 = Pack.bigEndianToInt(input, inOff); + X2 = Pack.bigEndianToInt(input, inOff + 4); + X1 = Pack.bigEndianToInt(input, inOff + 8); + X0 = Pack.bigEndianToInt(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); + + Pack.intToBigEndian(wKey[131] ^ X3, output, outOff); + Pack.intToBigEndian(wKey[130] ^ X2, output, outOff + 4); + Pack.intToBigEndian(wKey[129] ^ X1, output, outOff + 8); + Pack.intToBigEndian(wKey[128] ^ X0, output, outOff + 12); + } + + /** + * Decrypt one block of ciphertext. + * + * @param input the array containing the input data. + * @param inOff offset into the in array the data starts at. + * @param output the array the output data will be copied into. + * @param outOff the offset into the out array the output will start at. + */ + protected void decryptBlock( + byte[] input, + int inOff, + byte[] output, + int outOff) + { + X3 = wKey[131] ^ Pack.bigEndianToInt(input, inOff); + X2 = wKey[130] ^ Pack.bigEndianToInt(input, inOff + 4); + X1 = wKey[129] ^ Pack.bigEndianToInt(input, inOff + 8); + X0 = wKey[128] ^ Pack.bigEndianToInt(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); + + Pack.intToBigEndian(X3 ^ wKey[3], output, outOff); + Pack.intToBigEndian(X2 ^ wKey[2], output, outOff + 4); + Pack.intToBigEndian(X1 ^ wKey[1], output, outOff + 8); + Pack.intToBigEndian(X0 ^ wKey[0], output, outOff + 12); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/EntropyUtil.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/EntropyUtil.java new file mode 100644 index 00000000..b34e9938 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/EntropyUtil.java @@ -0,0 +1,46 @@ +package org.bouncycastle.crypto.prng; + +/** + * Utility methods for making use of EntropySources. + */ +public class EntropyUtil +{ + /** + * Generate numBytes worth of entropy from the passed in entropy source. + * + * @param entropySource the entropy source to request the data from. + * @param numBytes the number of bytes of entropy requested. + * @return a byte array populated with the random data. + */ + public static byte[] generateSeed(EntropySource entropySource, int numBytes) + { + byte[] bytes = new byte[numBytes]; + + if (numBytes * 8 <= entropySource.entropySize()) + { + byte[] ent = entropySource.getEntropy(); + + System.arraycopy(ent, 0, bytes, 0, bytes.length); + } + else + { + int entSize = entropySource.entropySize() / 8; + + for (int i = 0; i < bytes.length; i += entSize) + { + byte[] ent = entropySource.getEntropy(); + + if (ent.length <= bytes.length - i) + { + System.arraycopy(ent, 0, bytes, i, ent.length); + } + else + { + System.arraycopy(ent, 0, bytes, i, bytes.length - i); + } + } + } + + return bytes; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ISOTrailers.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ISOTrailers.java new file mode 100644 index 00000000..2793d60a --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ISOTrailers.java @@ -0,0 +1,56 @@ +package org.bouncycastle.crypto.signers; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.util.Integers; + +public class ISOTrailers +{ + private static final Map<String, Integer> trailerMap; + + static final public int TRAILER_IMPLICIT = 0xBC; + + static final public int TRAILER_RIPEMD160 = 0x31CC; + static final public int TRAILER_RIPEMD128 = 0x32CC; + static final public int TRAILER_SHA1 = 0x33CC; + static final public int TRAILER_SHA256 = 0x34CC; + static final public int TRAILER_SHA512 = 0x35CC; + static final public int TRAILER_SHA384 = 0x36CC; + static final public int TRAILER_WHIRLPOOL = 0x37CC; + static final public int TRAILER_SHA224 = 0x38CC; + static final public int TRAILER_SHA512_224 = 0x39CC; + static final public int TRAILER_SHA512_256 = 0x40CC; + + static + { + Map<String, Integer> trailers = new HashMap<String, Integer>(); + + trailers.put("RIPEMD128", Integers.valueOf(TRAILER_RIPEMD128)); + trailers.put("RIPEMD160", Integers.valueOf(TRAILER_RIPEMD160)); + + trailers.put("SHA-1", Integers.valueOf(TRAILER_SHA1)); + trailers.put("SHA-224", Integers.valueOf(TRAILER_SHA224)); + trailers.put("SHA-256", Integers.valueOf(TRAILER_SHA256)); + trailers.put("SHA-384", Integers.valueOf(TRAILER_SHA384)); + trailers.put("SHA-512", Integers.valueOf(TRAILER_SHA512)); + trailers.put("SHA-512/224", Integers.valueOf(TRAILER_SHA512_224)); + trailers.put("SHA-512/256", Integers.valueOf(TRAILER_SHA512_256)); + + trailers.put("Whirlpool", Integers.valueOf(TRAILER_WHIRLPOOL)); + + trailerMap = Collections.unmodifiableMap(trailers); + } + + public static Integer getTrailer(Digest digest) + { + return (Integer)trailerMap.get(digest.getAlgorithmName()); // JDK 1.4 compatibility + } + + public static boolean noTrailerAvailable(Digest digest) + { + return !trailerMap.containsKey(digest.getAlgorithmName()); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/Blake2bDigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/Blake2bDigestTest.java new file mode 100644 index 00000000..e962af94 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/Blake2bDigestTest.java @@ -0,0 +1,159 @@ +package org.bouncycastle.crypto.test; + +import java.io.UnsupportedEncodingException; + +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.digests.Blake2bDigest; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTest; + +public class Blake2bDigestTest + extends SimpleTest +{ + + private static final String[][] keyedTestVectors = + { // input/message, key, hash + + // Vectors from BLAKE2 web site: https://blake2.net/blake2b-test.txt + { + "", + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "10ebb67700b1868efb4417987acf4690ae9d972fb7a590c2f02871799aaa4786b5e996e8f0f4eb981fc214b005f42d2ff4233499391653df7aefcbc13fc51568" }, + + { + "00", + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "961f6dd1e4dd30f63901690c512e78e4b45e4742ed197c3c5e45c549fd25f2e4187b0bc9fe30492b16b0d0bc4ef9b0f34c7003fac09a5ef1532e69430234cebd" }, + + { + "0001", + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "da2cfbe2d8409a0f38026113884f84b50156371ae304c4430173d08a99d9fb1b983164a3770706d537f49e0c916d9f32b95cc37a95b99d857436f0232c88a965" }, + + { + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d", + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "f1aa2b044f8f0c638a3f362e677b5d891d6fd2ab0765f6ee1e4987de057ead357883d9b405b9d609eea1b869d97fb16d9b51017c553f3b93c0a1e0f1296fedcd" }, + + { + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3", + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "c230f0802679cb33822ef8b3b21bf7a9a28942092901d7dac3760300831026cf354c9232df3e084d9903130c601f63c1f4a4a4b8106e468cd443bbe5a734f45f" }, + + { + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe", + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + "142709d62e28fcccd0af97fad0f8465b971e82201dc51070faa0372aa43e92484be1c1e73ba10906d5d1853db6a4106e0a7bf9800d373d6dee2d46d62ef2a461" } }; + + private final static String[][] unkeyedTestVectors = + { // from: http://fossies.org/linux/john/src/rawBLAKE2_512_fmt_plug.c + // hash, input/message + // digests without leading $BLAKE2$ + { + "4245af08b46fbb290222ab8a68613621d92ce78577152d712467742417ebc1153668f1c9e1ec1e152a32a9c242dc686d175e087906377f0c483c5be2cb68953e", + "blake2" }, + { + "021ced8799296ceca557832ab941a50b4a11f83478cf141f51f933f653ab9fbcc05a037cddbed06e309bf334942c4e58cdf1a46e237911ccd7fcf9787cbc7fd0", + "hello world" }, + { + "1f7d9b7c9a90f7bfc66e52b69f3b6c3befbd6aee11aac860e99347a495526f30c9e51f6b0db01c24825092a09dd1a15740f0ade8def87e60c15da487571bcef7", + "verystrongandlongpassword" }, + { + "a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918", + "The quick brown fox jumps over the lazy dog" }, + { + "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce", + "" }, + { + "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923", + "abc" }, + }; + + public String getName() + { + return "Blake2b"; + } + + private void offsetTest( + Digest digest, + byte[] input, + byte[] expected) + { + byte[] resBuf = new byte[expected.length + 11]; + + digest.update(input, 0, input.length); + + digest.doFinal(resBuf, 11); + + if (!areEqual(Arrays.copyOfRange(resBuf, 11, resBuf.length), expected)) + { + fail("Offset failed got " + new String(Hex.encode(resBuf))); + } + } + + public void performTest() throws Exception + { + // test keyed test vectors: + + Blake2bDigest blake2bkeyed = new Blake2bDigest(Hex.decode(keyedTestVectors[0][1])); + for (int tv = 0; tv < keyedTestVectors.length; tv++) + { + + byte[] input = Hex.decode(keyedTestVectors[tv][0]); + blake2bkeyed.reset(); + + blake2bkeyed.update(input, 0, input.length); + byte[] keyedHash = new byte[64]; + blake2bkeyed.doFinal(keyedHash, 0); + + if (!Arrays.areEqual(Hex.decode(keyedTestVectors[tv][2]), keyedHash)) + { + fail("Blake2b mismatch on test vector ", + keyedTestVectors[tv][2], + new String(Hex.encode(keyedHash))); + } + + offsetTest(blake2bkeyed, input, keyedHash); + } + + Blake2bDigest blake2bunkeyed = new Blake2bDigest(); + // test unkeyed test vectors: + for (int i = 0; i < unkeyedTestVectors.length; i++) + { + + try + { + // blake2bunkeyed.update( + // unkeyedTestVectors[i][1].getBytes("UTF-8")); + // test update(byte b) + byte[] unkeyedInput = unkeyedTestVectors[i][1] + .getBytes("UTF-8"); + for (int j = 0; j < unkeyedInput.length; j++) + { + blake2bunkeyed.update(unkeyedInput[j]); + } + } catch (UnsupportedEncodingException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + byte[] unkeyedHash = new byte[64]; + blake2bunkeyed.doFinal(unkeyedHash, 0); + blake2bunkeyed.reset(); + + if (!Arrays.areEqual(Hex.decode(unkeyedTestVectors[i][0]), + unkeyedHash)) + { + fail("Blake2b mismatch on test vector ", + unkeyedTestVectors[i][0], + new String(Hex.encode(unkeyedHash))); + } + } + } + + public static void main(String[] args) throws Exception + { + runTest(new Blake2bDigestTest()); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/KeccakDigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/KeccakDigestTest.java new file mode 100644 index 00000000..7d9bdf12 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/KeccakDigestTest.java @@ -0,0 +1,363 @@ +package org.bouncycastle.crypto.test; + +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.Mac; +import org.bouncycastle.crypto.digests.KeccakDigest; +import org.bouncycastle.crypto.macs.HMac; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTest; + +/** + * Keccak Digest Test + */ +public class KeccakDigestTest + extends SimpleTest +{ + final static String[] messages = { + "", + "54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67", + "54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f672e" + }; + + final static String[] digests288 = { // the default settings + "6753e3380c09e385d0339eb6b050a68f66cfd60a73476e6fd6adeb72f5edd7c6f04a5d01", // message[0] + "0bbe6afae0d7e89054085c1cc47b1689772c89a41796891e197d1ca1b76f288154933ded", // message[1] + "82558a209b960ddeb531e6dcb281885b2400ca160472462486e79f071e88a3330a8a303d", // message[2] + "94049e1ad7ef5d5b0df2b880489e7ab09ec937c3bfc1b04470e503e1ac7b1133c18f86da", // 64k a-test + "a9cb5a75b5b81b7528301e72553ed6770214fa963956e790528afe420de33c074e6f4220", // random alphabet test + "eadaf5ba2ad6a2f6f338fce0e1efdad2a61bb38f6be6068b01093977acf99e97a5d5827c" // extremely long data test + }; + + final static String[] digests224 = { + "f71837502ba8e10837bdd8d365adb85591895602fc552b48b7390abd", + "310aee6b30c47350576ac2873fa89fd190cdc488442f3ef654cf23fe", + "c59d4eaeac728671c635ff645014e2afa935bebffdb5fbd207ffdeab", + "f621e11c142fbf35fa8c22841c3a812ba1e0151be4f38d80b9f1ff53", + "68b5fc8c87193155bba68a2485377e809ee4f81a85ef023b9e64add0", + "c42e4aee858e1a8ad2976896b9d23dd187f64436ee15969afdbc68c5" + }; + + final static String[] digests256 = { + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "4d741b6f1eb29cb2a9b9911c82f56fa8d73b04959d3d9d222895df6c0b28aa15", + "578951e24efd62a3d63a86f7cd19aaa53c898fe287d2552133220370240b572d", + "0047a916daa1f92130d870b542e22d3108444f5a7e4429f05762fb647e6ed9ed", + "db368762253ede6d4f1db87e0b799b96e554eae005747a2ea687456ca8bcbd03", + "5f313c39963dcf792b5470d4ade9f3a356a3e4021748690a958372e2b06f82a4" + }; + + final static String[] digests384 = { + "2c23146a63a29acf99e73b88f8c24eaa7dc60aa771780ccc006afbfa8fe2479b2dd2b21362337441ac12b515911957ff", + "283990fa9d5fb731d786c5bbee94ea4db4910f18c62c03d173fc0a5e494422e8a0b3da7574dae7fa0baf005e504063b3", + "9ad8e17325408eddb6edee6147f13856ad819bb7532668b605a24a2d958f88bd5c169e56dc4b2f89ffd325f6006d820b", + "c704cfe7a1a53208ca9526cd24251e0acdc252ecd978eee05acd16425cfb404ea81f5a9e2e5e97784d63ee6a0618a398", + "d4fe8586fd8f858dd2e4dee0bafc19b4c12b4e2a856054abc4b14927354931675cdcaf942267f204ea706c19f7beefc4", + "9b7168b4494a80a86408e6b9dc4e5a1837c85dd8ff452ed410f2832959c08c8c0d040a892eb9a755776372d4a8732315" + }; + + final static String[] digests512 = { + "0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e", + "d135bb84d0439dbac432247ee573a23ea7d3c9deb2a968eb31d47c4fb45f1ef4422d6c531b5b9bd6f449ebcc449ea94d0a8f05f62130fda612da53c79659f609", + "ab7192d2b11f51c7dd744e7b3441febf397ca07bf812cceae122ca4ded6387889064f8db9230f173f6d1ab6e24b6e50f065b039f799f5592360a6558eb52d760", + "34341ead153aa1d1fdcf6cf624c2b4f6894b6fd16dc38bd4ec971ac0385ad54fafcb2e0ed86a1e509456f4246fdcb02c3172824cd649d9ad54c51f7fb49ea67c", + "dc44d4f4d36b07ab5fc04016cbe53548e5a7778671c58a43cb379fd00c06719b8073141fc22191ffc3db5f8b8983ae8341fa37f18c1c969664393aa5ceade64e", + "3e122edaf37398231cfaca4c7c216c9d66d5b899ec1d7ac617c40c7261906a45fc01617a021e5da3bd8d4182695b5cb785a28237cbb167590e34718e56d8aab8" + }; + + // test vectors from http://www.di-mgt.com.au/hmac_sha3_testvectors.html + final static byte[][] macKeys = + { + Hex.decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"), + Hex.decode("4a656665"), + Hex.decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + Hex.decode("0102030405060708090a0b0c0d0e0f10111213141516171819"), + Hex.decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + "aaaaaa"), + Hex.decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + "aaaaaa"), + Hex.decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + }; + + final static String[] macData = + { + "4869205468657265", + "7768617420646f2079612077616e7420666f72206e6f7468696e673f", + "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" + + "dddddddddddddddddddddddddddddddddddd", + "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" + + "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd", + "54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a" + + "65204b6579202d2048617368204b6579204669727374", + "5468697320697320612074657374207573696e672061206c6172676572207468" + + "616e20626c6f636b2d73697a65206b657920616e642061206c61726765722074" + + "68616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565" + + "647320746f20626520686173686564206265666f7265206265696e6720757365" + + "642062792074686520484d414320616c676f726974686d2e", + "5468697320697320612074657374207573696e672061206c6172676572207468" + + "616e20626c6f636b2d73697a65206b657920616e642061206c61726765722074" + + "68616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565" + + "647320746f20626520686173686564206265666f7265206265696e6720757365\n" + + "642062792074686520484d414320616c676f726974686d2e" + }; + + final static String[] mac224 = + { + "b73d595a2ba9af815e9f2b4e53e78581ebd34a80b3bbaac4e702c4cc", + "e824fec96c074f22f99235bb942da1982664ab692ca8501053cbd414", + "770df38c99d6e2bacd68056dcfe07d4c89ae20b2686a6185e1faa449", + "305a8f2dfb94bad28861a03cbc4d590febe775c58cb4961c28428a0b", + "e7a52dfa45f95a217c100066b239aa8ad519be9b35d667268b1b57ff", + "ba13009405a929f398b348885caa5419191bb948ada32194afc84104", + "92649468be236c3c72c189909c063b13f994be05749dc91310db639e" + }; + + final static String[] mac256 = + { + "9663d10c73ee294054dc9faf95647cb99731d12210ff7075fb3d3395abfb9821", + "aa9aed448c7abc8b5e326ffa6a01cdedf7b4b831881468c044ba8dd4566369a1", + "95f43e50f8df80a21977d51a8db3ba572dcd71db24687e6f86f47c1139b26260", + "6331ba9b4af5804a68725b3663eb74814494b63c6093e35fb320a85d507936fd", + "b4d0cdee7ec2ba81a88b86918958312300a15622377929a054a9ce3ae1fac2b6", + "1fdc8cb4e27d07c10d897dec39c217792a6e64fa9c63a77ce42ad106ef284e02", + "fdaa10a0299aecff9bb411cf2d7748a4022e4a26be3fb5b11b33d8c2b7ef5484" + }; + + final static String[] mac384 = + { + "892dfdf5d51e4679bf320cd16d4c9dc6f749744608e003add7fba894acff87361efa4e5799be06b6461f43b60ae97048", + "5af5c9a77a23a6a93d80649e562ab77f4f3552e3c5caffd93bdf8b3cfc6920e3023fc26775d9df1f3c94613146ad2c9d", + "4243c29f2201992ff96441e3b91ff81d8c601d706fbc83252684a4bc51101ca9b2c06ddd03677303c502ac5331752a3c", + "b730724d3d4090cda1be799f63acbbe389fef7792fc18676fa5453aab398664650ed029c3498bbe8056f06c658e1e693", + "d62482ef601d7847439b55236e9679388ffcd53c62cd126f39be6ea63de762e26cd5974cb9a8de401b786b5555040f6f", + "4860ea191ac34994cf88957afe5a836ef36e4cc1a66d75bf77defb7576122d75f60660e4cf731c6effac06402787e2b9", + "fe9357e3cfa538eb0373a2ce8f1e26ad6590afdaf266f1300522e8896d27e73f654d0631c8fa598d4bb82af6b744f4f5" + }; + + final static String[] mac512 = + { + "8852c63be8cfc21541a4ee5e5a9a852fc2f7a9adec2ff3a13718ab4ed81aaea0b87b7eb397323548e261a64e7fc75198f6663a11b22cd957f7c8ec858a1c7755", + "c2962e5bbe1238007852f79d814dbbecd4682e6f097d37a363587c03bfa2eb0859d8d9c701e04cececfd3dd7bfd438f20b8b648e01bf8c11d26824b96cebbdcb", + "eb0ed9580e0ec11fc66cbb646b1be904eaff6da4556d9334f65ee4b2c85739157bae9027c51505e49d1bb81cfa55e6822db55262d5a252c088a29a5e95b84a66", + "b46193bb59f4f696bf702597616da91e2a4558a593f4b015e69141ba81e1e50ea580834c2b87f87baa25a3a03bfc9bb389847f2dc820beae69d30c4bb75369cb", + "d05888a6ebf8460423ea7bc85ea4ffda847b32df32291d2ce115fd187707325c7ce4f71880d91008084ce24a38795d20e6a28328a0f0712dc38253370da3ebb5", + "2c6b9748d35c4c8db0b4407dd2ed2381f133bdbd1dfaa69e30051eb6badfcca64299b88ae05fdbd3dd3dd7fe627e42e39e48b0fe8c7f1e85f2dbd52c2d753572", + "6adc502f14e27812402fc81a807b28bf8a53c87bea7a1df6256bf66f5de1a4cb741407ad15ab8abc136846057f881969fbb159c321c904bfb557b77afb7778c8" + }; + + final static KeyParameter truncKey = new KeyParameter(Hex.decode("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c")); + final static byte[] truncData = Hex.decode("546573742057697468205472756e636174696f6e"); + + final static byte[] trunc224 = Hex.decode("f52bbcfd654264e7133085c5e69b72c3"); + final static byte[] trunc256 = Hex.decode("745e7e687f8335280d54202ef13cecc6"); + final static byte[] trunc384 = Hex.decode("fa9aea2bc1e181e47cbb8c3df243814d"); + final static byte[] trunc512 = Hex.decode("04c929fead434bba190dacfa554ce3f5"); + + final static byte[] xtremeData = Hex.decode("61626364656667686263646566676869636465666768696a6465666768696a6b65666768696a6b6c666768696a6b6c6d6768696a6b6c6d6e68696a6b6c6d6e6f"); + + KeccakDigestTest() + { + } + + public String getName() + { + return "Keccak"; + } + + private void testDigest(Digest digest, String[] expected) + { + byte[] hash = new byte[digest.getDigestSize()]; + + for (int i = 0; i != messages.length; i++) + { + if (messages.length != 0) + { + byte[] data = Hex.decode(messages[i]); + + digest.update(data, 0, data.length); + } + + digest.doFinal(hash, 0); + + if (!Arrays.areEqual(Hex.decode(expected[i]), hash)) + { + fail("Keccak mismatch on " + digest.getAlgorithmName() + " index " + i); + } + } + + byte[] k64 = new byte[1024 * 64]; + + for (int i = 0; i != k64.length; i++) + { + k64[i] = (byte)'a'; + } + + digest.update(k64, 0, k64.length); + + digest.doFinal(hash, 0); + + if (!Arrays.areEqual(Hex.decode(expected[messages.length]), hash)) + { + fail("Keccak mismatch on " + digest.getAlgorithmName() + " 64k a"); + } + + for (int i = 0; i != k64.length; i++) + { + digest.update((byte)'a'); + } + + digest.doFinal(hash, 0); + + if (!Arrays.areEqual(Hex.decode(expected[messages.length]), hash)) + { + fail("Keccak mismatch on " + digest.getAlgorithmName() + " 64k a single"); + } + + + for (int i = 0; i != k64.length; i++) + { + k64[i] = (byte)('a' + (i % 26)); + } + + digest.update(k64, 0, k64.length); + + digest.doFinal(hash, 0); + + if (!Arrays.areEqual(Hex.decode(expected[messages.length + 1]), hash)) + { + fail("Keccak mismatch on " + digest.getAlgorithmName() + " 64k alpha"); + } + + for (int i = 0; i != 64; i++) + { + digest.update(k64[i * 1024]); + digest.update(k64, i * 1024 + 1, 1023); + } + + digest.doFinal(hash, 0); + + if (!Arrays.areEqual(Hex.decode(expected[messages.length + 1]), hash)) + { + fail("Keccak mismatch on " + digest.getAlgorithmName() + " 64k chunked alpha"); + } + + testDigestDoFinal(digest); + + // + // extremely long data test + // +// System.out.println("Starting very long"); +// for (int i = 0; i != 16384; i++) +// { +// for (int j = 0; j != 1024; j++) +// { +// digest.update(xtremeData, 0, xtremeData.length); +// } +// } +// +// digest.doFinal(hash, 0); +// +// if (!Arrays.areEqual(Hex.decode(expected[messages.length + 2]), hash)) +// { +// fail("Keccak mismatch on " + digest.getAlgorithmName() + " extreme data test"); +// } +// System.out.println("Done"); + } + + private void testDigestDoFinal(Digest digest) + { + byte[] hash = new byte[digest.getDigestSize()]; + digest.doFinal(hash, 0); + + for (int i = 0; i <= digest.getDigestSize(); ++i) + { + byte[] cmp = new byte[2 * digest.getDigestSize()]; + System.arraycopy(hash, 0, cmp, i, hash.length); + + byte[] buf = new byte[2 * digest.getDigestSize()]; + digest.doFinal(buf, i); + + if (!Arrays.areEqual(cmp, buf)) + { + fail("Keccak offset doFinal on " + digest.getAlgorithmName()); + } + } + } + + private void testMac(Digest digest, byte[][] keys, String[] data, String[] expected, byte[] truncExpected) + { + Mac mac = new HMac(digest); + + for (int i = 0; i != keys.length; i++) + { + mac.init(new KeyParameter(keys[i])); + + byte[] mData = Hex.decode(data[i]); + + mac.update(mData, 0, mData.length); + + byte[] macV = new byte[mac.getMacSize()]; + + mac.doFinal(macV, 0); + + if (!Arrays.areEqual(Hex.decode(expected[i]), macV)) + { + fail("Keccak HMAC mismatch on " + digest.getAlgorithmName()); + } + } + + mac = new HMac(digest); + + mac.init(truncKey); + + mac.update(truncData, 0, truncData.length); + + byte[] macV = new byte[mac.getMacSize()]; + + mac.doFinal(macV, 0); + + for (int i = 0; i != truncExpected.length; i++) + { + if (macV[i] != truncExpected[i]) + { + fail("mismatch on truncated HMAC for " + digest.getAlgorithmName()); + } + } + } + + public void performTest() throws Exception + { + testDigest(new KeccakDigest(), digests288); + testDigest(new KeccakDigest(224), digests224); + testDigest(new KeccakDigest(256), digests256); + testDigest(new KeccakDigest(384), digests384); + testDigest(new KeccakDigest(512), digests512); + + testMac(new KeccakDigest(224), macKeys, macData, mac224, trunc224); + testMac(new KeccakDigest(256), macKeys, macData, mac256, trunc256); + testMac(new KeccakDigest(384), macKeys, macData, mac384, trunc384); + testMac(new KeccakDigest(512), macKeys, macData, mac512, trunc512); + } + + protected Digest cloneDigest(Digest digest) + { + return new KeccakDigest((KeccakDigest)digest); + } + + public static void main( + String[] args) + { + runTest(new KeccakDigestTest()); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/RNGUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/RNGUtils.java new file mode 100644 index 00000000..166c49da --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/RNGUtils.java @@ -0,0 +1,11 @@ +package org.bouncycastle.crypto.test; + +import java.util.Random; + +class RNGUtils +{ + public static int nextInt(Random rng, int n) + { + return rng.nextInt(n); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SHAKEDigestTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHAKEDigestTest.java new file mode 100644 index 00000000..a3ec2eba --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SHAKEDigestTest.java @@ -0,0 +1,305 @@ +package org.bouncycastle.crypto.test; + +import java.io.BufferedReader; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + +import org.bouncycastle.crypto.digests.SHAKEDigest; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTest; + +/** + * SHAKE Digest Test + */ +public class SHAKEDigestTest + extends SimpleTest +{ + static class MySHAKEDigest extends SHAKEDigest + { + MySHAKEDigest(int bitLength) + { + super(bitLength); + } + + int myDoFinal(byte[] out, int outOff, int outLen, byte partialByte, int partialBits) + { + return doFinal(out, outOff, outLen, partialByte, partialBits); + } + } + + SHAKEDigestTest() + { + } + + public String getName() + { + return "SHAKE"; + } + + public void performTest() throws Exception + { + testVectors(); + } + + public void testVectors() throws Exception + { + BufferedReader r = new BufferedReader(new InputStreamReader( + getClass().getResourceAsStream("SHAKETestVectors.txt"))); + + String line; + while (null != (line = readLine(r))) + { + if (line.length() != 0) + { + TestVector v = readTestVector(r, line); + runTestVector(v); + } + } + + r.close(); + } + + private MySHAKEDigest createDigest(String algorithm) throws Exception + { + if (algorithm.startsWith("SHAKE-")) + { + int bits = parseDecimal(algorithm.substring("SHAKE-".length())); + return new MySHAKEDigest(bits); + } + throw new IllegalArgumentException("Unknown algorithm: " + algorithm); + } + + private byte[] decodeBinary(String block) + { + int bits = block.length(); + int fullBytes = bits / 8; + int totalBytes = (bits + 7) / 8; + byte[] result = new byte[totalBytes]; + + for (int i = 0; i < fullBytes; ++i) + { + String byteStr = reverse(block.substring(i * 8, (i + 1) * 8)); + result[i] = (byte)parseBinary(byteStr); + } + + if (totalBytes > fullBytes) + { + String byteStr = reverse(block.substring(fullBytes * 8)); + result[fullBytes] = (byte)parseBinary(byteStr); + } + + return result; + } + + private int parseBinary(String s) + { + return Integer.parseInt(s, 2); + } + + private int parseDecimal(String s) + { + return Integer.parseInt(s); + } + + private String readBlock(BufferedReader r) throws IOException + { + StringBuffer b = new StringBuffer(); + String line; + while ((line = readBlockLine(r)) != null) + { + b.append(line); + } + return b.toString(); + } + + private String readBlockLine(BufferedReader r) throws IOException + { + String line = readLine(r); + if (line == null || line.length() == 0) + { + return null; + } + + char[] chars = line.toCharArray(); + + int pos = 0; + for (int i = 0; i != chars.length; i++) + { + if (chars[i] != ' ') + { + chars[pos++] = chars[i]; + } + } + + return new String(chars, 0, pos); + } + + private TestVector readTestVector(BufferedReader r, String header) throws IOException + { + String[] parts = splitAround(header, TestVector.SAMPLE_OF); + + String algorithm = parts[0]; + int bits = parseDecimal(stripFromChar(parts[1], '-')); + + skipUntil(r, TestVector.MSG_HEADER); + String messageBlock = readBlock(r); + if (messageBlock.length() != bits) + { + throw new IllegalStateException("Test vector length mismatch"); + } + byte[] message = decodeBinary(messageBlock); + + skipUntil(r, TestVector.OUTPUT_HEADER); + byte[] output = Hex.decode(readBlock(r)); + + return new TestVector(algorithm, bits, message, output); + } + + private String readLine(BufferedReader r) throws IOException + { + String line = r.readLine(); + return line == null ? null : stripFromChar(line, '#').trim(); + } + + private String requireLine(BufferedReader r) throws IOException + { + String line = readLine(r); + if (line == null) + { + throw new EOFException(); + } + return line; + } + + private String reverse(String s) + { + return new StringBuffer(s).reverse().toString(); + } + + private void runTestVector(TestVector v) throws Exception + { + int bits = v.getBits(); + int partialBits = bits % 8; + + byte[] expected = v.getOutput(); + +// System.out.println(v.getAlgorithm() + " " + bits + "-bit"); +// System.out.println(Hex.toHexString(v.getMessage()).toUpperCase()); +// System.out.println(Hex.toHexString(expected).toUpperCase()); + + int outLen = expected.length; + + MySHAKEDigest d = createDigest(v.getAlgorithm()); + byte[] output = new byte[outLen]; + + byte[] m = v.getMessage(); + if (partialBits == 0) + { + d.update(m, 0, m.length); + d.doFinal(output, 0, outLen); + } + else + { + d.update(m, 0, m.length - 1); + d.myDoFinal(output, 0, outLen, m[m.length - 1], partialBits); + } + + if (!Arrays.areEqual(expected, output)) + { + fail(v.getAlgorithm() + " " + v.getBits() + "-bit test vector hash mismatch"); +// System.err.println(v.getAlgorithm() + " " + v.getBits() + "-bit test vector hash mismatch"); +// System.err.println(Hex.toHexString(output).toUpperCase()); + } + } + + private void skipUntil(BufferedReader r, String header) throws IOException + { + String line; + do + { + line = requireLine(r); + } + while (line.length() == 0); + if (!line.equals(header)) + { + throw new IOException("Expected: " + header); + } + } + + private String[] splitAround(String s, String separator) + { + List strings = new ArrayList(); + + String remaining = s; + int index; + + while ((index = remaining.indexOf(separator)) > 0) + { + strings.add(remaining.substring(0, index)); + remaining = remaining.substring(index + separator.length()); + } + strings.add(remaining); + + return (String[])strings.toArray(new String[strings.size()]); + } + + private String stripFromChar(String s, char c) + { + int i = s.indexOf(c); + if (i >= 0) + { + s = s.substring(0, i); + } + return s; + } + + public static void main( + String[] args) + { + runTest(new SHAKEDigestTest()); + } + + private static class TestVector + { + private static String SAMPLE_OF = " sample of "; + private static String MSG_HEADER = "Msg as bit string"; + private static String OUTPUT_HEADER = "Output val is"; + + private String algorithm; + private int bits; + private byte[] message; + private byte[] output; + + private TestVector(String algorithm, int bits, byte[] message, byte[] output) + { + this.algorithm = algorithm; + this.bits = bits; + this.message = message; + this.output = output; + } + + public String getAlgorithm() + { + return algorithm; + } + + public int getBits() + { + return bits; + } + + public byte[] getMessage() + { + return message; + } + + public byte[] getOutput() + { + return output; + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/SM4Test.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/SM4Test.java new file mode 100644 index 00000000..7c12e5b1 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/SM4Test.java @@ -0,0 +1,81 @@ +package org.bouncycastle.crypto.test; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.engines.SM4Engine; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTest; + +/** + * SM4 tester, vectors from <a href="http://eprint.iacr.org/2008/329.pdf">http://eprint.iacr.org/2008/329.pdf</a> + */ +public class SM4Test + extends CipherTest +{ + static SimpleTest[] tests = { + new BlockCipherVectorTest(0, new SM4Engine(), + new KeyParameter(Hex.decode("0123456789abcdeffedcba9876543210")), + "0123456789abcdeffedcba9876543210", + "681edf34d206965e86b3e94f536e4246") + }; + + SM4Test() + { + super(tests, new SM4Engine(), new KeyParameter(new byte[16])); + } + + public void performTest() + throws Exception + { + super.performTest(); + + test1000000(); + } + + private void test1000000() + { + byte[] plain = Hex.decode("0123456789abcdeffedcba9876543210"); + byte[] key = Hex.decode("0123456789abcdeffedcba9876543210"); + byte[] cipher = Hex.decode("595298c7c6fd271f0402f804c33d3f66"); + byte[] buf = new byte[16]; + + BlockCipher engine = new SM4Engine(); + + engine.init(true, new KeyParameter(key)); + + System.arraycopy(plain, 0, buf, 0, buf.length); + + for (int i = 0; i != 1000000; i++) + { + engine.processBlock(buf, 0, buf, 0); + } + + if (!areEqual(cipher, buf)) + { + fail("1000000 encryption test failed"); + } + + engine.init(false, new KeyParameter(key)); + + for (int i = 0; i != 1000000; i++) + { + engine.processBlock(buf, 0, buf, 0); + } + + if (!areEqual(plain, buf)) + { + fail("1000000 decryption test failed"); + } + } + + public String getName() + { + return "SM4"; + } + + public static void main( + String[] args) + { + runTest(new SM4Test()); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/TnepresTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/TnepresTest.java new file mode 100644 index 00000000..6b873587 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/TnepresTest.java @@ -0,0 +1,144 @@ +package org.bouncycastle.crypto.test; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.engines.TnepresEngine; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTest; + +/** + * Test vectors based on Floppy 4 of the Serpent AES submission. + */ +public class TnepresTest + extends CipherTest +{ + static SimpleTest[] tests = + { + new BlockCipherVectorTest(0, new TnepresEngine(), + new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")), + "00000000000000000000000000000000", "8910494504181950f98dd998a82b6749"), + new BlockCipherVectorTest(1, new TnepresEngine(), + new KeyParameter(Hex.decode("00000000000000000000000000000000")), + "80000000000000000000000000000000", "10b5ffb720b8cb9002a1142b0ba2e94a"), + new BlockCipherVectorTest(2, new TnepresEngine(), + new KeyParameter(Hex.decode("00000000000000000000000000000000")), + "00000000008000000000000000000000", "4f057a42d8d5bd9746e434680ddcd5e5"), + new BlockCipherVectorTest(3, new TnepresEngine(), + new KeyParameter(Hex.decode("00000000000000000000000000000000")), + "00000000000000000000400000000000", "99407bf8582ef12550886ef5b6f169b9"), + new BlockCipherVectorTest(4, new TnepresEngine(), + new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")), + "40000000000000000000000000000000", "d522a3b8d6d89d4d2a124fdd88f36896"), + new BlockCipherVectorTest(5, new TnepresEngine(), + new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")), + "00000000000200000000000000000000", "189b8ec3470085b3da97e82ca8964e32"), + new BlockCipherVectorTest(6, new TnepresEngine(), + new KeyParameter(Hex.decode("000000000000000000000000000000000000000000000000")), + "00000000000000000000008000000000", "f77d868cf760b9143a89809510ccb099"), + new BlockCipherVectorTest(7, new TnepresEngine(), + new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")), + "08000000000000000000000000000000", "d43b7b981b829342fce0e3ec6f5f4c82"), + new BlockCipherVectorTest(8, new TnepresEngine(), + new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")), + "00000000000000000100000000000000", "0bf30e1a0c33ccf6d5293177886912a7"), + new BlockCipherVectorTest(9, new TnepresEngine(), + new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000000000000")), + "00000000000000000000000000000001", "6a7f3b805d2ddcba49b89770ade5e507"), + new BlockCipherVectorTest(10, new TnepresEngine(), + new KeyParameter(Hex.decode("80000000000000000000000000000000")), + "00000000000000000000000000000000", "49afbfad9d5a34052cd8ffa5986bd2dd"), + new BlockCipherVectorTest(11, new TnepresEngine(), + new KeyParameter(Hex.decode("000000000000000000000000004000000000000000000000")), + "00000000000000000000000000000000", "ba8829b1de058c4b48615d851fc74f17"), + new BlockCipherVectorTest(12, new TnepresEngine(), + new KeyParameter(Hex.decode("0000000000000000000000000000000000000000000000000000000100000000")), + "00000000000000000000000000000000", "89f64377bf1e8a46c8247044e8056a98"), +/* + new BlockCipherMonteCarloTest(13, 10000, new TnepresEngine(), + new KeyParameter(Hex.decode("47f5f881daab9b67b43bd1342e339c19")), + "7a4f7db38c52a8b711b778a38d203b6b", "003380e19f10065740394f48e2fe80b7"), +*/ + new BlockCipherMonteCarloTest(13, 100, new TnepresEngine(), + new KeyParameter(Hex.decode("47f5f881daab9b67b43bd1342e339c19")), + "7a4f7db38c52a8b711b778a38d203b6b", "4db75303d815c2f7cc6ca935d1c5a046"), +/* + new BlockCipherMonteCarloTest(14, 10000, new TnepresEngine(), + new KeyParameter(Hex.decode("31fba879ebc5e80df35e6fa33eaf92d6")), + "70a05e12f74589009692a337f53ff614", "afb5425426906db26b70bdf842ac5400"), +*/ + new BlockCipherMonteCarloTest(14, 100, new TnepresEngine(), + new KeyParameter(Hex.decode("31fba879ebc5e80df35e6fa33eaf92d6")), + "70a05e12f74589009692a337f53ff614", "fc53a50f4d3bc9836001893d2f41742d"), +/* + new BlockCipherMonteCarloTest(15, 10000, new TnepresEngine(), + new KeyParameter(Hex.decode("bde6dd392307984695aee80e574f9977caae9aa78eda53e8")), + "9cc523d034a93740a0aa4e2054bb34d8", "1949d506ada7de1f1344986e8ea049b2"), +*/ + new BlockCipherMonteCarloTest(15, 100, new TnepresEngine(), + new KeyParameter(Hex.decode("bde6dd392307984695aee80e574f9977caae9aa78eda53e8")), + "9cc523d034a93740a0aa4e2054bb34d8", "77117e6a9e80f40b2a36b7d755573c2d"), +/* + new BlockCipherMonteCarloTest(16, 10000, new TnepresEngine(), + new KeyParameter(Hex.decode("60f6f8ad4290699dc50921a1bbcca92da914e7d9cf01a9317c79c0af8f2487a1")), + "ee1a61106fae2d381d686cbf854bab65", "e57f45559027cb1f2ed9603d814e1c34"), +*/ + new BlockCipherMonteCarloTest(16, 100, new TnepresEngine(), + new KeyParameter(Hex.decode("60f6f8ad4290699dc50921a1bbcca92da914e7d9cf01a9317c79c0af8f2487a1")), + "ee1a61106fae2d381d686cbf854bab65", "dcd7f13ea0dcdfd0139d1a42e2ffb84b") + }; + + TnepresTest() + { + super(tests, new TnepresEngine(), new KeyParameter(new byte[32])); + } + + public void performTest() + throws Exception + { + super.performTest(); + + doCbcMonte(new byte[16], new byte[16], new byte[16], Hex.decode("9ea101ecebaa41c712bcb0d9bab3e2e4")); + doCbcMonte(Hex.decode("9ea101ecebaa41c712bcb0d9bab3e2e4"), Hex.decode("9ea101ecebaa41c712bcb0d9bab3e2e4"), Hex.decode("b4813d8a66244188b9e92c75913fa2f4"), Hex.decode("f86b2c265b9c75869f31e2c684c13e9f")); + } + + private void doCbcMonte(byte[] key, byte[] iv, byte[] pt, byte[] expected) + { + BlockCipher c = new TnepresEngine(); + + byte[] ct = new byte[16]; + + System.arraycopy(iv, 0, ct, 0, 16); + + for (int i = 0; i < 10000; i++) + { + for (int k = 0; k != iv.length; k++) + { + iv[k] ^= pt[k]; + } + System.arraycopy(ct, 0, pt, 0, 16); + + c.init(true, new KeyParameter(key)); + + c.processBlock(iv, 0, ct, 0); + + System.arraycopy(ct, 0, iv, 0, 16); + } + + if (!Arrays.areEqual(expected, ct)) + { + fail("CBC monte test failed"); + } + } + + public String getName() + { + return "Tnepres"; + } + + public static void main( + String[] args) + { + runTest(new TnepresTest()); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/CAVPListener.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/CAVPListener.java new file mode 100644 index 00000000..81f86e57 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/CAVPListener.java @@ -0,0 +1,18 @@ +package org.bouncycastle.crypto.test.cavp; + +import java.util.Properties; + +public interface CAVPListener +{ + public void setup(); + + public void receiveStart(String name); + + public void receiveCAVPVectors(String name, Properties config, Properties vectors); + + public void receiveCommentLine(String commentLine); + + public void receiveEnd(); + + public void tearDown(); +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/CAVPReader.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/CAVPReader.java new file mode 100644 index 00000000..9cd33afc --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/CAVPReader.java @@ -0,0 +1,152 @@ +package org.bouncycastle.crypto.test.cavp; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.Mac; +import org.bouncycastle.crypto.digests.SHA1Digest; +import org.bouncycastle.crypto.digests.SHA224Digest; +import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.crypto.digests.SHA384Digest; +import org.bouncycastle.crypto.digests.SHA512Digest; +import org.bouncycastle.crypto.engines.AESFastEngine; +import org.bouncycastle.crypto.engines.DESedeEngine; +import org.bouncycastle.crypto.macs.CMac; +import org.bouncycastle.crypto.macs.HMac; + +public class CAVPReader +{ + + private static final Pattern COMMENT_PATTERN = Pattern.compile("^\\s*\\#\\s*(.*)$"); + private static final Pattern CONFIG_PATTERN = Pattern.compile("^\\s*+\\[\\s*+(.*?)\\s*+=\\s*+(.*?)\\s*+\\]\\s*+$"); + private static final Pattern VECTOR_PATTERN = Pattern.compile("^\\s*+(.*?)\\s*+=\\s*+(.*?)\\s*+$"); + private static final Pattern EMPTY_PATTERN = Pattern.compile("^\\s*+$"); + static final Pattern PATTERN_FOR_R = Pattern.compile("(\\d+)_BITS"); + private final CAVPListener listener; + private String name; + private BufferedReader lineReader; + + + public CAVPReader(CAVPListener listener) + { + this.listener = listener; + } + + public void setInput(String name, Reader reader) + { + this.name = name; + this.lineReader = new BufferedReader(reader); + } + + public void readAll() + throws IOException + { + + listener.setup(); + + Properties config = new Properties(); + + boolean startNewVector = true; + + Properties vectors = new Properties(); + + while (true) + { + final String line = lineReader.readLine(); + if (line == null) + { + listener.receiveEnd(); + break; + } + + final Matcher commentMatcher = COMMENT_PATTERN.matcher(line); + if (commentMatcher.matches()) + { + listener.receiveCommentLine(commentMatcher.group(1)); + continue; + } + + final Matcher configMatcher = CONFIG_PATTERN.matcher(line); + if (configMatcher.matches()) + { + config.put(configMatcher.group(1), configMatcher.group(2)); + continue; + } + + final Matcher vectorMatcher = VECTOR_PATTERN.matcher(line); + if (vectorMatcher.matches()) + { + vectors.put(vectorMatcher.group(1), vectorMatcher.group(2)); + startNewVector = false; + continue; + } + + final Matcher emptyMatcher = EMPTY_PATTERN.matcher(line); + if (emptyMatcher.matches()) + { + if (startNewVector) + { + continue; + } + + listener.receiveCAVPVectors(name, config, vectors); + vectors = new Properties(); + startNewVector = true; + } + } + + listener.tearDown(); + } + + static Mac createPRF(Properties config) + { + final Mac prf; + if (config.getProperty("PRF").matches("CMAC_AES\\d\\d\\d")) + { + BlockCipher blockCipher = new AESFastEngine(); + prf = new CMac(blockCipher); + } + else if (config.getProperty("PRF").matches("CMAC_TDES\\d")) + { + BlockCipher blockCipher = new DESedeEngine(); + prf = new CMac(blockCipher); + } + else if (config.getProperty("PRF").matches("HMAC_SHA1")) + { + Digest digest = new SHA1Digest(); + prf = new HMac(digest); + } + else if (config.getProperty("PRF").matches("HMAC_SHA224")) + { + Digest digest = new SHA224Digest(); + prf = new HMac(digest); + } + else if (config.getProperty("PRF").matches("HMAC_SHA256")) + { + Digest digest = new SHA256Digest(); + prf = new HMac(digest); + } + else if (config.getProperty("PRF").matches("HMAC_SHA384")) + { + Digest digest = new SHA384Digest(); + prf = new HMac(digest); + } + else if (config.getProperty("PRF").matches("HMAC_SHA512")) + { + Digest digest = new SHA512Digest(); + prf = new HMac(digest); + } + else + { + throw new IllegalStateException("Unknown Mac for PRF"); + } + return prf; + } + +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFCounterTests.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFCounterTests.java new file mode 100644 index 00000000..81f10824 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFCounterTests.java @@ -0,0 +1,119 @@ +package org.bouncycastle.crypto.test.cavp; + +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Properties; +import java.util.regex.Matcher; + +import org.bouncycastle.crypto.Mac; +import org.bouncycastle.crypto.generators.KDFCounterBytesGenerator; +import org.bouncycastle.crypto.params.KDFCounterParameters; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTestResult; +import org.bouncycastle.util.test.TestFailedException; + +public final class KDFCounterTests + implements CAVPListener +{ + private PrintWriter out; + + public void receiveCAVPVectors(String name, Properties config, + Properties vectors) + { + + // create Mac based PRF from PRF property, create the KDF + final Mac prf = CAVPReader.createPRF(config); + final KDFCounterBytesGenerator gen = new KDFCounterBytesGenerator(prf); + + + Matcher matcherForR = CAVPReader.PATTERN_FOR_R.matcher(config.getProperty("RLEN")); + if (!matcherForR.matches()) + { + throw new IllegalStateException("RLEN value should always match"); + } + final int r = Integer.parseInt(matcherForR.group(1)); + + final int count = Integer.parseInt(vectors.getProperty("COUNT")); + final int l = Integer.parseInt(vectors.getProperty("L")); + final byte[] ki = Hex.decode(vectors.getProperty("KI")); + + //Three variants of this KDF are possible, with the counter before the fixed data, after the fixed data, or in the middle of the fixed data. + if (config.getProperty("CTRLOCATION").matches("BEFORE_FIXED")) + { + final byte[] fixedInputData = Hex.decode(vectors.getProperty("FixedInputData")); + final KDFCounterParameters params = new KDFCounterParameters(ki, null, fixedInputData, r); + gen.init(params); + } + else if (config.getProperty("CTRLOCATION").matches("AFTER_FIXED")) + { + final byte[] fixedInputData = Hex.decode(vectors.getProperty("FixedInputData")); + final KDFCounterParameters params = new KDFCounterParameters(ki, fixedInputData, null, r); + gen.init(params); + } + else if (config.getProperty("CTRLOCATION").matches("MIDDLE_FIXED")) + { + final byte[] DataBeforeCtrData = Hex.decode(vectors.getProperty("DataBeforeCtrData")); + final byte[] DataAfterCtrData = Hex.decode(vectors.getProperty("DataAfterCtrData")); + final KDFCounterParameters params = new KDFCounterParameters(ki, DataBeforeCtrData, DataAfterCtrData, r); + gen.init(params); + } + else + { + return; // Unknown CTRLOCATION + } + + + final byte[] koGenerated = new byte[l / 8]; + gen.generateBytes(koGenerated, 0, koGenerated.length); + + final byte[] koVectors = Hex.decode(vectors.getProperty("KO")); + + compareKO(name, config, count, koGenerated, koVectors); + } + + private static void compareKO( + String name, Properties config, int test, byte[] calculatedOKM, byte[] testOKM) + { + + if (!Arrays.areEqual(calculatedOKM, testOKM)) + { + throw new TestFailedException(new SimpleTestResult( + false, name + " using " + config + " test " + test + " failed")); + + } + } + + public void receiveCommentLine(String commentLine) + { + // out.println("# " + commentLine); + } + + public void receiveStart(String name) + { + // do nothing + } + + public void receiveEnd() + { + out.println(" *** *** *** "); + } + + public void setup() + { + try + { + out = new PrintWriter(new FileWriter("KDFCTR.gen")); + } + catch (IOException e) + { + throw new IllegalStateException(e); + } + } + + public void tearDown() + { + out.close(); + } +}
\ No newline at end of file diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFDoublePipelineCounterTests.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFDoublePipelineCounterTests.java new file mode 100644 index 00000000..5b3df023 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFDoublePipelineCounterTests.java @@ -0,0 +1,107 @@ +package org.bouncycastle.crypto.test.cavp; + +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Properties; +import java.util.regex.Matcher; + +import org.bouncycastle.crypto.Mac; +import org.bouncycastle.crypto.generators.KDFDoublePipelineIterationBytesGenerator; +import org.bouncycastle.crypto.params.KDFDoublePipelineIterationParameters; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTestResult; +import org.bouncycastle.util.test.TestFailedException; + +public final class KDFDoublePipelineCounterTests + implements CAVPListener +{ + private PrintWriter out; + + public void receiveCAVPVectors(String name, Properties config, + Properties vectors) + { + // out.println(" === " + name + " === "); + // out.println(" --- config --- "); + // out.println(config); + // out.println(" --- vectors --- "); + // out.println(vectors); + + // always skip AFTER_FIXED + if (!config.getProperty("CTRLOCATION").matches("AFTER_ITER")) + { + return; + } + + // create Mac based PRF from PRF property, create the KDF + final Mac prf = CAVPReader.createPRF(config); + final KDFDoublePipelineIterationBytesGenerator gen = new KDFDoublePipelineIterationBytesGenerator(prf); + + + Matcher matcherForR = CAVPReader.PATTERN_FOR_R.matcher(config.getProperty("RLEN")); + if (!matcherForR.matches()) + { + throw new IllegalStateException("RLEN value should always match"); + } + final int r = Integer.parseInt(matcherForR.group(1)); + + final int count = Integer.parseInt(vectors.getProperty("COUNT")); + final int l = Integer.parseInt(vectors.getProperty("L")); + final byte[] ki = Hex.decode(vectors.getProperty("KI")); + final byte[] fixedInputData = Hex.decode(vectors.getProperty("FixedInputData")); + final KDFDoublePipelineIterationParameters params = KDFDoublePipelineIterationParameters.createWithCounter(ki, fixedInputData, r); + gen.init(params); + + final byte[] koGenerated = new byte[l / 8]; + gen.generateBytes(koGenerated, 0, koGenerated.length); + + final byte[] koVectors = Hex.decode(vectors.getProperty("KO")); + + compareKO(name, config, count, koGenerated, koVectors); + } + + private static void compareKO( + String name, Properties config, int test, byte[] calculatedOKM, byte[] testOKM) + { + + if (!Arrays.areEqual(calculatedOKM, testOKM)) + { + throw new TestFailedException(new SimpleTestResult( + false, name + " using " + config + " test " + test + " failed")); + + } + } + + public void receiveCommentLine(String commentLine) + { + // out.println("# " + commentLine); + } + + public void receiveStart(String name) + { + // do nothing + } + + public void receiveEnd() + { + out.println(" *** *** *** "); + } + + public void setup() + { + try + { + out = new PrintWriter(new FileWriter("KDFDblPipelineCounter.gen")); + } + catch (IOException e) + { + throw new IllegalStateException(e); + } + } + + public void tearDown() + { + out.close(); + } +}
\ No newline at end of file diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFDoublePipelineIterationNoCounterTests.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFDoublePipelineIterationNoCounterTests.java new file mode 100644 index 00000000..3923f9a1 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFDoublePipelineIterationNoCounterTests.java @@ -0,0 +1,88 @@ +package org.bouncycastle.crypto.test.cavp; + +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Properties; + +import org.bouncycastle.crypto.Mac; +import org.bouncycastle.crypto.generators.KDFDoublePipelineIterationBytesGenerator; +import org.bouncycastle.crypto.params.KDFDoublePipelineIterationParameters; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTestResult; +import org.bouncycastle.util.test.TestFailedException; + +public final class KDFDoublePipelineIterationNoCounterTests + implements CAVPListener +{ + private PrintWriter out; + + public void receiveCAVPVectors(String name, Properties config, + Properties vectors) + { + + + // create Mac based PRF from PRF property, create the KDF + final Mac prf = CAVPReader.createPRF(config); + final KDFDoublePipelineIterationBytesGenerator gen = new KDFDoublePipelineIterationBytesGenerator(prf); + + final int count = Integer.parseInt(vectors.getProperty("COUNT")); + final int l = Integer.parseInt(vectors.getProperty("L")); + final byte[] ki = Hex.decode(vectors.getProperty("KI")); + final byte[] fixedInputData = Hex.decode(vectors.getProperty("FixedInputData")); + final KDFDoublePipelineIterationParameters params = KDFDoublePipelineIterationParameters.createWithoutCounter(ki, fixedInputData); + gen.init(params); + + final byte[] koGenerated = new byte[l / 8]; + gen.generateBytes(koGenerated, 0, koGenerated.length); + + final byte[] koVectors = Hex.decode(vectors.getProperty("KO")); + + compareKO(name, config, count, koGenerated, koVectors); + } + + private static void compareKO( + String name, Properties config, int test, byte[] calculatedOKM, byte[] testOKM) + { + + if (!Arrays.areEqual(calculatedOKM, testOKM)) + { + throw new TestFailedException(new SimpleTestResult( + false, name + " using " + config + " test " + test + " failed")); + + } + } + + public void receiveCommentLine(String commentLine) + { + // out.println("# " + commentLine); + } + + public void receiveStart(String name) + { + // do nothing + } + + public void receiveEnd() + { + out.println(" *** *** *** "); + } + + public void setup() + { + try + { + out = new PrintWriter(new FileWriter("KDFDblPipelineNoCounter.gen")); + } + catch (IOException e) + { + throw new IllegalStateException(e); + } + } + + public void tearDown() + { + out.close(); + } +}
\ No newline at end of file diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFFeedbackCounterTests.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFFeedbackCounterTests.java new file mode 100644 index 00000000..6f8a0fde --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFFeedbackCounterTests.java @@ -0,0 +1,108 @@ +package org.bouncycastle.crypto.test.cavp; + +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Properties; +import java.util.regex.Matcher; + +import org.bouncycastle.crypto.Mac; +import org.bouncycastle.crypto.generators.KDFFeedbackBytesGenerator; +import org.bouncycastle.crypto.params.KDFFeedbackParameters; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTestResult; +import org.bouncycastle.util.test.TestFailedException; + +public final class KDFFeedbackCounterTests + implements CAVPListener +{ + private PrintWriter out; + + public void receiveCAVPVectors(String name, Properties config, + Properties vectors) + { + // out.println(" === " + name + " === "); + // out.println(" --- config --- "); + // out.println(config); + // out.println(" --- vectors --- "); + // out.println(vectors); + + // always skip AFTER_FIXED + if (!config.getProperty("CTRLOCATION").matches("AFTER_ITER")) + { + return; + } + + // create Mac based PRF from PRF property, create the KDF + final Mac prf = CAVPReader.createPRF(config); + final KDFFeedbackBytesGenerator gen = new KDFFeedbackBytesGenerator(prf); + + + Matcher matcherForR = CAVPReader.PATTERN_FOR_R.matcher(config.getProperty("RLEN")); + if (!matcherForR.matches()) + { + throw new IllegalStateException("RLEN value should always match"); + } + final int r = Integer.parseInt(matcherForR.group(1)); + + final int count = Integer.parseInt(vectors.getProperty("COUNT")); + final int l = Integer.parseInt(vectors.getProperty("L")); + final byte[] ki = Hex.decode(vectors.getProperty("KI")); + final byte[] iv = Hex.decode(vectors.getProperty("IV")); + final byte[] fixedInputData = Hex.decode(vectors.getProperty("FixedInputData")); + final KDFFeedbackParameters params = KDFFeedbackParameters.createWithCounter(ki, iv, fixedInputData, r); + gen.init(params); + + final byte[] koGenerated = new byte[l / 8]; + gen.generateBytes(koGenerated, 0, koGenerated.length); + + final byte[] koVectors = Hex.decode(vectors.getProperty("KO")); + + compareKO(name, config, count, koGenerated, koVectors); + } + + private static void compareKO( + String name, Properties config, int test, byte[] calculatedOKM, byte[] testOKM) + { + + if (!Arrays.areEqual(calculatedOKM, testOKM)) + { + throw new TestFailedException(new SimpleTestResult( + false, name + " using " + config + " test " + test + " failed")); + + } + } + + public void receiveCommentLine(String commentLine) + { + // out.println("# " + commentLine); + } + + public void receiveStart(String name) + { + // do nothing + } + + public void receiveEnd() + { + out.println(" *** *** *** "); + } + + public void setup() + { + try + { + out = new PrintWriter(new FileWriter("KDFFeedbackCounter.gen")); + } + catch (IOException e) + { + throw new IllegalStateException(e); + } + } + + public void tearDown() + { + out.close(); + } +}
\ No newline at end of file diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFFeedbackNoCounterTests.java b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFFeedbackNoCounterTests.java new file mode 100644 index 00000000..cd7d8b80 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/test/cavp/KDFFeedbackNoCounterTests.java @@ -0,0 +1,89 @@ +package org.bouncycastle.crypto.test.cavp; + +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Properties; + +import org.bouncycastle.crypto.Mac; +import org.bouncycastle.crypto.generators.KDFFeedbackBytesGenerator; +import org.bouncycastle.crypto.params.KDFFeedbackParameters; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTestResult; +import org.bouncycastle.util.test.TestFailedException; + +public final class KDFFeedbackNoCounterTests + implements CAVPListener +{ + private PrintWriter out; + + public void receiveCAVPVectors(String name, Properties config, + Properties vectors) + { + + + // create Mac based PRF from PRF property, create the KDF + final Mac prf = CAVPReader.createPRF(config); + final KDFFeedbackBytesGenerator gen = new KDFFeedbackBytesGenerator(prf); + + final int count = Integer.parseInt(vectors.getProperty("COUNT")); + final int l = Integer.parseInt(vectors.getProperty("L")); + final byte[] ki = Hex.decode(vectors.getProperty("KI")); + final byte[] iv = Hex.decode(vectors.getProperty("IV")); + final byte[] fixedInputData = Hex.decode(vectors.getProperty("FixedInputData")); + final KDFFeedbackParameters params = KDFFeedbackParameters.createWithoutCounter(ki, iv, fixedInputData); + gen.init(params); + + final byte[] koGenerated = new byte[l / 8]; + gen.generateBytes(koGenerated, 0, koGenerated.length); + + final byte[] koVectors = Hex.decode(vectors.getProperty("KO")); + + compareKO(name, config, count, koGenerated, koVectors); + } + + private static void compareKO( + String name, Properties config, int test, byte[] calculatedOKM, byte[] testOKM) + { + + if (!Arrays.areEqual(calculatedOKM, testOKM)) + { + throw new TestFailedException(new SimpleTestResult( + false, name + " using " + config + " test " + test + " failed")); + + } + } + + public void receiveCommentLine(String commentLine) + { +// out.println("# " + commentLine); + } + + public void receiveStart(String name) + { + // do nothing + } + + public void receiveEnd() + { + out.println(" *** *** *** "); + } + + public void setup() + { + try + { + out = new PrintWriter(new FileWriter("KDFFeedbackNoCounter.gen")); + } + catch (IOException e) + { + throw new IllegalStateException(e); + } + } + + public void tearDown() + { + out.close(); + } +}
\ No newline at end of file diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueueInputStream.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueueInputStream.java new file mode 100644 index 00000000..34bb035a --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueueInputStream.java @@ -0,0 +1,63 @@ +package org.bouncycastle.crypto.tls; + +import java.io.InputStream; + +public class ByteQueueInputStream + extends InputStream +{ + private ByteQueue buffer; + + public ByteQueueInputStream() + { + buffer = new ByteQueue(); + } + + public void addBytes(byte[] bytes) + { + buffer.addData(bytes, 0, bytes.length); + } + + public int peek(byte[] buf) + { + int bytesToRead = Math.min(buffer.available(), buf.length); + buffer.read(buf, 0, bytesToRead, 0); + return bytesToRead; + } + + public int read() + { + if (buffer.available() == 0) + { + return -1; + } + return buffer.removeData(1, 0)[0] & 0xFF; + } + + public int read(byte[] b) + { + return read(b, 0, b.length); + } + + public int read(byte[] b, int off, int len) + { + int bytesToRead = Math.min(buffer.available(), len); + buffer.removeData(b, off, bytesToRead, 0); + return bytesToRead; + } + + public long skip(long n) + { + int bytesToRemove = Math.min((int)n, buffer.available()); + buffer.removeData(bytesToRemove); + return bytesToRemove; + } + + public int available() + { + return buffer.available(); + } + + public void close() + { + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueueOutputStream.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueueOutputStream.java new file mode 100644 index 00000000..aa3b36a4 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueueOutputStream.java @@ -0,0 +1,32 @@ +package org.bouncycastle.crypto.tls; + +import java.io.IOException; +import java.io.OutputStream; + +import org.bouncycastle.crypto.tls.ByteQueue; + +public class ByteQueueOutputStream + extends OutputStream +{ + private ByteQueue buffer; + + public ByteQueueOutputStream() + { + buffer = new ByteQueue(); + } + + public ByteQueue getBuffer() + { + return buffer; + } + + public void write(int b) throws IOException + { + buffer.addData(new byte[]{ (byte)b }, 0, 1); + } + + public void write(byte[] b, int off, int len) throws IOException + { + buffer.addData(b, off, len); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/ByteQueueInputStreamTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/ByteQueueInputStreamTest.java new file mode 100644 index 00000000..de4d5598 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/ByteQueueInputStreamTest.java @@ -0,0 +1,127 @@ +package org.bouncycastle.crypto.tls.test; + +import org.bouncycastle.crypto.tls.ByteQueueInputStream; +import org.bouncycastle.util.Arrays; + +import junit.framework.TestCase; + +public class ByteQueueInputStreamTest + extends TestCase +{ + public void testAvailable() + { + ByteQueueInputStream in = new ByteQueueInputStream(); + + // buffer is empty + assertEquals(0, in.available()); + + // after adding once + in.addBytes(new byte[10]); + assertEquals(10, in.available()); + + // after adding more than once + in.addBytes(new byte[5]); + assertEquals(15, in.available()); + + // after reading a single byte + in.read(); + assertEquals(14, in.available()); + + // after reading into a byte array + in.read(new byte[4]); + assertEquals(10, in.available()); + + in.close();// so Eclipse doesn't whine about a resource leak + } + + public void testSkip() + { + ByteQueueInputStream in = new ByteQueueInputStream(); + + // skip when buffer is empty + assertEquals(0, in.skip(10)); + + // skip equal to available + in.addBytes(new byte[2]); + assertEquals(2, in.skip(2)); + assertEquals(0, in.available()); + + // skip less than available + in.addBytes(new byte[10]); + assertEquals(5, in.skip(5)); + assertEquals(5, in.available()); + + // skip more than available + assertEquals(5, in.skip(20)); + assertEquals(0, in.available()); + + in.close();// so Eclipse doesn't whine about a resource leak + } + + public void testRead() + { + ByteQueueInputStream in = new ByteQueueInputStream(); + in.addBytes(new byte[]{ 0x01, 0x02 }); + in.addBytes(new byte[]{ 0x03 }); + + assertEquals(0x01, in.read()); + assertEquals(0x02, in.read()); + assertEquals(0x03, in.read()); + assertEquals(-1, in.read()); + + in.close();// so Eclipse doesn't whine about a resource leak + } + + public void testReadArray() + { + ByteQueueInputStream in = new ByteQueueInputStream(); + in.addBytes(new byte[]{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }); + + byte[] buffer = new byte[5]; + + // read less than available into specified position + assertEquals(1, in.read(buffer, 2, 1)); + assertArrayEquals(new byte[]{ 0x00, 0x00, 0x01, 0x00, 0x00 }, buffer); + + // read equal to available + assertEquals(5, in.read(buffer)); + assertArrayEquals(new byte[]{ 0x02, 0x03, 0x04, 0x05, 0x06 }, buffer); + + // read more than available + in.addBytes(new byte[]{ 0x01, 0x02, 0x03 }); + assertEquals(3, in.read(buffer)); + assertArrayEquals(new byte[]{ 0x01, 0x02, 0x03, 0x05, 0x06 }, buffer); + + in.close();// so Eclipse doesn't whine about a resource leak + } + + public void testPeek() + { + ByteQueueInputStream in = new ByteQueueInputStream(); + + byte[] buffer = new byte[5]; + + // peek more than available + assertEquals(0, in.peek(buffer)); + assertArrayEquals(new byte[]{ 0x00, 0x00, 0x00, 0x00, 0x00 }, buffer); + + // peek less than available + in.addBytes(new byte[]{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }); + assertEquals(5, in.peek(buffer)); + assertArrayEquals(new byte[]{ 0x01, 0x02, 0x03, 0x04, 0x05 }, buffer); + assertEquals(6, in.available()); + + // peek equal to available + in.read(); + assertEquals(5, in.peek(buffer)); + assertArrayEquals(new byte[]{ 0x02, 0x03, 0x04, 0x05, 0x06 }, buffer); + assertEquals(5, in.available()); + + in.close();// so Eclipse doesn't whine about a resource leak + } + + private static void assertArrayEquals(byte[] a, byte[] b) + { + assertTrue(Arrays.areEqual(a, b)); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestClientProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestClientProtocol.java new file mode 100644 index 00000000..84109cdb --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestClientProtocol.java @@ -0,0 +1,30 @@ +package org.bouncycastle.crypto.tls.test; + +import java.io.IOException; +import java.security.SecureRandom; + +import org.bouncycastle.crypto.tls.DTLSClientProtocol; +import org.bouncycastle.crypto.tls.DigitallySigned; + +class DTLSTestClientProtocol extends DTLSClientProtocol +{ + protected final TlsTestConfig config; + + public DTLSTestClientProtocol(SecureRandom secureRandom, TlsTestConfig config) + { + super(secureRandom); + + this.config = config; + } + + protected byte[] generateCertificateVerify(ClientHandshakeState state, DigitallySigned certificateVerify) + throws IOException + { + if (certificateVerify.getAlgorithm() != null && config.clientAuthSigAlgClaimed != null) + { + certificateVerify = new DigitallySigned(config.clientAuthSigAlgClaimed, certificateVerify.getSignature()); + } + + return super.generateCertificateVerify(state, certificateVerify); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestServerProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestServerProtocol.java new file mode 100644 index 00000000..9244eeb8 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/DTLSTestServerProtocol.java @@ -0,0 +1,17 @@ +package org.bouncycastle.crypto.tls.test; + +import java.security.SecureRandom; + +import org.bouncycastle.crypto.tls.DTLSServerProtocol; + +class DTLSTestServerProtocol extends DTLSServerProtocol +{ + protected final TlsTestConfig config; + + public DTLSTestServerProtocol(SecureRandom secureRandom, TlsTestConfig config) + { + super(secureRandom); + + this.config = config; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsProtocolNonBlockingTest.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsProtocolNonBlockingTest.java new file mode 100644 index 00000000..564bb742 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsProtocolNonBlockingTest.java @@ -0,0 +1,126 @@ +package org.bouncycastle.crypto.tls.test; + +import java.io.IOException; +import java.security.SecureRandom; + +import org.bouncycastle.crypto.tls.TlsClientProtocol; +import org.bouncycastle.crypto.tls.TlsProtocol; +import org.bouncycastle.crypto.tls.TlsServerProtocol; +import org.bouncycastle.util.Arrays; + +import junit.framework.TestCase; + +public class TlsProtocolNonBlockingTest + extends TestCase +{ + public void testClientServerFragmented() throws IOException + { + // tests if it's really non-blocking when partial records arrive + testClientServer(true); + } + + public void testClientServerNonFragmented() throws IOException + { + testClientServer(false); + } + + private static void testClientServer(boolean fragment) throws IOException + { + SecureRandom secureRandom = new SecureRandom(); + + TlsClientProtocol clientProtocol = new TlsClientProtocol(secureRandom); + TlsServerProtocol serverProtocol = new TlsServerProtocol(secureRandom); + + clientProtocol.connect(new MockTlsClient(null)); + serverProtocol.accept(new MockTlsServer()); + + // pump handshake + boolean hadDataFromServer = true; + boolean hadDataFromClient = true; + while (hadDataFromServer || hadDataFromClient) + { + hadDataFromServer = pumpData(serverProtocol, clientProtocol, fragment); + hadDataFromClient = pumpData(clientProtocol, serverProtocol, fragment); + } + + // send data in both directions + byte[] data = new byte[1024]; + secureRandom.nextBytes(data); + writeAndRead(clientProtocol, serverProtocol, data, fragment); + writeAndRead(serverProtocol, clientProtocol, data, fragment); + + // close the connection + clientProtocol.close(); + pumpData(clientProtocol, serverProtocol, fragment); + checkClosed(serverProtocol); + checkClosed(clientProtocol); + } + + private static void writeAndRead(TlsProtocol writer, TlsProtocol reader, byte[] data, boolean fragment) + throws IOException + { + int dataSize = data.length; + writer.offerOutput(data, 0, dataSize); + pumpData(writer, reader, fragment); + + assertEquals(dataSize, reader.getAvailableInputBytes()); + byte[] readData = new byte[dataSize]; + reader.readInput(readData, 0, dataSize); + assertArrayEquals(data, readData); + } + + private static boolean pumpData(TlsProtocol from, TlsProtocol to, boolean fragment) throws IOException + { + int byteCount = from.getAvailableOutputBytes(); + if (byteCount == 0) + { + return false; + } + + if (fragment) + { + while (from.getAvailableOutputBytes() > 0) + { + byte[] buffer = new byte[1]; + from.readOutput(buffer, 0, 1); + to.offerInput(buffer); + } + } + else + { + byte[] buffer = new byte[byteCount]; + from.readOutput(buffer, 0, buffer.length); + to.offerInput(buffer); + } + + return true; + } + + private static void checkClosed(TlsProtocol protocol) + { + assertTrue(protocol.isClosed()); + + try + { + protocol.offerInput(new byte[10]); + fail("Input was accepted after close"); + } + catch (IOException e) + { + } + + try + { + protocol.offerOutput(new byte[10], 0, 10); + fail("Output was accepted after close"); + } + catch (IOException e) + { + } + } + + private static void assertArrayEquals(byte[] a, byte[] b) + { + assertTrue(Arrays.areEqual(a, b)); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestClientProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestClientProtocol.java new file mode 100644 index 00000000..2f6f751e --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestClientProtocol.java @@ -0,0 +1,31 @@ +package org.bouncycastle.crypto.tls.test; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.SecureRandom; + +import org.bouncycastle.crypto.tls.DigitallySigned; +import org.bouncycastle.crypto.tls.TlsClientProtocol; + +class TlsTestClientProtocol extends TlsClientProtocol +{ + protected final TlsTestConfig config; + + public TlsTestClientProtocol(InputStream input, OutputStream output, SecureRandom secureRandom, TlsTestConfig config) + { + super(input, output, secureRandom); + + this.config = config; + } + + protected void sendCertificateVerifyMessage(DigitallySigned certificateVerify) throws IOException + { + if (certificateVerify.getAlgorithm() != null && config.clientAuthSigAlgClaimed != null) + { + certificateVerify = new DigitallySigned(config.clientAuthSigAlgClaimed, certificateVerify.getSignature()); + } + + super.sendCertificateVerifyMessage(certificateVerify); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestServerProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestServerProtocol.java new file mode 100644 index 00000000..0bfc985b --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/test/TlsTestServerProtocol.java @@ -0,0 +1,19 @@ +package org.bouncycastle.crypto.tls.test; + +import java.io.InputStream; +import java.io.OutputStream; +import java.security.SecureRandom; + +import org.bouncycastle.crypto.tls.TlsServerProtocol; + +class TlsTestServerProtocol extends TlsServerProtocol +{ + protected final TlsTestConfig config; + + public TlsTestServerProtocol(InputStream input, OutputStream output, SecureRandom secureRandom, TlsTestConfig config) + { + super(input, output, secureRandom); + + this.config = config; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/DERMacData.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/DERMacData.java new file mode 100644 index 00000000..d895a1fd --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/DERMacData.java @@ -0,0 +1,114 @@ +package org.bouncycastle.crypto.util; + +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.DERTaggedObject; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Strings; + +/** + * Builder and holder class for preparing SP 800-56A compliant MacData. Elements in the data are encoded + * as DER objects with empty octet strings used to represent nulls in compulsory fields. + */ +public final class DERMacData +{ + public enum Type + { + UNILATERALU("KC_1_U"), + UNILATERALV("KC_1_V"), + BILATERALU("KC_2_U"), + BILATERALV("KC_2_V"); + + private final String enc; + + Type(String enc) + { + this.enc = enc; + } + + public byte[] getHeader() + { + return Strings.toByteArray(enc); + } + } + + /** + * Builder to create OtherInfo + */ + public static final class Builder + { + private final Type type; + + private ASN1OctetString idU; + private ASN1OctetString idV; + private ASN1OctetString ephemDataU; + private ASN1OctetString ephemDataV; + private byte[] text; + + /** + * Create a basic builder with just the compulsory fields. + * + * @param type the MAC header + * @param idU sender party ID. + * @param idV receiver party ID. + * @param ephemDataU ephemeral data from sender. + * @param ephemDataV ephemeral data from receiver. + */ + public Builder(Type type, byte[] idU, byte[] idV, byte[] ephemDataU, byte[] ephemDataV) + { + this.type = type; + this.idU = DerUtil.getOctetString(idU); + this.idV = DerUtil.getOctetString(idV); + this.ephemDataU = DerUtil.getOctetString(ephemDataU); + this.ephemDataV = DerUtil.getOctetString(ephemDataV); + } + + /** + * Add optional text. + * + * @param text optional agreed text to add to the MAC. + * @return the current builder instance. + */ + public Builder withText(byte[] text) + { + this.text = DerUtil.toByteArray(new DERTaggedObject(false, 0, DerUtil.getOctetString(text))); + + return this; + } + + public DERMacData build() + { + switch (type) + { + case UNILATERALU: + case BILATERALU: + return new DERMacData(concatenate(type.getHeader(), + DerUtil.toByteArray(idU), DerUtil.toByteArray(idV), + DerUtil.toByteArray(ephemDataU), DerUtil.toByteArray(ephemDataV), text)); + case UNILATERALV: + case BILATERALV: + return new DERMacData(concatenate(type.getHeader(), + DerUtil.toByteArray(idV), DerUtil.toByteArray(idU), + DerUtil.toByteArray(ephemDataV), DerUtil.toByteArray(ephemDataU), text)); + } + + throw new IllegalStateException("Unknown type encountered in build"); // should never happen + } + + private byte[] concatenate(byte[] header, byte[] id1, byte[] id2, byte[] ed1, byte[] ed2, byte[] text) + { + return Arrays.concatenate(Arrays.concatenate(header, id1, id2), Arrays.concatenate(ed1, ed2, text)); + } + } + + private final byte[] macData; + + private DERMacData(byte[] macData) + { + this.macData = macData; + } + + public byte[] getMacData() + { + return Arrays.clone(macData); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/DEROtherInfo.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/DEROtherInfo.java new file mode 100644 index 00000000..4e821f7e --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/DEROtherInfo.java @@ -0,0 +1,109 @@ +package org.bouncycastle.crypto.util; + +import java.io.IOException; + +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.ASN1TaggedObject; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.DERTaggedObject; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; + +/** + * Builder and holder class for preparing SP 800-56A compliant OtherInfo. The data is ultimately encoded as a DER SEQUENCE. + * Empty octet strings are used to represent nulls in compulsory fields. + */ +public class DEROtherInfo +{ + /** + * Builder to create OtherInfo + */ + public static final class Builder + { + private final AlgorithmIdentifier algorithmID; + private final ASN1OctetString partyUVInfo; + private final ASN1OctetString partyVInfo; + + private ASN1TaggedObject suppPubInfo; + private ASN1TaggedObject suppPrivInfo; + + /** + * Create a basic builder with just the compulsory fields. + * + * @param algorithmID the algorithm associated with this invocation of the KDF. + * @param partyUInfo sender party info. + * @param partyVInfo receiver party info. + */ + public Builder(AlgorithmIdentifier algorithmID, byte[] partyUInfo, byte[] partyVInfo) + { + this.algorithmID = algorithmID; + this.partyUVInfo = DerUtil.getOctetString(partyUInfo); + this.partyVInfo = DerUtil.getOctetString(partyVInfo); + } + + /** + * Add optional supplementary public info (DER tagged, implicit, 0). + * + * @param suppPubInfo supplementary public info. + * @return the current builder instance. + */ + public Builder withSuppPubInfo(byte[] suppPubInfo) + { + this.suppPubInfo = new DERTaggedObject(false, 0, DerUtil.getOctetString(suppPubInfo)); + + return this; + } + + /** + * Add optional supplementary private info (DER tagged, implicit, 1). + * + * @param suppPrivInfo supplementary private info. + * @return the current builder instance. + */ + public Builder withSuppPrivInfo(byte[] suppPrivInfo) + { + this.suppPrivInfo = new DERTaggedObject(false, 1, DerUtil.getOctetString(suppPrivInfo)); + + return this; + } + + /** + * Build the KTSOtherInfo. + * + * @return an KTSOtherInfo containing the data. + */ + public DEROtherInfo build() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(algorithmID); + v.add(partyUVInfo); + v.add(partyVInfo); + + if (suppPubInfo != null) + { + v.add(suppPubInfo); + } + + if (suppPrivInfo != null) + { + v.add(suppPrivInfo); + } + + return new DEROtherInfo(new DERSequence(v)); + } + } + + private final DERSequence sequence; + + private DEROtherInfo(DERSequence sequence) + { + this.sequence = sequence; + } + + public byte[] getEncoded() + throws IOException + { + return sequence.getEncoded(); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/DerUtil.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/DerUtil.java new file mode 100644 index 00000000..324c5ae4 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/DerUtil.java @@ -0,0 +1,39 @@ +package org.bouncycastle.crypto.util; + +import java.io.IOException; + +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.util.Arrays; + +class DerUtil +{ + static ASN1OctetString getOctetString(byte[] data) + { + if (data == null) + { + return new DEROctetString(new byte[0]); + } + + return new DEROctetString(Arrays.clone(data)); + } + + static byte[] toByteArray(ASN1Primitive primitive) + { + try + { + return primitive.getEncoded(); + } + catch (final IOException e) + { + throw new IllegalStateException("Cannot get encoding: " + e.getMessage()) + { + public Throwable getCause() + { + return e; + } + }; + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDF1Key.java b/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDF1Key.java new file mode 100644 index 00000000..7f7581c0 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDF1Key.java @@ -0,0 +1,67 @@ +package org.bouncycastle.jcajce; + +import org.bouncycastle.crypto.CharToByteConverter; + +/** + * A password based key for use with PBKDF1 as defined in PKCS#5. + */ +public class PBKDF1Key + implements PBKDFKey +{ + private final char[] password; + private final CharToByteConverter converter; + + /** + * Basic constructor for a password based key with generation parameters for PBKDF1. + * + * @param password password to use. + * @param converter the converter to use to turn the char array into octets. + */ + public PBKDF1Key(char[] password, CharToByteConverter converter) + { + this.password = new char[password.length]; + this.converter = converter; + + System.arraycopy(password, 0, this.password, 0, password.length); + } + + /** + * Return a reference to the char[] array holding the password. + * + * @return a reference to the password array. + */ + public char[] getPassword() + { + return password; + } + + /** + * Return the password based key derivation function this key is for, + * + * @return the string "PBKDF1" + */ + public String getAlgorithm() + { + return "PBKDF1"; + } + + /** + * Return the format encoding. + * + * @return the type name representing a char[] to byte[] conversion. + */ + public String getFormat() + { + return converter.getType(); + } + + /** + * Return the password converted to bytes. + * + * @return the password converted to a byte array. + */ + public byte[] getEncoded() + { + return converter.convert(password); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDF1KeyWithParameters.java b/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDF1KeyWithParameters.java new file mode 100644 index 00000000..d0899c1d --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDF1KeyWithParameters.java @@ -0,0 +1,53 @@ +package org.bouncycastle.jcajce; + +import javax.crypto.interfaces.PBEKey; + +import org.bouncycastle.crypto.CharToByteConverter; +import org.bouncycastle.util.Arrays; + +/** + * A password based key for use with PBKDF1 as defined in PKCS#5 with full PBE parameters. + */ +public class PBKDF1KeyWithParameters + extends PBKDF1Key + implements PBEKey +{ + private final byte[] salt; + private final int iterationCount; + + /** + * Basic constructor for a password based key with generation parameters for PBKDF1. + * + * @param password password to use. + * @param converter the converter to use to turn the char array into octets. + * @param salt salt for generation algorithm + * @param iterationCount iteration count for generation algorithm. + */ + public PBKDF1KeyWithParameters(char[] password, CharToByteConverter converter, byte[] salt, int iterationCount) + { + super(password, converter); + + this.salt = Arrays.clone(salt); + this.iterationCount = iterationCount; + } + + /** + * Return the salt to use in the key derivation function. + * + * @return the salt to use in the KDF. + */ + public byte[] getSalt() + { + return salt; + } + + /** + * Return the iteration count to use in the key derivation function. + * + * @return the iteration count to use in the KDF. + */ + public int getIterationCount() + { + return iterationCount; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDF2Key.java b/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDF2Key.java new file mode 100644 index 00000000..d9dc6e75 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDF2Key.java @@ -0,0 +1,65 @@ +package org.bouncycastle.jcajce; + +import org.bouncycastle.crypto.CharToByteConverter; +import org.bouncycastle.util.Arrays; + +/** + * A password based key for use with PBKDF2 as defined in PKCS#5. + */ +public class PBKDF2Key + implements PBKDFKey +{ + private final char[] password; + private final CharToByteConverter converter; + + /** + * Basic constructor for a password based key using PBKDF - secret key generation parameters will be passed separately.. + * + * @param password password to use. + */ + public PBKDF2Key(char[] password, CharToByteConverter converter) + { + this.password = Arrays.clone(password); + this.converter = converter; + } + + /** + * Return a reference to the char[] array holding the password. + * + * @return a reference to the password array. + */ + public char[] getPassword() + { + return password; + } + + /** + * Return the password based key derivation function this key is for, + * + * @return the string "PBKDF2" + */ + public String getAlgorithm() + { + return "PBKDF2"; + } + + /** + * Return the format encoding. + * + * @return the type name representing a char[] to byte[] conversion. + */ + public String getFormat() + { + return converter.getType(); + } + + /** + * Return the password converted to bytes. + * + * @return the password converted to a byte array. + */ + public byte[] getEncoded() + { + return converter.convert(password); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDF2KeyWithParameters.java b/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDF2KeyWithParameters.java new file mode 100644 index 00000000..b356cfbd --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDF2KeyWithParameters.java @@ -0,0 +1,53 @@ +package org.bouncycastle.jcajce; + +import javax.crypto.interfaces.PBEKey; + +import org.bouncycastle.crypto.CharToByteConverter; +import org.bouncycastle.util.Arrays; + +/** + * A password based key for use with PBKDF2 as defined in PKCS#5 with full PBE parameters. + */ +public class PBKDF2KeyWithParameters + extends PBKDF2Key + implements PBEKey +{ + private final byte[] salt; + private final int iterationCount; + + /** + * Basic constructor for a password based key with generation parameters using FIPS PBKDF. + * + * @param password password to use. + * @param converter converter to use for transforming characters into bytes. + * @param salt salt for generation algorithm + * @param iterationCount iteration count for generation algorithm. + */ + public PBKDF2KeyWithParameters(char[] password, CharToByteConverter converter, byte[] salt, int iterationCount) + { + super(password, converter); + + this.salt = Arrays.clone(salt); + this.iterationCount = iterationCount; + } + + /** + * Return the salt to use in the key derivation function. + * + * @return the salt to use in the KDF. + */ + public byte[] getSalt() + { + return salt; + } + + /** + * Return the iteration count to use in the key derivation function. + * + * @return the iteration count to use in the KDF. + */ + public int getIterationCount() + { + return iterationCount; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDFKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDFKey.java new file mode 100644 index 00000000..ae3d2ebd --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/PBKDFKey.java @@ -0,0 +1,11 @@ +package org.bouncycastle.jcajce; + +import javax.crypto.SecretKey; + +/** + * Base interface for keys associated with various password based key derivation functions (PBKDF). + */ +public interface PBKDFKey + extends SecretKey +{ +} diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/AlgorithmParametersSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/AlgorithmParametersSpi.java new file mode 100644 index 00000000..6af71e80 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/AlgorithmParametersSpi.java @@ -0,0 +1,167 @@ +package org.bouncycastle.jcajce.provider.asymmetric.ec; + +import java.io.IOException; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.InvalidParameterSpecException; + +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.DERNull; +import org.bouncycastle.asn1.x9.ECNamedCurveTable; +import org.bouncycastle.asn1.x9.X962Parameters; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util; +import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.math.ec.ECCurve; + +public class AlgorithmParametersSpi + extends java.security.AlgorithmParametersSpi +{ + private ECParameterSpec ecParameterSpec; + private String curveName; + + protected boolean isASN1FormatString(String format) + { + return format == null || format.equals("ASN.1"); + } + + @Override + protected void engineInit(AlgorithmParameterSpec algorithmParameterSpec) + throws InvalidParameterSpecException + { + if (algorithmParameterSpec instanceof ECGenParameterSpec) + { + ECGenParameterSpec ecGenParameterSpec = (ECGenParameterSpec)algorithmParameterSpec; + X9ECParameters params = ECUtils.getDomainParametersFromGenSpec(ecGenParameterSpec); + + if (params == null) + { + throw new InvalidParameterSpecException("EC curve name not recognized: " + ecGenParameterSpec.getName()); + } + curveName = ecGenParameterSpec.getName(); + ecParameterSpec = EC5Util.convertToSpec(params); + } + else if (algorithmParameterSpec instanceof ECParameterSpec) + { + curveName = null; + ecParameterSpec = (ECParameterSpec)algorithmParameterSpec; + } + else + { + throw new InvalidParameterSpecException("AlgorithmParameterSpec class not recognized: " + algorithmParameterSpec.getClass().getName()); + } + } + + @Override + protected void engineInit(byte[] bytes) + throws IOException + { + engineInit(bytes, "ASN.1"); + } + + @Override + protected void engineInit(byte[] bytes, String format) + throws IOException + { + if (isASN1FormatString(format)) + { + X962Parameters params = X962Parameters.getInstance(bytes); + + ECCurve curve = EC5Util.getCurve(BouncyCastleProvider.CONFIGURATION, params); + + if (params.isNamedCurve()) + { + curveName = ECNamedCurveTable.getName(ASN1ObjectIdentifier.getInstance(params.getParameters())); + } + + ecParameterSpec = EC5Util.convertToSpec(params, curve); + } + else + { + throw new IOException("Unknown encoded parameters format in AlgorithmParameters object: " + format); + } + } + + @Override + protected <T extends AlgorithmParameterSpec> T engineGetParameterSpec(Class<T> paramSpec) + throws InvalidParameterSpecException + { + if (ECParameterSpec.class.isAssignableFrom(paramSpec) || paramSpec == AlgorithmParameterSpec.class) + { + return (T)ecParameterSpec; + } + else if (ECGenParameterSpec.class.isAssignableFrom(paramSpec)) + { + if (curveName != null) + { + ASN1ObjectIdentifier namedCurveOid = ECUtil.getNamedCurveOid(curveName); + + if (namedCurveOid != null) + { + return (T)new ECGenParameterSpec(namedCurveOid.getId()); + } + return (T)new ECGenParameterSpec(curveName); + } + else + { + ASN1ObjectIdentifier namedCurveOid = ECUtil.getNamedCurveOid(EC5Util.convertSpec(ecParameterSpec, false)); + + if (namedCurveOid != null) + { + return (T)new ECGenParameterSpec(namedCurveOid.getId()); + } + } + } + throw new InvalidParameterSpecException("EC AlgorithmParameters cannot convert to " + paramSpec.getName()); + } + + @Override + protected byte[] engineGetEncoded() + throws IOException + { + return engineGetEncoded("ASN.1"); + } + + @Override + protected byte[] engineGetEncoded(String format) + throws IOException + { + if (isASN1FormatString(format)) + { + X962Parameters params; + + if (ecParameterSpec == null) // implicitly CA + { + params = new X962Parameters(DERNull.INSTANCE); + } + else if (curveName != null) + { + params = new X962Parameters(ECUtil.getNamedCurveOid(curveName)); + } + else + { + org.bouncycastle.jce.spec.ECParameterSpec ecSpec = EC5Util.convertSpec(ecParameterSpec, false); + X9ECParameters ecP = new X9ECParameters( + ecSpec.getCurve(), + ecSpec.getG(), + ecSpec.getN(), + ecSpec.getH(), + ecSpec.getSeed()); + + params = new X962Parameters(ecP); + } + + return params.getEncoded(); + } + + throw new IOException("Unknown parameters format in AlgorithmParameters object: " + format); + } + + @Override + protected String engineToString() + { + return "EC AlgorithmParameters "; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/ECUtils.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/ECUtils.java new file mode 100644 index 00000000..dc92e0e4 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/ECUtils.java @@ -0,0 +1,45 @@ +package org.bouncycastle.jcajce.provider.asymmetric.ec; + +import java.security.spec.ECGenParameterSpec; + +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil; + +class ECUtils +{ + static X9ECParameters getDomainParametersFromGenSpec(ECGenParameterSpec genSpec) + { + return getDomainParametersFromName(genSpec.getName()); + } + + static X9ECParameters getDomainParametersFromName(String curveName) + { + X9ECParameters domainParameters; + try + { + if (curveName.charAt(0) >= '0' && curveName.charAt(0) <= '2') + { + ASN1ObjectIdentifier oidID = new ASN1ObjectIdentifier(curveName); + domainParameters = ECUtil.getNamedCurveByOid(oidID); + } + else + { + if (curveName.indexOf(' ') > 0) + { + curveName = curveName.substring(curveName.indexOf(' ') + 1); + domainParameters = ECUtil.getNamedCurveByName(curveName); + } + else + { + domainParameters = ECUtil.getNamedCurveByName(curveName); + } + } + } + catch (IllegalArgumentException ex) + { + domainParameters = ECUtil.getNamedCurveByName(curveName); + } + return domainParameters; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/BaseAgreementSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/BaseAgreementSpi.java new file mode 100644 index 00000000..8dc0d2dd --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/BaseAgreementSpi.java @@ -0,0 +1,314 @@ +package org.bouncycastle.jcajce.provider.asymmetric.util; + +import java.math.BigInteger; +import java.security.NoSuchAlgorithmException; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; + +import javax.crypto.KeyAgreementSpi; +import javax.crypto.SecretKey; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.SecretKeySpec; + +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.bouncycastle.asn1.gnu.GNUObjectIdentifiers; +import org.bouncycastle.asn1.kisa.KISAObjectIdentifiers; +import org.bouncycastle.asn1.misc.MiscObjectIdentifiers; +import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; +import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers; +import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.crypto.DerivationFunction; +import org.bouncycastle.crypto.agreement.kdf.DHKDFParameters; +import org.bouncycastle.crypto.agreement.kdf.DHKEKGenerator; +import org.bouncycastle.crypto.params.DESParameters; +import org.bouncycastle.crypto.params.KDFParameters; +import org.bouncycastle.util.Integers; +import org.bouncycastle.util.Strings; + +public abstract class BaseAgreementSpi + extends KeyAgreementSpi +{ + private static final Map<String, ASN1ObjectIdentifier> defaultOids = new HashMap<String, ASN1ObjectIdentifier>(); + private static final Map<String, Integer> keySizes = new HashMap<String, Integer>(); + private static final Map<String, String> nameTable = new HashMap<String, String>(); + + private static final Hashtable oids = new Hashtable(); + private static final Hashtable des = new Hashtable(); + + static + { + Integer i64 = Integers.valueOf(64); + Integer i128 = Integers.valueOf(128); + Integer i192 = Integers.valueOf(192); + Integer i256 = Integers.valueOf(256); + + keySizes.put("DES", i64); + keySizes.put("DESEDE", i192); + keySizes.put("BLOWFISH", i128); + keySizes.put("AES", i256); + + keySizes.put(NISTObjectIdentifiers.id_aes128_ECB.getId(), i128); + keySizes.put(NISTObjectIdentifiers.id_aes192_ECB.getId(), i192); + keySizes.put(NISTObjectIdentifiers.id_aes256_ECB.getId(), i256); + keySizes.put(NISTObjectIdentifiers.id_aes128_CBC.getId(), i128); + keySizes.put(NISTObjectIdentifiers.id_aes192_CBC.getId(), i192); + keySizes.put(NISTObjectIdentifiers.id_aes256_CBC.getId(), i256); + keySizes.put(NISTObjectIdentifiers.id_aes128_CFB.getId(), i128); + keySizes.put(NISTObjectIdentifiers.id_aes192_CFB.getId(), i192); + keySizes.put(NISTObjectIdentifiers.id_aes256_CFB.getId(), i256); + keySizes.put(NISTObjectIdentifiers.id_aes128_OFB.getId(), i128); + keySizes.put(NISTObjectIdentifiers.id_aes192_OFB.getId(), i192); + keySizes.put(NISTObjectIdentifiers.id_aes256_OFB.getId(), i256); + keySizes.put(NISTObjectIdentifiers.id_aes128_wrap.getId(), i128); + keySizes.put(NISTObjectIdentifiers.id_aes192_wrap.getId(), i192); + keySizes.put(NISTObjectIdentifiers.id_aes256_wrap.getId(), i256); + keySizes.put(NISTObjectIdentifiers.id_aes128_CCM.getId(), i128); + keySizes.put(NISTObjectIdentifiers.id_aes192_CCM.getId(), i192); + keySizes.put(NISTObjectIdentifiers.id_aes256_CCM.getId(), i256); + keySizes.put(NISTObjectIdentifiers.id_aes128_GCM.getId(), i128); + keySizes.put(NISTObjectIdentifiers.id_aes192_GCM.getId(), i192); + keySizes.put(NISTObjectIdentifiers.id_aes256_GCM.getId(), i256); + keySizes.put(NTTObjectIdentifiers.id_camellia128_wrap.getId(), i128); + keySizes.put(NTTObjectIdentifiers.id_camellia192_wrap.getId(), i192); + keySizes.put(NTTObjectIdentifiers.id_camellia256_wrap.getId(), i256); + keySizes.put(KISAObjectIdentifiers.id_npki_app_cmsSeed_wrap.getId(), i128); + + keySizes.put(PKCSObjectIdentifiers.id_alg_CMS3DESwrap.getId(), i192); + keySizes.put(PKCSObjectIdentifiers.des_EDE3_CBC.getId(), i192); + keySizes.put(OIWObjectIdentifiers.desCBC.getId(), i64); + + keySizes.put(PKCSObjectIdentifiers.id_hmacWithSHA1.getId(), Integers.valueOf(160)); + keySizes.put(PKCSObjectIdentifiers.id_hmacWithSHA256.getId(), i256); + keySizes.put(PKCSObjectIdentifiers.id_hmacWithSHA384.getId(), Integers.valueOf(384)); + keySizes.put(PKCSObjectIdentifiers.id_hmacWithSHA512.getId(), Integers.valueOf(512)); + + defaultOids.put("DESEDE", PKCSObjectIdentifiers.des_EDE3_CBC); + defaultOids.put("AES", NISTObjectIdentifiers.id_aes256_CBC); + defaultOids.put("CAMELLIA", NTTObjectIdentifiers.id_camellia256_cbc); + defaultOids.put("SEED", KISAObjectIdentifiers.id_seedCBC); + defaultOids.put("DES", OIWObjectIdentifiers.desCBC); + + nameTable.put(MiscObjectIdentifiers.cast5CBC.getId(), "CAST5"); + nameTable.put(MiscObjectIdentifiers.as_sys_sec_alg_ideaCBC.getId(), "IDEA"); + nameTable.put(MiscObjectIdentifiers.cryptlib_algorithm_blowfish_ECB.getId(), "Blowfish"); + nameTable.put(MiscObjectIdentifiers.cryptlib_algorithm_blowfish_CBC.getId(), "Blowfish"); + nameTable.put(MiscObjectIdentifiers.cryptlib_algorithm_blowfish_CFB.getId(), "Blowfish"); + nameTable.put(MiscObjectIdentifiers.cryptlib_algorithm_blowfish_OFB.getId(), "Blowfish"); + nameTable.put(OIWObjectIdentifiers.desECB.getId(), "DES"); + nameTable.put(OIWObjectIdentifiers.desCBC.getId(), "DES"); + nameTable.put(OIWObjectIdentifiers.desCFB.getId(), "DES"); + nameTable.put(OIWObjectIdentifiers.desOFB.getId(), "DES"); + nameTable.put(OIWObjectIdentifiers.desEDE.getId(), "DESede"); + nameTable.put(PKCSObjectIdentifiers.des_EDE3_CBC.getId(), "DESede"); + nameTable.put(PKCSObjectIdentifiers.id_alg_CMS3DESwrap.getId(), "DESede"); + nameTable.put(PKCSObjectIdentifiers.id_alg_CMSRC2wrap.getId(), "RC2"); + nameTable.put(PKCSObjectIdentifiers.id_hmacWithSHA1.getId(), "HmacSHA1"); + nameTable.put(PKCSObjectIdentifiers.id_hmacWithSHA224.getId(), "HmacSHA224"); + nameTable.put(PKCSObjectIdentifiers.id_hmacWithSHA256.getId(), "HmacSHA256"); + nameTable.put(PKCSObjectIdentifiers.id_hmacWithSHA384.getId(), "HmacSHA384"); + nameTable.put(PKCSObjectIdentifiers.id_hmacWithSHA512.getId(), "HmacSHA512"); + nameTable.put(NTTObjectIdentifiers.id_camellia128_cbc.getId(), "Camellia"); + nameTable.put(NTTObjectIdentifiers.id_camellia192_cbc.getId(), "Camellia"); + nameTable.put(NTTObjectIdentifiers.id_camellia256_cbc.getId(), "Camellia"); + nameTable.put(NTTObjectIdentifiers.id_camellia128_wrap.getId(), "Camellia"); + nameTable.put(NTTObjectIdentifiers.id_camellia192_wrap.getId(), "Camellia"); + nameTable.put(NTTObjectIdentifiers.id_camellia256_wrap.getId(), "Camellia"); + nameTable.put(KISAObjectIdentifiers.id_npki_app_cmsSeed_wrap.getId(), "SEED"); + nameTable.put(KISAObjectIdentifiers.id_seedCBC.getId(), "SEED"); + nameTable.put(KISAObjectIdentifiers.id_seedMAC.getId(), "SEED"); + nameTable.put(CryptoProObjectIdentifiers.gostR28147_gcfb.getId(), "GOST28147"); + + nameTable.put(NISTObjectIdentifiers.id_aes128_wrap.getId(), "AES"); + nameTable.put(NISTObjectIdentifiers.id_aes128_CCM.getId(), "AES"); + nameTable.put(NISTObjectIdentifiers.id_aes128_CCM.getId(), "AES"); + + oids.put("DESEDE", PKCSObjectIdentifiers.des_EDE3_CBC); + oids.put("AES", NISTObjectIdentifiers.id_aes256_CBC); + oids.put("DES", OIWObjectIdentifiers.desCBC); + + des.put("DES", "DES"); + des.put("DESEDE", "DES"); + des.put(OIWObjectIdentifiers.desCBC.getId(), "DES"); + des.put(PKCSObjectIdentifiers.des_EDE3_CBC.getId(), "DES"); + des.put(PKCSObjectIdentifiers.id_alg_CMS3DESwrap.getId(), "DES"); + } + + private final String kaAlgorithm; + private final DerivationFunction kdf; + + protected BigInteger result; + protected byte[] ukmParameters; + + public BaseAgreementSpi(String kaAlgorithm, DerivationFunction kdf) + { + this.kaAlgorithm = kaAlgorithm; + this.kdf = kdf; + } + + protected static String getAlgorithm(String algDetails) + { + if (algDetails.indexOf('[') > 0) + { + return algDetails.substring(0, algDetails.indexOf('[')); + } + + if (algDetails.startsWith(NISTObjectIdentifiers.aes.getId())) + { + return "AES"; + } + if (algDetails.startsWith(GNUObjectIdentifiers.Serpent.getId())) + { + return "Serpent"; + } + + String name = (String)nameTable.get(Strings.toUpperCase(algDetails)); + + if (name != null) + { + return name; + } + + return algDetails; + } + + protected static int getKeySize(String algDetails) + { + if (algDetails.indexOf('[') > 0) + { + return (Integer.parseInt(algDetails.substring(algDetails.indexOf('[') + 1, algDetails.indexOf(']'))) + 7) / 8; + } + + String algKey = Strings.toUpperCase(algDetails); + if (!keySizes.containsKey(algKey)) + { + return -1; + } + + return ((Integer)keySizes.get(algKey)).intValue(); + } + + protected static byte[] trimZeroes(byte[] secret) + { + if (secret[0] != 0) + { + return secret; + } + else + { + int ind = 0; + while (ind < secret.length && secret[ind] == 0) + { + ind++; + } + + byte[] rv = new byte[secret.length - ind]; + + System.arraycopy(secret, ind, rv, 0, rv.length); + + return rv; + } + } + + protected byte[] engineGenerateSecret() + throws IllegalStateException + { + if (kdf != null) + { + throw new UnsupportedOperationException( + "KDF can only be used when algorithm is known"); + } + + return bigIntToBytes(result); + } + + protected int engineGenerateSecret( + byte[] sharedSecret, + int offset) + throws IllegalStateException, ShortBufferException + { + byte[] secret = engineGenerateSecret(); + + if (sharedSecret.length - offset < secret.length) + { + throw new ShortBufferException(kaAlgorithm + " key agreement: need " + secret.length + " bytes"); + } + + System.arraycopy(secret, 0, sharedSecret, offset, secret.length); + + return secret.length; + } + + protected SecretKey engineGenerateSecret( + String algorithm) + throws NoSuchAlgorithmException + { + byte[] secret = bigIntToBytes(result); + String algKey = Strings.toUpperCase(algorithm); + String oidAlgorithm = algorithm; + + if (oids.containsKey(algKey)) + { + oidAlgorithm = ((ASN1ObjectIdentifier)oids.get(algKey)).getId(); + } + + int keySize = getKeySize(oidAlgorithm); + + if (kdf != null) + { + if (keySize < 0) + { + throw new NoSuchAlgorithmException("unknown algorithm encountered: " + oidAlgorithm); + } + byte[] keyBytes = new byte[keySize / 8]; + + if (kdf instanceof DHKEKGenerator) + { + ASN1ObjectIdentifier oid; + try + { + oid = new ASN1ObjectIdentifier(oidAlgorithm); + } + catch (IllegalArgumentException e) + { + throw new NoSuchAlgorithmException("no OID for algorithm: " + oidAlgorithm); + } + DHKDFParameters params = new DHKDFParameters(oid, keySize, secret, ukmParameters); + + kdf.init(params); + } + else + { + KDFParameters params = new KDFParameters(secret, ukmParameters); + + kdf.init(params); + } + + kdf.generateBytes(keyBytes, 0, keyBytes.length); + + secret = keyBytes; + } + else + { + if (keySize > 0) + { + byte[] keyBytes = new byte[keySize / 8]; + + System.arraycopy(secret, 0, keyBytes, 0, keyBytes.length); + + secret = keyBytes; + } + } + + if (des.containsKey(oidAlgorithm)) + { + DESParameters.setOddParity(secret); + } + + return new SecretKeySpec(secret, getAlgorithm(algorithm)); + } + + protected abstract byte[] bigIntToBytes(BigInteger result); +} diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/DESUtil.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/DESUtil.java new file mode 100644 index 00000000..2409fb03 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/DESUtil.java @@ -0,0 +1,53 @@ +package org.bouncycastle.jcajce.provider.asymmetric.util; + +import java.util.HashSet; +import java.util.Set; + +import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.util.Strings; + +public class DESUtil +{ + private static final Set<String> des = new HashSet<String>(); + + static + { + des.add("DES"); + des.add("DESEDE"); + des.add(OIWObjectIdentifiers.desCBC.getId()); + des.add(PKCSObjectIdentifiers.des_EDE3_CBC.getId()); + des.add(PKCSObjectIdentifiers.des_EDE3_CBC.getId()); + des.add(PKCSObjectIdentifiers.id_alg_CMS3DESwrap.getId()); + } + + public static boolean isDES(String algorithmID) + { + String name = Strings.toUpperCase(algorithmID); + + return des.contains(name); + } + + /** + * 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/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Blake2b.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Blake2b.java new file mode 100644 index 00000000..0eb978a9 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Blake2b.java @@ -0,0 +1,114 @@ +package org.bouncycastle.jcajce.provider.digest; + +import org.bouncycastle.asn1.misc.MiscObjectIdentifiers; +import org.bouncycastle.crypto.digests.Blake2bDigest; +import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; + +public class Blake2b +{ + private Blake2b() + { + + } + + static public class Blake2b512 + extends BCMessageDigest + implements Cloneable + { + public Blake2b512() + { + super(new Blake2bDigest(512)); + } + + public Object clone() + throws CloneNotSupportedException + { + Blake2b512 d = (Blake2b512)super.clone(); + d.digest = new Blake2bDigest((Blake2bDigest)digest); + + return d; + } + } + + static public class Blake2b384 + extends BCMessageDigest + implements Cloneable + { + public Blake2b384() + { + super(new Blake2bDigest(384)); + } + + public Object clone() + throws CloneNotSupportedException + { + Blake2b384 d = (Blake2b384)super.clone(); + d.digest = new Blake2bDigest((Blake2bDigest)digest); + + return d; + } + } + + static public class Blake2b256 + extends BCMessageDigest + implements Cloneable + { + public Blake2b256() + { + super(new Blake2bDigest(256)); + } + + public Object clone() + throws CloneNotSupportedException + { + Blake2b256 d = (Blake2b256)super.clone(); + d.digest = new Blake2bDigest((Blake2bDigest)digest); + + return d; + } + } + + static public class Blake2b160 + extends BCMessageDigest + implements Cloneable + { + public Blake2b160() + { + super(new Blake2bDigest(160)); + } + + public Object clone() + throws CloneNotSupportedException + { + Blake2b160 d = (Blake2b160)super.clone(); + d.digest = new Blake2bDigest((Blake2bDigest)digest); + + return d; + } + } + + public static class Mappings + extends DigestAlgorithmProvider + { + private static final String PREFIX = Blake2b.class.getName(); + + public Mappings() + { + } + + public void configure(ConfigurableProvider provider) + { + provider.addAlgorithm("MessageDigest.BLAKE2B-512", PREFIX + "$Blake2b512"); + provider.addAlgorithm("Alg.Alias.MessageDigest." + MiscObjectIdentifiers.id_blake2b512, "BLAKE2B-512"); + + provider.addAlgorithm("MessageDigest.BLAKE2B-384", PREFIX + "$Blake2b384"); + provider.addAlgorithm("Alg.Alias.MessageDigest." + MiscObjectIdentifiers.id_blake2b384, "BLAKE2B-384"); + + provider.addAlgorithm("MessageDigest.BLAKE2B-256", PREFIX + "$Blake2b256"); + provider.addAlgorithm("Alg.Alias.MessageDigest." + MiscObjectIdentifiers.id_blake2b256, "BLAKE2B-256"); + + provider.addAlgorithm("MessageDigest.BLAKE2B-160", PREFIX + "$Blake2b160"); + provider.addAlgorithm("Alg.Alias.MessageDigest." + MiscObjectIdentifiers.id_blake2b160, "BLAKE2B-160"); + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Keccak.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Keccak.java new file mode 100644 index 00000000..f8b989bf --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Keccak.java @@ -0,0 +1,195 @@ +package org.bouncycastle.jcajce.provider.digest; + +import org.bouncycastle.crypto.CipherKeyGenerator; +import org.bouncycastle.crypto.digests.KeccakDigest; +import org.bouncycastle.crypto.macs.HMac; +import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac; + +public class Keccak +{ + private Keccak() + { + + } + + static public class DigestKeccak + extends BCMessageDigest + implements Cloneable + { + public DigestKeccak(int size) + { + super(new KeccakDigest(size)); + } + + public Object clone() + throws CloneNotSupportedException + { + BCMessageDigest d = (BCMessageDigest)super.clone(); + d.digest = new KeccakDigest((KeccakDigest)digest); + + return d; + } + } + + static public class Digest224 + extends DigestKeccak + { + public Digest224() + { + super(224); + } + } + + static public class Digest256 + extends DigestKeccak + { + public Digest256() + { + super(256); + } + } + + static public class Digest288 + extends DigestKeccak + { + public Digest288() + { + super(288); + } + } + + static public class Digest384 + extends DigestKeccak + { + public Digest384() + { + super(384); + } + } + + static public class Digest512 + extends DigestKeccak + { + public Digest512() + { + super(512); + } + } + + public static class HashMac224 + extends BaseMac + { + public HashMac224() + { + super(new HMac(new KeccakDigest(224))); + } + } + + public static class HashMac256 + extends BaseMac + { + public HashMac256() + { + super(new HMac(new KeccakDigest(256))); + } + } + + public static class HashMac288 + extends BaseMac + { + public HashMac288() + { + super(new HMac(new KeccakDigest(288))); + } + } + + public static class HashMac384 + extends BaseMac + { + public HashMac384() + { + super(new HMac(new KeccakDigest(384))); + } + } + + public static class HashMac512 + extends BaseMac + { + public HashMac512() + { + super(new HMac(new KeccakDigest(512))); + } + } + + public static class KeyGenerator224 + extends BaseKeyGenerator + { + public KeyGenerator224() + { + super("HMACKECCAK224", 224, new CipherKeyGenerator()); + } + } + + public static class KeyGenerator256 + extends BaseKeyGenerator + { + public KeyGenerator256() + { + super("HMACKECCAK256", 256, new CipherKeyGenerator()); + } + } + + public static class KeyGenerator288 + extends BaseKeyGenerator + { + public KeyGenerator288() + { + super("HMACKECCAK288", 288, new CipherKeyGenerator()); + } + } + + public static class KeyGenerator384 + extends BaseKeyGenerator + { + public KeyGenerator384() + { + super("HMACKECCAK384", 384, new CipherKeyGenerator()); + } + } + + public static class KeyGenerator512 + extends BaseKeyGenerator + { + public KeyGenerator512() + { + super("HMACKECCAK512", 512, new CipherKeyGenerator()); + } + } + + public static class Mappings + extends DigestAlgorithmProvider + { + private static final String PREFIX = Keccak.class.getName(); + + public Mappings() + { + } + + public void configure(ConfigurableProvider provider) + { + provider.addAlgorithm("MessageDigest.KECCAK-224", PREFIX + "$Digest224"); + provider.addAlgorithm("MessageDigest.KECCAK-288", PREFIX + "$Digest288"); + provider.addAlgorithm("MessageDigest.KECCAK-256", PREFIX + "$Digest256"); + provider.addAlgorithm("MessageDigest.KECCAK-384", PREFIX + "$Digest384"); + provider.addAlgorithm("MessageDigest.KECCAK-512", PREFIX + "$Digest512"); + + addHMACAlgorithm(provider, "KECCAK224", PREFIX + "$HashMac224", PREFIX + "$KeyGenerator224"); + addHMACAlgorithm(provider, "KECCAK256", PREFIX + "$HashMac256", PREFIX + "$KeyGenerator256"); + addHMACAlgorithm(provider, "KECCAK288", PREFIX + "$HashMac288", PREFIX + "$KeyGenerator288"); + addHMACAlgorithm(provider, "KECCAK384", PREFIX + "$HashMac384", PREFIX + "$KeyGenerator384"); + addHMACAlgorithm(provider, "KECCAK512", PREFIX + "$HashMac512", PREFIX + "$KeyGenerator512"); + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GcmSpecUtil.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GcmSpecUtil.java new file mode 100644 index 00000000..5ccc8ff0 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GcmSpecUtil.java @@ -0,0 +1,78 @@ +package org.bouncycastle.jcajce.provider.symmetric; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidParameterSpecException; + +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.cms.GCMParameters; +import org.bouncycastle.util.Integers; + +class GcmSpecUtil +{ + static final Class gcmSpecClass = lookup("javax.crypto.spec.GCMParameterSpec"); + + static boolean gcmSpecExists() + { + return gcmSpecClass != null; + } + + static boolean isGcmSpec(AlgorithmParameterSpec paramSpec) + { + return gcmSpecClass != null && gcmSpecClass.isInstance(paramSpec); + } + + static boolean isGcmSpec(Class paramSpecClass) + { + return gcmSpecClass == paramSpecClass; + } + + static AlgorithmParameterSpec extractGcmSpec(ASN1Primitive spec) + throws InvalidParameterSpecException + { + try + { + GCMParameters gcmParams = GCMParameters.getInstance(spec); + Constructor constructor = gcmSpecClass.getConstructor(new Class[]{Integer.TYPE, byte[].class}); + + return (AlgorithmParameterSpec)constructor.newInstance(new Object[] { Integers.valueOf(gcmParams.getIcvLen() * 8), gcmParams.getNonce() }); + } + catch (NoSuchMethodException e) + { + throw new InvalidParameterSpecException("No constructor found!"); // should never happen + } + catch (Exception e) + { + throw new InvalidParameterSpecException("Construction failed: " + e.getMessage()); // should never happen + } + } + + static GCMParameters extractGcmParameters(AlgorithmParameterSpec paramSpec) + throws InvalidParameterSpecException + { + try + { + Method tLen = gcmSpecClass.getDeclaredMethod("getTLen", new Class[0]); + Method iv= gcmSpecClass.getDeclaredMethod("getIV", new Class[0]); + + return new GCMParameters((byte[])iv.invoke(paramSpec, new Object[0]), ((Integer)tLen.invoke(paramSpec, new Object[0])).intValue() / 8); + } + catch (Exception e) + { + throw new InvalidParameterSpecException("Cannot process GCMParameterSpec"); + } + } + + private static Class lookup(String className) + { + try + { + return GcmSpecUtil.class.getClassLoader().loadClass(className); + } + catch (Exception e) + { + return null; + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/OpenSSLPBKDF.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/OpenSSLPBKDF.java new file mode 100644 index 00000000..5888adfa --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/OpenSSLPBKDF.java @@ -0,0 +1,86 @@ +package org.bouncycastle.jcajce.provider.symmetric; + +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; + +import javax.crypto.SecretKey; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.SecretKeySpec; + +import org.bouncycastle.crypto.generators.OpenSSLPBEParametersGenerator; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseSecretKeyFactory; +import org.bouncycastle.jcajce.provider.util.AlgorithmProvider; +import org.bouncycastle.util.Strings; + +public final class OpenSSLPBKDF +{ + private OpenSSLPBKDF() + { + } + + public static class PBKDF + extends BaseSecretKeyFactory + { + public PBKDF() + { + super("PBKDF-OpenSSL", null); + } + + protected SecretKey engineGenerateSecret( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof PBEKeySpec) + { + PBEKeySpec pbeSpec = (PBEKeySpec)keySpec; + + if (pbeSpec.getSalt() == null) + { + throw new InvalidKeySpecException("missing required salt"); + } + + if (pbeSpec.getIterationCount() <= 0) + { + throw new InvalidKeySpecException("positive iteration count required: " + + pbeSpec.getIterationCount()); + } + + if (pbeSpec.getKeyLength() <= 0) + { + throw new InvalidKeySpecException("positive key length required: " + + pbeSpec.getKeyLength()); + } + + if (pbeSpec.getPassword().length == 0) + { + throw new IllegalArgumentException("password empty"); + } + + OpenSSLPBEParametersGenerator pGen = new OpenSSLPBEParametersGenerator(); + + pGen.init(Strings.toByteArray(pbeSpec.getPassword()), pbeSpec.getSalt()); + + return new SecretKeySpec(((KeyParameter)pGen.generateDerivedParameters(pbeSpec.getKeyLength())).getKey(), "OpenSSLPBKDF"); + } + + throw new InvalidKeySpecException("Invalid KeySpec"); + } + } + + public static class Mappings + extends AlgorithmProvider + { + private static final String PREFIX = OpenSSLPBKDF.class.getName(); + + public Mappings() + { + } + + public void configure(ConfigurableProvider provider) + { + provider.addAlgorithm("SecretKeyFactory.PBKDF-OPENSSL", PREFIX + "$PBKDF"); + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SM4.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SM4.java new file mode 100644 index 00000000..05e037ab --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SM4.java @@ -0,0 +1,162 @@ +package org.bouncycastle.jcajce.provider.symmetric; + +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.spec.IvParameterSpec; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.CipherKeyGenerator; +import org.bouncycastle.crypto.engines.SM4Engine; +import org.bouncycastle.crypto.generators.Poly1305KeyGenerator; +import org.bouncycastle.crypto.macs.CMac; +import org.bouncycastle.crypto.macs.GMac; +import org.bouncycastle.crypto.modes.GCMBlockCipher; +import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameterGenerator; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac; +import org.bouncycastle.jcajce.provider.symmetric.util.BlockCipherProvider; +import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters; + +public final class SM4 +{ + private SM4() + { + } + + public static class ECB + extends BaseBlockCipher + { + public ECB() + { + super(new BlockCipherProvider() + { + public BlockCipher get() + { + return new SM4Engine(); + } + }); + } + } + + public static class KeyGen + extends BaseKeyGenerator + { + public KeyGen() + { + super("SM4", 128, new CipherKeyGenerator()); + } + } + + public static class CMAC + extends BaseMac + { + public CMAC() + { + super(new CMac(new SM4Engine())); + } + } + + public static class GMAC + extends BaseMac + { + public GMAC() + { + super(new GMac(new GCMBlockCipher(new SM4Engine()))); + } + } + + public static class Poly1305 + extends BaseMac + { + public Poly1305() + { + super(new org.bouncycastle.crypto.macs.Poly1305(new SM4Engine())); + } + } + + public static class Poly1305KeyGen + extends BaseKeyGenerator + { + public Poly1305KeyGen() + { + super("Poly1305-SM4", 256, new Poly1305KeyGenerator()); + } + } + + public static class AlgParamGen + extends BaseAlgorithmParameterGenerator + { + protected void engineInit( + AlgorithmParameterSpec genParamSpec, + SecureRandom random) + throws InvalidAlgorithmParameterException + { + throw new InvalidAlgorithmParameterException("No supported AlgorithmParameterSpec for SM4 parameter generation."); + } + + protected AlgorithmParameters engineGenerateParameters() + { + byte[] iv = new byte[16]; + + if (random == null) + { + random = new SecureRandom(); + } + + random.nextBytes(iv); + + AlgorithmParameters params; + + try + { + params = createParametersInstance("SM4"); + params.init(new IvParameterSpec(iv)); + } + catch (Exception e) + { + throw new RuntimeException(e.getMessage()); + } + + return params; + } + } + + public static class AlgParams + extends IvAlgorithmParameters + { + protected String engineToString() + { + return "SM4 IV"; + } + } + + public static class Mappings + extends SymmetricAlgorithmProvider + { + private static final String PREFIX = SM4.class.getName(); + + public Mappings() + { + } + + public void configure(ConfigurableProvider provider) + { + provider.addAlgorithm("AlgorithmParameters.SM4", PREFIX + "$AlgParams"); + + provider.addAlgorithm("AlgorithmParameterGenerator.SM4", PREFIX + "$AlgParamGen"); + + provider.addAlgorithm("Cipher.SM4", PREFIX + "$ECB"); + + provider.addAlgorithm("KeyGenerator.SM4", PREFIX + "$KeyGen"); + + addCMacAlgorithm(provider, "SM4", PREFIX + "$CMAC", PREFIX + "$KeyGen"); + addGMacAlgorithm(provider, "SM4", PREFIX + "$GMAC", PREFIX + "$KeyGen"); + addPoly1305Algorithm(provider, "SM4", PREFIX + "$Poly1305", PREFIX + "$Poly1305KeyGen"); + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/KTSParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/KTSParameterSpec.java new file mode 100644 index 00000000..a6d1a8d2 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/KTSParameterSpec.java @@ -0,0 +1,157 @@ +package org.bouncycastle.jcajce.spec; + +import java.security.spec.AlgorithmParameterSpec; + +import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; +import org.bouncycastle.util.Arrays; + +/** + * Parameter spec for doing KTS based wrapping via the Cipher API. + */ +public class KTSParameterSpec + implements AlgorithmParameterSpec +{ + private final String wrappingKeyAlgorithm; + private final int keySizeInBits; + private final AlgorithmParameterSpec parameterSpec; + private final AlgorithmIdentifier kdfAlgorithm; + private byte[] otherInfo; + + /** + * Builder class for creating a KTSParameterSpec. + */ + public static final class Builder + { + private final String algorithmName; + private final int keySizeInBits; + + private AlgorithmParameterSpec parameterSpec; + private AlgorithmIdentifier kdfAlgorithm; + private byte[] otherInfo; + + /** + * Basic builder. + * + * @param algorithmName the algorithm name for the secret key we use for wrapping. + * @param keySizeInBits the size of the wrapping key we want to produce in bits. + */ + public Builder(String algorithmName, int keySizeInBits) + { + this(algorithmName, keySizeInBits, null); + } + + /** + * Basic builder. + * + * @param algorithmName the algorithm name for the secret key we use for wrapping. + * @param keySizeInBits the size of the wrapping key we want to produce in bits. + * @param otherInfo the otherInfo/IV encoding to be applied to the KDF. + */ + public Builder(String algorithmName, int keySizeInBits, byte[] otherInfo) + { + this.algorithmName = algorithmName; + this.keySizeInBits = keySizeInBits; + this.kdfAlgorithm = new AlgorithmIdentifier(X9ObjectIdentifiers.id_kdf_kdf3, new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256)); + this.otherInfo = (otherInfo == null) ? new byte[0] : Arrays.clone(otherInfo); + } + + /** + * Set the algorithm parameter spec to be used with the wrapper. + * + * @param parameterSpec the algorithm parameter spec to be used in wrapping/unwrapping. + * @return the current Builder instance. + */ + public Builder withParameterSpec(AlgorithmParameterSpec parameterSpec) + { + this.parameterSpec = parameterSpec; + + return this; + } + + /** + * Set the KDF algorithm and digest algorithm for wrap key generation. + * + * @param kdfAlgorithm the KDF algorithm to apply. + * @return the current Builder instance. + */ + public Builder withKdfAlgorithm(AlgorithmIdentifier kdfAlgorithm) + { + this.kdfAlgorithm = kdfAlgorithm; + + return this; + } + + /** + * Build the new parameter spec. + * + * @return a new parameter spec configured according to the builder state. + */ + public KTSParameterSpec build() + { + return new KTSParameterSpec(algorithmName, keySizeInBits, parameterSpec, kdfAlgorithm, otherInfo); + } + } + + private KTSParameterSpec( + String wrappingKeyAlgorithm, int keySizeInBits, + AlgorithmParameterSpec parameterSpec, AlgorithmIdentifier kdfAlgorithm, byte[] otherInfo) + { + this.wrappingKeyAlgorithm = wrappingKeyAlgorithm; + this.keySizeInBits = keySizeInBits; + this.parameterSpec = parameterSpec; + this.kdfAlgorithm = kdfAlgorithm; + this.otherInfo = otherInfo; + } + + /** + * Return the name of the algorithm for the wrapping key this key spec should use. + * + * @return the key algorithm. + */ + public String getKeyAlgorithmName() + { + return wrappingKeyAlgorithm; + } + + /** + * Return the size of the key (in bits) for the wrapping key this key spec should use. + * + * @return length in bits of the key to be calculated. + */ + public int getKeySize() + { + return keySizeInBits; + } + + /** + * Return the algorithm parameter spec to be applied with the private key when the encapsulation is decrypted. + * + * @return the algorithm parameter spec to be used with the private key. + */ + public AlgorithmParameterSpec getParameterSpec() + { + return parameterSpec; + } + + /** + * Return the AlgorithmIdentifier for the KDF to do key derivation after extracting the secret. + * + * @return the AlgorithmIdentifier for the SecretKeyFactory's KDF. + */ + public AlgorithmIdentifier getKdfAlgorithm() + { + return kdfAlgorithm; + } + + /** + * Return the otherInfo data for initialising the KDF. + * + * @return the otherInfo data. + */ + public byte[] getOtherInfo() + { + return Arrays.clone(otherInfo); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/MQVParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/MQVParameterSpec.java new file mode 100644 index 00000000..907abfb4 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/MQVParameterSpec.java @@ -0,0 +1,70 @@ +package org.bouncycastle.jcajce.spec; + +import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.AlgorithmParameterSpec; + +import org.bouncycastle.util.Arrays; + +public class MQVParameterSpec + implements AlgorithmParameterSpec +{ + private final PublicKey ephemeralPublicKey; + private final PrivateKey ephemeralPrivateKey; + private final PublicKey otherPartyEphemeralKey; + private final byte[] userKeyingMaterial; + + public MQVParameterSpec(PublicKey ephemeralPublicKey, PrivateKey ephemeralPrivateKey, PublicKey otherPartyEphemeralKey, byte[] userKeyingMaterial) + { + this.ephemeralPublicKey = ephemeralPublicKey; + this.ephemeralPrivateKey = ephemeralPrivateKey; + this.otherPartyEphemeralKey = otherPartyEphemeralKey; + this.userKeyingMaterial = Arrays.clone(userKeyingMaterial); + } + + public MQVParameterSpec(PublicKey ephemeralPublicKey, PrivateKey ephemeralPrivateKey, PublicKey otherPartyEphemeralKey) + { + this(ephemeralPublicKey, ephemeralPrivateKey, otherPartyEphemeralKey, null); + } + + public MQVParameterSpec(KeyPair ephemeralKeyPair, PublicKey otherPartyEphemeralKey, byte[] userKeyingMaterial) + { + this(ephemeralKeyPair.getPublic(), ephemeralKeyPair.getPrivate(), otherPartyEphemeralKey, userKeyingMaterial); + } + + public MQVParameterSpec(PrivateKey ephemeralPrivateKey, PublicKey otherPartyEphemeralKey, byte[] userKeyingMaterial) + { + this(null, ephemeralPrivateKey, otherPartyEphemeralKey, userKeyingMaterial); + } + + public MQVParameterSpec(KeyPair ephemeralKeyPair, PublicKey otherPartyEphemeralKey) + { + this(ephemeralKeyPair.getPublic(), ephemeralKeyPair.getPrivate(), otherPartyEphemeralKey, null); + } + + public MQVParameterSpec(PrivateKey ephemeralPrivateKey, PublicKey otherPartyEphemeralKey) + { + this(null, ephemeralPrivateKey, otherPartyEphemeralKey, null); + } + + public PrivateKey getEphemeralPrivateKey() + { + return ephemeralPrivateKey; + } + + public PublicKey getEphemeralPublicKey() + { + return ephemeralPublicKey; + } + + public PublicKey getOtherPartyEphemeralKey() + { + return otherPartyEphemeralKey; + } + + public byte[] getUserKeyingMaterial() + { + return Arrays.clone(userKeyingMaterial); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/UserKeyingMaterialSpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/UserKeyingMaterialSpec.java new file mode 100644 index 00000000..d8135310 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/UserKeyingMaterialSpec.java @@ -0,0 +1,21 @@ +package org.bouncycastle.jcajce.spec; + +import java.security.spec.AlgorithmParameterSpec; + +import org.bouncycastle.util.Arrays; + +public class UserKeyingMaterialSpec + implements AlgorithmParameterSpec +{ + private final byte[] userKeyingMaterial; + + public UserKeyingMaterialSpec(byte[] userKeyingMaterial) + { + this.userKeyingMaterial = Arrays.clone(userKeyingMaterial); + } + + public byte[] getUserKeyingMaterial() + { + return Arrays.clone(userKeyingMaterial); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/util/AlgorithmParametersUtils.java b/bcprov/src/main/java/org/bouncycastle/jcajce/util/AlgorithmParametersUtils.java new file mode 100644 index 00000000..4a1a92a5 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/util/AlgorithmParametersUtils.java @@ -0,0 +1,68 @@ +/***************************************************************/ +/****** DO NOT EDIT THIS CLASS bc-java SOURCE FILE ******/ +/***************************************************************/ +package org.bouncycastle.jcajce.util; + +import java.io.IOException; +import java.security.AlgorithmParameters; + +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1Primitive; + +/** + * General JCA/JCE utility methods. + */ +public class AlgorithmParametersUtils +{ + + + private AlgorithmParametersUtils() + { + + } + + /** + * Extract an ASN.1 encodable from an AlgorithmParameters object. + * + * @param params the object to get the encoding used to create the return value. + * @return an ASN.1 object representing the primitives making up the params parameter. + * @throws IOException if an encoding cannot be extracted. + */ + public static ASN1Encodable extractParameters(AlgorithmParameters params) + throws IOException + { + // we try ASN.1 explicitly first just in case and then role back to the default. + ASN1Encodable asn1Params; + try + { + asn1Params = ASN1Primitive.fromByteArray(params.getEncoded("ASN.1")); + } + catch (Exception ex) + { + asn1Params = ASN1Primitive.fromByteArray(params.getEncoded()); + } + + return asn1Params; + } + + /** + * Load an AlgorithmParameters object with the passed in ASN.1 encodable - if possible. + * + * @param params the AlgorithmParameters object to be initialised. + * @param sParams the ASN.1 encodable to initialise params with. + * @throws IOException if the parameters cannot be initialised. + */ + public static void loadParameters(AlgorithmParameters params, ASN1Encodable sParams) + throws IOException + { + // we try ASN.1 explicitly first just in case and then role back to the default. + try + { + params.init(sParams.toASN1Primitive().getEncoded(), "ASN.1"); + } + catch (Exception ex) + { + params.init(sParams.toASN1Primitive().getEncoded()); + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/util/MessageDigestUtils.java b/bcprov/src/main/java/org/bouncycastle/jcajce/util/MessageDigestUtils.java new file mode 100644 index 00000000..1bf8b31b --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/util/MessageDigestUtils.java @@ -0,0 +1,55 @@ +package org.bouncycastle.jcajce.util; + +import java.util.HashMap; +import java.util.Map; + +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.bouncycastle.asn1.gnu.GNUObjectIdentifiers; +import org.bouncycastle.asn1.iso.ISOIECObjectIdentifiers; +import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; +import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers; + +public class MessageDigestUtils +{ + private static Map<ASN1ObjectIdentifier, String> digestOidMap = new HashMap<ASN1ObjectIdentifier, String>(); + + static + { + digestOidMap.put(PKCSObjectIdentifiers.md2, "MD2"); + digestOidMap.put(PKCSObjectIdentifiers.md4, "MD4"); + digestOidMap.put(PKCSObjectIdentifiers.md5, "MD5"); + digestOidMap.put(OIWObjectIdentifiers.idSHA1, "SHA-1"); + digestOidMap.put(NISTObjectIdentifiers.id_sha224, "SHA-224"); + digestOidMap.put(NISTObjectIdentifiers.id_sha256, "SHA-256"); + digestOidMap.put(NISTObjectIdentifiers.id_sha384, "SHA-384"); + digestOidMap.put(NISTObjectIdentifiers.id_sha512, "SHA-512"); + digestOidMap.put(TeleTrusTObjectIdentifiers.ripemd128, "RIPEMD-128"); + digestOidMap.put(TeleTrusTObjectIdentifiers.ripemd160, "RIPEMD-160"); + digestOidMap.put(TeleTrusTObjectIdentifiers.ripemd256, "RIPEMD-128"); + digestOidMap.put(ISOIECObjectIdentifiers.ripemd128, "RIPEMD-128"); + digestOidMap.put(ISOIECObjectIdentifiers.ripemd160, "RIPEMD-160"); + digestOidMap.put(CryptoProObjectIdentifiers.gostR3411, "GOST3411"); + digestOidMap.put(GNUObjectIdentifiers.Tiger_192, "Tiger"); + digestOidMap.put(ISOIECObjectIdentifiers.whirlpool, "Whirlpool"); + } + + /** + * Attempt to find a standard JCA name for the digest represented by the passed in OID. + * + * @param digestAlgOID the OID of the digest algorithm of interest. + * @return a string representing the standard name - the OID as a string if none available. + */ + public static String getDigestName(ASN1ObjectIdentifier digestAlgOID) + { + String name = (String)digestOidMap.get(digestAlgOID); // for pre 1.5 JDK + if (name != null) + { + return name; + } + + return digestAlgOID.getId(); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECIESVectorTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECIESVectorTest.java new file mode 100644 index 00000000..95589561 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECIESVectorTest.java @@ -0,0 +1,236 @@ +package org.bouncycastle.jce.provider.test; + +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.Security; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import javax.crypto.Cipher; + +import org.bouncycastle.crypto.prng.FixedSecureRandom; +import org.bouncycastle.jce.interfaces.ECPrivateKey; +import org.bouncycastle.jce.interfaces.ECPublicKey; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jce.spec.IESParameterSpec; +import org.bouncycastle.util.encoders.Base64; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTest; + +/** + * Test for ECIES - Elliptic Curve Integrated Encryption Scheme + */ +public class ECIESVectorTest + extends SimpleTest +{ + static byte[] message = Hex.decode("0102030405060708090a0b0c0d0e0f10111213141516"); + + static byte[] derivation1 = Hex.decode("202122232425262728292a2b2c2d2e2f"); + static byte[] derivation2 = Hex.decode("202122232425262728292a2b2c2d2e2f404142434445464748"); + + static byte[] encoding1 = Hex.decode("af"); + static byte[] encoding2 = Hex.decode("303132333435363738393a3b3c3d3e3f"); + static byte[] encoding3 = Hex.decode("101112131415161718191a1b1c1d1e1f303132333435363738393a3b3c3d3e3f"); + + static byte[] p256_1_pub = Base64.decode("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGVoxUX5AiyggqzcaXG3yG6cH6PKSX6fVOnCo5SKolfR8kwc6S8zmADXlpnjzMLNUVvGDL805VKIXNJHijq4+gw=="); + static byte[] p256_1_pri = Base64.decode("MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQg+dn4oLSJcx5lxZhVxJCip13O/OblrNzNyCj2b9sNbQegCgYIKoZIzj0DAQehRANCAAQZWjFRfkCLKCCrNxpcbfIbpwfo8pJfp9U6cKjlIqiV9HyTBzpLzOYANeWmePMws1RW8YMvzTlUohc0keKOrj6D"); + static byte[] p256_1_eph = Hex.decode("35ee388194396b5febbddb7e3618eaaba44f3bae766dac70f75ae7b84b210948"); + + static byte[] p256_1_no_params = Hex.decode("04bc8f4da6ea423c0698744b927ec71b67126d97ef9dd804c141a0dfcb466cc1c7df25539375ddb3ae7d08cc46fa2a78434c9f6123b108e39a5fe614729c3d8ae43dfbe605c746ba0c546c0c25ba5a00304587fbed07a35ca06415cf6ad4d6a69d01122c99e2c854fe818f"); + + static byte[] p256_1_with_params11 = Hex.decode("04bc8f4da6ea423c0698744b927ec71b67126d97ef9dd804c141a0dfcb466cc1c7df25539375ddb3ae7d08cc46fa2a78434c9f6123b108e39a5fe614729c3d8ae4a57474b50621e97254c1f3b32635f694feb57e873a33c5d1948ee93ea5dc7577d42d7c2e41ab08f85835"); + static byte[] p256_1_with_params12 = Hex.decode("04bc8f4da6ea423c0698744b927ec71b67126d97ef9dd804c141a0dfcb466cc1c7df25539375ddb3ae7d08cc46fa2a78434c9f6123b108e39a5fe614729c3d8ae4a57474b50621e97254c1f3b32635f694feb57e873a3394725300acb7855f71a149d3f51a2e6fbd7f5389"); + static byte[] p256_1_with_params13 = Hex.decode("04bc8f4da6ea423c0698744b927ec71b67126d97ef9dd804c141a0dfcb466cc1c7df25539375ddb3ae7d08cc46fa2a78434c9f6123b108e39a5fe614729c3d8ae4a57474b50621e97254c1f3b32635f694feb57e873a33920872328f8d05ec561ba8c6e58d82b79c360078"); + static byte[] p256_1_with_params21 = Hex.decode("04bc8f4da6ea423c0698744b927ec71b67126d97ef9dd804c141a0dfcb466cc1c7df25539375ddb3ae7d08cc46fa2a78434c9f6123b108e39a5fe614729c3d8ae4da4d2bb723cd2ca918d1835dc2716a767085facd5df4499a4f4b6726651fb5e88178f01d831d9e5be0c1"); + static byte[] p256_1_with_params22 = Hex.decode("04bc8f4da6ea423c0698744b927ec71b67126d97ef9dd804c141a0dfcb466cc1c7df25539375ddb3ae7d08cc46fa2a78434c9f6123b108e39a5fe614729c3d8ae4da4d2bb723cd2ca918d1835dc2716a767085facd5df457c384a0e6402a5bb073120f08c706b49a4775d0"); + static byte[] p256_1_with_params23 = Hex.decode("04bc8f4da6ea423c0698744b927ec71b67126d97ef9dd804c141a0dfcb466cc1c7df25539375ddb3ae7d08cc46fa2a78434c9f6123b108e39a5fe614729c3d8ae4da4d2bb723cd2ca918d1835dc2716a767085facd5df4039427c24c55a2627df0ebea00f9d1eded9dd64b"); + + static byte[] p256_2_pub = Base64.decode("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAER4gx8nnRAkP+u76j0COZD81Cu9CTw3vczLnu1DG7ObI/VCzrDzJuswfzNWmxOFYXiXmZMAAkkEFA40nDSGOoqA=="); + static byte[] p256_2_pri = Base64.decode("MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgkflw6MQj4RbyuPFHoDE3i1dEaROS9uDSjGvrvPjqL7mgCgYIKoZIzj0DAQehRANCAARHiDHyedECQ/67vqPQI5kPzUK70JPDe9zMue7UMbs5sj9ULOsPMm6zB/M1abE4VheJeZkwACSQQUDjScNIY6io"); + static byte[] p256_2_eph = Hex.decode("0c37e1e0559a60d0b9c5b7b139a3f2df022b23abbd194bd95c8eff0aab1fd544"); + + static byte[] p256_2_no_params = Hex.decode("04d1ef981f81edae5e2e517b498504105b636d950cc13a9e1e31c607d8f0adc00c2152f2abae4870274f8788c08be35a7908f5547416405f24818ef4e8e3ae8dacd99a5cadfe82652a18c6058fc5ef7ffca626235fdbf7e2537f494d049d6f4e53d3d3df489cefb31101e1"); + + static byte[] p256_2_with_params11 = Hex.decode("04d1ef981f81edae5e2e517b498504105b636d950cc13a9e1e31c607d8f0adc00c2152f2abae4870274f8788c08be35a7908f5547416405f24818ef4e8e3ae8dace0e423753f63e054824edbc298a5645d7a4010cc7e65947090625dc0d750c50b2d563f264718d95818d4"); + static byte[] p256_2_with_params12 = Hex.decode("04d1ef981f81edae5e2e517b498504105b636d950cc13a9e1e31c607d8f0adc00c2152f2abae4870274f8788c08be35a7908f5547416405f24818ef4e8e3ae8dace0e423753f63e054824edbc298a5645d7a4010cc7e6502850f65c7a2c466444dea69d95a2df9a4b6cf2c"); + static byte[] p256_2_with_params13 = Hex.decode("04d1ef981f81edae5e2e517b498504105b636d950cc13a9e1e31c607d8f0adc00c2152f2abae4870274f8788c08be35a7908f5547416405f24818ef4e8e3ae8dace0e423753f63e054824edbc298a5645d7a4010cc7e651188a654e47322a60e53ea20a9e02f0e78bf8a32"); + static byte[] p256_2_with_params21 = Hex.decode("04d1ef981f81edae5e2e517b498504105b636d950cc13a9e1e31c607d8f0adc00c2152f2abae4870274f8788c08be35a7908f5547416405f24818ef4e8e3ae8dacdbdbe5e5f72245f4b5c28ee1ae21442868aef592d4047c8149babf4e7faffb5474bd6d84d6b9f1c9faf5"); + static byte[] p256_2_with_params22 = Hex.decode("04d1ef981f81edae5e2e517b498504105b636d950cc13a9e1e31c607d8f0adc00c2152f2abae4870274f8788c08be35a7908f5547416405f24818ef4e8e3ae8dacdbdbe5e5f72245f4b5c28ee1ae21442868aef592d40471190bd9b4bddd63983e4963378c9f55c703a7e6"); + static byte[] p256_2_with_params23 = Hex.decode("04d1ef981f81edae5e2e517b498504105b636d950cc13a9e1e31c607d8f0adc00c2152f2abae4870274f8788c08be35a7908f5547416405f24818ef4e8e3ae8dacdbdbe5e5f72245f4b5c28ee1ae21442868aef592d404ca4307e077a693c6410815c7985b0678a42cbd84"); + + static byte[] p521_1_pub = Base64.decode("MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBLNtLEbQBq2YGA2Q+eCwulIDggr1dCj8CqY/Yj/HuzicYGmVkNpr/gRfZHX4FRrh9HsGrS7tW+UA1pCQmn3p3aeEADiaXGIybDBsnuU20xntomQG/d/OHDkEaPee9nFNbi9Oha7BHTA/x2yyLMhQHeUlMgjz0DQqRxrwGJmvI85eNZpM="); + static byte[] p521_1_pri = Base64.decode("MIH3AgEAMBAGByqGSM49AgEGBSuBBAAjBIHfMIHcAgEBBEIA1QAfnUvK1fAaENAArV0YnNGvnu2H1vGHNgsG3QV7p16gjd6UOgmGCZVM9SLlRH3fi9K1Xreyl4sQH3NeY+ZnInqgBwYFK4EEACOhgYkDgYYABAEs20sRtAGrZgYDZD54LC6UgOCCvV0KPwKpj9iP8e7OJxgaZWQ2mv+BF9kdfgVGuH0ewatLu1b5QDWkJCafendp4QAOJpcYjJsMGye5TbTGe2iZAb9384cOQRo9572cU1uL06FrsEdMD/HbLIsyFAd5SUyCPPQNCpHGvAYma8jzl41mkw=="); + static byte[] p521_1_eph = Hex.decode("d25c44819712555824360eef5afda947373ec462c2a0fd5bebbe0b0e4e40b4ebf483bc5b5eeb84542fc226dc2e5307ceec59fe7c557522b77589210b434295aabfc7"); + + static byte[] p521_1_no_params = Hex.decode("04009d332615586356df7caaabb4ddd1c7902f35595c0a708acf570659061313e44a5d581b0e646e0c623b737327ed2a6fb7391ed76ee5fdceb9480f8b414e37be786301f7f6162afa812f45c15aba6887c6559b4c379c98fddd95af179c457a6a0e414cd921fd3b5736068f2ce6bec2fbf28e0e230784a38bf6f7b6e42672e52959db1b7ee2102423e67f5ee7f46e87a26370b7734548f3baee7729c5ff1c1638b93f068628320c7c56b7a68fb86d"); + + static byte[] p521_1_with_params11 = Hex.decode("04009d332615586356df7caaabb4ddd1c7902f35595c0a708acf570659061313e44a5d581b0e646e0c623b737327ed2a6fb7391ed76ee5fdceb9480f8b414e37be786301f7f6162afa812f45c15aba6887c6559b4c379c98fddd95af179c457a6a0e414cd921fd3b5736068f2ce6bec2fbf28e0e230784a38bf6f7b6e42672e52959db1b7e49225382108fb5bdd6b4ad57fee63c2819d10c1bc7517bd0a5070c123d4e24d66f9f0bf21ec105629cb2"); + static byte[] p521_1_with_params12 = Hex.decode("04009d332615586356df7caaabb4ddd1c7902f35595c0a708acf570659061313e44a5d581b0e646e0c623b737327ed2a6fb7391ed76ee5fdceb9480f8b414e37be786301f7f6162afa812f45c15aba6887c6559b4c379c98fddd95af179c457a6a0e414cd921fd3b5736068f2ce6bec2fbf28e0e230784a38bf6f7b6e42672e52959db1b7e49225382108fb5bdd6b4ad57fee63c2819d10c1bc751a51c6c78d905b0b7702d2edf89d0cf30f0d21c93"); + static byte[] p521_1_with_params13 = Hex.decode("04009d332615586356df7caaabb4ddd1c7902f35595c0a708acf570659061313e44a5d581b0e646e0c623b737327ed2a6fb7391ed76ee5fdceb9480f8b414e37be786301f7f6162afa812f45c15aba6887c6559b4c379c98fddd95af179c457a6a0e414cd921fd3b5736068f2ce6bec2fbf28e0e230784a38bf6f7b6e42672e52959db1b7e49225382108fb5bdd6b4ad57fee63c2819d10c1bc7513a9fb7f21f78ef3467b54153b8e2998de5f46724"); + static byte[] p521_1_with_params21 = Hex.decode("04009d332615586356df7caaabb4ddd1c7902f35595c0a708acf570659061313e44a5d581b0e646e0c623b737327ed2a6fb7391ed76ee5fdceb9480f8b414e37be786301f7f6162afa812f45c15aba6887c6559b4c379c98fddd95af179c457a6a0e414cd921fd3b5736068f2ce6bec2fbf28e0e230784a38bf6f7b6e42672e52959db1b7e1e0e7ac9ade1646519a365ef578f0bbe3556a818c83a0ce6226db7a3f3964e7a18955f9150fb030dcb09"); + static byte[] p521_1_with_params22 = Hex.decode("04009d332615586356df7caaabb4ddd1c7902f35595c0a708acf570659061313e44a5d581b0e646e0c623b737327ed2a6fb7391ed76ee5fdceb9480f8b414e37be786301f7f6162afa812f45c15aba6887c6559b4c379c98fddd95af179c457a6a0e414cd921fd3b5736068f2ce6bec2fbf28e0e230784a38bf6f7b6e42672e52959db1b7e1e0e7ac9ade1646519a365ef578f0bbe3556a818c83ade9180b72d7d2e9218395ea7d83c00cf03c6b39d"); + static byte[] p521_1_with_params23 = Hex.decode("04009d332615586356df7caaabb4ddd1c7902f35595c0a708acf570659061313e44a5d581b0e646e0c623b737327ed2a6fb7391ed76ee5fdceb9480f8b414e37be786301f7f6162afa812f45c15aba6887c6559b4c379c98fddd95af179c457a6a0e414cd921fd3b5736068f2ce6bec2fbf28e0e230784a38bf6f7b6e42672e52959db1b7e1e0e7ac9ade1646519a365ef578f0bbe3556a818c83a605f2a4ef486f9215e546d618eb7624443ee1a7d"); + + static byte[] p521_2_pub = Base64.decode("MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQACXj0hmGm68PQxI12y5sKMn4mN6mIMOEn1l6PBOeGtk8QuJHt1AS3X5DcPorJTxhuhdcdDHA3if1utfAw9hl7tCIBQELwgw2Vo3gYkdbG7Hj6O9kf8WpqIEJ3UsC4S0hITMY1knO41BaO4UdO+nUiN+PZPRRSqFwm3C5v3fffLSGbpIQ="); + static byte[] p521_2_pri = Base64.decode("MIH3AgEAMBAGByqGSM49AgEGBSuBBAAjBIHfMIHcAgEBBEIB6s6VR/8WgiOvSjHT/n5Y91wESyI9e4GABUNWMXMPhNspwb9++7fyanbeliHNz9K61SjDG7rLXs90K1iRvHPJwmygBwYFK4EEACOhgYkDgYYABAAJePSGYabrw9DEjXbLmwoyfiY3qYgw4SfWXo8E54a2TxC4ke3UBLdfkNw+islPGG6F1x0McDeJ/W618DD2GXu0IgFAQvCDDZWjeBiR1sbsePo72R/xamogQndSwLhLSEhMxjWSc7jUFo7hR076dSI349k9FFKoXCbcLm/d998tIZukhA=="); + static byte[] p521_2_eph = Hex.decode("c9ee178c7f200a51a0053931c697001205f8923fd04b538c3fd5df2be43a6f304581f57138906bba0be631f7e0fd2804d00b5cb053559f080c13c913358f6e1fcc6d"); + + static byte[] p521_2_no_params = Hex.decode("0400ddfd813bbba275fec8896a140036c9ee2fee49793cadc0a4d976d1b7827357b846e1e5aa9326b449c6ab268e3b2d145894cc02d33d849c108db1f696db6e867c2c01d129fcc80937aeb2beff58ebc9e9777ba864546aed12d86c5e6a89767cf5321ec323283032278698642c238b3fda4a5d5e9ea2f0b91ef7d979fda529687931ace65922842dba0351c1cebde53ee4ba9afadbd3a03a88507046f62c7383f96142c09df58be5b0a661893d41"); + + static byte[] p521_2_with_params11 = Hex.decode("0400ddfd813bbba275fec8896a140036c9ee2fee49793cadc0a4d976d1b7827357b846e1e5aa9326b449c6ab268e3b2d145894cc02d33d849c108db1f696db6e867c2c01d129fcc80937aeb2beff58ebc9e9777ba864546aed12d86c5e6a89767cf5321ec323283032278698642c238b3fda4a5d5e9ea2f0b91ef7d979fda529687931ace674ccf084d69e0bc9a728548d887a162ddf53a0002ed034b51a5406b831ce9ea713be7df54934b77dcc84"); + static byte[] p521_2_with_params12 = Hex.decode("0400ddfd813bbba275fec8896a140036c9ee2fee49793cadc0a4d976d1b7827357b846e1e5aa9326b449c6ab268e3b2d145894cc02d33d849c108db1f696db6e867c2c01d129fcc80937aeb2beff58ebc9e9777ba864546aed12d86c5e6a89767cf5321ec323283032278698642c238b3fda4a5d5e9ea2f0b91ef7d979fda529687931ace674ccf084d69e0bc9a728548d887a162ddf53a0002ed0767534663184e99c0958451ea417729e29e02a60"); + static byte[] p521_2_with_params13 = Hex.decode("0400ddfd813bbba275fec8896a140036c9ee2fee49793cadc0a4d976d1b7827357b846e1e5aa9326b449c6ab268e3b2d145894cc02d33d849c108db1f696db6e867c2c01d129fcc80937aeb2beff58ebc9e9777ba864546aed12d86c5e6a89767cf5321ec323283032278698642c238b3fda4a5d5e9ea2f0b91ef7d979fda529687931ace674ccf084d69e0bc9a728548d887a162ddf53a0002ed0517e1040bf7d57f2c8f9c3ce69e5a05a9a292700"); + static byte[] p521_2_with_params21 = Hex.decode("0400ddfd813bbba275fec8896a140036c9ee2fee49793cadc0a4d976d1b7827357b846e1e5aa9326b449c6ab268e3b2d145894cc02d33d849c108db1f696db6e867c2c01d129fcc80937aeb2beff58ebc9e9777ba864546aed12d86c5e6a89767cf5321ec323283032278698642c238b3fda4a5d5e9ea2f0b91ef7d979fda529687931ace68a9e28bb5d49c07d5f5f59a27311619103aea50e48ddc66e0f20686c8d0c329db510facc1c014f800920"); + static byte[] p521_2_with_params22 = Hex.decode("0400ddfd813bbba275fec8896a140036c9ee2fee49793cadc0a4d976d1b7827357b846e1e5aa9326b449c6ab268e3b2d145894cc02d33d849c108db1f696db6e867c2c01d129fcc80937aeb2beff58ebc9e9777ba864546aed12d86c5e6a89767cf5321ec323283032278698642c238b3fda4a5d5e9ea2f0b91ef7d979fda529687931ace68a9e28bb5d49c07d5f5f59a27311619103aea50e48ddbc970a66c80886782713e25ece75a23dec995121"); + static byte[] p521_2_with_params23 = Hex.decode("0400ddfd813bbba275fec8896a140036c9ee2fee49793cadc0a4d976d1b7827357b846e1e5aa9326b449c6ab268e3b2d145894cc02d33d849c108db1f696db6e867c2c01d129fcc80937aeb2beff58ebc9e9777ba864546aed12d86c5e6a89767cf5321ec323283032278698642c238b3fda4a5d5e9ea2f0b91ef7d979fda529687931ace68a9e28bb5d49c07d5f5f59a27311619103aea50e48dd7c3d8a51a95f88813d04a0c681355f6e86bb44e5"); + + static byte[] old_p256_1_no_params = Hex.decode("04bc8f4da6ea423c0698744b927ec71b67126d97ef9dd804c141a0dfcb466cc1c7df25539375ddb3ae7d08cc46fa2a78434c9f6123b108e39a5fe614729c3d8ae43dfbe605c746ba0c546c0c25ba5a00304587fbed07a3ebe44837c43b86025cc08e333b1631a8227b8d49"); + + static byte[] old_p256_1_with_params11 = Hex.decode("04bc8f4da6ea423c0698744b927ec71b67126d97ef9dd804c141a0dfcb466cc1c7df25539375ddb3ae7d08cc46fa2a78434c9f6123b108e39a5fe614729c3d8ae4a57474b50621e97254c1f3b32635f694feb57e873a332ce23d272262d47a528057eb5b0b11631707b28f"); + static byte[] old_p256_1_with_params12 = Hex.decode("04bc8f4da6ea423c0698744b927ec71b67126d97ef9dd804c141a0dfcb466cc1c7df25539375ddb3ae7d08cc46fa2a78434c9f6123b108e39a5fe614729c3d8ae4a57474b50621e97254c1f3b32635f694feb57e873a3331e3e514868c771270632c9a9b852f2060d415da"); + static byte[] old_p256_1_with_params13 = Hex.decode("04bc8f4da6ea423c0698744b927ec71b67126d97ef9dd804c141a0dfcb466cc1c7df25539375ddb3ae7d08cc46fa2a78434c9f6123b108e39a5fe614729c3d8ae4a57474b50621e97254c1f3b32635f694feb57e873a3353e10e6260e937bcdd244201a0de1d3941439076"); + static byte[] old_p256_1_with_params21 = Hex.decode("04bc8f4da6ea423c0698744b927ec71b67126d97ef9dd804c141a0dfcb466cc1c7df25539375ddb3ae7d08cc46fa2a78434c9f6123b108e39a5fe614729c3d8ae4da4d2bb723cd2ca918d1835dc2716a767085facd5df4912a36ccc3c49de7d3d6b1fb312aeacf2d0b30e7"); + static byte[] old_p256_1_with_params22 = Hex.decode("04bc8f4da6ea423c0698744b927ec71b67126d97ef9dd804c141a0dfcb466cc1c7df25539375ddb3ae7d08cc46fa2a78434c9f6123b108e39a5fe614729c3d8ae4da4d2bb723cd2ca918d1835dc2716a767085facd5df4171c056b90a3badd0bb689403ab214546b5f6411"); + static byte[] old_p256_1_with_params23 = Hex.decode("04bc8f4da6ea423c0698744b927ec71b67126d97ef9dd804c141a0dfcb466cc1c7df25539375ddb3ae7d08cc46fa2a78434c9f6123b108e39a5fe614729c3d8ae4da4d2bb723cd2ca918d1835dc2716a767085facd5df4ad1a6a561fe0582495cd14ac4494e4610ecddef1"); + + ECIESVectorTest() + { + } + + public String getName() + { + return "ECIESVectorTest"; + } + + public void performTest() + throws Exception + { + KeyFactory ecFact = KeyFactory.getInstance("EC", "BC"); + + KeyPair keyPair = new KeyPair(ecFact.generatePublic(new X509EncodedKeySpec(p256_1_pub)), ecFact.generatePrivate(new PKCS8EncodedKeySpec(p256_1_pri))); + + doTestNoParams("ECIES with P-256 None", keyPair, "ECIES", p256_1_eph, p256_1_no_params); + doTestWithParams("ECIES with P-256 KP1 P11", keyPair, "ECIES", p256_1_eph, new IESParameterSpec(derivation1, encoding1, 128), p256_1_with_params11); + doTestWithParams("ECIES with P-256 KP1 P11", keyPair, "ECIES", p256_1_eph, new IESParameterSpec(derivation1, encoding2, 128), p256_1_with_params12); + doTestWithParams("ECIES with P-256 KP1 P11", keyPair, "ECIES", p256_1_eph, new IESParameterSpec(derivation1, encoding3, 128), p256_1_with_params13); + doTestWithParams("ECIES with P-256 KP1 P11", keyPair, "ECIES", p256_1_eph, new IESParameterSpec(derivation2, encoding1, 128), p256_1_with_params21); + doTestWithParams("ECIES with P-256 KP1 P11", keyPair, "ECIES", p256_1_eph, new IESParameterSpec(derivation2, encoding2, 128), p256_1_with_params22); + doTestWithParams("ECIES with P-256 KP1 P11", keyPair, "ECIES", p256_1_eph, new IESParameterSpec(derivation2, encoding3, 128), p256_1_with_params23); + + doTestNoParams("ECIES with P-256 None", keyPair, "OldECIES", p256_1_eph, old_p256_1_no_params); + doTestWithParams("ECIES with P-256 KP1 P11", keyPair, "OldECIES", p256_1_eph, new IESParameterSpec(derivation1, encoding1, 128), old_p256_1_with_params11); + doTestWithParams("ECIES with P-256 KP1 P11", keyPair, "OldECIES", p256_1_eph, new IESParameterSpec(derivation1, encoding2, 128), old_p256_1_with_params12); + doTestWithParams("ECIES with P-256 KP1 P11", keyPair, "OldECIES", p256_1_eph, new IESParameterSpec(derivation1, encoding3, 128), old_p256_1_with_params13); + doTestWithParams("ECIES with P-256 KP1 P11", keyPair, "OldECIES", p256_1_eph, new IESParameterSpec(derivation2, encoding1, 128), old_p256_1_with_params21); + doTestWithParams("ECIES with P-256 KP1 P11", keyPair, "OldECIES", p256_1_eph, new IESParameterSpec(derivation2, encoding2, 128), old_p256_1_with_params22); + doTestWithParams("ECIES with P-256 KP1 P11", keyPair, "OldECIES", p256_1_eph, new IESParameterSpec(derivation2, encoding3, 128), old_p256_1_with_params23); + + keyPair = new KeyPair(ecFact.generatePublic(new X509EncodedKeySpec(p256_2_pub)), ecFact.generatePrivate(new PKCS8EncodedKeySpec(p256_2_pri))); + + doTestNoParams("ECIES with P-256 None", keyPair, "ECIES", p256_2_eph, p256_2_no_params); + doTestWithParams("ECIES with P-256 KP2 P11", keyPair, "ECIES", p256_2_eph, new IESParameterSpec(derivation1, encoding1, 128), p256_2_with_params11); + doTestWithParams("ECIES with P-256 KP2 P11", keyPair, "ECIES", p256_2_eph, new IESParameterSpec(derivation1, encoding2, 128), p256_2_with_params12); + doTestWithParams("ECIES with P-256 KP2 P11", keyPair, "ECIES", p256_2_eph, new IESParameterSpec(derivation1, encoding3, 128), p256_2_with_params13); + doTestWithParams("ECIES with P-256 KP2 P11", keyPair, "ECIES", p256_2_eph, new IESParameterSpec(derivation2, encoding1, 128), p256_2_with_params21); + doTestWithParams("ECIES with P-256 KP2 P11", keyPair, "ECIES", p256_2_eph, new IESParameterSpec(derivation2, encoding2, 128), p256_2_with_params22); + doTestWithParams("ECIES with P-256 KP2 P11", keyPair, "ECIES", p256_2_eph, new IESParameterSpec(derivation2, encoding3, 128), p256_2_with_params23); + + keyPair = new KeyPair(ecFact.generatePublic(new X509EncodedKeySpec(p521_1_pub)), ecFact.generatePrivate(new PKCS8EncodedKeySpec(p521_1_pri))); + + doTestNoParams("ECIES with P-521 None", keyPair, "ECIES", p521_1_eph, p521_1_no_params); + doTestWithParams("ECIES with P-521 KP1 P11", keyPair, "ECIES", p521_1_eph, new IESParameterSpec(derivation1, encoding1, 128), p521_1_with_params11); + doTestWithParams("ECIES with P-521 KP1 P11", keyPair, "ECIES", p521_1_eph, new IESParameterSpec(derivation1, encoding2, 128), p521_1_with_params12); + doTestWithParams("ECIES with P-521 KP1 P11", keyPair, "ECIES", p521_1_eph, new IESParameterSpec(derivation1, encoding3, 128), p521_1_with_params13); + doTestWithParams("ECIES with P-521 KP1 P11", keyPair, "ECIES", p521_1_eph, new IESParameterSpec(derivation2, encoding1, 128), p521_1_with_params21); + doTestWithParams("ECIES with P-521 KP1 P11", keyPair, "ECIES", p521_1_eph, new IESParameterSpec(derivation2, encoding2, 128), p521_1_with_params22); + doTestWithParams("ECIES with P-521 KP1 P11", keyPair, "ECIES", p521_1_eph, new IESParameterSpec(derivation2, encoding3, 128), p521_1_with_params23); + + keyPair = new KeyPair(ecFact.generatePublic(new X509EncodedKeySpec(p521_2_pub)), ecFact.generatePrivate(new PKCS8EncodedKeySpec(p521_2_pri))); + + doTestNoParams("ECIES with default", keyPair, "ECIES", p521_2_eph, p521_2_no_params); + doTestWithParams("ECIES with P-521 KP2 P11", keyPair, "ECIES", p521_2_eph, new IESParameterSpec(derivation1, encoding1, 128), p521_2_with_params11); + doTestWithParams("ECIES with P-521 KP2 P12", keyPair, "ECIES", p521_2_eph, new IESParameterSpec(derivation1, encoding2, 128), p521_2_with_params12); + doTestWithParams("ECIES with P-521 KP2 P13", keyPair, "ECIES", p521_2_eph, new IESParameterSpec(derivation1, encoding3, 128), p521_2_with_params13); + doTestWithParams("ECIES with P-521 KP2 P21", keyPair, "ECIES", p521_2_eph, new IESParameterSpec(derivation2, encoding1, 128), p521_2_with_params21); + doTestWithParams("ECIES with P-521 KP2 P21", keyPair, "ECIES", p521_2_eph, new IESParameterSpec(derivation2, encoding2, 128), p521_2_with_params22); + doTestWithParams("ECIES with P-521 KP2 P21", keyPair, "ECIES", p521_2_eph, new IESParameterSpec(derivation2, encoding3, 128), p521_2_with_params23); + } + + public void doTestNoParams( + String testname, + KeyPair keyPair, + String cipher, + byte[] ephPrivateValue, + byte[] expected) + throws Exception + { + + + byte[] out1, out2; + + // Generate static key pair + ECPublicKey Pub = (ECPublicKey)keyPair.getPublic(); + ECPrivateKey Priv = (ECPrivateKey)keyPair.getPrivate(); + + Cipher c1 = Cipher.getInstance(cipher); + Cipher c2 = Cipher.getInstance(cipher); + + // Testing with null parameters and DHAES mode off + c1.init(Cipher.ENCRYPT_MODE, Pub, new FixedSecureRandom(ephPrivateValue)); + c2.init(Cipher.DECRYPT_MODE, Priv, new FixedSecureRandom(ephPrivateValue)); + out1 = c1.doFinal(message, 0, message.length); + + if (!areEqual(out1, expected)) + { + fail(testname + " test failed encrypt with null parameters, DHAES mode false."); + } + + out2 = c2.doFinal(out1, 0, out1.length); + if (!areEqual(out2, message)) + { + fail(testname + " test failed decrypt with null parameters, DHAES mode false."); + } + } + + public void doTestWithParams( + String testname, + KeyPair keyPair, + String cipher, + byte[] ephPrivateValue, + IESParameterSpec p, + byte[] expected) + throws Exception + { + byte[] out1, out2; + + // Generate static key pair + ECPublicKey Pub = (ECPublicKey)keyPair.getPublic(); + ECPrivateKey Priv = (ECPrivateKey)keyPair.getPrivate(); + + Cipher c1 = Cipher.getInstance(cipher); + Cipher c2 = Cipher.getInstance(cipher); + + // Testing with given parameters and DHAES mode off + c1.init(Cipher.ENCRYPT_MODE, Pub, p, new FixedSecureRandom(ephPrivateValue)); + c2.init(Cipher.DECRYPT_MODE, Priv, p, new FixedSecureRandom(ephPrivateValue)); + out1 = c1.doFinal(message, 0, message.length); + + if (!areEqual(out1, expected)) + { + fail(testname + " test failed encrypt with non-null parameters, DHAES mode false."); + } + out2 = c2.doFinal(out1, 0, out1.length); + if (!areEqual(out2, message)) + { + fail(testname + " test failed decrypt with non-null parameters, DHAES mode false."); + } + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new ECIESVectorTest()); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/KeccakTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/KeccakTest.java new file mode 100644 index 00000000..ed1659b3 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/KeccakTest.java @@ -0,0 +1,136 @@ +package org.bouncycastle.jce.provider.test; + +import java.security.MessageDigest; +import java.security.Security; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTest; + +public class KeccakTest + extends SimpleTest +{ + final static String provider = "BC"; + + static private byte[] nullMsg = new byte[0]; + + static private String[][] nullVectors = + { + { "KECCAK-224", "f71837502ba8e10837bdd8d365adb85591895602fc552b48b7390abd" }, + { "KECCAK-256", "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" }, + { "KECCAK-384", "2c23146a63a29acf99e73b88f8c24eaa7dc60aa771780ccc006afbfa8fe2479b2dd2b21362337441ac12b515911957ff" }, + { "KECCAK-512", "0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e" }, + }; + + static private byte[] shortMsg = Hex.decode("54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67"); + + static private String[][] shortVectors = + { + { "KECCAK-224", "310aee6b30c47350576ac2873fa89fd190cdc488442f3ef654cf23fe" }, + { "KECCAK-256", "4d741b6f1eb29cb2a9b9911c82f56fa8d73b04959d3d9d222895df6c0b28aa15" }, + { "KECCAK-384", "283990fa9d5fb731d786c5bbee94ea4db4910f18c62c03d173fc0a5e494422e8a0b3da7574dae7fa0baf005e504063b3" }, + { "KECCAK-512", "d135bb84d0439dbac432247ee573a23ea7d3c9deb2a968eb31d47c4fb45f1ef4422d6c531b5b9bd6f449ebcc449ea94d0a8f05f62130fda612da53c79659f609" }, + }; + + public String getName() + { + return "KECCAK"; + } + + void test(String algorithm, byte[] message, String expected) + throws Exception + { + MessageDigest digest = MessageDigest.getInstance(algorithm, provider); + + byte[] result = digest.digest(message); + byte[] result2 = digest.digest(message); + + // test zero results valid + if (!MessageDigest.isEqual(result, Hex.decode(expected))) + { + fail("null result not equal for " + algorithm); + } + + // test one digest the same message with the same instance + if (!MessageDigest.isEqual(result, result2)) + { + fail("Result object 1 not equal"); + } + + if (!MessageDigest.isEqual(result, Hex.decode(expected))) + { + fail("Result object 1 not equal"); + } + + // test two, single byte updates + for (int i = 0; i < message.length; i++) + { + digest.update(message[i]); + } + result2 = digest.digest(); + + if (!MessageDigest.isEqual(result, result2)) + { + fail("Result object 2 not equal"); + } + + // test three, two half updates + digest.update(message, 0, message.length/2); + digest.update(message, message.length/2, message.length-message.length/2); + result2 = digest.digest(); + + if (!MessageDigest.isEqual(result, result2)) + { + fail("Result object 3 not equal"); + } + + // test four, clone test + digest.update(message, 0, message.length/2); + MessageDigest d = (MessageDigest)digest.clone(); + digest.update(message, message.length/2, message.length-message.length/2); + result2 = digest.digest(); + + if (!MessageDigest.isEqual(result, result2)) + { + fail("Result object 4(a) not equal"); + } + + d.update(message, message.length/2, message.length-message.length/2); + result2 = d.digest(); + + if (!MessageDigest.isEqual(result, result2)) + { + fail("Result object 4(b) not equal"); + } + + // test five, check reset() method + digest.update(message, 0, message.length/2); + digest.reset(); + digest.update(message, 0, message.length/2); + digest.update(message, message.length/2, message.length-message.length/2); + result2 = digest.digest(); + + if (!MessageDigest.isEqual(result, result2)) + { + fail("Result object 5 not equal"); + } + + } + + public void performTest() + throws Exception + { + for (int i = 0; i != nullVectors.length; i++) + { + test(nullVectors[i][0], nullMsg, nullVectors[i][1]); + test(shortVectors[i][0], shortMsg, shortVectors[i][1]); + } + } + + public static void main(String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new KeccakTest()); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/SM4Test.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/SM4Test.java new file mode 100644 index 00000000..428a9cfe --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/SM4Test.java @@ -0,0 +1,153 @@ +package org.bouncycastle.jce.provider.test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.security.Key; +import java.security.Security; + +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.CipherOutputStream; +import javax.crypto.spec.SecretKeySpec; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.encoders.Hex; + +/** + * basic test class for SM4 + */ +public class SM4Test + extends BaseBlockCipherTest +{ + static String[] cipherTests = + { + "128", + "0123456789abcdeffedcba9876543210", + "0123456789abcdeffedcba9876543210", + "681edf34d206965e86b3e94f536e4246" + }; + + public SM4Test() + { + super("SM4"); + } + + public void test( + int strength, + byte[] keyBytes, + byte[] input, + byte[] output) + throws Exception + { + Key key; + Cipher in, out; + CipherInputStream cIn; + CipherOutputStream cOut; + ByteArrayInputStream bIn; + ByteArrayOutputStream bOut; + + key = new SecretKeySpec(keyBytes, "SM4"); + + in = Cipher.getInstance("SM4/ECB/NoPadding", "BC"); + out = Cipher.getInstance("SM4/ECB/NoPadding", "BC"); + + try + { + out.init(Cipher.ENCRYPT_MODE, key); + } + catch (Exception e) + { + fail("SM4 failed initialisation - " + e.toString(), e); + } + + try + { + in.init(Cipher.DECRYPT_MODE, key); + } + catch (Exception e) + { + fail("SM4 failed initialisation - " + e.toString(), e); + } + + // + // encryption pass + // + bOut = new ByteArrayOutputStream(); + + cOut = new CipherOutputStream(bOut, out); + + try + { + for (int i = 0; i != input.length / 2; i++) + { + cOut.write(input[i]); + } + cOut.write(input, input.length / 2, input.length - input.length / 2); + cOut.close(); + } + catch (IOException e) + { + fail("SM4 failed encryption - " + e.toString(), e); + } + + byte[] bytes; + + bytes = bOut.toByteArray(); + + if (!areEqual(bytes, output)) + { + fail("SM4 failed encryption - expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(bytes))); + } + + // + // decryption pass + // + bIn = new ByteArrayInputStream(bytes); + + cIn = new CipherInputStream(bIn, in); + + try + { + DataInputStream dIn = new DataInputStream(cIn); + + bytes = new byte[input.length]; + + for (int i = 0; i != input.length / 2; i++) + { + bytes[i] = (byte)dIn.read(); + } + dIn.readFully(bytes, input.length / 2, bytes.length - input.length / 2); + } + catch (Exception e) + { + fail("SM4 failed encryption - " + e.toString(), e); + } + + if (!areEqual(bytes, input)) + { + fail("SM4 failed decryption - expected " + new String(Hex.encode(input)) + " got " + new String(Hex.encode(bytes))); + } + } + + public void performTest() + throws Exception + { + for (int i = 0; i != cipherTests.length; i += 4) + { + test(Integer.parseInt(cipherTests[i]), + Hex.decode(cipherTests[i + 1]), + Hex.decode(cipherTests[i + 2]), + Hex.decode(cipherTests[i + 3])); + } + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new SM4Test()); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/nist/AllTests.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/nist/AllTests.java new file mode 100644 index 00000000..434631a0 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/nist/AllTests.java @@ -0,0 +1,47 @@ +package org.bouncycastle.jce.provider.test.nist; + +import java.security.Security; + +import junit.extensions.TestSetup; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +public class AllTests + extends TestCase +{ + public static void main (String[] args) + { + junit.textui.TestRunner.run(suite()); + } + + public static Test suite() + { + TestSuite suite = new TestSuite("CertPath Tests"); + + suite.addTestSuite(NistCertPathTest.class); + suite.addTestSuite(NistCertPathReviewerTest.class); + + return new BCTestSetup(suite); + } + + static class BCTestSetup + extends TestSetup + { + public BCTestSetup(Test test) + { + super(test); + } + + protected void setUp() + { + Security.addProvider(new BouncyCastleProvider()); + } + + protected void tearDown() + { + Security.removeProvider("BC"); + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/rsa3/AllTests.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/rsa3/AllTests.java new file mode 100644 index 00000000..c29e5ecf --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/rsa3/AllTests.java @@ -0,0 +1,46 @@ +package org.bouncycastle.jce.provider.test.rsa3; + +import java.security.Security; + +import junit.extensions.TestSetup; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +public class AllTests + extends TestCase +{ + public static void main (String[] args) + { + junit.textui.TestRunner.run(suite()); + } + + public static Test suite() + { + TestSuite suite = new TestSuite("Forgery Tests"); + + suite.addTestSuite(RSA3CertTest.class); + + return new BCTestSetup(suite); + } + + static class BCTestSetup + extends TestSetup + { + public BCTestSetup(Test test) + { + super(test); + } + + protected void setUp() + { + Security.addProvider(new BouncyCastleProvider()); + } + + protected void tearDown() + { + Security.removeProvider("BC"); + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/Primes.java b/bcprov/src/main/java/org/bouncycastle/math/Primes.java new file mode 100644 index 00000000..dc4c017c --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/Primes.java @@ -0,0 +1,674 @@ +package org.bouncycastle.math; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.BigIntegers; + +/** + * Utility methods for generating primes and testing for primality. + */ +public abstract class Primes +{ + public static final int SMALL_FACTOR_LIMIT = 211; + + private static final BigInteger ONE = BigInteger.valueOf(1); + private static final BigInteger TWO = BigInteger.valueOf(2); + private static final BigInteger THREE = BigInteger.valueOf(3); + + /** + * Used to return the output from the + * {@linkplain Primes#enhancedMRProbablePrimeTest(BigInteger, SecureRandom, int) Enhanced + * Miller-Rabin Probabilistic Primality Test} + */ + public static class MROutput + { + private static MROutput probablyPrime() + { + return new MROutput(false, null); + } + + private static MROutput provablyCompositeWithFactor(BigInteger factor) + { + return new MROutput(true, factor); + } + + private static MROutput provablyCompositeNotPrimePower() + { + return new MROutput(true, null); + } + + private boolean provablyComposite; + private BigInteger factor; + + private MROutput(boolean provablyComposite, BigInteger factor) + { + this.provablyComposite = provablyComposite; + this.factor = factor; + } + + public BigInteger getFactor() + { + return factor; + } + + public boolean isProvablyComposite() + { + return provablyComposite; + } + + public boolean isNotPrimePower() + { + return provablyComposite && factor == null; + } + } + + /** + * Used to return the output from the + * {@linkplain Primes#generateSTRandomPrime(Digest, int, byte[]) Shawe-Taylor Random_Prime + * Routine} + */ + public static class STOutput + { + private BigInteger prime; + private byte[] primeSeed; + private int primeGenCounter; + + private STOutput(BigInteger prime, byte[] primeSeed, int primeGenCounter) + { + this.prime = prime; + this.primeSeed = primeSeed; + this.primeGenCounter = primeGenCounter; + } + + public BigInteger getPrime() + { + return prime; + } + + public byte[] getPrimeSeed() + { + return primeSeed; + } + + public int getPrimeGenCounter() + { + return primeGenCounter; + } + } + + /** + * FIPS 186-4 C.6 Shawe-Taylor Random_Prime Routine + * + * Construct a provable prime number using a hash function. + * + * @param hash + * the {@link Digest} instance to use (as "Hash()"). Cannot be null. + * @param length + * the length (in bits) of the prime to be generated. Must be at least 2. + * @param inputSeed + * the seed to be used for the generation of the requested prime. Cannot be null or + * empty. + * @return an {@link STOutput} instance containing the requested prime. + */ + public static STOutput generateSTRandomPrime(Digest hash, int length, byte[] inputSeed) + { + if (hash == null) + { + throw new IllegalArgumentException("'hash' cannot be null"); + } + if (length < 2) + { + throw new IllegalArgumentException("'length' must be >= 2"); + } + if (inputSeed == null || inputSeed.length == 0) + { + throw new IllegalArgumentException("'inputSeed' cannot be null or empty"); + } + + return implSTRandomPrime(hash, length, Arrays.clone(inputSeed)); + } + + /** + * FIPS 186-4 C.3.2 Enhanced Miller-Rabin Probabilistic Primality Test + * + * Run several iterations of the Miller-Rabin algorithm with randomly-chosen bases. This is an + * alternative to {@link #isMRProbablePrime(BigInteger, SecureRandom, int)} that provides more + * information about a composite candidate, which may be useful when generating or validating + * RSA moduli. + * + * @param candidate + * the {@link BigInteger} instance to test for primality. + * @param random + * the source of randomness to use to choose bases. + * @param iterations + * the number of randomly-chosen bases to perform the test for. + * @return an {@link MROutput} instance that can be further queried for details. + */ + public static MROutput enhancedMRProbablePrimeTest(BigInteger candidate, SecureRandom random, int iterations) + { + checkCandidate(candidate, "candidate"); + + if (random == null) + { + throw new IllegalArgumentException("'random' cannot be null"); + } + if (iterations < 1) + { + throw new IllegalArgumentException("'iterations' must be > 0"); + } + + if (candidate.bitLength() == 2) + { + return MROutput.probablyPrime(); + } + if (!candidate.testBit(0)) + { + return MROutput.provablyCompositeWithFactor(TWO); + } + + BigInteger w = candidate; + BigInteger wSubOne = candidate.subtract(ONE); + BigInteger wSubTwo = candidate.subtract(TWO); + + int a = wSubOne.getLowestSetBit(); + BigInteger m = wSubOne.shiftRight(a); + + for (int i = 0; i < iterations; ++i) + { + BigInteger b = BigIntegers.createRandomInRange(TWO, wSubTwo, random); + BigInteger g = b.gcd(w); + + if (g.compareTo(ONE) > 0) + { + return MROutput.provablyCompositeWithFactor(g); + } + + BigInteger z = b.modPow(m, w); + + if (z.equals(ONE) || z.equals(wSubOne)) + { + continue; + } + + boolean primeToBase = false; + + BigInteger x = z; + for (int j = 1; j < a; ++j) + { + z = z.modPow(TWO, w); + + if (z.equals(wSubOne)) + { + primeToBase = true; + break; + } + + if (z.equals(ONE)) + { + break; + } + + x = z; + } + + if (!primeToBase) + { + if (!z.equals(ONE)) + { + x = z; + z = z.modPow(TWO, w); + + if (!z.equals(ONE)) + { + x = z; + } + } + + g = x.subtract(ONE).gcd(w); + + if (g.compareTo(ONE) > 0) + { + return MROutput.provablyCompositeWithFactor(g); + } + + return MROutput.provablyCompositeNotPrimePower(); + } + } + + return MROutput.probablyPrime(); + } + + /** + * A fast check for small divisors, up to some implementation-specific limit. + * + * @param candidate + * the {@link BigInteger} instance to test for division by small factors. + * + * @return <code>true</code> if the candidate is found to have any small factors, + * <code>false</code> otherwise. + */ + public static boolean hasAnySmallFactors(BigInteger candidate) + { + checkCandidate(candidate, "candidate"); + + return implHasAnySmallFactors(candidate); + } + + /** + * FIPS 186-4 C.3.1 Miller-Rabin Probabilistic Primality Test + * + * Run several iterations of the Miller-Rabin algorithm with randomly-chosen bases. + * + * @param candidate + * the {@link BigInteger} instance to test for primality. + * @param random + * the source of randomness to use to choose bases. + * @param iterations + * the number of randomly-chosen bases to perform the test for. + * @return <code>false</code> if any witness to compositeness is found amongst the chosen bases + * (so <code>candidate</code> is definitely NOT prime), or else <code>true</code> + * (indicating primality with some probability dependent on the number of iterations + * that were performed). + */ + public static boolean isMRProbablePrime(BigInteger candidate, SecureRandom random, int iterations) + { + checkCandidate(candidate, "candidate"); + + if (random == null) + { + throw new IllegalArgumentException("'random' cannot be null"); + } + if (iterations < 1) + { + throw new IllegalArgumentException("'iterations' must be > 0"); + } + + if (candidate.bitLength() == 2) + { + return true; + } + if (!candidate.testBit(0)) + { + return false; + } + + BigInteger w = candidate; + BigInteger wSubOne = candidate.subtract(ONE); + BigInteger wSubTwo = candidate.subtract(TWO); + + int a = wSubOne.getLowestSetBit(); + BigInteger m = wSubOne.shiftRight(a); + + for (int i = 0; i < iterations; ++i) + { + BigInteger b = BigIntegers.createRandomInRange(TWO, wSubTwo, random); + + if (!implMRProbablePrimeToBase(w, wSubOne, m, a, b)) + { + return false; + } + } + + return true; + } + + /** + * FIPS 186-4 C.3.1 Miller-Rabin Probabilistic Primality Test (to a fixed base). + * + * Run a single iteration of the Miller-Rabin algorithm against the specified base. + * + * @param candidate + * the {@link BigInteger} instance to test for primality. + * @param base + * the base value to use for this iteration. + * @return <code>false</code> if the specified base is a witness to compositeness (so + * <code>candidate</code> is definitely NOT prime), or else <code>true</code>. + */ + public static boolean isMRProbablePrimeToBase(BigInteger candidate, BigInteger base) + { + checkCandidate(candidate, "candidate"); + checkCandidate(base, "base"); + + if (base.compareTo(candidate.subtract(ONE)) >= 0) + { + throw new IllegalArgumentException("'base' must be < ('candidate' - 1)"); + } + + if (candidate.bitLength() == 2) + { + return true; + } + + BigInteger w = candidate; + BigInteger wSubOne = candidate.subtract(ONE); + + int a = wSubOne.getLowestSetBit(); + BigInteger m = wSubOne.shiftRight(a); + + return implMRProbablePrimeToBase(w, wSubOne, m, a, base); + } + + private static void checkCandidate(BigInteger n, String name) + { + if (n == null || n.signum() < 1 || n.bitLength() < 2) + { + throw new IllegalArgumentException("'" + name + "' must be non-null and >= 2"); + } + } + + private static boolean implHasAnySmallFactors(BigInteger x) + { + /* + * Bundle trial divisors into ~32-bit moduli then use fast tests on the ~32-bit remainders. + */ + int m = 2 * 3 * 5 * 7 * 11 * 13 * 17 * 19 * 23; + int r = x.mod(BigInteger.valueOf(m)).intValue(); + if ((r % 2) == 0 || (r % 3) == 0 || (r % 5) == 0 || (r % 7) == 0 || (r % 11) == 0 || (r % 13) == 0 + || (r % 17) == 0 || (r % 19) == 0 || (r % 23) == 0) + { + return true; + } + + m = 29 * 31 * 37 * 41 * 43; + r = x.mod(BigInteger.valueOf(m)).intValue(); + if ((r % 29) == 0 || (r % 31) == 0 || (r % 37) == 0 || (r % 41) == 0 || (r % 43) == 0) + { + return true; + } + + m = 47 * 53 * 59 * 61 * 67; + r = x.mod(BigInteger.valueOf(m)).intValue(); + if ((r % 47) == 0 || (r % 53) == 0 || (r % 59) == 0 || (r % 61) == 0 || (r % 67) == 0) + { + return true; + } + + m = 71 * 73 * 79 * 83; + r = x.mod(BigInteger.valueOf(m)).intValue(); + if ((r % 71) == 0 || (r % 73) == 0 || (r % 79) == 0 || (r % 83) == 0) + { + return true; + } + + m = 89 * 97 * 101 * 103; + r = x.mod(BigInteger.valueOf(m)).intValue(); + if ((r % 89) == 0 || (r % 97) == 0 || (r % 101) == 0 || (r % 103) == 0) + { + return true; + } + + m = 107 * 109 * 113 * 127; + r = x.mod(BigInteger.valueOf(m)).intValue(); + if ((r % 107) == 0 || (r % 109) == 0 || (r % 113) == 0 || (r % 127) == 0) + { + return true; + } + + m = 131 * 137 * 139 * 149; + r = x.mod(BigInteger.valueOf(m)).intValue(); + if ((r % 131) == 0 || (r % 137) == 0 || (r % 139) == 0 || (r % 149) == 0) + { + return true; + } + + m = 151 * 157 * 163 * 167; + r = x.mod(BigInteger.valueOf(m)).intValue(); + if ((r % 151) == 0 || (r % 157) == 0 || (r % 163) == 0 || (r % 167) == 0) + { + return true; + } + + m = 173 * 179 * 181 * 191; + r = x.mod(BigInteger.valueOf(m)).intValue(); + if ((r % 173) == 0 || (r % 179) == 0 || (r % 181) == 0 || (r % 191) == 0) + { + return true; + } + + m = 193 * 197 * 199 * 211; + r = x.mod(BigInteger.valueOf(m)).intValue(); + if ((r % 193) == 0 || (r % 197) == 0 || (r % 199) == 0 || (r % 211) == 0) + { + return true; + } + + /* + * NOTE: Unit tests depend on SMALL_FACTOR_LIMIT matching the + * highest small factor tested here. + */ + return false; + } + + private static boolean implMRProbablePrimeToBase(BigInteger w, BigInteger wSubOne, BigInteger m, int a, BigInteger b) + { + BigInteger z = b.modPow(m, w); + + if (z.equals(ONE) || z.equals(wSubOne)) + { + return true; + } + + boolean result = false; + + for (int j = 1; j < a; ++j) + { + z = z.modPow(TWO, w); + + if (z.equals(wSubOne)) + { + result = true; + break; + } + + if (z.equals(ONE)) + { + return false; + } + } + + return result; + } + + private static STOutput implSTRandomPrime(Digest d, int length, byte[] primeSeed) + { + int dLen = d.getDigestSize(); + + if (length < 33) + { + int primeGenCounter = 0; + + byte[] c0 = new byte[dLen]; + byte[] c1 = new byte[dLen]; + + for (;;) + { + hash(d, primeSeed, c0, 0); + inc(primeSeed, 1); + + hash(d, primeSeed, c1, 0); + inc(primeSeed, 1); + + int c = extract32(c0) ^ extract32(c1); + c &= (-1 >>> (32 - length)); + c |= (1 << (length - 1)) | 1; + + ++primeGenCounter; + + long c64 = c & 0xFFFFFFFFL; + if (isPrime32(c64)) + { + return new STOutput(BigInteger.valueOf(c64), primeSeed, primeGenCounter); + } + + if (primeGenCounter > (4 * length)) + { + throw new IllegalStateException("Too many iterations in Shawe-Taylor Random_Prime Routine"); + } + } + } + + STOutput rec = implSTRandomPrime(d, (length + 3) / 2, primeSeed); + + BigInteger c0 = rec.getPrime(); + primeSeed = rec.getPrimeSeed(); + int primeGenCounter = rec.getPrimeGenCounter(); + + int outlen = 8 * dLen; + int iterations = (length - 1) / outlen; + + int oldCounter = primeGenCounter; + + BigInteger x = hashGen(d, primeSeed, iterations + 1); + x = x.mod(ONE.shiftLeft(length - 1)).setBit(length - 1); + + BigInteger c0x2 = c0.shiftLeft(1); + BigInteger tx2 = x.subtract(ONE).divide(c0x2).add(ONE).shiftLeft(1); + int dt = 0; + + BigInteger c = tx2.multiply(c0).add(ONE); + + /* + * TODO Since the candidate primes are generated by constant steps ('c0x2'), sieving could + * be used here in place of the 'hasAnySmallFactors' approach. + */ + for (;;) + { + if (c.bitLength() > length) + { + tx2 = ONE.shiftLeft(length - 1).subtract(ONE).divide(c0x2).add(ONE).shiftLeft(1); + c = tx2.multiply(c0).add(ONE); + } + + ++primeGenCounter; + + /* + * This is an optimization of the original algorithm, using trial division to screen out + * many non-primes quickly. + * + * NOTE: 'primeSeed' is still incremented as if we performed the full check! + */ + if (!implHasAnySmallFactors(c)) + { + BigInteger a = hashGen(d, primeSeed, iterations + 1); + a = a.mod(c.subtract(THREE)).add(TWO); + + tx2 = tx2.add(BigInteger.valueOf(dt)); + dt = 0; + + BigInteger z = a.modPow(tx2, c); + + if (c.gcd(z.subtract(ONE)).equals(ONE) && z.modPow(c0, c).equals(ONE)) + { + return new STOutput(c, primeSeed, primeGenCounter); + } + } + else + { + inc(primeSeed, iterations + 1); + } + + if (primeGenCounter >= ((4 * length) + oldCounter)) + { + throw new IllegalStateException("Too many iterations in Shawe-Taylor Random_Prime Routine"); + } + + dt += 2; + c = c.add(c0x2); + } + } + + private static int extract32(byte[] bs) + { + int result = 0; + + int count = Math.min(4, bs.length); + for (int i = 0; i < count; ++i) + { + int b = bs[bs.length - (i + 1)] & 0xFF; + result |= (b << (8 * i)); + } + + return result; + } + + private static void hash(Digest d, byte[] input, byte[] output, int outPos) + { + d.update(input, 0, input.length); + d.doFinal(output, outPos); + } + + private static BigInteger hashGen(Digest d, byte[] seed, int count) + { + int dLen = d.getDigestSize(); + int pos = count * dLen; + byte[] buf = new byte[pos]; + for (int i = 0; i < count; ++i) + { + pos -= dLen; + hash(d, seed, buf, pos); + inc(seed, 1); + } + return new BigInteger(1, buf); + } + + private static void inc(byte[] seed, int c) + { + int pos = seed.length; + while (c > 0 && --pos >= 0) + { + c += (seed[pos] & 0xFF); + seed[pos] = (byte)c; + c >>>= 8; + } + } + + private static boolean isPrime32(long x) + { + if (x >>> 32 != 0L) + { + throw new IllegalArgumentException("Size limit exceeded"); + } + + /* + * Use wheel factorization with 2, 3, 5 to select trial divisors. + */ + + if (x <= 5L) + { + return x == 2L || x == 3L || x == 5L; + } + + if ((x & 1L) == 0L || (x % 3L) == 0L || (x % 5L) == 0L) + { + return false; + } + + long[] ds = new long[]{ 1L, 7L, 11L, 13L, 17L, 19L, 23L, 29L }; + long base = 0L; + for (int pos = 1;; pos = 0) + { + /* + * Trial division by wheel-selected divisors + */ + while (pos < ds.length) + { + long d = base + ds[pos]; + if (x % d == 0L) + { + return x < 30L; + } + ++pos; + } + + base += 30L; + + if (base * base >= x) + { + return true; + } + } + } +}
\ No newline at end of file diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1Curve.java new file mode 100644 index 00000000..8d555f63 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1Curve.java @@ -0,0 +1,80 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; + +public class SecP128R1Curve extends ECCurve.AbstractFp +{ + public static final BigInteger q = new BigInteger(1, + Hex.decode("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF")); + + private static final int SecP128R1_DEFAULT_COORDS = COORD_JACOBIAN; + + protected SecP128R1Point infinity; + + public SecP128R1Curve() + { + super(q); + + this.infinity = new SecP128R1Point(this, null, null); + + this.a = fromBigInteger(new BigInteger(1, + Hex.decode("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC"))); + this.b = fromBigInteger(new BigInteger(1, + Hex.decode("E87579C11079F43DD824993C2CEE5ED3"))); + this.order = new BigInteger(1, Hex.decode("FFFFFFFE0000000075A30D1B9038A115")); + this.cofactor = BigInteger.valueOf(1); + + this.coord = SecP128R1_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecP128R1Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_JACOBIAN: + return true; + default: + return false; + } + } + + public BigInteger getQ() + { + return q; + } + + public int getFieldSize() + { + return q.bitLength(); + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecP128R1FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecP128R1Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecP128R1Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1Field.java new file mode 100644 index 00000000..171b4927 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1Field.java @@ -0,0 +1,220 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat128; +import org.bouncycastle.math.raw.Nat256; + +public class SecP128R1Field +{ + private static final long M = 0xFFFFFFFFL; + + // 2^128 - 2^97 - 1 + static final int[] P = new int[] { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFD }; + static final int[] PExt = new int[] { 0x00000001, 0x00000000, 0x00000000, 0x00000004, 0xFFFFFFFE, + 0xFFFFFFFF, 0x00000003, 0xFFFFFFFC }; + private static final int[] PExtInv = new int[]{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFB, + 0x00000001, 0x00000000, 0xFFFFFFFC, 0x00000003 }; + private static final int P3 = 0xFFFFFFFD; + private static final int PExt7 = 0xFFFFFFFC; + + public static void add(int[] x, int[] y, int[] z) + { + int c = Nat128.add(x, y, z); + if (c != 0 || (z[3] == P3 && Nat128.gte(z, P))) + { + addPInvTo(z); + } + } + + public static void addExt(int[] xx, int[] yy, int[] zz) + { + int c = Nat256.add(xx, yy, zz); + if (c != 0 || (zz[7] == PExt7 && Nat256.gte(zz, PExt))) + { + Nat.addTo(PExtInv.length, PExtInv, zz); + } + } + + public static void addOne(int[] x, int[] z) + { + int c = Nat.inc(4, x, z); + if (c != 0 || (z[3] == P3 && Nat128.gte(z, P))) + { + addPInvTo(z); + } + } + + public static int[] fromBigInteger(BigInteger x) + { + int[] z = Nat128.fromBigInteger(x); + if (z[3] == P3 && Nat128.gte(z, P)) + { + Nat128.subFrom(P, z); + } + return z; + } + + public static void half(int[] x, int[] z) + { + if ((x[0] & 1) == 0) + { + Nat.shiftDownBit(4, x, 0, z); + } + else + { + int c = Nat128.add(x, P, z); + Nat.shiftDownBit(4, z, c); + } + } + + public static void multiply(int[] x, int[] y, int[] z) + { + int[] tt = Nat128.createExt(); + Nat128.mul(x, y, tt); + reduce(tt, z); + } + + public static void multiplyAddToExt(int[] x, int[] y, int[] zz) + { + int c = Nat128.mulAddTo(x, y, zz); + if (c != 0 || (zz[7] == PExt7 && Nat256.gte(zz, PExt))) + { + Nat.addTo(PExtInv.length, PExtInv, zz); + } + } + + public static void negate(int[] x, int[] z) + { + if (Nat128.isZero(x)) + { + Nat128.zero(z); + } + else + { + Nat128.sub(P, x, z); + } + } + + public static void reduce(int[] xx, int[] z) + { + long x0 = xx[0] & M, x1 = xx[1] & M, x2 = xx[2] & M, x3 = xx[3] & M; + long x4 = xx[4] & M, x5 = xx[5] & M, x6 = xx[6] & M, x7 = xx[7] & M; + + x3 += x7; x6 += (x7 << 1); + x2 += x6; x5 += (x6 << 1); + x1 += x5; x4 += (x5 << 1); + x0 += x4; x3 += (x4 << 1); + + z[0] = (int)x0; x1 += (x0 >>> 32); + z[1] = (int)x1; x2 += (x1 >>> 32); + z[2] = (int)x2; x3 += (x2 >>> 32); + z[3] = (int)x3; + + reduce32((int)(x3 >>> 32), z); + } + + public static void reduce32(int x, int[] z) + { + while (x != 0) + { + long c, x4 = x & M; + + c = (z[0] & M) + x4; + z[0] = (int)c; c >>= 32; + if (c != 0) + { + c += (z[1] & M); + z[1] = (int)c; c >>= 32; + c += (z[2] & M); + z[2] = (int)c; c >>= 32; + } + c += (z[3] & M) + (x4 << 1); + z[3] = (int)c; c >>= 32; + +// assert c >= 0 && c <= 2; + + x = (int)c; + } + } + + public static void square(int[] x, int[] z) + { + int[] tt = Nat128.createExt(); + Nat128.square(x, tt); + reduce(tt, z); + } + + public static void squareN(int[] x, int n, int[] z) + { +// assert n > 0; + + int[] tt = Nat128.createExt(); + Nat128.square(x, tt); + reduce(tt, z); + + while (--n > 0) + { + Nat128.square(z, tt); + reduce(tt, z); + } + } + + public static void subtract(int[] x, int[] y, int[] z) + { + int c = Nat128.sub(x, y, z); + if (c != 0) + { + subPInvFrom(z); + } + } + + public static void subtractExt(int[] xx, int[] yy, int[] zz) + { + int c = Nat.sub(10, xx, yy, zz); + if (c != 0) + { + Nat.subFrom(PExtInv.length, PExtInv, zz); + } + } + + public static void twice(int[] x, int[] z) + { + int c = Nat.shiftUpBit(4, x, 0, z); + if (c != 0 || (z[3] == P3 && Nat128.gte(z, P))) + { + addPInvTo(z); + } + } + + private static void addPInvTo(int[] z) + { + long c = (z[0] & M) + 1; + z[0] = (int)c; c >>= 32; + if (c != 0) + { + c += (z[1] & M); + z[1] = (int)c; c >>= 32; + c += (z[2] & M); + z[2] = (int)c; c >>= 32; + } + c += (z[3] & M) + 2; + z[3] = (int)c; + } + + private static void subPInvFrom(int[] z) + { + long c = (z[0] & M) - 1; + z[0] = (int)c; c >>= 32; + if (c != 0) + { + c += (z[1] & M); + z[1] = (int)c; c >>= 32; + c += (z[2] & M); + z[2] = (int)c; c >>= 32; + } + c += (z[3] & M) - 2; + z[3] = (int)c; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1FieldElement.java new file mode 100644 index 00000000..91d999d6 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1FieldElement.java @@ -0,0 +1,199 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.raw.Mod; +import org.bouncycastle.math.raw.Nat128; +import org.bouncycastle.util.Arrays; + +public class SecP128R1FieldElement extends ECFieldElement +{ + public static final BigInteger Q = SecP128R1Curve.q; + + protected int[] x; + + public SecP128R1FieldElement(BigInteger x) + { + if (x == null || x.signum() < 0 || x.compareTo(Q) >= 0) + { + throw new IllegalArgumentException("x value invalid for SecP128R1FieldElement"); + } + + this.x = SecP128R1Field.fromBigInteger(x); + } + + public SecP128R1FieldElement() + { + this.x = Nat128.create(); + } + + protected SecP128R1FieldElement(int[] x) + { + this.x = x; + } + + public boolean isZero() + { + return Nat128.isZero(x); + } + + public boolean isOne() + { + return Nat128.isOne(x); + } + + public boolean testBitZero() + { + return Nat128.getBit(x, 0) == 1; + } + + public BigInteger toBigInteger() + { + return Nat128.toBigInteger(x); + } + + public String getFieldName() + { + return "SecP128R1Field"; + } + + public int getFieldSize() + { + return Q.bitLength(); + } + + public ECFieldElement add(ECFieldElement b) + { + int[] z = Nat128.create(); + SecP128R1Field.add(x, ((SecP128R1FieldElement)b).x, z); + return new SecP128R1FieldElement(z); + } + + public ECFieldElement addOne() + { + int[] z = Nat128.create(); + SecP128R1Field.addOne(x, z); + return new SecP128R1FieldElement(z); + } + + public ECFieldElement subtract(ECFieldElement b) + { + int[] z = Nat128.create(); + SecP128R1Field.subtract(x, ((SecP128R1FieldElement)b).x, z); + return new SecP128R1FieldElement(z); + } + + public ECFieldElement multiply(ECFieldElement b) + { + int[] z = Nat128.create(); + SecP128R1Field.multiply(x, ((SecP128R1FieldElement)b).x, z); + return new SecP128R1FieldElement(z); + } + + public ECFieldElement divide(ECFieldElement b) + { +// return multiply(b.invert()); + int[] z = Nat128.create(); + Mod.invert(SecP128R1Field.P, ((SecP128R1FieldElement)b).x, z); + SecP128R1Field.multiply(z, x, z); + return new SecP128R1FieldElement(z); + } + + public ECFieldElement negate() + { + int[] z = Nat128.create(); + SecP128R1Field.negate(x, z); + return new SecP128R1FieldElement(z); + } + + public ECFieldElement square() + { + int[] z = Nat128.create(); + SecP128R1Field.square(x, z); + return new SecP128R1FieldElement(z); + } + + public ECFieldElement invert() + { +// return new SecP128R1FieldElement(toBigInteger().modInverse(Q)); + int[] z = Nat128.create(); + Mod.invert(SecP128R1Field.P, x, z); + return new SecP128R1FieldElement(z); + } + + // D.1.4 91 + /** + * return a sqrt root - the routine verifies that the calculation returns the right value - if + * none exists it returns null. + */ + public ECFieldElement sqrt() + { + /* + * Raise this element to the exponent 2^126 - 2^95 + * + * Breaking up the exponent's binary representation into "repunits", we get: + * { 31 1s } { 95 0s } + * + * Therefore we need an addition chain containing 31 (the length of the repunit) We use: + * 1, 2, 4, 8, 10, 20, 30, [31] + */ + + int[] x1 = this.x; + if (Nat128.isZero(x1) || Nat128.isOne(x1)) + { + return this; + } + + int[] x2 = Nat128.create(); + SecP128R1Field.square(x1, x2); + SecP128R1Field.multiply(x2, x1, x2); + int[] x4 = Nat128.create(); + SecP128R1Field.squareN(x2, 2, x4); + SecP128R1Field.multiply(x4, x2, x4); + int[] x8 = Nat128.create(); + SecP128R1Field.squareN(x4, 4, x8); + SecP128R1Field.multiply(x8, x4, x8); + int[] x10 = x4; + SecP128R1Field.squareN(x8, 2, x10); + SecP128R1Field.multiply(x10, x2, x10); + int[] x20 = x2; + SecP128R1Field.squareN(x10, 10, x20); + SecP128R1Field.multiply(x20, x10, x20); + int[] x30 = x8; + SecP128R1Field.squareN(x20, 10, x30); + SecP128R1Field.multiply(x30, x10, x30); + int[] x31 = x10; + SecP128R1Field.square(x30, x31); + SecP128R1Field.multiply(x31, x1, x31); + + int[] t1 = x31; + SecP128R1Field.squareN(t1, 95, t1); + + int[] t2 = x30; + SecP128R1Field.square(t1, t2); + + return Nat128.eq(x1, t2) ? new SecP128R1FieldElement(t1) : null; + } + + public boolean equals(Object other) + { + if (other == this) + { + return true; + } + + if (!(other instanceof SecP128R1FieldElement)) + { + return false; + } + + SecP128R1FieldElement o = (SecP128R1FieldElement)other; + return Nat128.eq(x, o.x); + } + + public int hashCode() + { + return Q.hashCode() ^ Arrays.hashCode(x, 0, 4); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1Point.java new file mode 100644 index 00000000..b7934da7 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP128R1Point.java @@ -0,0 +1,308 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat128; + +public class SecP128R1Point extends ECPoint.AbstractFp +{ + /** + * 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 SecP128R1Point(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(boolean)} + */ + public SecP128R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecP128R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecP128R1Point(null, getAffineXCoord(), getAffineYCoord()); + } + + public ECPoint add(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return this; + } + if (this == b) + { + return twice(); + } + + ECCurve curve = this.getCurve(); + + SecP128R1FieldElement X1 = (SecP128R1FieldElement)this.x, Y1 = (SecP128R1FieldElement)this.y; + SecP128R1FieldElement X2 = (SecP128R1FieldElement)b.getXCoord(), Y2 = (SecP128R1FieldElement)b.getYCoord(); + + SecP128R1FieldElement Z1 = (SecP128R1FieldElement)this.zs[0]; + SecP128R1FieldElement Z2 = (SecP128R1FieldElement)b.getZCoord(0); + + int c; + int[] tt1 = Nat128.createExt(); + int[] t2 = Nat128.create(); + int[] t3 = Nat128.create(); + int[] t4 = Nat128.create(); + + boolean Z1IsOne = Z1.isOne(); + int[] U2, S2; + if (Z1IsOne) + { + U2 = X2.x; + S2 = Y2.x; + } + else + { + S2 = t3; + SecP128R1Field.square(Z1.x, S2); + + U2 = t2; + SecP128R1Field.multiply(S2, X2.x, U2); + + SecP128R1Field.multiply(S2, Z1.x, S2); + SecP128R1Field.multiply(S2, Y2.x, S2); + } + + boolean Z2IsOne = Z2.isOne(); + int[] U1, S1; + if (Z2IsOne) + { + U1 = X1.x; + S1 = Y1.x; + } + else + { + S1 = t4; + SecP128R1Field.square(Z2.x, S1); + + U1 = tt1; + SecP128R1Field.multiply(S1, X1.x, U1); + + SecP128R1Field.multiply(S1, Z2.x, S1); + SecP128R1Field.multiply(S1, Y1.x, S1); + } + + int[] H = Nat128.create(); + SecP128R1Field.subtract(U1, U2, H); + + int[] R = t2; + SecP128R1Field.subtract(S1, S2, R); + + // Check if b == this or b == -this + if (Nat128.isZero(H)) + { + if (Nat128.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.getInfinity(); + } + + int[] HSquared = t3; + SecP128R1Field.square(H, HSquared); + + int[] G = Nat128.create(); + SecP128R1Field.multiply(HSquared, H, G); + + int[] V = t3; + SecP128R1Field.multiply(HSquared, U1, V); + + SecP128R1Field.negate(G, G); + Nat128.mul(S1, G, tt1); + + c = Nat128.addBothTo(V, V, G); + SecP128R1Field.reduce32(c, G); + + SecP128R1FieldElement X3 = new SecP128R1FieldElement(t4); + SecP128R1Field.square(R, X3.x); + SecP128R1Field.subtract(X3.x, G, X3.x); + + SecP128R1FieldElement Y3 = new SecP128R1FieldElement(G); + SecP128R1Field.subtract(V, X3.x, Y3.x); + SecP128R1Field.multiplyAddToExt(Y3.x, R, tt1); + SecP128R1Field.reduce(tt1, Y3.x); + + SecP128R1FieldElement Z3 = new SecP128R1FieldElement(H); + if (!Z1IsOne) + { + SecP128R1Field.multiply(Z3.x, Z1.x, Z3.x); + } + if (!Z2IsOne) + { + SecP128R1Field.multiply(Z3.x, Z2.x, Z3.x); + } + + ECFieldElement[] zs = new ECFieldElement[]{ Z3 }; + + return new SecP128R1Point(curve, X3, Y3, zs, this.withCompression); + } + + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + SecP128R1FieldElement Y1 = (SecP128R1FieldElement)this.y; + if (Y1.isZero()) + { + return curve.getInfinity(); + } + + SecP128R1FieldElement X1 = (SecP128R1FieldElement)this.x, Z1 = (SecP128R1FieldElement)this.zs[0]; + + int c; + int[] t1 = Nat128.create(); + int[] t2 = Nat128.create(); + + int[] Y1Squared = Nat128.create(); + SecP128R1Field.square(Y1.x, Y1Squared); + + int[] T = Nat128.create(); + SecP128R1Field.square(Y1Squared, T); + + boolean Z1IsOne = Z1.isOne(); + + int[] Z1Squared = Z1.x; + if (!Z1IsOne) + { + Z1Squared = t2; + SecP128R1Field.square(Z1.x, Z1Squared); + } + + SecP128R1Field.subtract(X1.x, Z1Squared, t1); + + int[] M = t2; + SecP128R1Field.add(X1.x, Z1Squared, M); + SecP128R1Field.multiply(M, t1, M); + c = Nat128.addBothTo(M, M, M); + SecP128R1Field.reduce32(c, M); + + int[] S = Y1Squared; + SecP128R1Field.multiply(Y1Squared, X1.x, S); + c = Nat.shiftUpBits(4, S, 2, 0); + SecP128R1Field.reduce32(c, S); + + c = Nat.shiftUpBits(4, T, 3, 0, t1); + SecP128R1Field.reduce32(c, t1); + + SecP128R1FieldElement X3 = new SecP128R1FieldElement(T); + SecP128R1Field.square(M, X3.x); + SecP128R1Field.subtract(X3.x, S, X3.x); + SecP128R1Field.subtract(X3.x, S, X3.x); + + SecP128R1FieldElement Y3 = new SecP128R1FieldElement(S); + SecP128R1Field.subtract(S, X3.x, Y3.x); + SecP128R1Field.multiply(Y3.x, M, Y3.x); + SecP128R1Field.subtract(Y3.x, t1, Y3.x); + + SecP128R1FieldElement Z3 = new SecP128R1FieldElement(M); + SecP128R1Field.twice(Y1.x, Z3.x); + if (!Z1IsOne) + { + SecP128R1Field.multiply(Z3.x, Z1.x, Z3.x); + } + + return new SecP128R1Point(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twicePlus(ECPoint b) + { + if (this == b) + { + return threeTimes(); + } + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return twice(); + } + + ECFieldElement Y1 = this.y; + if (Y1.isZero()) + { + return b; + } + + return twice().add(b); + } + + public ECPoint threeTimes() + { + if (this.isInfinity() || this.y.isZero()) + { + return this; + } + + // NOTE: Be careful about recursions between twicePlus and threeTimes + return twice().add(this); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + return new SecP128R1Point(curve, this.x, this.y.negate(), this.zs, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160K1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160K1Curve.java new file mode 100644 index 00000000..9285e7c8 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160K1Curve.java @@ -0,0 +1,77 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECConstants; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; + +public class SecP160K1Curve extends ECCurve.AbstractFp +{ + public static final BigInteger q = SecP160R2Curve.q; + + private static final int SECP160K1_DEFAULT_COORDS = COORD_JACOBIAN; + + protected SecP160K1Point infinity; + + public SecP160K1Curve() + { + super(q); + + this.infinity = new SecP160K1Point(this, null, null); + + this.a = fromBigInteger(ECConstants.ZERO); + this.b = fromBigInteger(BigInteger.valueOf(7)); + this.order = new BigInteger(1, Hex.decode("0100000000000000000001B8FA16DFAB9ACA16B6B3")); + this.cofactor = BigInteger.valueOf(1); + this.coord = SECP160K1_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecP160K1Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_JACOBIAN: + return true; + default: + return false; + } + } + + public BigInteger getQ() + { + return q; + } + + public int getFieldSize() + { + return q.bitLength(); + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecP160R2FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecP160K1Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecP160K1Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160K1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160K1Point.java new file mode 100644 index 00000000..37a520a1 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160K1Point.java @@ -0,0 +1,298 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat160; + +public class SecP160K1Point extends ECPoint.AbstractFp +{ + /** + * 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 SecP160K1Point(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(boolean)} + */ + public SecP160K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecP160K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, + boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecP160K1Point(null, getAffineXCoord(), getAffineYCoord()); + } + + // B.3 pg 62 + public ECPoint add(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return this; + } + if (this == b) + { + return twice(); + } + + ECCurve curve = this.getCurve(); + + SecP160R2FieldElement X1 = (SecP160R2FieldElement)this.x, Y1 = (SecP160R2FieldElement)this.y; + SecP160R2FieldElement X2 = (SecP160R2FieldElement)b.getXCoord(), Y2 = (SecP160R2FieldElement)b.getYCoord(); + + SecP160R2FieldElement Z1 = (SecP160R2FieldElement)this.zs[0]; + SecP160R2FieldElement Z2 = (SecP160R2FieldElement)b.getZCoord(0); + + int c; + int[] tt1 = Nat160.createExt(); + int[] t2 = Nat160.create(); + int[] t3 = Nat160.create(); + int[] t4 = Nat160.create(); + + boolean Z1IsOne = Z1.isOne(); + int[] U2, S2; + if (Z1IsOne) + { + U2 = X2.x; + S2 = Y2.x; + } + else + { + S2 = t3; + SecP160R2Field.square(Z1.x, S2); + + U2 = t2; + SecP160R2Field.multiply(S2, X2.x, U2); + + SecP160R2Field.multiply(S2, Z1.x, S2); + SecP160R2Field.multiply(S2, Y2.x, S2); + } + + boolean Z2IsOne = Z2.isOne(); + int[] U1, S1; + if (Z2IsOne) + { + U1 = X1.x; + S1 = Y1.x; + } + else + { + S1 = t4; + SecP160R2Field.square(Z2.x, S1); + + U1 = tt1; + SecP160R2Field.multiply(S1, X1.x, U1); + + SecP160R2Field.multiply(S1, Z2.x, S1); + SecP160R2Field.multiply(S1, Y1.x, S1); + } + + int[] H = Nat160.create(); + SecP160R2Field.subtract(U1, U2, H); + + int[] R = t2; + SecP160R2Field.subtract(S1, S2, R); + + // Check if b == this or b == -this + if (Nat160.isZero(H)) + { + if (Nat160.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.getInfinity(); + } + + int[] HSquared = t3; + SecP160R2Field.square(H, HSquared); + + int[] G = Nat160.create(); + SecP160R2Field.multiply(HSquared, H, G); + + int[] V = t3; + SecP160R2Field.multiply(HSquared, U1, V); + + SecP160R2Field.negate(G, G); + Nat160.mul(S1, G, tt1); + + c = Nat160.addBothTo(V, V, G); + SecP160R2Field.reduce32(c, G); + + SecP160R2FieldElement X3 = new SecP160R2FieldElement(t4); + SecP160R2Field.square(R, X3.x); + SecP160R2Field.subtract(X3.x, G, X3.x); + + SecP160R2FieldElement Y3 = new SecP160R2FieldElement(G); + SecP160R2Field.subtract(V, X3.x, Y3.x); + SecP160R2Field.multiplyAddToExt(Y3.x, R, tt1); + SecP160R2Field.reduce(tt1, Y3.x); + + SecP160R2FieldElement Z3 = new SecP160R2FieldElement(H); + if (!Z1IsOne) + { + SecP160R2Field.multiply(Z3.x, Z1.x, Z3.x); + } + if (!Z2IsOne) + { + SecP160R2Field.multiply(Z3.x, Z2.x, Z3.x); + } + + ECFieldElement[] zs = new ECFieldElement[] { Z3 }; + + return new SecP160K1Point(curve, X3, Y3, zs, this.withCompression); + } + + // B.3 pg 62 + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + SecP160R2FieldElement Y1 = (SecP160R2FieldElement)this.y; + if (Y1.isZero()) + { + return curve.getInfinity(); + } + + SecP160R2FieldElement X1 = (SecP160R2FieldElement)this.x, Z1 = (SecP160R2FieldElement)this.zs[0]; + + int c; + + int[] Y1Squared = Nat160.create(); + SecP160R2Field.square(Y1.x, Y1Squared); + + int[] T = Nat160.create(); + SecP160R2Field.square(Y1Squared, T); + + int[] M = Nat160.create(); + SecP160R2Field.square(X1.x, M); + c = Nat160.addBothTo(M, M, M); + SecP160R2Field.reduce32(c, M); + + int[] S = Y1Squared; + SecP160R2Field.multiply(Y1Squared, X1.x, S); + c = Nat.shiftUpBits(5, S, 2, 0); + SecP160R2Field.reduce32(c, S); + + int[] t1 = Nat160.create(); + c = Nat.shiftUpBits(5, T, 3, 0, t1); + SecP160R2Field.reduce32(c, t1); + + SecP160R2FieldElement X3 = new SecP160R2FieldElement(T); + SecP160R2Field.square(M, X3.x); + SecP160R2Field.subtract(X3.x, S, X3.x); + SecP160R2Field.subtract(X3.x, S, X3.x); + + SecP160R2FieldElement Y3 = new SecP160R2FieldElement(S); + SecP160R2Field.subtract(S, X3.x, Y3.x); + SecP160R2Field.multiply(Y3.x, M, Y3.x); + SecP160R2Field.subtract(Y3.x, t1, Y3.x); + + SecP160R2FieldElement Z3 = new SecP160R2FieldElement(M); + SecP160R2Field.twice(Y1.x, Z3.x); + if (!Z1.isOne()) + { + SecP160R2Field.multiply(Z3.x, Z1.x, Z3.x); + } + + return new SecP160K1Point(curve, X3, Y3, new ECFieldElement[] { Z3 }, this.withCompression); + } + + public ECPoint twicePlus(ECPoint b) + { + if (this == b) + { + return threeTimes(); + } + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return twice(); + } + + ECFieldElement Y1 = this.y; + if (Y1.isZero()) + { + return b; + } + + return twice().add(b); + } + + public ECPoint threeTimes() + { + if (this.isInfinity() || this.y.isZero()) + { + return this; + } + + // NOTE: Be careful about recursions between twicePlus and threeTimes + return twice().add(this); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + return new SecP160K1Point(curve, this.x, this.y.negate(), this.zs, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1Curve.java new file mode 100644 index 00000000..fd39a4a1 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1Curve.java @@ -0,0 +1,80 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; + +public class SecP160R1Curve extends ECCurve.AbstractFp +{ + public static final BigInteger q = new BigInteger(1, + Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF")); + + private static final int SecP160R1_DEFAULT_COORDS = COORD_JACOBIAN; + + protected SecP160R1Point infinity; + + public SecP160R1Curve() + { + super(q); + + this.infinity = new SecP160R1Point(this, null, null); + + this.a = fromBigInteger(new BigInteger(1, + Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC"))); + this.b = fromBigInteger(new BigInteger(1, + Hex.decode("1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45"))); + this.order = new BigInteger(1, Hex.decode("0100000000000000000001F4C8F927AED3CA752257")); + this.cofactor = BigInteger.valueOf(1); + + this.coord = SecP160R1_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecP160R1Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_JACOBIAN: + return true; + default: + return false; + } + } + + public BigInteger getQ() + { + return q; + } + + public int getFieldSize() + { + return q.bitLength(); + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecP160R1FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecP160R1Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecP160R1Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1Field.java new file mode 100644 index 00000000..91ba0e58 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1Field.java @@ -0,0 +1,187 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat160; + +public class SecP160R1Field +{ + private static final long M = 0xFFFFFFFFL; + + // 2^160 - 2^31 - 1 + static final int[] P = new int[] { 0x7FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}; + static final int[] PExt = new int[] { 0x00000001, 0x40000001, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFE, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + private static final int[] PExtInv = new int[]{ 0xFFFFFFFF, 0xBFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0x00000001, 0x00000001 }; + private static final int P4 = 0xFFFFFFFF; + private static final int PExt9 = 0xFFFFFFFF; + private static final int PInv = 0x80000001; + + public static void add(int[] x, int[] y, int[] z) + { + int c = Nat160.add(x, y, z); + if (c != 0 || (z[4] == P4 && Nat160.gte(z, P))) + { + Nat.addWordTo(5, PInv, z); + } + } + + public static void addExt(int[] xx, int[] yy, int[] zz) + { + int c = Nat.add(10, xx, yy, zz); + if (c != 0 || (zz[9] == PExt9 && Nat.gte(10, zz, PExt))) + { + if (Nat.addTo(PExtInv.length, PExtInv, zz) != 0) + { + Nat.incAt(10, zz, PExtInv.length); + } + } + } + + public static void addOne(int[] x, int[] z) + { + int c = Nat.inc(5, x, z); + if (c != 0 || (z[4] == P4 && Nat160.gte(z, P))) + { + Nat.addWordTo(5, PInv, z); + } + } + + public static int[] fromBigInteger(BigInteger x) + { + int[] z = Nat160.fromBigInteger(x); + if (z[4] == P4 && Nat160.gte(z, P)) + { + Nat160.subFrom(P, z); + } + return z; + } + + public static void half(int[] x, int[] z) + { + if ((x[0] & 1) == 0) + { + Nat.shiftDownBit(5, x, 0, z); + } + else + { + int c = Nat160.add(x, P, z); + Nat.shiftDownBit(5, z, c); + } + } + + public static void multiply(int[] x, int[] y, int[] z) + { + int[] tt = Nat160.createExt(); + Nat160.mul(x, y, tt); + reduce(tt, z); + } + + public static void multiplyAddToExt(int[] x, int[] y, int[] zz) + { + int c = Nat160.mulAddTo(x, y, zz); + if (c != 0 || (zz[9] == PExt9 && Nat.gte(10, zz, PExt))) + { + if (Nat.addTo(PExtInv.length, PExtInv, zz) != 0) + { + Nat.incAt(10, zz, PExtInv.length); + } + } + } + + public static void negate(int[] x, int[] z) + { + if (Nat160.isZero(x)) + { + Nat160.zero(z); + } + else + { + Nat160.sub(P, x, z); + } + } + + public static void reduce(int[] xx, int[] z) + { + long x5 = xx[5] & M, x6 = xx[6] & M, x7 = xx[7] & M, x8 = xx[8] & M, x9 = xx[9] & M; + + long c = 0; + c += (xx[0] & M) + x5 + (x5 << 31); + z[0] = (int)c; c >>>= 32; + c += (xx[1] & M) + x6 + (x6 << 31); + z[1] = (int)c; c >>>= 32; + c += (xx[2] & M) + x7 + (x7 << 31); + z[2] = (int)c; c >>>= 32; + c += (xx[3] & M) + x8 + (x8 << 31); + z[3] = (int)c; c >>>= 32; + c += (xx[4] & M) + x9 + (x9 << 31); + z[4] = (int)c; c >>>= 32; + +// assert c >>> 32 == 0; + + reduce32((int)c, z); + } + + public static void reduce32(int x, int[] z) + { + if ((x != 0 && Nat160.mulWordsAdd(PInv, x, z, 0) != 0) + || (z[4] == P4 && Nat160.gte(z, P))) + { + Nat.addWordTo(5, PInv, z); + } + } + + public static void square(int[] x, int[] z) + { + int[] tt = Nat160.createExt(); + Nat160.square(x, tt); + reduce(tt, z); + } + + public static void squareN(int[] x, int n, int[] z) + { +// assert n > 0; + + int[] tt = Nat160.createExt(); + Nat160.square(x, tt); + reduce(tt, z); + + while (--n > 0) + { + Nat160.square(z, tt); + reduce(tt, z); + } + } + + public static void subtract(int[] x, int[] y, int[] z) + { + int c = Nat160.sub(x, y, z); + if (c != 0) + { + Nat.subWordFrom(5, PInv, z); + } + } + + public static void subtractExt(int[] xx, int[] yy, int[] zz) + { + int c = Nat.sub(10, xx, yy, zz); + if (c != 0) + { + if (Nat.subFrom(PExtInv.length, PExtInv, zz) != 0) + { + Nat.decAt(10, zz, PExtInv.length); + } + } + } + + public static void twice(int[] x, int[] z) + { + int c = Nat.shiftUpBit(5, x, 0, z); + if (c != 0 || (z[4] == P4 && Nat160.gte(z, P))) + { + Nat.addWordTo(5, PInv, z); + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1FieldElement.java new file mode 100644 index 00000000..1b411b0c --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1FieldElement.java @@ -0,0 +1,202 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.raw.Mod; +import org.bouncycastle.math.raw.Nat160; +import org.bouncycastle.util.Arrays; + +public class SecP160R1FieldElement extends ECFieldElement +{ + public static final BigInteger Q = SecP160R1Curve.q; + + protected int[] x; + + public SecP160R1FieldElement(BigInteger x) + { + if (x == null || x.signum() < 0 || x.compareTo(Q) >= 0) + { + throw new IllegalArgumentException("x value invalid for SecP160R1FieldElement"); + } + + this.x = SecP160R1Field.fromBigInteger(x); + } + + public SecP160R1FieldElement() + { + this.x = Nat160.create(); + } + + protected SecP160R1FieldElement(int[] x) + { + this.x = x; + } + + public boolean isZero() + { + return Nat160.isZero(x); + } + + public boolean isOne() + { + return Nat160.isOne(x); + } + + public boolean testBitZero() + { + return Nat160.getBit(x, 0) == 1; + } + + public BigInteger toBigInteger() + { + return Nat160.toBigInteger(x); + } + + public String getFieldName() + { + return "SecP160R1Field"; + } + + public int getFieldSize() + { + return Q.bitLength(); + } + + public ECFieldElement add(ECFieldElement b) + { + int[] z = Nat160.create(); + SecP160R1Field.add(x, ((SecP160R1FieldElement)b).x, z); + return new SecP160R1FieldElement(z); + } + + public ECFieldElement addOne() + { + int[] z = Nat160.create(); + SecP160R1Field.addOne(x, z); + return new SecP160R1FieldElement(z); + } + + public ECFieldElement subtract(ECFieldElement b) + { + int[] z = Nat160.create(); + SecP160R1Field.subtract(x, ((SecP160R1FieldElement)b).x, z); + return new SecP160R1FieldElement(z); + } + + public ECFieldElement multiply(ECFieldElement b) + { + int[] z = Nat160.create(); + SecP160R1Field.multiply(x, ((SecP160R1FieldElement)b).x, z); + return new SecP160R1FieldElement(z); + } + + public ECFieldElement divide(ECFieldElement b) + { +// return multiply(b.invert()); + int[] z = Nat160.create(); + Mod.invert(SecP160R1Field.P, ((SecP160R1FieldElement)b).x, z); + SecP160R1Field.multiply(z, x, z); + return new SecP160R1FieldElement(z); + } + + public ECFieldElement negate() + { + int[] z = Nat160.create(); + SecP160R1Field.negate(x, z); + return new SecP160R1FieldElement(z); + } + + public ECFieldElement square() + { + int[] z = Nat160.create(); + SecP160R1Field.square(x, z); + return new SecP160R1FieldElement(z); + } + + public ECFieldElement invert() + { +// return new SecP160R1FieldElement(toBigInteger().modInverse(Q)); + int[] z = Nat160.create(); + Mod.invert(SecP160R1Field.P, x, z); + return new SecP160R1FieldElement(z); + } + + // D.1.4 91 + /** + * return a sqrt root - the routine verifies that the calculation returns the right value - if + * none exists it returns null. + */ + public ECFieldElement sqrt() + { + /* + * Raise this element to the exponent 2^158 - 2^29 + * + * Breaking up the exponent's binary representation into "repunits", we get: + * { 129 1s } { 29 0s } + * + * Therefore we need an addition chain containing 129 (the length of the repunit) We use: + * 1, 2, 4, 8, 16, 32, 64, 128, [129] + */ + + int[] x1 = this.x; + if (Nat160.isZero(x1) || Nat160.isOne(x1)) + { + return this; + } + + int[] x2 = Nat160.create(); + SecP160R1Field.square(x1, x2); + SecP160R1Field.multiply(x2, x1, x2); + int[] x4 = Nat160.create(); + SecP160R1Field.squareN(x2, 2, x4); + SecP160R1Field.multiply(x4, x2, x4); + int[] x8 = x2; + SecP160R1Field.squareN(x4, 4, x8); + SecP160R1Field.multiply(x8, x4, x8); + int[] x16 = x4; + SecP160R1Field.squareN(x8, 8, x16); + SecP160R1Field.multiply(x16, x8, x16); + int[] x32 = x8; + SecP160R1Field.squareN(x16, 16, x32); + SecP160R1Field.multiply(x32, x16, x32); + int[] x64 = x16; + SecP160R1Field.squareN(x32, 32, x64); + SecP160R1Field.multiply(x64, x32, x64); + int[] x128 = x32; + SecP160R1Field.squareN(x64, 64, x128); + SecP160R1Field.multiply(x128, x64, x128); + int[] x129 = x64; + SecP160R1Field.square(x128, x129); + SecP160R1Field.multiply(x129, x1, x129); + + int[] t1 = x129; + SecP160R1Field.squareN(t1, 29, t1); + + int[] t2 = x128; + SecP160R1Field.square(t1, t2); + + return Nat160.eq(x1, t2) ? new SecP160R1FieldElement(t1) : null; + } + + public boolean equals(Object other) + { + if (other == this) + { + return true; + } + + if (!(other instanceof SecP160R1FieldElement)) + { + return false; + } + + SecP160R1FieldElement o = (SecP160R1FieldElement)other; + return Nat160.eq(x, o.x); + } + + public int hashCode() + { + return Q.hashCode() ^ Arrays.hashCode(x, 0, 5); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1Point.java new file mode 100644 index 00000000..42aaa777 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R1Point.java @@ -0,0 +1,308 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat160; + +public class SecP160R1Point extends ECPoint.AbstractFp +{ + /** + * 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 SecP160R1Point(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(boolean)} + */ + public SecP160R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecP160R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecP160R1Point(null, getAffineXCoord(), getAffineYCoord()); + } + + public ECPoint add(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return this; + } + if (this == b) + { + return twice(); + } + + ECCurve curve = this.getCurve(); + + SecP160R1FieldElement X1 = (SecP160R1FieldElement)this.x, Y1 = (SecP160R1FieldElement)this.y; + SecP160R1FieldElement X2 = (SecP160R1FieldElement)b.getXCoord(), Y2 = (SecP160R1FieldElement)b.getYCoord(); + + SecP160R1FieldElement Z1 = (SecP160R1FieldElement)this.zs[0]; + SecP160R1FieldElement Z2 = (SecP160R1FieldElement)b.getZCoord(0); + + int c; + int[] tt1 = Nat160.createExt(); + int[] t2 = Nat160.create(); + int[] t3 = Nat160.create(); + int[] t4 = Nat160.create(); + + boolean Z1IsOne = Z1.isOne(); + int[] U2, S2; + if (Z1IsOne) + { + U2 = X2.x; + S2 = Y2.x; + } + else + { + S2 = t3; + SecP160R1Field.square(Z1.x, S2); + + U2 = t2; + SecP160R1Field.multiply(S2, X2.x, U2); + + SecP160R1Field.multiply(S2, Z1.x, S2); + SecP160R1Field.multiply(S2, Y2.x, S2); + } + + boolean Z2IsOne = Z2.isOne(); + int[] U1, S1; + if (Z2IsOne) + { + U1 = X1.x; + S1 = Y1.x; + } + else + { + S1 = t4; + SecP160R1Field.square(Z2.x, S1); + + U1 = tt1; + SecP160R1Field.multiply(S1, X1.x, U1); + + SecP160R1Field.multiply(S1, Z2.x, S1); + SecP160R1Field.multiply(S1, Y1.x, S1); + } + + int[] H = Nat160.create(); + SecP160R1Field.subtract(U1, U2, H); + + int[] R = t2; + SecP160R1Field.subtract(S1, S2, R); + + // Check if b == this or b == -this + if (Nat160.isZero(H)) + { + if (Nat160.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.getInfinity(); + } + + int[] HSquared = t3; + SecP160R1Field.square(H, HSquared); + + int[] G = Nat160.create(); + SecP160R1Field.multiply(HSquared, H, G); + + int[] V = t3; + SecP160R1Field.multiply(HSquared, U1, V); + + SecP160R1Field.negate(G, G); + Nat160.mul(S1, G, tt1); + + c = Nat160.addBothTo(V, V, G); + SecP160R1Field.reduce32(c, G); + + SecP160R1FieldElement X3 = new SecP160R1FieldElement(t4); + SecP160R1Field.square(R, X3.x); + SecP160R1Field.subtract(X3.x, G, X3.x); + + SecP160R1FieldElement Y3 = new SecP160R1FieldElement(G); + SecP160R1Field.subtract(V, X3.x, Y3.x); + SecP160R1Field.multiplyAddToExt(Y3.x, R, tt1); + SecP160R1Field.reduce(tt1, Y3.x); + + SecP160R1FieldElement Z3 = new SecP160R1FieldElement(H); + if (!Z1IsOne) + { + SecP160R1Field.multiply(Z3.x, Z1.x, Z3.x); + } + if (!Z2IsOne) + { + SecP160R1Field.multiply(Z3.x, Z2.x, Z3.x); + } + + ECFieldElement[] zs = new ECFieldElement[]{ Z3 }; + + return new SecP160R1Point(curve, X3, Y3, zs, this.withCompression); + } + + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + SecP160R1FieldElement Y1 = (SecP160R1FieldElement)this.y; + if (Y1.isZero()) + { + return curve.getInfinity(); + } + + SecP160R1FieldElement X1 = (SecP160R1FieldElement)this.x, Z1 = (SecP160R1FieldElement)this.zs[0]; + + int c; + int[] t1 = Nat160.create(); + int[] t2 = Nat160.create(); + + int[] Y1Squared = Nat160.create(); + SecP160R1Field.square(Y1.x, Y1Squared); + + int[] T = Nat160.create(); + SecP160R1Field.square(Y1Squared, T); + + boolean Z1IsOne = Z1.isOne(); + + int[] Z1Squared = Z1.x; + if (!Z1IsOne) + { + Z1Squared = t2; + SecP160R1Field.square(Z1.x, Z1Squared); + } + + SecP160R1Field.subtract(X1.x, Z1Squared, t1); + + int[] M = t2; + SecP160R1Field.add(X1.x, Z1Squared, M); + SecP160R1Field.multiply(M, t1, M); + c = Nat160.addBothTo(M, M, M); + SecP160R1Field.reduce32(c, M); + + int[] S = Y1Squared; + SecP160R1Field.multiply(Y1Squared, X1.x, S); + c = Nat.shiftUpBits(5, S, 2, 0); + SecP160R1Field.reduce32(c, S); + + c = Nat.shiftUpBits(5, T, 3, 0, t1); + SecP160R1Field.reduce32(c, t1); + + SecP160R1FieldElement X3 = new SecP160R1FieldElement(T); + SecP160R1Field.square(M, X3.x); + SecP160R1Field.subtract(X3.x, S, X3.x); + SecP160R1Field.subtract(X3.x, S, X3.x); + + SecP160R1FieldElement Y3 = new SecP160R1FieldElement(S); + SecP160R1Field.subtract(S, X3.x, Y3.x); + SecP160R1Field.multiply(Y3.x, M, Y3.x); + SecP160R1Field.subtract(Y3.x, t1, Y3.x); + + SecP160R1FieldElement Z3 = new SecP160R1FieldElement(M); + SecP160R1Field.twice(Y1.x, Z3.x); + if (!Z1IsOne) + { + SecP160R1Field.multiply(Z3.x, Z1.x, Z3.x); + } + + return new SecP160R1Point(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twicePlus(ECPoint b) + { + if (this == b) + { + return threeTimes(); + } + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return twice(); + } + + ECFieldElement Y1 = this.y; + if (Y1.isZero()) + { + return b; + } + + return twice().add(b); + } + + public ECPoint threeTimes() + { + if (this.isInfinity() || this.y.isZero()) + { + return this; + } + + // NOTE: Be careful about recursions between twicePlus and threeTimes + return twice().add(this); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + return new SecP160R1Point(curve, this.x, this.y.negate(), this.zs, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2Curve.java new file mode 100644 index 00000000..3e1cbbb4 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2Curve.java @@ -0,0 +1,80 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; + +public class SecP160R2Curve extends ECCurve.AbstractFp +{ + public static final BigInteger q = new BigInteger(1, + Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73")); + + private static final int SecP160R2_DEFAULT_COORDS = COORD_JACOBIAN; + + protected SecP160R2Point infinity; + + public SecP160R2Curve() + { + super(q); + + this.infinity = new SecP160R2Point(this, null, null); + + this.a = fromBigInteger(new BigInteger(1, + Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC70"))); + this.b = fromBigInteger(new BigInteger(1, + Hex.decode("B4E134D3FB59EB8BAB57274904664D5AF50388BA"))); + this.order = new BigInteger(1, Hex.decode("0100000000000000000000351EE786A818F3A1A16B")); + this.cofactor = BigInteger.valueOf(1); + + this.coord = SecP160R2_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecP160R2Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_JACOBIAN: + return true; + default: + return false; + } + } + + public BigInteger getQ() + { + return q; + } + + public int getFieldSize() + { + return q.bitLength(); + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecP160R2FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecP160R2Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecP160R2Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2Field.java new file mode 100644 index 00000000..70c5e0c9 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2Field.java @@ -0,0 +1,177 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat160; + +public class SecP160R2Field +{ + // 2^160 - 2^32 - 2^14 - 2^12 - 2^9 - 2^8 - 2^7 - 2^3 - 2^2 - 1 + static final int[] P = new int[]{ 0xFFFFAC73, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + static final int[] PExt = new int[]{ 0x1B44BBA9, 0x0000A71A, 0x00000001, 0x00000000, 0x00000000, + 0xFFFF58E6, 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + private static final int[] PExtInv = new int[]{ 0xE4BB4457, 0xFFFF58E5, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, + 0x0000A719, 0x00000002 }; + private static final int P4 = 0xFFFFFFFF; + private static final int PExt9 = 0xFFFFFFFF; + private static final int PInv33 = 0x538D; + + public static void add(int[] x, int[] y, int[] z) + { + int c = Nat160.add(x, y, z); + if (c != 0 || (z[4] == P4 && Nat160.gte(z, P))) + { + Nat.add33To(5, PInv33, z); + } + } + + public static void addExt(int[] xx, int[] yy, int[] zz) + { + int c = Nat.add(10, xx, yy, zz); + if (c != 0 || (zz[9] == PExt9 && Nat.gte(10, zz, PExt))) + { + if (Nat.addTo(PExtInv.length, PExtInv, zz) != 0) + { + Nat.incAt(10, zz, PExtInv.length); + } + } + } + + public static void addOne(int[] x, int[] z) + { + int c = Nat.inc(5, x, z); + if (c != 0 || (z[4] == P4 && Nat160.gte(z, P))) + { + Nat.add33To(5, PInv33, z); + } + } + + public static int[] fromBigInteger(BigInteger x) + { + int[] z = Nat160.fromBigInteger(x); + if (z[4] == P4 && Nat160.gte(z, P)) + { + Nat160.subFrom(P, z); + } + return z; + } + + public static void half(int[] x, int[] z) + { + if ((x[0] & 1) == 0) + { + Nat.shiftDownBit(5, x, 0, z); + } + else + { + int c = Nat160.add(x, P, z); + Nat.shiftDownBit(5, z, c); + } + } + + public static void multiply(int[] x, int[] y, int[] z) + { + int[] tt = Nat160.createExt(); + Nat160.mul(x, y, tt); + reduce(tt, z); + } + + public static void multiplyAddToExt(int[] x, int[] y, int[] zz) + { + int c = Nat160.mulAddTo(x, y, zz); + if (c != 0 || (zz[9] == PExt9 && Nat.gte(10, zz, PExt))) + { + if (Nat.addTo(PExtInv.length, PExtInv, zz) != 0) + { + Nat.incAt(10, zz, PExtInv.length); + } + } + } + + public static void negate(int[] x, int[] z) + { + if (Nat160.isZero(x)) + { + Nat160.zero(z); + } + else + { + Nat160.sub(P, x, z); + } + } + + public static void reduce(int[] xx, int[] z) + { + long cc = Nat160.mul33Add(PInv33, xx, 5, xx, 0, z, 0); + int c = Nat160.mul33DWordAdd(PInv33, cc, z, 0); + + // assert c == 0 || c == 1; + + if (c != 0 || (z[4] == P4 && Nat160.gte(z, P))) + { + Nat.add33To(5, PInv33, z); + } + } + + public static void reduce32(int x, int[] z) + { + if ((x != 0 && Nat160.mul33WordAdd(PInv33, x, z, 0) != 0) + || (z[4] == P4 && Nat160.gte(z, P))) + { + Nat.add33To(5, PInv33, z); + } + } + + public static void square(int[] x, int[] z) + { + int[] tt = Nat160.createExt(); + Nat160.square(x, tt); + reduce(tt, z); + } + + public static void squareN(int[] x, int n, int[] z) + { +// assert n > 0; + + int[] tt = Nat160.createExt(); + Nat160.square(x, tt); + reduce(tt, z); + + while (--n > 0) + { + Nat160.square(z, tt); + reduce(tt, z); + } + } + + public static void subtract(int[] x, int[] y, int[] z) + { + int c = Nat160.sub(x, y, z); + if (c != 0) + { + Nat.sub33From(5, PInv33, z); + } + } + + public static void subtractExt(int[] xx, int[] yy, int[] zz) + { + int c = Nat.sub(10, xx, yy, zz); + if (c != 0) + { + if (Nat.subFrom(PExtInv.length, PExtInv, zz) != 0) + { + Nat.decAt(10, zz, PExtInv.length); + } + } + } + + public static void twice(int[] x, int[] z) + { + int c = Nat.shiftUpBit(5, x, 0, z); + if (c != 0 || (z[4] == P4 && Nat160.gte(z, P))) + { + Nat.add33To(5, PInv33, z); + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2FieldElement.java new file mode 100644 index 00000000..37171360 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2FieldElement.java @@ -0,0 +1,217 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.raw.Mod; +import org.bouncycastle.math.raw.Nat160; +import org.bouncycastle.util.Arrays; + +public class SecP160R2FieldElement extends ECFieldElement +{ + public static final BigInteger Q = SecP160R2Curve.q; + + protected int[] x; + + public SecP160R2FieldElement(BigInteger x) + { + if (x == null || x.signum() < 0 || x.compareTo(Q) >= 0) + { + throw new IllegalArgumentException("x value invalid for SecP160R2FieldElement"); + } + + this.x = SecP160R2Field.fromBigInteger(x); + } + + public SecP160R2FieldElement() + { + this.x = Nat160.create(); + } + + protected SecP160R2FieldElement(int[] x) + { + this.x = x; + } + + public boolean isZero() + { + return Nat160.isZero(x); + } + + public boolean isOne() + { + return Nat160.isOne(x); + } + + public boolean testBitZero() + { + return Nat160.getBit(x, 0) == 1; + } + + public BigInteger toBigInteger() + { + return Nat160.toBigInteger(x); + } + + public String getFieldName() + { + return "SecP160R2Field"; + } + + public int getFieldSize() + { + return Q.bitLength(); + } + + public ECFieldElement add(ECFieldElement b) + { + int[] z = Nat160.create(); + SecP160R2Field.add(x, ((SecP160R2FieldElement)b).x, z); + return new SecP160R2FieldElement(z); + } + + public ECFieldElement addOne() + { + int[] z = Nat160.create(); + SecP160R2Field.addOne(x, z); + return new SecP160R2FieldElement(z); + } + + public ECFieldElement subtract(ECFieldElement b) + { + int[] z = Nat160.create(); + SecP160R2Field.subtract(x, ((SecP160R2FieldElement)b).x, z); + return new SecP160R2FieldElement(z); + } + + public ECFieldElement multiply(ECFieldElement b) + { + int[] z = Nat160.create(); + SecP160R2Field.multiply(x, ((SecP160R2FieldElement)b).x, z); + return new SecP160R2FieldElement(z); + } + + public ECFieldElement divide(ECFieldElement b) + { +// return multiply(b.invert()); + int[] z = Nat160.create(); + Mod.invert(SecP160R2Field.P, ((SecP160R2FieldElement)b).x, z); + SecP160R2Field.multiply(z, x, z); + return new SecP160R2FieldElement(z); + } + + public ECFieldElement negate() + { + int[] z = Nat160.create(); + SecP160R2Field.negate(x, z); + return new SecP160R2FieldElement(z); + } + + public ECFieldElement square() + { + int[] z = Nat160.create(); + SecP160R2Field.square(x, z); + return new SecP160R2FieldElement(z); + } + + public ECFieldElement invert() + { +// return new SecP160R2FieldElement(toBigInteger().modInverse(Q)); + int[] z = Nat160.create(); + Mod.invert(SecP160R2Field.P, x, z); + return new SecP160R2FieldElement(z); + } + + // D.1.4 91 + /** + * return a sqrt root - the routine verifies that the calculation returns the right value - if + * none exists it returns null. + */ + public ECFieldElement sqrt() + { + /* + * Raise this element to the exponent 2^158 - 2^30 - 2^12 - 2^10 - 2^7 - 2^6 - 2^5 - 2^1 - 2^0 + * + * Breaking up the exponent's binary representation into "repunits", we get: { 127 1s } { 1 + * 0s } { 17 1s } { 1 0s } { 1 1s } { 1 0s } { 2 1s } { 3 0s } { 3 1s } { 1 0s } { 1 1s } + * + * Therefore we need an addition chain containing 1, 2, 3, 17, 127 (the lengths of the repunits) + * We use: [1], [2], [3], 4, 7, 14, [17], 31, 62, 124, [127] + */ + + int[] x1 = this.x; + if (Nat160.isZero(x1) || Nat160.isOne(x1)) + { + return this; + } + + int[] x2 = Nat160.create(); + SecP160R2Field.square(x1, x2); + SecP160R2Field.multiply(x2, x1, x2); + int[] x3 = Nat160.create(); + SecP160R2Field.square(x2, x3); + SecP160R2Field.multiply(x3, x1, x3); + int[] x4 = Nat160.create(); + SecP160R2Field.square(x3, x4); + SecP160R2Field.multiply(x4, x1, x4); + int[] x7 = Nat160.create(); + SecP160R2Field.squareN(x4, 3, x7); + SecP160R2Field.multiply(x7, x3, x7); + int[] x14 = x4; + SecP160R2Field.squareN(x7, 7, x14); + SecP160R2Field.multiply(x14, x7, x14); + int[] x17 = x7; + SecP160R2Field.squareN(x14, 3, x17); + SecP160R2Field.multiply(x17, x3, x17); + int[] x31 = Nat160.create(); + SecP160R2Field.squareN(x17, 14, x31); + SecP160R2Field.multiply(x31, x14, x31); + int[] x62 = x14; + SecP160R2Field.squareN(x31, 31, x62); + SecP160R2Field.multiply(x62, x31, x62); + int[] x124 = x31; + SecP160R2Field.squareN(x62, 62, x124); + SecP160R2Field.multiply(x124, x62, x124); + int[] x127 = x62; + SecP160R2Field.squareN(x124, 3, x127); + SecP160R2Field.multiply(x127, x3, x127); + + int[] t1 = x127; + SecP160R2Field.squareN(t1, 18, t1); + SecP160R2Field.multiply(t1, x17, t1); + SecP160R2Field.squareN(t1, 2, t1); + SecP160R2Field.multiply(t1, x1, t1); + SecP160R2Field.squareN(t1, 3, t1); + SecP160R2Field.multiply(t1, x2, t1); + SecP160R2Field.squareN(t1, 6, t1); + SecP160R2Field.multiply(t1, x3, t1); + SecP160R2Field.squareN(t1, 2, t1); + SecP160R2Field.multiply(t1, x1, t1); + + int[] t2 = x2; + SecP160R2Field.square(t1, t2); + + return Nat160.eq(x1, t2) ? new SecP160R2FieldElement(t1) : null; + } + + public boolean equals(Object other) + { + if (other == this) + { + return true; + } + + if (!(other instanceof SecP160R2FieldElement)) + { + return false; + } + + SecP160R2FieldElement o = (SecP160R2FieldElement)other; + return Nat160.eq(x, o.x); + } + + public int hashCode() + { + return Q.hashCode() ^ Arrays.hashCode(x, 0, 5); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2Point.java new file mode 100644 index 00000000..49350b87 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecP160R2Point.java @@ -0,0 +1,308 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat160; + +public class SecP160R2Point extends ECPoint.AbstractFp +{ + /** + * 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 SecP160R2Point(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(boolean)} + */ + public SecP160R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecP160R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecP160R2Point(null, getAffineXCoord(), getAffineYCoord()); + } + + public ECPoint add(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return this; + } + if (this == b) + { + return twice(); + } + + ECCurve curve = this.getCurve(); + + SecP160R2FieldElement X1 = (SecP160R2FieldElement)this.x, Y1 = (SecP160R2FieldElement)this.y; + SecP160R2FieldElement X2 = (SecP160R2FieldElement)b.getXCoord(), Y2 = (SecP160R2FieldElement)b.getYCoord(); + + SecP160R2FieldElement Z1 = (SecP160R2FieldElement)this.zs[0]; + SecP160R2FieldElement Z2 = (SecP160R2FieldElement)b.getZCoord(0); + + int c; + int[] tt1 = Nat160.createExt(); + int[] t2 = Nat160.create(); + int[] t3 = Nat160.create(); + int[] t4 = Nat160.create(); + + boolean Z1IsOne = Z1.isOne(); + int[] U2, S2; + if (Z1IsOne) + { + U2 = X2.x; + S2 = Y2.x; + } + else + { + S2 = t3; + SecP160R2Field.square(Z1.x, S2); + + U2 = t2; + SecP160R2Field.multiply(S2, X2.x, U2); + + SecP160R2Field.multiply(S2, Z1.x, S2); + SecP160R2Field.multiply(S2, Y2.x, S2); + } + + boolean Z2IsOne = Z2.isOne(); + int[] U1, S1; + if (Z2IsOne) + { + U1 = X1.x; + S1 = Y1.x; + } + else + { + S1 = t4; + SecP160R2Field.square(Z2.x, S1); + + U1 = tt1; + SecP160R2Field.multiply(S1, X1.x, U1); + + SecP160R2Field.multiply(S1, Z2.x, S1); + SecP160R2Field.multiply(S1, Y1.x, S1); + } + + int[] H = Nat160.create(); + SecP160R2Field.subtract(U1, U2, H); + + int[] R = t2; + SecP160R2Field.subtract(S1, S2, R); + + // Check if b == this or b == -this + if (Nat160.isZero(H)) + { + if (Nat160.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.getInfinity(); + } + + int[] HSquared = t3; + SecP160R2Field.square(H, HSquared); + + int[] G = Nat160.create(); + SecP160R2Field.multiply(HSquared, H, G); + + int[] V = t3; + SecP160R2Field.multiply(HSquared, U1, V); + + SecP160R2Field.negate(G, G); + Nat160.mul(S1, G, tt1); + + c = Nat160.addBothTo(V, V, G); + SecP160R2Field.reduce32(c, G); + + SecP160R2FieldElement X3 = new SecP160R2FieldElement(t4); + SecP160R2Field.square(R, X3.x); + SecP160R2Field.subtract(X3.x, G, X3.x); + + SecP160R2FieldElement Y3 = new SecP160R2FieldElement(G); + SecP160R2Field.subtract(V, X3.x, Y3.x); + SecP160R2Field.multiplyAddToExt(Y3.x, R, tt1); + SecP160R2Field.reduce(tt1, Y3.x); + + SecP160R2FieldElement Z3 = new SecP160R2FieldElement(H); + if (!Z1IsOne) + { + SecP160R2Field.multiply(Z3.x, Z1.x, Z3.x); + } + if (!Z2IsOne) + { + SecP160R2Field.multiply(Z3.x, Z2.x, Z3.x); + } + + ECFieldElement[] zs = new ECFieldElement[]{ Z3 }; + + return new SecP160R2Point(curve, X3, Y3, zs, this.withCompression); + } + + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + SecP160R2FieldElement Y1 = (SecP160R2FieldElement)this.y; + if (Y1.isZero()) + { + return curve.getInfinity(); + } + + SecP160R2FieldElement X1 = (SecP160R2FieldElement)this.x, Z1 = (SecP160R2FieldElement)this.zs[0]; + + int c; + int[] t1 = Nat160.create(); + int[] t2 = Nat160.create(); + + int[] Y1Squared = Nat160.create(); + SecP160R2Field.square(Y1.x, Y1Squared); + + int[] T = Nat160.create(); + SecP160R2Field.square(Y1Squared, T); + + boolean Z1IsOne = Z1.isOne(); + + int[] Z1Squared = Z1.x; + if (!Z1IsOne) + { + Z1Squared = t2; + SecP160R2Field.square(Z1.x, Z1Squared); + } + + SecP160R2Field.subtract(X1.x, Z1Squared, t1); + + int[] M = t2; + SecP160R2Field.add(X1.x, Z1Squared, M); + SecP160R2Field.multiply(M, t1, M); + c = Nat160.addBothTo(M, M, M); + SecP160R2Field.reduce32(c, M); + + int[] S = Y1Squared; + SecP160R2Field.multiply(Y1Squared, X1.x, S); + c = Nat.shiftUpBits(5, S, 2, 0); + SecP160R2Field.reduce32(c, S); + + c = Nat.shiftUpBits(5, T, 3, 0, t1); + SecP160R2Field.reduce32(c, t1); + + SecP160R2FieldElement X3 = new SecP160R2FieldElement(T); + SecP160R2Field.square(M, X3.x); + SecP160R2Field.subtract(X3.x, S, X3.x); + SecP160R2Field.subtract(X3.x, S, X3.x); + + SecP160R2FieldElement Y3 = new SecP160R2FieldElement(S); + SecP160R2Field.subtract(S, X3.x, Y3.x); + SecP160R2Field.multiply(Y3.x, M, Y3.x); + SecP160R2Field.subtract(Y3.x, t1, Y3.x); + + SecP160R2FieldElement Z3 = new SecP160R2FieldElement(M); + SecP160R2Field.twice(Y1.x, Z3.x); + if (!Z1IsOne) + { + SecP160R2Field.multiply(Z3.x, Z1.x, Z3.x); + } + + return new SecP160R2Point(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twicePlus(ECPoint b) + { + if (this == b) + { + return threeTimes(); + } + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return twice(); + } + + ECFieldElement Y1 = this.y; + if (Y1.isZero()) + { + return b; + } + + return twice().add(b); + } + + public ECPoint threeTimes() + { + if (this.isInfinity() || this.y.isZero()) + { + return this; + } + + // NOTE: Be careful about recursions between twicePlus and threeTimes + return twice().add(this); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + return new SecP160R2Point(curve, this.x, this.y.negate(), this.zs, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113Field.java new file mode 100644 index 00000000..17483b02 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113Field.java @@ -0,0 +1,226 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.raw.Interleave; +import org.bouncycastle.math.raw.Nat128; + +public class SecT113Field +{ + private static final long M49 = -1L >>> 15; + private static final long M57 = -1L >>> 7; + + public static void add(long[] x, long[] y, long[] z) + { + z[0] = x[0] ^ y[0]; + z[1] = x[1] ^ y[1]; + } + + public static void addExt(long[] xx, long[] yy, long[] zz) + { + zz[0] = xx[0] ^ yy[0]; + zz[1] = xx[1] ^ yy[1]; + zz[2] = xx[2] ^ yy[2]; + zz[3] = xx[3] ^ yy[3]; + } + + public static void addOne(long[] x, long[] z) + { + z[0] = x[0] ^ 1L; + z[1] = x[1]; + } + + public static long[] fromBigInteger(BigInteger x) + { + long[] z = Nat128.fromBigInteger64(x); + reduce15(z, 0); + return z; + } + + public static void invert(long[] x, long[] z) + { + if (Nat128.isZero64(x)) + { + throw new IllegalStateException(); + } + + // Itoh-Tsujii inversion + + long[] t0 = Nat128.create64(); + long[] t1 = Nat128.create64(); + + square(x, t0); + multiply(t0, x, t0); + square(t0, t0); + multiply(t0, x, t0); + squareN(t0, 3, t1); + multiply(t1, t0, t1); + square(t1, t1); + multiply(t1, x, t1); + squareN(t1, 7, t0); + multiply(t0, t1, t0); + squareN(t0, 14, t1); + multiply(t1, t0, t1); + squareN(t1, 28, t0); + multiply(t0, t1, t0); + squareN(t0, 56, t1); + multiply(t1, t0, t1); + square(t1, z); + } + + public static void multiply(long[] x, long[] y, long[] z) + { + long[] tt = Nat128.createExt64(); + implMultiply(x, y, tt); + reduce(tt, z); + } + + public static void multiplyAddToExt(long[] x, long[] y, long[] zz) + { + long[] tt = Nat128.createExt64(); + implMultiply(x, y, tt); + addExt(zz, tt, zz); + } + + public static void reduce(long[] xx, long[] z) + { + long x0 = xx[0], x1 = xx[1], x2 = xx[2], x3 = xx[3]; + + x1 ^= (x3 << 15) ^ (x3 << 24); + x2 ^= (x3 >>> 49) ^ (x3 >>> 40); + + x0 ^= (x2 << 15) ^ (x2 << 24); + x1 ^= (x2 >>> 49) ^ (x2 >>> 40); + + long t = x1 >>> 49; + z[0] = x0 ^ t ^ (t << 9); + z[1] = x1 & M49; + } + + public static void reduce15(long[] z, int zOff) + { + long z1 = z[zOff + 1], t = z1 >>> 49; + z[zOff ] ^= t ^ (t << 9); + z[zOff + 1] = z1 & M49; + } + + public static void sqrt(long[] x, long[] z) + { + long u0 = Interleave.unshuffle(x[0]), u1 = Interleave.unshuffle(x[1]); + long e0 = (u0 & 0x00000000FFFFFFFFL) | (u1 << 32); + long c0 = (u0 >>> 32) | (u1 & 0xFFFFFFFF00000000L); + + z[0] = e0 ^ (c0 << 57) ^ (c0 << 5); + z[1] = (c0 >>> 7) ^ (c0 >>> 59); + } + + public static void square(long[] x, long[] z) + { + long[] tt = Nat128.createExt64(); + implSquare(x, tt); + reduce(tt, z); + } + + public static void squareAddToExt(long[] x, long[] zz) + { + long[] tt = Nat128.createExt64(); + implSquare(x, tt); + addExt(zz, tt, zz); + } + + public static void squareN(long[] x, int n, long[] z) + { +// assert n > 0; + + long[] tt = Nat128.createExt64(); + implSquare(x, tt); + reduce(tt, z); + + while (--n > 0) + { + implSquare(z, tt); + reduce(tt, z); + } + } + + public static int trace(long[] x) + { + // Non-zero-trace bits: 0 + return (int)(x[0]) & 1; + } + + protected static void implMultiply(long[] x, long[] y, long[] zz) + { + /* + * "Three-way recursion" as described in "Batch binary Edwards", Daniel J. Bernstein. + */ + + long f0 = x[0], f1 = x[1]; + f1 = ((f0 >>> 57) ^ (f1 << 7)) & M57; + f0 &= M57; + + long g0 = y[0], g1 = y[1]; + g1 = ((g0 >>> 57) ^ (g1 << 7)) & M57; + g0 &= M57; + + long[] H = new long[6]; + + implMulw(f0, g0, H, 0); // H(0) 57/56 bits + implMulw(f1, g1, H, 2); // H(INF) 57/54 bits + implMulw(f0 ^ f1, g0 ^ g1, H, 4); // H(1) 57/56 bits + + long r = H[1] ^ H[2]; + long z0 = H[0], + z3 = H[3], + z1 = H[4] ^ z0 ^ r, + z2 = H[5] ^ z3 ^ r; + + zz[0] = z0 ^ (z1 << 57); + zz[1] = (z1 >>> 7) ^ (z2 << 50); + zz[2] = (z2 >>> 14) ^ (z3 << 43); + zz[3] = (z3 >>> 21); + } + + protected static void implMulw(long x, long y, long[] z, int zOff) + { +// assert x >>> 57 == 0; +// assert y >>> 57 == 0; + + long[] u = new long[8]; +// u[0] = 0; + u[1] = y; + u[2] = u[1] << 1; + u[3] = u[2] ^ y; + u[4] = u[2] << 1; + u[5] = u[4] ^ y; + u[6] = u[3] << 1; + u[7] = u[6] ^ y; + + int j = (int)x; + long g, h = 0, l = u[j & 7]; + int k = 48; + do + { + j = (int)(x >>> k); + g = u[j & 7] + ^ u[(j >>> 3) & 7] << 3 + ^ u[(j >>> 6) & 7] << 6; + l ^= (g << k); + h ^= (g >>> -k); + } + while ((k -= 9) > 0); + + h ^= ((x & 0x0100804020100800L) & ((y << 7) >> 63)) >>> 8; + +// assert h >>> 49 == 0; + + z[zOff ] = l & M57; + z[zOff + 1] = (l >>> 57) ^ (h << 7); + } + + protected static void implSquare(long[] x, long[] zz) + { + Interleave.expand64To128(x[0], zz, 0); + Interleave.expand64To128(x[1], zz, 2); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113FieldElement.java new file mode 100644 index 00000000..6fda7ef1 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113FieldElement.java @@ -0,0 +1,221 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.raw.Nat128; +import org.bouncycastle.util.Arrays; + +public class SecT113FieldElement extends ECFieldElement +{ + protected long[] x; + + public SecT113FieldElement(BigInteger x) + { + if (x == null || x.signum() < 0 || x.bitLength() > 113) + { + throw new IllegalArgumentException("x value invalid for SecT113FieldElement"); + } + + this.x = SecT113Field.fromBigInteger(x); + } + + public SecT113FieldElement() + { + this.x = Nat128.create64(); + } + + protected SecT113FieldElement(long[] x) + { + this.x = x; + } + +// public int bitLength() +// { +// return x.degree(); +// } + + public boolean isOne() + { + return Nat128.isOne64(x); + } + + public boolean isZero() + { + return Nat128.isZero64(x); + } + + public boolean testBitZero() + { + return (x[0] & 1L) != 0L; + } + + public BigInteger toBigInteger() + { + return Nat128.toBigInteger64(x); + } + + public String getFieldName() + { + return "SecT113Field"; + } + + public int getFieldSize() + { + return 113; + } + + public ECFieldElement add(ECFieldElement b) + { + long[] z = Nat128.create64(); + SecT113Field.add(x, ((SecT113FieldElement)b).x, z); + return new SecT113FieldElement(z); + } + + public ECFieldElement addOne() + { + long[] z = Nat128.create64(); + SecT113Field.addOne(x, z); + return new SecT113FieldElement(z); + } + + public ECFieldElement subtract(ECFieldElement b) + { + // Addition and subtraction are the same in F2m + return add(b); + } + + public ECFieldElement multiply(ECFieldElement b) + { + long[] z = Nat128.create64(); + SecT113Field.multiply(x, ((SecT113FieldElement)b).x, z); + return new SecT113FieldElement(z); + } + + public ECFieldElement multiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + return multiplyPlusProduct(b, x, y); + } + + public ECFieldElement multiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + long[] ax = this.x, bx = ((SecT113FieldElement)b).x; + long[] xx = ((SecT113FieldElement)x).x, yx = ((SecT113FieldElement)y).x; + + long[] tt = Nat128.createExt64(); + SecT113Field.multiplyAddToExt(ax, bx, tt); + SecT113Field.multiplyAddToExt(xx, yx, tt); + + long[] z = Nat128.create64(); + SecT113Field.reduce(tt, z); + return new SecT113FieldElement(z); + } + + public ECFieldElement divide(ECFieldElement b) + { + return multiply(b.invert()); + } + + public ECFieldElement negate() + { + return this; + } + + public ECFieldElement square() + { + long[] z = Nat128.create64(); + SecT113Field.square(x, z); + return new SecT113FieldElement(z); + } + + public ECFieldElement squareMinusProduct(ECFieldElement x, ECFieldElement y) + { + return squarePlusProduct(x, y); + } + + public ECFieldElement squarePlusProduct(ECFieldElement x, ECFieldElement y) + { + long[] ax = this.x; + long[] xx = ((SecT113FieldElement)x).x, yx = ((SecT113FieldElement)y).x; + + long[] tt = Nat128.createExt64(); + SecT113Field.squareAddToExt(ax, tt); + SecT113Field.multiplyAddToExt(xx, yx, tt); + + long[] z = Nat128.create64(); + SecT113Field.reduce(tt, z); + return new SecT113FieldElement(z); + } + + public ECFieldElement squarePow(int pow) + { + if (pow < 1) + { + return this; + } + + long[] z = Nat128.create64(); + SecT113Field.squareN(x, pow, z); + return new SecT113FieldElement(z); + } + + public ECFieldElement invert() + { + long[] z = Nat128.create64(); + SecT113Field.invert(x, z); + return new SecT113FieldElement(z); + } + + public ECFieldElement sqrt() + { + long[] z = Nat128.create64(); + SecT113Field.sqrt(x, z); + return new SecT113FieldElement(z); + } + + public int getRepresentation() + { + return ECFieldElement.F2m.TPB; + } + + public int getM() + { + return 113; + } + + public int getK1() + { + return 9; + } + + public int getK2() + { + return 0; + } + + public int getK3() + { + return 0; + } + + public boolean equals(Object other) + { + if (other == this) + { + return true; + } + + if (!(other instanceof SecT113FieldElement)) + { + return false; + } + + SecT113FieldElement o = (SecT113FieldElement)other; + return Nat128.eq64(x, o.x); + } + + public int hashCode() + { + return 113009 ^ Arrays.hashCode(x, 0, 2); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R1Curve.java new file mode 100644 index 00000000..7c12d0a3 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R1Curve.java @@ -0,0 +1,101 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECCurve.AbstractF2m; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; + +public class SecT113R1Curve extends AbstractF2m +{ + private static final int SecT113R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected SecT113R1Point infinity; + + public SecT113R1Curve() + { + super(113, 9, 0, 0); + + this.infinity = new SecT113R1Point(this, null, null); + + this.a = fromBigInteger(new BigInteger(1, Hex.decode("003088250CA6E7C7FE649CE85820F7"))); + this.b = fromBigInteger(new BigInteger(1, Hex.decode("00E8BEE4D3E2260744188BE0E9C723"))); + this.order = new BigInteger(1, Hex.decode("0100000000000000D9CCEC8A39E56F")); + this.cofactor = BigInteger.valueOf(2); + + this.coord = SecT113R1_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecT113R1Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + public int getFieldSize() + { + return 113; + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecT113FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecT113R1Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecT113R1Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } + + public boolean isKoblitz() + { + return false; + } + + public int getM() + { + return 113; + } + + public boolean isTrinomial() + { + return true; + } + + public int getK1() + { + return 9; + } + + public int getK2() + { + return 0; + } + + public int getK3() + { + return 0; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R1Point.java new file mode 100644 index 00000000..28226c28 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R1Point.java @@ -0,0 +1,308 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECConstants; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.ECPoint.AbstractF2m; + +public class SecT113R1Point extends AbstractF2m +{ + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT113R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + { + this(curve, x, y, false); + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)} + */ + public SecT113R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecT113R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecT113R1Point(null, getAffineXCoord(), getAffineYCoord()); + } + + public ECFieldElement getYCoord() + { + ECFieldElement X = x, L = y; + + 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); + + ECFieldElement Z = zs[0]; + if (!Z.isOne()) + { + Y = Y.divide(Z); + } + + return Y; + } + + protected boolean getCompressionYTilde() + { + ECFieldElement X = this.getRawXCoord(); + if (X.isZero()) + { + return false; + } + + ECFieldElement Y = this.getRawYCoord(); + + // Y is actually Lambda (X + Y/X) here + return Y.testBitZero() != X.testBitZero(); + } + + public ECPoint add(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + ECFieldElement X2 = b.getRawXCoord(); + + if (X1.isZero()) + { + if (X2.isZero()) + { + return curve.getInfinity(); + } + + return b.add(this); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0); + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.multiply(Z1); + S2 = S2.multiply(Z1); + } + + boolean 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 twice(); + } + + return curve.getInfinity(); + } + + ECFieldElement X3, L3, Z3; + if (X2.isZero()) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.normalize(); + X1 = p.getXCoord(); + ECFieldElement Y1 = p.getYCoord(); + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.add(Y2).divide(X1); + + X3 = L.square().add(L).add(X1).add(curve.getA()); + if (X3.isZero()) + { + return new SecT113R1Point(curve, X3, curve.getB().sqrt(), this.withCompression); + } + + ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1); + L3 = Y3.divide(X3).add(X3); + Z3 = curve.fromBigInteger(ECConstants.ONE); + } + else + { + B = B.square(); + + ECFieldElement AU1 = A.multiply(U1); + ECFieldElement AU2 = A.multiply(U2); + + X3 = AU1.multiply(AU2); + if (X3.isZero()) + { + return new SecT113R1Point(curve, X3, curve.getB().sqrt(), this.withCompression); + } + + 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 SecT113R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return curve.getInfinity(); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square(); + ECFieldElement a = curve.getA(); + ECFieldElement aZ1Sq = Z1IsOne ? a : a.multiply(Z1Sq); + ECFieldElement T = L1.square().add(L1Z1).add(aZ1Sq); + if (T.isZero()) + { + return new SecT113R1Point(curve, T, curve.getB().sqrt(), withCompression); + } + + ECFieldElement X3 = T.square(); + ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq); + + ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1); + ECFieldElement L3 = X1Z1.squarePlusProduct(T, L1Z1).add(X3).add(Z3); + + return new SecT113R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twicePlus(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return twice(); + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return b; + } + + ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0); + if (X2.isZero() || !Z2.isOne()) + { + return twice().add(b); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(); + + ECFieldElement X1Sq = X1.square(); + ECFieldElement L1Sq = L1.square(); + ECFieldElement Z1Sq = Z1.square(); + ECFieldElement L1Z1 = L1.multiply(Z1); + + ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1); + ECFieldElement L2plus1 = L2.addOne(); + ECFieldElement A = curve.getA().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.getInfinity(); + } + + if (A.isZero()) + { + return new SecT113R1Point(curve, A, curve.getB().sqrt(), withCompression); + } + + 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 SecT113R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + ECFieldElement X = this.x; + if (X.isZero()) + { + return this; + } + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.y, Z = this.zs[0]; + return new SecT113R1Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R2Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R2Curve.java new file mode 100644 index 00000000..209987bc --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R2Curve.java @@ -0,0 +1,101 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECCurve.AbstractF2m; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; + +public class SecT113R2Curve extends AbstractF2m +{ + private static final int SecT113R2_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected SecT113R2Point infinity; + + public SecT113R2Curve() + { + super(113, 9, 0, 0); + + this.infinity = new SecT113R2Point(this, null, null); + + this.a = fromBigInteger(new BigInteger(1, Hex.decode("00689918DBEC7E5A0DD6DFC0AA55C7"))); + this.b = fromBigInteger(new BigInteger(1, Hex.decode("0095E9A9EC9B297BD4BF36E059184F"))); + this.order = new BigInteger(1, Hex.decode("010000000000000108789B2496AF93")); + this.cofactor = BigInteger.valueOf(2); + + this.coord = SecT113R2_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecT113R2Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + public int getFieldSize() + { + return 113; + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecT113FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecT113R2Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecT113R2Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } + + public boolean isKoblitz() + { + return false; + } + + public int getM() + { + return 113; + } + + public boolean isTrinomial() + { + return true; + } + + public int getK1() + { + return 9; + } + + public int getK2() + { + return 0; + } + + public int getK3() + { + return 0; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R2Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R2Point.java new file mode 100644 index 00000000..6b7a2de3 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT113R2Point.java @@ -0,0 +1,308 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECConstants; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.ECPoint.AbstractF2m; + +public class SecT113R2Point extends AbstractF2m +{ + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT113R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + { + this(curve, x, y, false); + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)} + */ + public SecT113R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecT113R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecT113R2Point(null, getAffineXCoord(), getAffineYCoord()); + } + + public ECFieldElement getYCoord() + { + ECFieldElement X = x, L = y; + + 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); + + ECFieldElement Z = zs[0]; + if (!Z.isOne()) + { + Y = Y.divide(Z); + } + + return Y; + } + + protected boolean getCompressionYTilde() + { + ECFieldElement X = this.getRawXCoord(); + if (X.isZero()) + { + return false; + } + + ECFieldElement Y = this.getRawYCoord(); + + // Y is actually Lambda (X + Y/X) here + return Y.testBitZero() != X.testBitZero(); + } + + public ECPoint add(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + ECFieldElement X2 = b.getRawXCoord(); + + if (X1.isZero()) + { + if (X2.isZero()) + { + return curve.getInfinity(); + } + + return b.add(this); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0); + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.multiply(Z1); + S2 = S2.multiply(Z1); + } + + boolean 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 twice(); + } + + return curve.getInfinity(); + } + + ECFieldElement X3, L3, Z3; + if (X2.isZero()) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.normalize(); + X1 = p.getXCoord(); + ECFieldElement Y1 = p.getYCoord(); + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.add(Y2).divide(X1); + + X3 = L.square().add(L).add(X1).add(curve.getA()); + if (X3.isZero()) + { + return new SecT113R2Point(curve, X3, curve.getB().sqrt(), this.withCompression); + } + + ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1); + L3 = Y3.divide(X3).add(X3); + Z3 = curve.fromBigInteger(ECConstants.ONE); + } + else + { + B = B.square(); + + ECFieldElement AU1 = A.multiply(U1); + ECFieldElement AU2 = A.multiply(U2); + + X3 = AU1.multiply(AU2); + if (X3.isZero()) + { + return new SecT113R2Point(curve, X3, curve.getB().sqrt(), this.withCompression); + } + + 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 SecT113R2Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return curve.getInfinity(); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square(); + ECFieldElement a = curve.getA(); + ECFieldElement aZ1Sq = Z1IsOne ? a : a.multiply(Z1Sq); + ECFieldElement T = L1.square().add(L1Z1).add(aZ1Sq); + if (T.isZero()) + { + return new SecT113R2Point(curve, T, curve.getB().sqrt(), withCompression); + } + + ECFieldElement X3 = T.square(); + ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq); + + ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1); + ECFieldElement L3 = X1Z1.squarePlusProduct(T, L1Z1).add(X3).add(Z3); + + return new SecT113R2Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twicePlus(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return twice(); + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return b; + } + + ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0); + if (X2.isZero() || !Z2.isOne()) + { + return twice().add(b); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(); + + ECFieldElement X1Sq = X1.square(); + ECFieldElement L1Sq = L1.square(); + ECFieldElement Z1Sq = Z1.square(); + ECFieldElement L1Z1 = L1.multiply(Z1); + + ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1); + ECFieldElement L2plus1 = L2.addOne(); + ECFieldElement A = curve.getA().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.getInfinity(); + } + + if (A.isZero()) + { + return new SecT113R2Point(curve, A, curve.getB().sqrt(), withCompression); + } + + 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 SecT113R2Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + ECFieldElement X = this.x; + if (X.isZero()) + { + return this; + } + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.y, Z = this.zs[0]; + return new SecT113R2Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131Field.java new file mode 100644 index 00000000..ab25c5df --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131Field.java @@ -0,0 +1,332 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.raw.Interleave; +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat192; + +public class SecT131Field +{ + private static final long M03 = -1L >>> 61; + private static final long M44 = -1L >>> 20; + + private static final long[] ROOT_Z = new long[]{ 0x26BC4D789AF13523L, 0x26BC4D789AF135E2L, 0x6L }; + + public static void add(long[] x, long[] y, long[] z) + { + z[0] = x[0] ^ y[0]; + z[1] = x[1] ^ y[1]; + z[2] = x[2] ^ y[2]; + } + + public static void addExt(long[] xx, long[] yy, long[] zz) + { + zz[0] = xx[0] ^ yy[0]; + zz[1] = xx[1] ^ yy[1]; + zz[2] = xx[2] ^ yy[2]; + zz[3] = xx[3] ^ yy[3]; + zz[4] = xx[4] ^ yy[4]; + } + + public static void addOne(long[] x, long[] z) + { + z[0] = x[0] ^ 1L; + z[1] = x[1]; + z[2] = x[2]; + } + + public static long[] fromBigInteger(BigInteger x) + { + long[] z = Nat192.fromBigInteger64(x); + reduce61(z, 0); + return z; + } + + public static void invert(long[] x, long[] z) + { + if (Nat192.isZero64(x)) + { + throw new IllegalStateException(); + } + + // Itoh-Tsujii inversion + + long[] t0 = Nat192.create64(); + long[] t1 = Nat192.create64(); + + square(x, t0); + multiply(t0, x, t0); + squareN(t0, 2, t1); + multiply(t1, t0, t1); + squareN(t1, 4, t0); + multiply(t0, t1, t0); + squareN(t0, 8, t1); + multiply(t1, t0, t1); + squareN(t1, 16, t0); + multiply(t0, t1, t0); + squareN(t0, 32, t1); + multiply(t1, t0, t1); + square(t1, t1); + multiply(t1, x, t1); + squareN(t1, 65, t0); + multiply(t0, t1, t0); + square(t0, z); + } + + public static void multiply(long[] x, long[] y, long[] z) + { + long[] tt = Nat192.createExt64(); + implMultiply(x, y, tt); + reduce(tt, z); + } + + public static void multiplyAddToExt(long[] x, long[] y, long[] zz) + { + long[] tt = Nat192.createExt64(); + implMultiply(x, y, tt); + addExt(zz, tt, zz); + } + + public static void reduce(long[] xx, long[] z) + { + long x0 = xx[0], x1 = xx[1], x2 = xx[2], x3 = xx[3], x4 = xx[4]; + + x1 ^= (x4 << 61) ^ (x4 << 63); + x2 ^= (x4 >>> 3) ^ (x4 >>> 1) ^ x4 ^ (x4 << 5); + x3 ^= (x4 >>> 59); + + x0 ^= (x3 << 61) ^ (x3 << 63); + x1 ^= (x3 >>> 3) ^ (x3 >>> 1) ^ x3 ^ (x3 << 5); + x2 ^= (x3 >>> 59); + + long t = x2 >>> 3; + z[0] = x0 ^ t ^ (t << 2) ^ (t << 3) ^ (t << 8); + z[1] = x1 ^ (t >>> 56); + z[2] = x2 & M03; + } + + public static void reduce61(long[] z, int zOff) + { + long z2 = z[zOff + 2], t = z2 >>> 3; + z[zOff ] ^= t ^ (t << 2) ^ (t << 3) ^ (t << 8); + z[zOff + 1] ^= (t >>> 56); + z[zOff + 2] = z2 & M03; + } + + public static void sqrt(long[] x, long[] z) + { + long[] odd = Nat192.create64(); + + long u0, u1; + u0 = Interleave.unshuffle(x[0]); u1 = Interleave.unshuffle(x[1]); + long e0 = (u0 & 0x00000000FFFFFFFFL) | (u1 << 32); + odd[0] = (u0 >>> 32) | (u1 & 0xFFFFFFFF00000000L); + + u0 = Interleave.unshuffle(x[2]); + long e1 = (u0 & 0x00000000FFFFFFFFL); + odd[1] = (u0 >>> 32); + + multiply(odd, ROOT_Z, z); + + z[0] ^= e0; + z[1] ^= e1; + } + + public static void square(long[] x, long[] z) + { + long[] tt = Nat.create64(5); + implSquare(x, tt); + reduce(tt, z); + } + + public static void squareAddToExt(long[] x, long[] zz) + { + long[] tt = Nat.create64(5); + implSquare(x, tt); + addExt(zz, tt, zz); + } + + public static void squareN(long[] x, int n, long[] z) + { +// assert n > 0; + + long[] tt = Nat.create64(5); + implSquare(x, tt); + reduce(tt, z); + + while (--n > 0) + { + implSquare(z, tt); + reduce(tt, z); + } + } + + public static int trace(long[] x) + { + // Non-zero-trace bits: 0, 123, 129 + return (int)(x[0] ^ (x[1] >>> 59) ^ (x[2] >>> 1)) & 1; + } + + protected static void implCompactExt(long[] zz) + { + long z0 = zz[0], z1 = zz[1], z2 = zz[2], z3 = zz[3], z4 = zz[4], z5 = zz[5]; + zz[0] = z0 ^ (z1 << 44); + zz[1] = (z1 >>> 20) ^ (z2 << 24); + zz[2] = (z2 >>> 40) ^ (z3 << 4) + ^ (z4 << 48); + zz[3] = (z3 >>> 60) ^ (z5 << 28) + ^ (z4 >>> 16); + zz[4] = (z5 >>> 36); + zz[5] = 0; + } + + protected static void implMultiply(long[] x, long[] y, long[] zz) + { + /* + * "Five-way recursion" as described in "Batch binary Edwards", Daniel J. Bernstein. + */ + + long f0 = x[0], f1 = x[1], f2 = x[2]; + f2 = ((f1 >>> 24) ^ (f2 << 40)) & M44; + f1 = ((f0 >>> 44) ^ (f1 << 20)) & M44; + f0 &= M44; + + long g0 = y[0], g1 = y[1], g2 = y[2]; + g2 = ((g1 >>> 24) ^ (g2 << 40)) & M44; + g1 = ((g0 >>> 44) ^ (g1 << 20)) & M44; + g0 &= M44; + + long[] H = new long[10]; + + implMulw(f0, g0, H, 0); // H(0) 44/43 bits + implMulw(f2, g2, H, 2); // H(INF) 44/41 bits + + long t0 = f0 ^ f1 ^ f2; + long t1 = g0 ^ g1 ^ g2; + + implMulw(t0, t1, H, 4); // H(1) 44/43 bits + + long t2 = (f1 << 1) ^ (f2 << 2); + long t3 = (g1 << 1) ^ (g2 << 2); + + implMulw(f0 ^ t2, g0 ^ t3, H, 6); // H(t) 44/45 bits + implMulw(t0 ^ t2, t1 ^ t3, H, 8); // H(t + 1) 44/45 bits + + long t4 = H[6] ^ H[8]; + long t5 = H[7] ^ H[9]; + + // assert t5 >>> 44 == 0; + + // Calculate V + long v0 = (t4 << 1) ^ H[6]; + long v1 = t4 ^ (t5 << 1) ^ H[7]; + long v2 = t5; + + // Calculate U + long u0 = H[0]; + long u1 = H[1] ^ H[0] ^ H[4]; + long u2 = H[1] ^ H[5]; + + // Calculate W + long w0 = u0 ^ v0 ^ (H[2] << 4) ^ (H[2] << 1); + long w1 = u1 ^ v1 ^ (H[3] << 4) ^ (H[3] << 1); + long w2 = u2 ^ v2; + + // Propagate carries + w1 ^= (w0 >>> 44); w0 &= M44; + w2 ^= (w1 >>> 44); w1 &= M44; + + // assert (w0 & 1L) == 0; + + // Divide W by t + + w0 = (w0 >>> 1) ^ ((w1 & 1L) << 43); + w1 = (w1 >>> 1) ^ ((w2 & 1L) << 43); + w2 = (w2 >>> 1); + + // Divide W by (t + 1) + + w0 ^= (w0 << 1); + w0 ^= (w0 << 2); + w0 ^= (w0 << 4); + w0 ^= (w0 << 8); + w0 ^= (w0 << 16); + w0 ^= (w0 << 32); + + w0 &= M44; w1 ^= (w0 >>> 43); + + w1 ^= (w1 << 1); + w1 ^= (w1 << 2); + w1 ^= (w1 << 4); + w1 ^= (w1 << 8); + w1 ^= (w1 << 16); + w1 ^= (w1 << 32); + + w1 &= M44; w2 ^= (w1 >>> 43); + + w2 ^= (w2 << 1); + w2 ^= (w2 << 2); + w2 ^= (w2 << 4); + w2 ^= (w2 << 8); + w2 ^= (w2 << 16); + w2 ^= (w2 << 32); + + // assert w2 >>> 42 == 0; + + zz[0] = u0; + zz[1] = u1 ^ w0 ^ H[2]; + zz[2] = u2 ^ w1 ^ w0 ^ H[3]; + zz[3] = w2 ^ w1; + zz[4] = w2 ^ H[2]; + zz[5] = H[3]; + + implCompactExt(zz); + } + + protected static void implMulw(long x, long y, long[] z, int zOff) + { +// assert x >>> 45 == 0; +// assert y >>> 45 == 0; + + long[] u = new long[8]; +// u[0] = 0; + u[1] = y; + u[2] = u[1] << 1; + u[3] = u[2] ^ y; + u[4] = u[2] << 1; + u[5] = u[4] ^ y; + u[6] = u[3] << 1; + u[7] = u[6] ^ y; + + int j = (int)x; + long g, h = 0, l = u[j & 7] + ^ u[(j >>> 3) & 7] << 3 + ^ u[(j >>> 6) & 7] << 6; + int k = 33; + do + { + j = (int)(x >>> k); + g = u[j & 7] + ^ u[(j >>> 3) & 7] << 3 + ^ u[(j >>> 6) & 7] << 6 + ^ u[(j >>> 9) & 7] << 9; + l ^= (g << k); + h ^= (g >>> -k); + } + while ((k -= 12) > 0); + +// assert h >>> 25 == 0; + + z[zOff ] = l & M44; + z[zOff + 1] = (l >>> 44) ^ (h << 20); + } + + protected static void implSquare(long[] x, long[] zz) + { + Interleave.expand64To128(x[0], zz, 0); + Interleave.expand64To128(x[1], zz, 2); + + zz[4] = Interleave.expand8to16((int)x[2]) & 0xFFFFFFFFL; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131FieldElement.java new file mode 100644 index 00000000..5dc18c5e --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131FieldElement.java @@ -0,0 +1,222 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat192; +import org.bouncycastle.util.Arrays; + +public class SecT131FieldElement extends ECFieldElement +{ + protected long[] x; + + public SecT131FieldElement(BigInteger x) + { + if (x == null || x.signum() < 0 || x.bitLength() > 131) + { + throw new IllegalArgumentException("x value invalid for SecT131FieldElement"); + } + + this.x = SecT131Field.fromBigInteger(x); + } + + public SecT131FieldElement() + { + this.x = Nat192.create64(); + } + + protected SecT131FieldElement(long[] x) + { + this.x = x; + } + +// public int bitLength() +// { +// return x.degree(); +// } + + public boolean isOne() + { + return Nat192.isOne64(x); + } + + public boolean isZero() + { + return Nat192.isZero64(x); + } + + public boolean testBitZero() + { + return (x[0] & 1L) != 0L; + } + + public BigInteger toBigInteger() + { + return Nat192.toBigInteger64(x); + } + + public String getFieldName() + { + return "SecT131Field"; + } + + public int getFieldSize() + { + return 131; + } + + public ECFieldElement add(ECFieldElement b) + { + long[] z = Nat192.create64(); + SecT131Field.add(x, ((SecT131FieldElement)b).x, z); + return new SecT131FieldElement(z); + } + + public ECFieldElement addOne() + { + long[] z = Nat192.create64(); + SecT131Field.addOne(x, z); + return new SecT131FieldElement(z); + } + + public ECFieldElement subtract(ECFieldElement b) + { + // Addition and subtraction are the same in F2m + return add(b); + } + + public ECFieldElement multiply(ECFieldElement b) + { + long[] z = Nat192.create64(); + SecT131Field.multiply(x, ((SecT131FieldElement)b).x, z); + return new SecT131FieldElement(z); + } + + public ECFieldElement multiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + return multiplyPlusProduct(b, x, y); + } + + public ECFieldElement multiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + long[] ax = this.x, bx = ((SecT131FieldElement)b).x; + long[] xx = ((SecT131FieldElement)x).x, yx = ((SecT131FieldElement)y).x; + + long[] tt = Nat.create64(5); + SecT131Field.multiplyAddToExt(ax, bx, tt); + SecT131Field.multiplyAddToExt(xx, yx, tt); + + long[] z = Nat192.create64(); + SecT131Field.reduce(tt, z); + return new SecT131FieldElement(z); + } + + public ECFieldElement divide(ECFieldElement b) + { + return multiply(b.invert()); + } + + public ECFieldElement negate() + { + return this; + } + + public ECFieldElement square() + { + long[] z = Nat192.create64(); + SecT131Field.square(x, z); + return new SecT131FieldElement(z); + } + + public ECFieldElement squareMinusProduct(ECFieldElement x, ECFieldElement y) + { + return squarePlusProduct(x, y); + } + + public ECFieldElement squarePlusProduct(ECFieldElement x, ECFieldElement y) + { + long[] ax = this.x; + long[] xx = ((SecT131FieldElement)x).x, yx = ((SecT131FieldElement)y).x; + + long[] tt = Nat.create64(5); + SecT131Field.squareAddToExt(ax, tt); + SecT131Field.multiplyAddToExt(xx, yx, tt); + + long[] z = Nat192.create64(); + SecT131Field.reduce(tt, z); + return new SecT131FieldElement(z); + } + + public ECFieldElement squarePow(int pow) + { + if (pow < 1) + { + return this; + } + + long[] z = Nat192.create64(); + SecT131Field.squareN(x, pow, z); + return new SecT131FieldElement(z); + } + + public ECFieldElement invert() + { + long[] z = Nat192.create64(); + SecT131Field.invert(x, z); + return new SecT131FieldElement(z); + } + + public ECFieldElement sqrt() + { + long[] z = Nat192.create64(); + SecT131Field.sqrt(x, z); + return new SecT131FieldElement(z); + } + + public int getRepresentation() + { + return ECFieldElement.F2m.PPB; + } + + public int getM() + { + return 131; + } + + public int getK1() + { + return 2; + } + + public int getK2() + { + return 3; + } + + public int getK3() + { + return 8; + } + + public boolean equals(Object other) + { + if (other == this) + { + return true; + } + + if (!(other instanceof SecT131FieldElement)) + { + return false; + } + + SecT131FieldElement o = (SecT131FieldElement)other; + return Nat192.eq64(x, o.x); + } + + public int hashCode() + { + return 131832 ^ Arrays.hashCode(x, 0, 3); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R1Curve.java new file mode 100644 index 00000000..6b216adf --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R1Curve.java @@ -0,0 +1,101 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECCurve.AbstractF2m; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; + +public class SecT131R1Curve extends AbstractF2m +{ + private static final int SecT131R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected SecT131R1Point infinity; + + public SecT131R1Curve() + { + super(131, 2, 3, 8); + + this.infinity = new SecT131R1Point(this, null, null); + + this.a = fromBigInteger(new BigInteger(1, Hex.decode("07A11B09A76B562144418FF3FF8C2570B8"))); + this.b = fromBigInteger(new BigInteger(1, Hex.decode("0217C05610884B63B9C6C7291678F9D341"))); + this.order = new BigInteger(1, Hex.decode("0400000000000000023123953A9464B54D")); + this.cofactor = BigInteger.valueOf(2); + + this.coord = SecT131R1_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecT131R1Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + public int getFieldSize() + { + return 131; + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecT131FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecT131R1Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecT131R1Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } + + public boolean isKoblitz() + { + return false; + } + + public int getM() + { + return 131; + } + + public boolean isTrinomial() + { + return false; + } + + public int getK1() + { + return 2; + } + + public int getK2() + { + return 3; + } + + public int getK3() + { + return 8; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R1Point.java new file mode 100644 index 00000000..4a276dd5 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R1Point.java @@ -0,0 +1,308 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECConstants; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.ECPoint.AbstractF2m; + +public class SecT131R1Point extends AbstractF2m +{ + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT131R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + { + this(curve, x, y, false); + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)} + */ + public SecT131R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecT131R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecT131R1Point(null, getAffineXCoord(), getAffineYCoord()); + } + + public ECFieldElement getYCoord() + { + ECFieldElement X = x, L = y; + + 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); + + ECFieldElement Z = zs[0]; + if (!Z.isOne()) + { + Y = Y.divide(Z); + } + + return Y; + } + + protected boolean getCompressionYTilde() + { + ECFieldElement X = this.getRawXCoord(); + if (X.isZero()) + { + return false; + } + + ECFieldElement Y = this.getRawYCoord(); + + // Y is actually Lambda (X + Y/X) here + return Y.testBitZero() != X.testBitZero(); + } + + public ECPoint add(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + ECFieldElement X2 = b.getRawXCoord(); + + if (X1.isZero()) + { + if (X2.isZero()) + { + return curve.getInfinity(); + } + + return b.add(this); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0); + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.multiply(Z1); + S2 = S2.multiply(Z1); + } + + boolean 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 twice(); + } + + return curve.getInfinity(); + } + + ECFieldElement X3, L3, Z3; + if (X2.isZero()) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.normalize(); + X1 = p.getXCoord(); + ECFieldElement Y1 = p.getYCoord(); + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.add(Y2).divide(X1); + + X3 = L.square().add(L).add(X1).add(curve.getA()); + if (X3.isZero()) + { + return new SecT131R1Point(curve, X3, curve.getB().sqrt(), this.withCompression); + } + + ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1); + L3 = Y3.divide(X3).add(X3); + Z3 = curve.fromBigInteger(ECConstants.ONE); + } + else + { + B = B.square(); + + ECFieldElement AU1 = A.multiply(U1); + ECFieldElement AU2 = A.multiply(U2); + + X3 = AU1.multiply(AU2); + if (X3.isZero()) + { + return new SecT131R1Point(curve, X3, curve.getB().sqrt(), this.withCompression); + } + + 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 SecT131R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return curve.getInfinity(); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square(); + ECFieldElement a = curve.getA(); + ECFieldElement aZ1Sq = Z1IsOne ? a : a.multiply(Z1Sq); + ECFieldElement T = L1.square().add(L1Z1).add(aZ1Sq); + if (T.isZero()) + { + return new SecT131R1Point(curve, T, curve.getB().sqrt(), withCompression); + } + + ECFieldElement X3 = T.square(); + ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq); + + ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1); + ECFieldElement L3 = X1Z1.squarePlusProduct(T, L1Z1).add(X3).add(Z3); + + return new SecT131R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twicePlus(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return twice(); + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return b; + } + + ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0); + if (X2.isZero() || !Z2.isOne()) + { + return twice().add(b); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(); + + ECFieldElement X1Sq = X1.square(); + ECFieldElement L1Sq = L1.square(); + ECFieldElement Z1Sq = Z1.square(); + ECFieldElement L1Z1 = L1.multiply(Z1); + + ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1); + ECFieldElement L2plus1 = L2.addOne(); + ECFieldElement A = curve.getA().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.getInfinity(); + } + + if (A.isZero()) + { + return new SecT131R1Point(curve, A, curve.getB().sqrt(), withCompression); + } + + 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 SecT131R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + ECFieldElement X = this.x; + if (X.isZero()) + { + return this; + } + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.y, Z = this.zs[0]; + return new SecT131R1Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R2Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R2Curve.java new file mode 100644 index 00000000..8d19e375 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R2Curve.java @@ -0,0 +1,101 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECCurve.AbstractF2m; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; + +public class SecT131R2Curve extends AbstractF2m +{ + private static final int SecT131R2_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected SecT131R2Point infinity; + + public SecT131R2Curve() + { + super(131, 2, 3, 8); + + this.infinity = new SecT131R2Point(this, null, null); + + this.a = fromBigInteger(new BigInteger(1, Hex.decode("03E5A88919D7CAFCBF415F07C2176573B2"))); + this.b = fromBigInteger(new BigInteger(1, Hex.decode("04B8266A46C55657AC734CE38F018F2192"))); + this.order = new BigInteger(1, Hex.decode("0400000000000000016954A233049BA98F")); + this.cofactor = BigInteger.valueOf(2); + + this.coord = SecT131R2_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecT131R2Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + public int getFieldSize() + { + return 131; + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecT131FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecT131R2Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecT131R2Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } + + public boolean isKoblitz() + { + return false; + } + + public int getM() + { + return 131; + } + + public boolean isTrinomial() + { + return false; + } + + public int getK1() + { + return 2; + } + + public int getK2() + { + return 3; + } + + public int getK3() + { + return 8; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R2Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R2Point.java new file mode 100644 index 00000000..7e85c63a --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT131R2Point.java @@ -0,0 +1,308 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECConstants; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.ECPoint.AbstractF2m; + +public class SecT131R2Point extends AbstractF2m +{ + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT131R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + { + this(curve, x, y, false); + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)} + */ + public SecT131R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecT131R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecT131R2Point(null, getAffineXCoord(), getAffineYCoord()); + } + + public ECFieldElement getYCoord() + { + ECFieldElement X = x, L = y; + + 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); + + ECFieldElement Z = zs[0]; + if (!Z.isOne()) + { + Y = Y.divide(Z); + } + + return Y; + } + + protected boolean getCompressionYTilde() + { + ECFieldElement X = this.getRawXCoord(); + if (X.isZero()) + { + return false; + } + + ECFieldElement Y = this.getRawYCoord(); + + // Y is actually Lambda (X + Y/X) here + return Y.testBitZero() != X.testBitZero(); + } + + public ECPoint add(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + ECFieldElement X2 = b.getRawXCoord(); + + if (X1.isZero()) + { + if (X2.isZero()) + { + return curve.getInfinity(); + } + + return b.add(this); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0); + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.multiply(Z1); + S2 = S2.multiply(Z1); + } + + boolean 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 twice(); + } + + return curve.getInfinity(); + } + + ECFieldElement X3, L3, Z3; + if (X2.isZero()) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.normalize(); + X1 = p.getXCoord(); + ECFieldElement Y1 = p.getYCoord(); + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.add(Y2).divide(X1); + + X3 = L.square().add(L).add(X1).add(curve.getA()); + if (X3.isZero()) + { + return new SecT131R2Point(curve, X3, curve.getB().sqrt(), this.withCompression); + } + + ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1); + L3 = Y3.divide(X3).add(X3); + Z3 = curve.fromBigInteger(ECConstants.ONE); + } + else + { + B = B.square(); + + ECFieldElement AU1 = A.multiply(U1); + ECFieldElement AU2 = A.multiply(U2); + + X3 = AU1.multiply(AU2); + if (X3.isZero()) + { + return new SecT131R2Point(curve, X3, curve.getB().sqrt(), this.withCompression); + } + + 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 SecT131R2Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return curve.getInfinity(); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square(); + ECFieldElement a = curve.getA(); + ECFieldElement aZ1Sq = Z1IsOne ? a : a.multiply(Z1Sq); + ECFieldElement T = L1.square().add(L1Z1).add(aZ1Sq); + if (T.isZero()) + { + return new SecT131R2Point(curve, T, curve.getB().sqrt(), withCompression); + } + + ECFieldElement X3 = T.square(); + ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq); + + ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1); + ECFieldElement L3 = X1Z1.squarePlusProduct(T, L1Z1).add(X3).add(Z3); + + return new SecT131R2Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twicePlus(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return twice(); + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return b; + } + + ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0); + if (X2.isZero() || !Z2.isOne()) + { + return twice().add(b); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(); + + ECFieldElement X1Sq = X1.square(); + ECFieldElement L1Sq = L1.square(); + ECFieldElement Z1Sq = Z1.square(); + ECFieldElement L1Z1 = L1.multiply(Z1); + + ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1); + ECFieldElement L2plus1 = L2.addOne(); + ECFieldElement A = curve.getA().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.getInfinity(); + } + + if (A.isZero()) + { + return new SecT131R2Point(curve, A, curve.getB().sqrt(), withCompression); + } + + 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 SecT131R2Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + ECFieldElement X = this.x; + if (X.isZero()) + { + return this; + } + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.y, Z = this.zs[0]; + return new SecT131R2Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163Field.java new file mode 100644 index 00000000..12eca28d --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163Field.java @@ -0,0 +1,341 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.raw.Interleave; +import org.bouncycastle.math.raw.Nat192; + +public class SecT163Field +{ + private static final long M35 = -1L >>> 29; + private static final long M55 = -1L >>> 9; + + private static final long[] ROOT_Z = new long[]{ 0xB6DB6DB6DB6DB6B0L, 0x492492492492DB6DL, 0x492492492L }; + + public static void add(long[] x, long[] y, long[] z) + { + z[0] = x[0] ^ y[0]; + z[1] = x[1] ^ y[1]; + z[2] = x[2] ^ y[2]; + } + + public static void addExt(long[] xx, long[] yy, long[] zz) + { + zz[0] = xx[0] ^ yy[0]; + zz[1] = xx[1] ^ yy[1]; + zz[2] = xx[2] ^ yy[2]; + zz[3] = xx[3] ^ yy[3]; + zz[4] = xx[4] ^ yy[4]; + zz[5] = xx[5] ^ yy[5]; + } + + public static void addOne(long[] x, long[] z) + { + z[0] = x[0] ^ 1L; + z[1] = x[1]; + z[2] = x[2]; + } + + public static long[] fromBigInteger(BigInteger x) + { + long[] z = Nat192.fromBigInteger64(x); + reduce29(z, 0); + return z; + } + + public static void invert(long[] x, long[] z) + { + if (Nat192.isZero64(x)) + { + throw new IllegalStateException(); + } + + // Itoh-Tsujii inversion with bases { 2, 3 } + + long[] t0 = Nat192.create64(); + long[] t1 = Nat192.create64(); + + square(x, t0); + + // 3 | 162 + squareN(t0, 1, t1); + multiply(t0, t1, t0); + squareN(t1, 1, t1); + multiply(t0, t1, t0); + + // 3 | 54 + squareN(t0, 3, t1); + multiply(t0, t1, t0); + squareN(t1, 3, t1); + multiply(t0, t1, t0); + + // 3 | 18 + squareN(t0, 9, t1); + multiply(t0, t1, t0); + squareN(t1, 9, t1); + multiply(t0, t1, t0); + + // 3 | 6 + squareN(t0, 27, t1); + multiply(t0, t1, t0); + squareN(t1, 27, t1); + multiply(t0, t1, t0); + + // 2 | 2 + squareN(t0, 81, t1); + multiply(t0, t1, z); + } + + public static void multiply(long[] x, long[] y, long[] z) + { + long[] tt = Nat192.createExt64(); + implMultiply(x, y, tt); + reduce(tt, z); + } + + public static void multiplyAddToExt(long[] x, long[] y, long[] zz) + { + long[] tt = Nat192.createExt64(); + implMultiply(x, y, tt); + addExt(zz, tt, zz); + } + + public static void reduce(long[] xx, long[] z) + { + long x0 = xx[0], x1 = xx[1], x2 = xx[2], x3 = xx[3], x4 = xx[4], x5 = xx[5]; + + x2 ^= (x5 << 29) ^ (x5 << 32) ^ (x5 << 35) ^ (x5 << 36); + x3 ^= (x5 >>> 35) ^ (x5 >>> 32) ^ (x5 >>> 29) ^ (x5 >>> 28); + + x1 ^= (x4 << 29) ^ (x4 << 32) ^ (x4 << 35) ^ (x4 << 36); + x2 ^= (x4 >>> 35) ^ (x4 >>> 32) ^ (x4 >>> 29) ^ (x4 >>> 28); + + x0 ^= (x3 << 29) ^ (x3 << 32) ^ (x3 << 35) ^ (x3 << 36); + x1 ^= (x3 >>> 35) ^ (x3 >>> 32) ^ (x3 >>> 29) ^ (x3 >>> 28); + + long t = x2 >>> 35; + z[0] = x0 ^ t ^ (t << 3) ^ (t << 6) ^ (t << 7); + z[1] = x1; + z[2] = x2 & M35; + } + + public static void reduce29(long[] z, int zOff) + { + long z2 = z[zOff + 2], t = z2 >>> 35; + z[zOff ] ^= t ^ (t << 3) ^ (t << 6) ^ (t << 7); + z[zOff + 2] = z2 & M35; + } + + public static void sqrt(long[] x, long[] z) + { + long[] odd = Nat192.create64(); + + long u0, u1; + u0 = Interleave.unshuffle(x[0]); u1 = Interleave.unshuffle(x[1]); + long e0 = (u0 & 0x00000000FFFFFFFFL) | (u1 << 32); + odd[0] = (u0 >>> 32) | (u1 & 0xFFFFFFFF00000000L); + + u0 = Interleave.unshuffle(x[2]); + long e1 = (u0 & 0x00000000FFFFFFFFL); + odd[1] = (u0 >>> 32); + + multiply(odd, ROOT_Z, z); + + z[0] ^= e0; + z[1] ^= e1; + } + + public static void square(long[] x, long[] z) + { + long[] tt = Nat192.createExt64(); + implSquare(x, tt); + reduce(tt, z); + } + + public static void squareAddToExt(long[] x, long[] zz) + { + long[] tt = Nat192.createExt64(); + implSquare(x, tt); + addExt(zz, tt, zz); + } + + public static void squareN(long[] x, int n, long[] z) + { +// assert n > 0; + + long[] tt = Nat192.createExt64(); + implSquare(x, tt); + reduce(tt, z); + + while (--n > 0) + { + implSquare(z, tt); + reduce(tt, z); + } + } + + public static int trace(long[] x) + { + // Non-zero-trace bits: 0, 157 + return (int)(x[0] ^ (x[2] >>> 29)) & 1; + } + + protected static void implCompactExt(long[] zz) + { + long z0 = zz[0], z1 = zz[1], z2 = zz[2], z3 = zz[3], z4 = zz[4], z5 = zz[5]; + zz[0] = z0 ^ (z1 << 55); + zz[1] = (z1 >>> 9) ^ (z2 << 46); + zz[2] = (z2 >>> 18) ^ (z3 << 37); + zz[3] = (z3 >>> 27) ^ (z4 << 28); + zz[4] = (z4 >>> 36) ^ (z5 << 19); + zz[5] = (z5 >>> 45); + } + + protected static void implMultiply(long[] x, long[] y, long[] zz) + { + /* + * "Five-way recursion" as described in "Batch binary Edwards", Daniel J. Bernstein. + */ + + long f0 = x[0], f1 = x[1], f2 = x[2]; + f2 = ((f1 >>> 46) ^ (f2 << 18)); + f1 = ((f0 >>> 55) ^ (f1 << 9)) & M55; + f0 &= M55; + + long g0 = y[0], g1 = y[1], g2 = y[2]; + g2 = ((g1 >>> 46) ^ (g2 << 18)); + g1 = ((g0 >>> 55) ^ (g1 << 9)) & M55; + g0 &= M55; + + long[] H = new long[10]; + + implMulw(f0, g0, H, 0); // H(0) 55/54 bits + implMulw(f2, g2, H, 2); // H(INF) 55/50 bits + + long t0 = f0 ^ f1 ^ f2; + long t1 = g0 ^ g1 ^ g2; + + implMulw(t0, t1, H, 4); // H(1) 55/54 bits + + long t2 = (f1 << 1) ^ (f2 << 2); + long t3 = (g1 << 1) ^ (g2 << 2); + + implMulw(f0 ^ t2, g0 ^ t3, H, 6); // H(t) 55/56 bits + implMulw(t0 ^ t2, t1 ^ t3, H, 8); // H(t + 1) 55/56 bits + + long t4 = H[6] ^ H[8]; + long t5 = H[7] ^ H[9]; + +// assert t5 >>> 55 == 0; + + // Calculate V + long v0 = (t4 << 1) ^ H[6]; + long v1 = t4 ^ (t5 << 1) ^ H[7]; + long v2 = t5; + + // Calculate U + long u0 = H[0]; + long u1 = H[1] ^ H[0] ^ H[4]; + long u2 = H[1] ^ H[5]; + + // Calculate W + long w0 = u0 ^ v0 ^ (H[2] << 4) ^ (H[2] << 1); + long w1 = u1 ^ v1 ^ (H[3] << 4) ^ (H[3] << 1); + long w2 = u2 ^ v2; + + // Propagate carries + w1 ^= (w0 >>> 55); w0 &= M55; + w2 ^= (w1 >>> 55); w1 &= M55; + +// assert (w0 & 1L) == 0; + + // Divide W by t + + w0 = (w0 >>> 1) ^ ((w1 & 1L) << 54); + w1 = (w1 >>> 1) ^ ((w2 & 1L) << 54); + w2 = (w2 >>> 1); + + // Divide W by (t + 1) + + w0 ^= (w0 << 1); + w0 ^= (w0 << 2); + w0 ^= (w0 << 4); + w0 ^= (w0 << 8); + w0 ^= (w0 << 16); + w0 ^= (w0 << 32); + + w0 &= M55; w1 ^= (w0 >>> 54); + + w1 ^= (w1 << 1); + w1 ^= (w1 << 2); + w1 ^= (w1 << 4); + w1 ^= (w1 << 8); + w1 ^= (w1 << 16); + w1 ^= (w1 << 32); + + w1 &= M55; w2 ^= (w1 >>> 54); + + w2 ^= (w2 << 1); + w2 ^= (w2 << 2); + w2 ^= (w2 << 4); + w2 ^= (w2 << 8); + w2 ^= (w2 << 16); + w2 ^= (w2 << 32); + +// assert w2 >>> 52 == 0; + + zz[0] = u0; + zz[1] = u1 ^ w0 ^ H[2]; + zz[2] = u2 ^ w1 ^ w0 ^ H[3]; + zz[3] = w2 ^ w1; + zz[4] = w2 ^ H[2]; + zz[5] = H[3]; + + implCompactExt(zz); + } + + protected static void implMulw(long x, long y, long[] z, int zOff) + { +// assert x >>> 56 == 0; +// assert y >>> 56 == 0; + + long[] u = new long[8]; +// u[0] = 0; + u[1] = y; + u[2] = u[1] << 1; + u[3] = u[2] ^ y; + u[4] = u[2] << 1; + u[5] = u[4] ^ y; + u[6] = u[3] << 1; + u[7] = u[6] ^ y; + + int j = (int)x; + long g, h = 0, l = u[j & 3]; + int k = 47; + do + { + j = (int)(x >>> k); + g = u[j & 7] + ^ u[(j >>> 3) & 7] << 3 + ^ u[(j >>> 6) & 7] << 6; + l ^= (g << k); + h ^= (g >>> -k); + } + while ((k -= 9) > 0); + +// assert h >>> 47 == 0; + + z[zOff ] = l & M55; + z[zOff + 1] = (l >>> 55) ^ (h << 9); + } + + protected static void implSquare(long[] x, long[] zz) + { + Interleave.expand64To128(x[0], zz, 0); + Interleave.expand64To128(x[1], zz, 2); + + long x2 = x[2]; + zz[4] = Interleave.expand32to64((int)x2); + zz[5] = Interleave.expand8to16((int)(x2 >>> 32)) & 0xFFFFFFFFL; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163FieldElement.java new file mode 100644 index 00000000..7a95c222 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163FieldElement.java @@ -0,0 +1,221 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.raw.Nat192; +import org.bouncycastle.util.Arrays; + +public class SecT163FieldElement extends ECFieldElement +{ + protected long[] x; + + public SecT163FieldElement(BigInteger x) + { + if (x == null || x.signum() < 0 || x.bitLength() > 163) + { + throw new IllegalArgumentException("x value invalid for SecT163FieldElement"); + } + + this.x = SecT163Field.fromBigInteger(x); + } + + public SecT163FieldElement() + { + this.x = Nat192.create64(); + } + + protected SecT163FieldElement(long[] x) + { + this.x = x; + } + +// public int bitLength() +// { +// return x.degree(); +// } + + public boolean isOne() + { + return Nat192.isOne64(x); + } + + public boolean isZero() + { + return Nat192.isZero64(x); + } + + public boolean testBitZero() + { + return (x[0] & 1L) != 0L; + } + + public BigInteger toBigInteger() + { + return Nat192.toBigInteger64(x); + } + + public String getFieldName() + { + return "SecT163Field"; + } + + public int getFieldSize() + { + return 163; + } + + public ECFieldElement add(ECFieldElement b) + { + long[] z = Nat192.create64(); + SecT163Field.add(x, ((SecT163FieldElement)b).x, z); + return new SecT163FieldElement(z); + } + + public ECFieldElement addOne() + { + long[] z = Nat192.create64(); + SecT163Field.addOne(x, z); + return new SecT163FieldElement(z); + } + + public ECFieldElement subtract(ECFieldElement b) + { + // Addition and subtraction are the same in F2m + return add(b); + } + + public ECFieldElement multiply(ECFieldElement b) + { + long[] z = Nat192.create64(); + SecT163Field.multiply(x, ((SecT163FieldElement)b).x, z); + return new SecT163FieldElement(z); + } + + public ECFieldElement multiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + return multiplyPlusProduct(b, x, y); + } + + public ECFieldElement multiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + long[] ax = this.x, bx = ((SecT163FieldElement)b).x; + long[] xx = ((SecT163FieldElement)x).x, yx = ((SecT163FieldElement)y).x; + + long[] tt = Nat192.createExt64(); + SecT163Field.multiplyAddToExt(ax, bx, tt); + SecT163Field.multiplyAddToExt(xx, yx, tt); + + long[] z = Nat192.create64(); + SecT163Field.reduce(tt, z); + return new SecT163FieldElement(z); + } + + public ECFieldElement divide(ECFieldElement b) + { + return multiply(b.invert()); + } + + public ECFieldElement negate() + { + return this; + } + + public ECFieldElement square() + { + long[] z = Nat192.create64(); + SecT163Field.square(x, z); + return new SecT163FieldElement(z); + } + + public ECFieldElement squareMinusProduct(ECFieldElement x, ECFieldElement y) + { + return squarePlusProduct(x, y); + } + + public ECFieldElement squarePlusProduct(ECFieldElement x, ECFieldElement y) + { + long[] ax = this.x; + long[] xx = ((SecT163FieldElement)x).x, yx = ((SecT163FieldElement)y).x; + + long[] tt = Nat192.createExt64(); + SecT163Field.squareAddToExt(ax, tt); + SecT163Field.multiplyAddToExt(xx, yx, tt); + + long[] z = Nat192.create64(); + SecT163Field.reduce(tt, z); + return new SecT163FieldElement(z); + } + + public ECFieldElement squarePow(int pow) + { + if (pow < 1) + { + return this; + } + + long[] z = Nat192.create64(); + SecT163Field.squareN(x, pow, z); + return new SecT163FieldElement(z); + } + + public ECFieldElement invert() + { + long[] z = Nat192.create64(); + SecT163Field.invert(x, z); + return new SecT163FieldElement(z); + } + + public ECFieldElement sqrt() + { + long[] z = Nat192.create64(); + SecT163Field.sqrt(x, z); + return new SecT163FieldElement(z); + } + + public int getRepresentation() + { + return ECFieldElement.F2m.PPB; + } + + public int getM() + { + return 163; + } + + public int getK1() + { + return 3; + } + + public int getK2() + { + return 6; + } + + public int getK3() + { + return 7; + } + + public boolean equals(Object other) + { + if (other == this) + { + return true; + } + + if (!(other instanceof SecT163FieldElement)) + { + return false; + } + + SecT163FieldElement o = (SecT163FieldElement)other; + return Nat192.eq64(x, o.x); + } + + public int hashCode() + { + return 163763 ^ Arrays.hashCode(x, 0, 3); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163K1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163K1Curve.java new file mode 100644 index 00000000..387cc555 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163K1Curve.java @@ -0,0 +1,108 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECCurve.AbstractF2m; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECMultiplier; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.WTauNafMultiplier; +import org.bouncycastle.util.encoders.Hex; + +public class SecT163K1Curve extends AbstractF2m +{ + private static final int SecT163K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected SecT163K1Point infinity; + + public SecT163K1Curve() + { + super(163, 3, 6, 7); + + this.infinity = new SecT163K1Point(this, null, null); + + this.a = fromBigInteger(BigInteger.valueOf(1)); + this.b = this.a; + this.order = new BigInteger(1, Hex.decode("04000000000000000000020108A2E0CC0D99F8A5EF")); + this.cofactor = BigInteger.valueOf(2); + + this.coord = SecT163K1_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecT163K1Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + protected ECMultiplier createDefaultMultiplier() + { + return new WTauNafMultiplier(); + } + + public int getFieldSize() + { + return 163; + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecT163FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecT163K1Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecT163K1Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } + + public boolean isKoblitz() + { + return true; + } + + public int getM() + { + return 163; + } + + public boolean isTrinomial() + { + return false; + } + + public int getK1() + { + return 3; + } + + public int getK2() + { + return 6; + } + + public int getK3() + { + return 7; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163K1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163K1Point.java new file mode 100644 index 00000000..9c483195 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163K1Point.java @@ -0,0 +1,314 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECConstants; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.ECPoint.AbstractF2m; + +public class SecT163K1Point extends AbstractF2m +{ + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT163K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + { + this(curve, x, y, false); + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)} + */ + public SecT163K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecT163K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecT163K1Point(null, this.getAffineXCoord(), this.getAffineYCoord()); + } + + public ECFieldElement getYCoord() + { + ECFieldElement X = x, L = y; + + 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); + + ECFieldElement Z = zs[0]; + if (!Z.isOne()) + { + Y = Y.divide(Z); + } + + return Y; + } + + protected boolean getCompressionYTilde() + { + ECFieldElement X = this.getRawXCoord(); + if (X.isZero()) + { + return false; + } + + ECFieldElement Y = this.getRawYCoord(); + + // Y is actually Lambda (X + Y/X) here + return Y.testBitZero() != X.testBitZero(); + } + + public ECPoint add(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + ECFieldElement X2 = b.getRawXCoord(); + + if (X1.isZero()) + { + if (X2.isZero()) + { + return curve.getInfinity(); + } + + return b.add(this); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0); + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.multiply(Z1); + S2 = S2.multiply(Z1); + } + + boolean 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 twice(); + } + + return curve.getInfinity(); + } + + ECFieldElement X3, L3, Z3; + if (X2.isZero()) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.normalize(); + X1 = p.getXCoord(); + ECFieldElement Y1 = p.getYCoord(); + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.add(Y2).divide(X1); + +// X3 = L.square().add(L).add(X1).add(curve.getA()); + X3 = L.square().add(L).add(X1).addOne(); + if (X3.isZero()) + { +// return new SecT163K1Point(curve, X3, curve.getB().sqrt(), this.withCompression); + return new SecT163K1Point(curve, X3, curve.getB(), this.withCompression); + } + + ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1); + L3 = Y3.divide(X3).add(X3); + Z3 = curve.fromBigInteger(ECConstants.ONE); + } + else + { + B = B.square(); + + ECFieldElement AU1 = A.multiply(U1); + ECFieldElement AU2 = A.multiply(U2); + + X3 = AU1.multiply(AU2); + if (X3.isZero()) + { +// return new SecT163K1Point(curve, X3, curve.getB().sqrt(), this.withCompression); + return new SecT163K1Point(curve, X3, curve.getB(), this.withCompression); + } + + 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 SecT163K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return curve.getInfinity(); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square(); + ECFieldElement T = L1.square().add(L1Z1).add(Z1Sq); + if (T.isZero()) + { +// return new SecT163K1Point(curve, T, curve.getB().sqrt(), withCompression); + return new SecT163K1Point(curve, T, curve.getB(), withCompression); + } + + ECFieldElement X3 = T.square(); + ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq); + + ECFieldElement t1 = L1.add(X1).square(); + ECFieldElement L3 = t1.add(T).add(Z1Sq).multiply(t1).add(X3); + + return new SecT163K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twicePlus(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return twice(); + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return b; + } + + // NOTE: twicePlus() only optimized for lambda-affine argument + ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0); + if (X2.isZero() || !Z2.isOne()) + { + return twice().add(b); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(); + + ECFieldElement X1Sq = X1.square(); + ECFieldElement L1Sq = L1.square(); + ECFieldElement Z1Sq = Z1.square(); + ECFieldElement L1Z1 = L1.multiply(Z1); + +// ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1); + ECFieldElement T = Z1Sq.add(L1Sq).add(L1Z1); + ECFieldElement L2plus1 = L2.addOne(); +// ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement A = L2.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.getInfinity(); + } + + if (A.isZero()) + { +// return new SecT163K1Point(curve, A, curve.getB().sqrt(), withCompression); + return new SecT163K1Point(curve, A, curve.getB(), withCompression); + } + + 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 SecT163K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + ECFieldElement X = this.x; + if (X.isZero()) + { + return this; + } + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.y, Z = this.zs[0]; + return new SecT163K1Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R1Curve.java new file mode 100644 index 00000000..88b14c0b --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R1Curve.java @@ -0,0 +1,101 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECCurve.AbstractF2m; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; + +public class SecT163R1Curve extends AbstractF2m +{ + private static final int SecT163R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected SecT163R1Point infinity; + + public SecT163R1Curve() + { + super(163, 3, 6, 7); + + this.infinity = new SecT163R1Point(this, null, null); + + this.a = fromBigInteger(new BigInteger(1, Hex.decode("07B6882CAAEFA84F9554FF8428BD88E246D2782AE2"))); + this.b = fromBigInteger(new BigInteger(1, Hex.decode("0713612DCDDCB40AAB946BDA29CA91F73AF958AFD9"))); + this.order = new BigInteger(1, Hex.decode("03FFFFFFFFFFFFFFFFFFFF48AAB689C29CA710279B")); + this.cofactor = BigInteger.valueOf(2); + + this.coord = SecT163R1_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecT163R1Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + public int getFieldSize() + { + return 163; + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecT163FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecT163R1Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecT163R1Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } + + public boolean isKoblitz() + { + return false; + } + + public int getM() + { + return 163; + } + + public boolean isTrinomial() + { + return false; + } + + public int getK1() + { + return 3; + } + + public int getK2() + { + return 6; + } + + public int getK3() + { + return 7; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R1Point.java new file mode 100644 index 00000000..1c3355ee --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R1Point.java @@ -0,0 +1,308 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECConstants; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.ECPoint.AbstractF2m; + +public class SecT163R1Point extends AbstractF2m +{ + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT163R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + { + this(curve, x, y, false); + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)} + */ + public SecT163R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecT163R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecT163R1Point(null, getAffineXCoord(), getAffineYCoord()); + } + + public ECFieldElement getYCoord() + { + ECFieldElement X = x, L = y; + + 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); + + ECFieldElement Z = zs[0]; + if (!Z.isOne()) + { + Y = Y.divide(Z); + } + + return Y; + } + + protected boolean getCompressionYTilde() + { + ECFieldElement X = this.getRawXCoord(); + if (X.isZero()) + { + return false; + } + + ECFieldElement Y = this.getRawYCoord(); + + // Y is actually Lambda (X + Y/X) here + return Y.testBitZero() != X.testBitZero(); + } + + public ECPoint add(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + ECFieldElement X2 = b.getRawXCoord(); + + if (X1.isZero()) + { + if (X2.isZero()) + { + return curve.getInfinity(); + } + + return b.add(this); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0); + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.multiply(Z1); + S2 = S2.multiply(Z1); + } + + boolean 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 twice(); + } + + return curve.getInfinity(); + } + + ECFieldElement X3, L3, Z3; + if (X2.isZero()) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.normalize(); + X1 = p.getXCoord(); + ECFieldElement Y1 = p.getYCoord(); + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.add(Y2).divide(X1); + + X3 = L.square().add(L).add(X1).add(curve.getA()); + if (X3.isZero()) + { + return new SecT163R1Point(curve, X3, curve.getB().sqrt(), this.withCompression); + } + + ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1); + L3 = Y3.divide(X3).add(X3); + Z3 = curve.fromBigInteger(ECConstants.ONE); + } + else + { + B = B.square(); + + ECFieldElement AU1 = A.multiply(U1); + ECFieldElement AU2 = A.multiply(U2); + + X3 = AU1.multiply(AU2); + if (X3.isZero()) + { + return new SecT163R1Point(curve, X3, curve.getB().sqrt(), this.withCompression); + } + + 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 SecT163R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return curve.getInfinity(); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square(); + ECFieldElement a = curve.getA(); + ECFieldElement aZ1Sq = Z1IsOne ? a : a.multiply(Z1Sq); + ECFieldElement T = L1.square().add(L1Z1).add(aZ1Sq); + if (T.isZero()) + { + return new SecT163R1Point(curve, T, curve.getB().sqrt(), withCompression); + } + + ECFieldElement X3 = T.square(); + ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq); + + ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1); + ECFieldElement L3 = X1Z1.squarePlusProduct(T, L1Z1).add(X3).add(Z3); + + return new SecT163R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twicePlus(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return twice(); + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return b; + } + + ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0); + if (X2.isZero() || !Z2.isOne()) + { + return twice().add(b); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(); + + ECFieldElement X1Sq = X1.square(); + ECFieldElement L1Sq = L1.square(); + ECFieldElement Z1Sq = Z1.square(); + ECFieldElement L1Z1 = L1.multiply(Z1); + + ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1); + ECFieldElement L2plus1 = L2.addOne(); + ECFieldElement A = curve.getA().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.getInfinity(); + } + + if (A.isZero()) + { + return new SecT163R1Point(curve, A, curve.getB().sqrt(), withCompression); + } + + 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 SecT163R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + ECFieldElement X = this.x; + if (X.isZero()) + { + return this; + } + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.y, Z = this.zs[0]; + return new SecT163R1Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R2Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R2Curve.java new file mode 100644 index 00000000..44054fee --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R2Curve.java @@ -0,0 +1,101 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECCurve.AbstractF2m; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; + +public class SecT163R2Curve extends AbstractF2m +{ + private static final int SecT163R2_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected SecT163R2Point infinity; + + public SecT163R2Curve() + { + super(163, 3, 6, 7); + + this.infinity = new SecT163R2Point(this, null, null); + + this.a = fromBigInteger(BigInteger.valueOf(1)); + this.b = fromBigInteger(new BigInteger(1, Hex.decode("020A601907B8C953CA1481EB10512F78744A3205FD"))); + this.order = new BigInteger(1, Hex.decode("040000000000000000000292FE77E70C12A4234C33")); + this.cofactor = BigInteger.valueOf(2); + + this.coord = SecT163R2_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecT163R2Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + public int getFieldSize() + { + return 163; + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecT163FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecT163R2Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecT163R2Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } + + public boolean isKoblitz() + { + return false; + } + + public int getM() + { + return 163; + } + + public boolean isTrinomial() + { + return false; + } + + public int getK1() + { + return 3; + } + + public int getK2() + { + return 6; + } + + public int getK3() + { + return 7; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R2Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R2Point.java new file mode 100644 index 00000000..dcadede8 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT163R2Point.java @@ -0,0 +1,309 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECConstants; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.ECPoint.AbstractF2m; + +public class SecT163R2Point extends AbstractF2m +{ + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT163R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + { + this(curve, x, y, false); + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)} + */ + public SecT163R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecT163R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecT163R2Point(null, getAffineXCoord(), getAffineYCoord()); + } + + public ECFieldElement getYCoord() + { + ECFieldElement X = x, L = y; + + 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); + + ECFieldElement Z = zs[0]; + if (!Z.isOne()) + { + Y = Y.divide(Z); + } + + return Y; + } + + protected boolean getCompressionYTilde() + { + ECFieldElement X = this.getRawXCoord(); + if (X.isZero()) + { + return false; + } + + ECFieldElement Y = this.getRawYCoord(); + + // Y is actually Lambda (X + Y/X) here + return Y.testBitZero() != X.testBitZero(); + } + + public ECPoint add(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + ECFieldElement X2 = b.getRawXCoord(); + + if (X1.isZero()) + { + if (X2.isZero()) + { + return curve.getInfinity(); + } + + return b.add(this); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0); + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.multiply(Z1); + S2 = S2.multiply(Z1); + } + + boolean 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 twice(); + } + + return curve.getInfinity(); + } + + ECFieldElement X3, L3, Z3; + if (X2.isZero()) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.normalize(); + X1 = p.getXCoord(); + ECFieldElement Y1 = p.getYCoord(); + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.add(Y2).divide(X1); + +// X3 = L.square().add(L).add(X1).add(curve.getA()); + X3 = L.square().add(L).add(X1).addOne(); + if (X3.isZero()) + { + return new SecT163R2Point(curve, X3, curve.getB().sqrt(), this.withCompression); + } + + ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1); + L3 = Y3.divide(X3).add(X3); + Z3 = curve.fromBigInteger(ECConstants.ONE); + } + else + { + B = B.square(); + + ECFieldElement AU1 = A.multiply(U1); + ECFieldElement AU2 = A.multiply(U2); + + X3 = AU1.multiply(AU2); + if (X3.isZero()) + { + return new SecT163R2Point(curve, X3, curve.getB().sqrt(), this.withCompression); + } + + 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 SecT163R2Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return curve.getInfinity(); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square(); + ECFieldElement T = L1.square().add(L1Z1).add(Z1Sq); + if (T.isZero()) + { + return new SecT163R2Point(curve, T, curve.getB().sqrt(), withCompression); + } + + ECFieldElement X3 = T.square(); + ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq); + + ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1); + ECFieldElement L3 = X1Z1.squarePlusProduct(T, L1Z1).add(X3).add(Z3); + + return new SecT163R2Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twicePlus(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return twice(); + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return b; + } + + ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0); + if (X2.isZero() || !Z2.isOne()) + { + return twice().add(b); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(); + + ECFieldElement X1Sq = X1.square(); + ECFieldElement L1Sq = L1.square(); + ECFieldElement Z1Sq = Z1.square(); + ECFieldElement L1Z1 = L1.multiply(Z1); + +// ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1); + ECFieldElement T = Z1Sq.add(L1Sq).add(L1Z1); + ECFieldElement L2plus1 = L2.addOne(); +// ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement A = L2.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.getInfinity(); + } + + if (A.isZero()) + { + return new SecT163R2Point(curve, A, curve.getB().sqrt(), withCompression); + } + + 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 SecT163R2Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + ECFieldElement X = this.x; + if (X.isZero()) + { + return this; + } + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.y, Z = this.zs[0]; + return new SecT163R2Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193Field.java new file mode 100644 index 00000000..2e5e1866 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193Field.java @@ -0,0 +1,306 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.raw.Interleave; +import org.bouncycastle.math.raw.Nat256; + +public class SecT193Field +{ + private static final long M01 = 1L; + private static final long M49 = -1L >>> 15; + + public static void add(long[] x, long[] y, long[] z) + { + z[0] = x[0] ^ y[0]; + z[1] = x[1] ^ y[1]; + z[2] = x[2] ^ y[2]; + z[3] = x[3] ^ y[3]; + } + + public static void addExt(long[] xx, long[] yy, long[] zz) + { + zz[0] = xx[0] ^ yy[0]; + zz[1] = xx[1] ^ yy[1]; + zz[2] = xx[2] ^ yy[2]; + zz[3] = xx[3] ^ yy[3]; + zz[4] = xx[4] ^ yy[4]; + zz[5] = xx[5] ^ yy[5]; + zz[6] = xx[6] ^ yy[6]; + } + + public static void addOne(long[] x, long[] z) + { + z[0] = x[0] ^ 1L; + z[1] = x[1]; + z[2] = x[2]; + z[3] = x[3]; + } + + public static long[] fromBigInteger(BigInteger x) + { + long[] z = Nat256.fromBigInteger64(x); + reduce63(z, 0); + return z; + } + + public static void invert(long[] x, long[] z) + { + if (Nat256.isZero64(x)) + { + throw new IllegalStateException(); + } + + // Itoh-Tsujii inversion with bases { 2, 3 } + + long[] t0 = Nat256.create64(); + long[] t1 = Nat256.create64(); + + square(x, t0); + + // 3 | 192 + squareN(t0, 1, t1); + multiply(t0, t1, t0); + squareN(t1, 1, t1); + multiply(t0, t1, t0); + + // 2 | 64 + squareN(t0, 3, t1); + multiply(t0, t1, t0); + + // 2 | 32 + squareN(t0, 6, t1); + multiply(t0, t1, t0); + + // 2 | 16 + squareN(t0, 12, t1); + multiply(t0, t1, t0); + + // 2 | 8 + squareN(t0, 24, t1); + multiply(t0, t1, t0); + + // 2 | 4 + squareN(t0, 48, t1); + multiply(t0, t1, t0); + + // 2 | 2 + squareN(t0, 96, t1); + multiply(t0, t1, z); + } + + public static void multiply(long[] x, long[] y, long[] z) + { + long[] tt = Nat256.createExt64(); + implMultiply(x, y, tt); + reduce(tt, z); + } + + public static void multiplyAddToExt(long[] x, long[] y, long[] zz) + { + long[] tt = Nat256.createExt64(); + implMultiply(x, y, tt); + addExt(zz, tt, zz); + } + + public static void reduce(long[] xx, long[] z) + { + long x0 = xx[0], x1 = xx[1], x2 = xx[2], x3 = xx[3], x4 = xx[4], x5 = xx[5], x6 = xx[6]; + + x2 ^= (x6 << 63); + x3 ^= (x6 >>> 1) ^ (x6 << 14); + x4 ^= (x6 >>> 50); + + x1 ^= (x5 << 63); + x2 ^= (x5 >>> 1) ^ (x5 << 14); + x3 ^= (x5 >>> 50); + + x0 ^= (x4 << 63); + x1 ^= (x4 >>> 1) ^ (x4 << 14); + x2 ^= (x4 >>> 50); + + long t = x3 >>> 1; + z[0] = x0 ^ t ^ (t << 15); + z[1] = x1 ^ (t >>> 49); + z[2] = x2; + z[3] = x3 & M01; + } + + public static void reduce63(long[] z, int zOff) + { + long z3 = z[zOff + 3], t = z3 >>> 1; + z[zOff ] ^= t ^ (t << 15); + z[zOff + 1] ^= (t >>> 49); + z[zOff + 3] = z3 & M01; + } + + public static void sqrt(long[] x, long[] z) + { + long u0, u1; + u0 = Interleave.unshuffle(x[0]); u1 = Interleave.unshuffle(x[1]); + long e0 = (u0 & 0x00000000FFFFFFFFL) | (u1 << 32); + long c0 = (u0 >>> 32) | (u1 & 0xFFFFFFFF00000000L); + + u0 = Interleave.unshuffle(x[2]); + long e1 = (u0 & 0x00000000FFFFFFFFL) ^ (x[3] << 32); + long c1 = (u0 >>> 32); + + z[0] = e0 ^ (c0 << 8); + z[1] = e1 ^ (c1 << 8) ^ (c0 >>> 56) ^ (c0 << 33); + z[2] = (c1 >>> 56) ^ (c1 << 33) ^ (c0 >>> 31); + z[3] = (c1 >>> 31); + } + + public static void square(long[] x, long[] z) + { + long[] tt = Nat256.createExt64(); + implSquare(x, tt); + reduce(tt, z); + } + + public static void squareAddToExt(long[] x, long[] zz) + { + long[] tt = Nat256.createExt64(); + implSquare(x, tt); + addExt(zz, tt, zz); + } + + public static void squareN(long[] x, int n, long[] z) + { +// assert n > 0; + + long[] tt = Nat256.createExt64(); + implSquare(x, tt); + reduce(tt, z); + + while (--n > 0) + { + implSquare(z, tt); + reduce(tt, z); + } + } + + public static int trace(long[] x) + { + // Non-zero-trace bits: 0 + return (int)(x[0]) & 1; + } + + protected static void implCompactExt(long[] zz) + { + long z0 = zz[0], z1 = zz[1], z2 = zz[2], z3 = zz[3], z4 = zz[4], z5 = zz[5], z6 = zz[6], z7 = zz[7]; + zz[0] = z0 ^ (z1 << 49); + zz[1] = (z1 >>> 15) ^ (z2 << 34); + zz[2] = (z2 >>> 30) ^ (z3 << 19); + zz[3] = (z3 >>> 45) ^ (z4 << 4) + ^ (z5 << 53); + zz[4] = (z4 >>> 60) ^ (z6 << 38) + ^ (z5 >>> 11); + zz[5] = (z6 >>> 26) ^ (z7 << 23); + zz[6] = (z7 >>> 41); + zz[7] = 0; + } + + protected static void implExpand(long[] x, long[] z) + { + long x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3]; + z[0] = x0 & M49; + z[1] = ((x0 >>> 49) ^ (x1 << 15)) & M49; + z[2] = ((x1 >>> 34) ^ (x2 << 30)) & M49; + z[3] = ((x2 >>> 19) ^ (x3 << 45)); + } + + protected static void implMultiply(long[] x, long[] y, long[] zz) + { + /* + * "Two-level seven-way recursion" as described in "Batch binary Edwards", Daniel J. Bernstein. + */ + + long[] f = new long[4], g = new long[4]; + implExpand(x, f); + implExpand(y, g); + + implMulwAcc(f[0], g[0], zz, 0); + implMulwAcc(f[1], g[1], zz, 1); + implMulwAcc(f[2], g[2], zz, 2); + implMulwAcc(f[3], g[3], zz, 3); + + // U *= (1 - t^n) + for (int i = 5; i > 0; --i) + { + zz[i] ^= zz[i - 1]; + } + + implMulwAcc(f[0] ^ f[1], g[0] ^ g[1], zz, 1); + implMulwAcc(f[2] ^ f[3], g[2] ^ g[3], zz, 3); + + // V *= (1 - t^2n) + for (int i = 7; i > 1; --i) + { + zz[i] ^= zz[i - 2]; + } + + // Double-length recursion + { + long c0 = f[0] ^ f[2], c1 = f[1] ^ f[3]; + long d0 = g[0] ^ g[2], d1 = g[1] ^ g[3]; + implMulwAcc(c0 ^ c1, d0 ^ d1, zz, 3); + long[] t = new long[3]; + implMulwAcc(c0, d0, t, 0); + implMulwAcc(c1, d1, t, 1); + long t0 = t[0], t1 = t[1], t2 = t[2]; + zz[2] ^= t0; + zz[3] ^= t0 ^ t1; + zz[4] ^= t2 ^ t1; + zz[5] ^= t2; + } + + implCompactExt(zz); + } + + protected static void implMulwAcc(long x, long y, long[] z, int zOff) + { +// assert x >>> 49 == 0; +// assert y >>> 49 == 0; + + long[] u = new long[8]; +// u[0] = 0; + u[1] = y; + u[2] = u[1] << 1; + u[3] = u[2] ^ y; + u[4] = u[2] << 1; + u[5] = u[4] ^ y; + u[6] = u[3] << 1; + u[7] = u[6] ^ y; + + int j = (int)x; + long g, h = 0, l = u[j & 7] + ^ (u[(j >>> 3) & 7] << 3); + int k = 36; + do + { + j = (int)(x >>> k); + g = u[j & 7] + ^ u[(j >>> 3) & 7] << 3 + ^ u[(j >>> 6) & 7] << 6 + ^ u[(j >>> 9) & 7] << 9 + ^ u[(j >>> 12) & 7] << 12; + l ^= (g << k); + h ^= (g >>> -k); + } + while ((k -= 15) > 0); + +// assert h >>> 33 == 0; + + z[zOff ] ^= l & M49; + z[zOff + 1] ^= (l >>> 49) ^ (h << 15); + } + + protected static void implSquare(long[] x, long[] zz) + { + Interleave.expand64To128(x[0], zz, 0); + Interleave.expand64To128(x[1], zz, 2); + Interleave.expand64To128(x[2], zz, 4); + zz[6] = (x[3] & M01); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193FieldElement.java new file mode 100644 index 00000000..d7a712db --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193FieldElement.java @@ -0,0 +1,221 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.raw.Nat256; +import org.bouncycastle.util.Arrays; + +public class SecT193FieldElement extends ECFieldElement +{ + protected long[] x; + + public SecT193FieldElement(BigInteger x) + { + if (x == null || x.signum() < 0 || x.bitLength() > 193) + { + throw new IllegalArgumentException("x value invalid for SecT193FieldElement"); + } + + this.x = SecT193Field.fromBigInteger(x); + } + + public SecT193FieldElement() + { + this.x = Nat256.create64(); + } + + protected SecT193FieldElement(long[] x) + { + this.x = x; + } + +// public int bitLength() +// { +// return x.degree(); +// } + + public boolean isOne() + { + return Nat256.isOne64(x); + } + + public boolean isZero() + { + return Nat256.isZero64(x); + } + + public boolean testBitZero() + { + return (x[0] & 1L) != 0L; + } + + public BigInteger toBigInteger() + { + return Nat256.toBigInteger64(x); + } + + public String getFieldName() + { + return "SecT193Field"; + } + + public int getFieldSize() + { + return 193; + } + + public ECFieldElement add(ECFieldElement b) + { + long[] z = Nat256.create64(); + SecT193Field.add(x, ((SecT193FieldElement)b).x, z); + return new SecT193FieldElement(z); + } + + public ECFieldElement addOne() + { + long[] z = Nat256.create64(); + SecT193Field.addOne(x, z); + return new SecT193FieldElement(z); + } + + public ECFieldElement subtract(ECFieldElement b) + { + // Addition and subtraction are the same in F2m + return add(b); + } + + public ECFieldElement multiply(ECFieldElement b) + { + long[] z = Nat256.create64(); + SecT193Field.multiply(x, ((SecT193FieldElement)b).x, z); + return new SecT193FieldElement(z); + } + + public ECFieldElement multiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + return multiplyPlusProduct(b, x, y); + } + + public ECFieldElement multiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + long[] ax = this.x, bx = ((SecT193FieldElement)b).x; + long[] xx = ((SecT193FieldElement)x).x, yx = ((SecT193FieldElement)y).x; + + long[] tt = Nat256.createExt64(); + SecT193Field.multiplyAddToExt(ax, bx, tt); + SecT193Field.multiplyAddToExt(xx, yx, tt); + + long[] z = Nat256.create64(); + SecT193Field.reduce(tt, z); + return new SecT193FieldElement(z); + } + + public ECFieldElement divide(ECFieldElement b) + { + return multiply(b.invert()); + } + + public ECFieldElement negate() + { + return this; + } + + public ECFieldElement square() + { + long[] z = Nat256.create64(); + SecT193Field.square(x, z); + return new SecT193FieldElement(z); + } + + public ECFieldElement squareMinusProduct(ECFieldElement x, ECFieldElement y) + { + return squarePlusProduct(x, y); + } + + public ECFieldElement squarePlusProduct(ECFieldElement x, ECFieldElement y) + { + long[] ax = this.x; + long[] xx = ((SecT193FieldElement)x).x, yx = ((SecT193FieldElement)y).x; + + long[] tt = Nat256.createExt64(); + SecT193Field.squareAddToExt(ax, tt); + SecT193Field.multiplyAddToExt(xx, yx, tt); + + long[] z = Nat256.create64(); + SecT193Field.reduce(tt, z); + return new SecT193FieldElement(z); + } + + public ECFieldElement squarePow(int pow) + { + if (pow < 1) + { + return this; + } + + long[] z = Nat256.create64(); + SecT193Field.squareN(x, pow, z); + return new SecT193FieldElement(z); + } + + public ECFieldElement invert() + { + long[] z = Nat256.create64(); + SecT193Field.invert(x, z); + return new SecT193FieldElement(z); + } + + public ECFieldElement sqrt() + { + long[] z = Nat256.create64(); + SecT193Field.sqrt(x, z); + return new SecT193FieldElement(z); + } + + public int getRepresentation() + { + return ECFieldElement.F2m.TPB; + } + + public int getM() + { + return 193; + } + + public int getK1() + { + return 15; + } + + public int getK2() + { + return 0; + } + + public int getK3() + { + return 0; + } + + public boolean equals(Object other) + { + if (other == this) + { + return true; + } + + if (!(other instanceof SecT193FieldElement)) + { + return false; + } + + SecT193FieldElement o = (SecT193FieldElement)other; + return Nat256.eq64(x, o.x); + } + + public int hashCode() + { + return 1930015 ^ Arrays.hashCode(x, 0, 4); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R1Curve.java new file mode 100644 index 00000000..2dd53d14 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R1Curve.java @@ -0,0 +1,101 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECCurve.AbstractF2m; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; + +public class SecT193R1Curve extends AbstractF2m +{ + private static final int SecT193R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected SecT193R1Point infinity; + + public SecT193R1Curve() + { + super(193, 15, 0, 0); + + this.infinity = new SecT193R1Point(this, null, null); + + this.a = fromBigInteger(new BigInteger(1, Hex.decode("0017858FEB7A98975169E171F77B4087DE098AC8A911DF7B01"))); + this.b = fromBigInteger(new BigInteger(1, Hex.decode("00FDFB49BFE6C3A89FACADAA7A1E5BBC7CC1C2E5D831478814"))); + this.order = new BigInteger(1, Hex.decode("01000000000000000000000000C7F34A778F443ACC920EBA49")); + this.cofactor = BigInteger.valueOf(2); + + this.coord = SecT193R1_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecT193R1Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + public int getFieldSize() + { + return 193; + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecT193FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecT193R1Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecT193R1Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } + + public boolean isKoblitz() + { + return false; + } + + public int getM() + { + return 193; + } + + public boolean isTrinomial() + { + return true; + } + + public int getK1() + { + return 15; + } + + public int getK2() + { + return 0; + } + + public int getK3() + { + return 0; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R1Point.java new file mode 100644 index 00000000..9997b8e1 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R1Point.java @@ -0,0 +1,308 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECConstants; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.ECPoint.AbstractF2m; + +public class SecT193R1Point extends AbstractF2m +{ + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT193R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + { + this(curve, x, y, false); + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)} + */ + public SecT193R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecT193R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecT193R1Point(null, getAffineXCoord(), getAffineYCoord()); + } + + public ECFieldElement getYCoord() + { + ECFieldElement X = x, L = y; + + 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); + + ECFieldElement Z = zs[0]; + if (!Z.isOne()) + { + Y = Y.divide(Z); + } + + return Y; + } + + protected boolean getCompressionYTilde() + { + ECFieldElement X = this.getRawXCoord(); + if (X.isZero()) + { + return false; + } + + ECFieldElement Y = this.getRawYCoord(); + + // Y is actually Lambda (X + Y/X) here + return Y.testBitZero() != X.testBitZero(); + } + + public ECPoint add(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + ECFieldElement X2 = b.getRawXCoord(); + + if (X1.isZero()) + { + if (X2.isZero()) + { + return curve.getInfinity(); + } + + return b.add(this); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0); + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.multiply(Z1); + S2 = S2.multiply(Z1); + } + + boolean 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 twice(); + } + + return curve.getInfinity(); + } + + ECFieldElement X3, L3, Z3; + if (X2.isZero()) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.normalize(); + X1 = p.getXCoord(); + ECFieldElement Y1 = p.getYCoord(); + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.add(Y2).divide(X1); + + X3 = L.square().add(L).add(X1).add(curve.getA()); + if (X3.isZero()) + { + return new SecT193R1Point(curve, X3, curve.getB().sqrt(), this.withCompression); + } + + ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1); + L3 = Y3.divide(X3).add(X3); + Z3 = curve.fromBigInteger(ECConstants.ONE); + } + else + { + B = B.square(); + + ECFieldElement AU1 = A.multiply(U1); + ECFieldElement AU2 = A.multiply(U2); + + X3 = AU1.multiply(AU2); + if (X3.isZero()) + { + return new SecT193R1Point(curve, X3, curve.getB().sqrt(), this.withCompression); + } + + 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 SecT193R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return curve.getInfinity(); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square(); + ECFieldElement a = curve.getA(); + ECFieldElement aZ1Sq = Z1IsOne ? a : a.multiply(Z1Sq); + ECFieldElement T = L1.square().add(L1Z1).add(aZ1Sq); + if (T.isZero()) + { + return new SecT193R1Point(curve, T, curve.getB().sqrt(), withCompression); + } + + ECFieldElement X3 = T.square(); + ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq); + + ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1); + ECFieldElement L3 = X1Z1.squarePlusProduct(T, L1Z1).add(X3).add(Z3); + + return new SecT193R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twicePlus(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return twice(); + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return b; + } + + ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0); + if (X2.isZero() || !Z2.isOne()) + { + return twice().add(b); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(); + + ECFieldElement X1Sq = X1.square(); + ECFieldElement L1Sq = L1.square(); + ECFieldElement Z1Sq = Z1.square(); + ECFieldElement L1Z1 = L1.multiply(Z1); + + ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1); + ECFieldElement L2plus1 = L2.addOne(); + ECFieldElement A = curve.getA().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.getInfinity(); + } + + if (A.isZero()) + { + return new SecT193R1Point(curve, A, curve.getB().sqrt(), withCompression); + } + + 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 SecT193R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + ECFieldElement X = this.x; + if (X.isZero()) + { + return this; + } + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.y, Z = this.zs[0]; + return new SecT193R1Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R2Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R2Curve.java new file mode 100644 index 00000000..334ce3a8 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R2Curve.java @@ -0,0 +1,101 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECCurve.AbstractF2m; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; + +public class SecT193R2Curve extends AbstractF2m +{ + private static final int SecT193R2_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected SecT193R2Point infinity; + + public SecT193R2Curve() + { + super(193, 15, 0, 0); + + this.infinity = new SecT193R2Point(this, null, null); + + this.a = fromBigInteger(new BigInteger(1, Hex.decode("0163F35A5137C2CE3EA6ED8667190B0BC43ECD69977702709B"))); + this.b = fromBigInteger(new BigInteger(1, Hex.decode("00C9BB9E8927D4D64C377E2AB2856A5B16E3EFB7F61D4316AE"))); + this.order = new BigInteger(1, Hex.decode("010000000000000000000000015AAB561B005413CCD4EE99D5")); + this.cofactor = BigInteger.valueOf(2); + + this.coord = SecT193R2_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecT193R2Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + public int getFieldSize() + { + return 193; + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecT193FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecT193R2Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecT193R2Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } + + public boolean isKoblitz() + { + return false; + } + + public int getM() + { + return 193; + } + + public boolean isTrinomial() + { + return true; + } + + public int getK1() + { + return 15; + } + + public int getK2() + { + return 0; + } + + public int getK3() + { + return 0; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R2Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R2Point.java new file mode 100644 index 00000000..f3bbb706 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT193R2Point.java @@ -0,0 +1,308 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECConstants; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.ECPoint.AbstractF2m; + +public class SecT193R2Point extends AbstractF2m +{ + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT193R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + { + this(curve, x, y, false); + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)} + */ + public SecT193R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecT193R2Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecT193R2Point(null, getAffineXCoord(), getAffineYCoord()); + } + + public ECFieldElement getYCoord() + { + ECFieldElement X = x, L = y; + + 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); + + ECFieldElement Z = zs[0]; + if (!Z.isOne()) + { + Y = Y.divide(Z); + } + + return Y; + } + + protected boolean getCompressionYTilde() + { + ECFieldElement X = this.getRawXCoord(); + if (X.isZero()) + { + return false; + } + + ECFieldElement Y = this.getRawYCoord(); + + // Y is actually Lambda (X + Y/X) here + return Y.testBitZero() != X.testBitZero(); + } + + public ECPoint add(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + ECFieldElement X2 = b.getRawXCoord(); + + if (X1.isZero()) + { + if (X2.isZero()) + { + return curve.getInfinity(); + } + + return b.add(this); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0); + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.multiply(Z1); + S2 = S2.multiply(Z1); + } + + boolean 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 twice(); + } + + return curve.getInfinity(); + } + + ECFieldElement X3, L3, Z3; + if (X2.isZero()) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.normalize(); + X1 = p.getXCoord(); + ECFieldElement Y1 = p.getYCoord(); + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.add(Y2).divide(X1); + + X3 = L.square().add(L).add(X1).add(curve.getA()); + if (X3.isZero()) + { + return new SecT193R2Point(curve, X3, curve.getB().sqrt(), this.withCompression); + } + + ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1); + L3 = Y3.divide(X3).add(X3); + Z3 = curve.fromBigInteger(ECConstants.ONE); + } + else + { + B = B.square(); + + ECFieldElement AU1 = A.multiply(U1); + ECFieldElement AU2 = A.multiply(U2); + + X3 = AU1.multiply(AU2); + if (X3.isZero()) + { + return new SecT193R2Point(curve, X3, curve.getB().sqrt(), this.withCompression); + } + + 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 SecT193R2Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return curve.getInfinity(); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square(); + ECFieldElement a = curve.getA(); + ECFieldElement aZ1Sq = Z1IsOne ? a : a.multiply(Z1Sq); + ECFieldElement T = L1.square().add(L1Z1).add(aZ1Sq); + if (T.isZero()) + { + return new SecT193R2Point(curve, T, curve.getB().sqrt(), withCompression); + } + + ECFieldElement X3 = T.square(); + ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq); + + ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1); + ECFieldElement L3 = X1Z1.squarePlusProduct(T, L1Z1).add(X3).add(Z3); + + return new SecT193R2Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twicePlus(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return twice(); + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return b; + } + + ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0); + if (X2.isZero() || !Z2.isOne()) + { + return twice().add(b); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(); + + ECFieldElement X1Sq = X1.square(); + ECFieldElement L1Sq = L1.square(); + ECFieldElement Z1Sq = Z1.square(); + ECFieldElement L1Z1 = L1.multiply(Z1); + + ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1); + ECFieldElement L2plus1 = L2.addOne(); + ECFieldElement A = curve.getA().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.getInfinity(); + } + + if (A.isZero()) + { + return new SecT193R2Point(curve, A, curve.getB().sqrt(), withCompression); + } + + 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 SecT193R2Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + ECFieldElement X = this.x; + if (X.isZero()) + { + return this; + } + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.y, Z = this.zs[0]; + return new SecT193R2Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233Field.java new file mode 100644 index 00000000..e5ab93a5 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233Field.java @@ -0,0 +1,318 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.raw.Interleave; +import org.bouncycastle.math.raw.Nat256; + +public class SecT233Field +{ + private static final long M41 = -1L >>> 23; + private static final long M59 = -1L >>> 5; + + public static void add(long[] x, long[] y, long[] z) + { + z[0] = x[0] ^ y[0]; + z[1] = x[1] ^ y[1]; + z[2] = x[2] ^ y[2]; + z[3] = x[3] ^ y[3]; + } + + public static void addExt(long[] xx, long[] yy, long[] zz) + { + zz[0] = xx[0] ^ yy[0]; + zz[1] = xx[1] ^ yy[1]; + zz[2] = xx[2] ^ yy[2]; + zz[3] = xx[3] ^ yy[3]; + zz[4] = xx[4] ^ yy[4]; + zz[5] = xx[5] ^ yy[5]; + zz[6] = xx[6] ^ yy[6]; + zz[7] = xx[7] ^ yy[7]; + } + + public static void addOne(long[] x, long[] z) + { + z[0] = x[0] ^ 1L; + z[1] = x[1]; + z[2] = x[2]; + z[3] = x[3]; + } + + public static long[] fromBigInteger(BigInteger x) + { + long[] z = Nat256.fromBigInteger64(x); + reduce23(z, 0); + return z; + } + + public static void invert(long[] x, long[] z) + { + if (Nat256.isZero64(x)) + { + throw new IllegalStateException(); + } + + // Itoh-Tsujii inversion + + long[] t0 = Nat256.create64(); + long[] t1 = Nat256.create64(); + + square(x, t0); + multiply(t0, x, t0); + square(t0, t0); + multiply(t0, x, t0); + squareN(t0, 3, t1); + multiply(t1, t0, t1); + square(t1, t1); + multiply(t1, x, t1); + squareN(t1, 7, t0); + multiply(t0, t1, t0); + squareN(t0, 14, t1); + multiply(t1, t0, t1); + square(t1, t1); + multiply(t1, x, t1); + squareN(t1, 29, t0); + multiply(t0, t1, t0); + squareN(t0, 58, t1); + multiply(t1, t0, t1); + squareN(t1, 116, t0); + multiply(t0, t1, t0); + square(t0, z); + } + + public static void multiply(long[] x, long[] y, long[] z) + { + long[] tt = Nat256.createExt64(); + implMultiply(x, y, tt); + reduce(tt, z); + } + + public static void multiplyAddToExt(long[] x, long[] y, long[] zz) + { + long[] tt = Nat256.createExt64(); + implMultiply(x, y, tt); + addExt(zz, tt, zz); + } + + public static void reduce(long[] xx, long[] z) + { + long x0 = xx[0], x1 = xx[1], x2 = xx[2], x3 = xx[3]; + long x4 = xx[4], x5 = xx[5], x6 = xx[6], x7 = xx[7]; + + x3 ^= (x7 << 23); + x4 ^= (x7 >>> 41) ^ (x7 << 33); + x5 ^= (x7 >>> 31); + + x2 ^= (x6 << 23); + x3 ^= (x6 >>> 41) ^ (x6 << 33); + x4 ^= (x6 >>> 31); + + x1 ^= (x5 << 23); + x2 ^= (x5 >>> 41) ^ (x5 << 33); + x3 ^= (x5 >>> 31); + + x0 ^= (x4 << 23); + x1 ^= (x4 >>> 41) ^ (x4 << 33); + x2 ^= (x4 >>> 31); + + long t = x3 >>> 41; + z[0] = x0 ^ t; + z[1] = x1 ^ (t << 10); + z[2] = x2; + z[3] = x3 & M41; + } + + public static void reduce23(long[] z, int zOff) + { + long z3 = z[zOff + 3], t = z3 >>> 41; + z[zOff ] ^= t; + z[zOff + 1] ^= (t << 10); + z[zOff + 3] = z3 & M41; + } + + public static void square(long[] x, long[] z) + { + long[] tt = Nat256.createExt64(); + implSquare(x, tt); + reduce(tt, z); + } + + public static void squareAddToExt(long[] x, long[] zz) + { + long[] tt = Nat256.createExt64(); + implSquare(x, tt); + addExt(zz, tt, zz); + } + + public static void squareN(long[] x, int n, long[] z) + { +// assert n > 0; + + long[] tt = Nat256.createExt64(); + implSquare(x, tt); + reduce(tt, z); + + while (--n > 0) + { + implSquare(z, tt); + reduce(tt, z); + } + } + + public static void sqrt(long[] x, long[] z) + { + long u0, u1; + u0 = Interleave.unshuffle(x[0]); u1 = Interleave.unshuffle(x[1]); + long e0 = (u0 & 0x00000000FFFFFFFFL) | (u1 << 32); + long c0 = (u0 >>> 32) | (u1 & 0xFFFFFFFF00000000L); + + u0 = Interleave.unshuffle(x[2]); u1 = Interleave.unshuffle(x[3]); + long e1 = (u0 & 0x00000000FFFFFFFFL) | (u1 << 32); + long c1 = (u0 >>> 32) | (u1 & 0xFFFFFFFF00000000L); + + long c2; + c2 = (c1 >>> 27); + c1 ^= (c0 >>> 27) | (c1 << 37); + c0 ^= (c0 << 37); + + long[] tt = Nat256.createExt64(); + + int[] shifts = { 32, 117, 191 }; + for (int i = 0; i < shifts.length; ++i) + { + int w = shifts[i] >>> 6, s = shifts[i] & 63; +// assert s != 0; + tt[w ] ^= (c0 << s); + tt[w + 1] ^= (c1 << s) | (c0 >>> -s); + tt[w + 2] ^= (c2 << s) | (c1 >>> -s); + tt[w + 3] ^= (c2 >>> -s); + } + + reduce(tt, z); + + z[0] ^= e0; + z[1] ^= e1; + } + + public static int trace(long[] x) + { + // Non-zero-trace bits: 0, 159 + return (int)(x[0] ^ (x[2] >>> 31)) & 1; + } + + protected static void implCompactExt(long[] zz) + { + long z0 = zz[0], z1 = zz[1], z2 = zz[2], z3 = zz[3], z4 = zz[4], z5 = zz[5], z6 = zz[6], z7 = zz[7]; + zz[0] = z0 ^ (z1 << 59); + zz[1] = (z1 >>> 5) ^ (z2 << 54); + zz[2] = (z2 >>> 10) ^ (z3 << 49); + zz[3] = (z3 >>> 15) ^ (z4 << 44); + zz[4] = (z4 >>> 20) ^ (z5 << 39); + zz[5] = (z5 >>> 25) ^ (z6 << 34); + zz[6] = (z6 >>> 30) ^ (z7 << 29); + zz[7] = (z7 >>> 35); + } + + protected static void implExpand(long[] x, long[] z) + { + long x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3]; + z[0] = x0 & M59; + z[1] = ((x0 >>> 59) ^ (x1 << 5)) & M59; + z[2] = ((x1 >>> 54) ^ (x2 << 10)) & M59; + z[3] = ((x2 >>> 49) ^ (x3 << 15)); + } + + protected static void implMultiply(long[] x, long[] y, long[] zz) + { + /* + * "Two-level seven-way recursion" as described in "Batch binary Edwards", Daniel J. Bernstein. + */ + + long[] f = new long[4], g = new long[4]; + implExpand(x, f); + implExpand(y, g); + + implMulwAcc(f[0], g[0], zz, 0); + implMulwAcc(f[1], g[1], zz, 1); + implMulwAcc(f[2], g[2], zz, 2); + implMulwAcc(f[3], g[3], zz, 3); + + // U *= (1 - t^n) + for (int i = 5; i > 0; --i) + { + zz[i] ^= zz[i - 1]; + } + + implMulwAcc(f[0] ^ f[1], g[0] ^ g[1], zz, 1); + implMulwAcc(f[2] ^ f[3], g[2] ^ g[3], zz, 3); + + // V *= (1 - t^2n) + for (int i = 7; i > 1; --i) + { + zz[i] ^= zz[i - 2]; + } + + // Double-length recursion + { + long c0 = f[0] ^ f[2], c1 = f[1] ^ f[3]; + long d0 = g[0] ^ g[2], d1 = g[1] ^ g[3]; + implMulwAcc(c0 ^ c1, d0 ^ d1, zz, 3); + long[] t = new long[3]; + implMulwAcc(c0, d0, t, 0); + implMulwAcc(c1, d1, t, 1); + long t0 = t[0], t1 = t[1], t2 = t[2]; + zz[2] ^= t0; + zz[3] ^= t0 ^ t1; + zz[4] ^= t2 ^ t1; + zz[5] ^= t2; + } + + implCompactExt(zz); + } + + protected static void implMulwAcc(long x, long y, long[] z, int zOff) + { +// assert x >>> 59 == 0; +// assert y >>> 59 == 0; + + long[] u = new long[8]; +// u[0] = 0; + u[1] = y; + u[2] = u[1] << 1; + u[3] = u[2] ^ y; + u[4] = u[2] << 1; + u[5] = u[4] ^ y; + u[6] = u[3] << 1; + u[7] = u[6] ^ y; + + int j = (int)x; + long g, h = 0, l = u[j & 7] + ^ (u[(j >>> 3) & 7] << 3); + int k = 54; + do + { + j = (int)(x >>> k); + g = u[j & 7] + ^ u[(j >>> 3) & 7] << 3; + l ^= (g << k); + h ^= (g >>> -k); + } + while ((k -= 6) > 0); + +// assert h >>> 53 == 0; + + z[zOff ] ^= l & M59; + z[zOff + 1] ^= (l >>> 59) ^ (h << 5); + } + + protected static void implSquare(long[] x, long[] zz) + { + Interleave.expand64To128(x[0], zz, 0); + Interleave.expand64To128(x[1], zz, 2); + Interleave.expand64To128(x[2], zz, 4); + + long x3 = x[3]; + zz[6] = Interleave.expand32to64((int)x3); + zz[7] = Interleave.expand16to32((int)(x3 >>> 32)) & 0xFFFFFFFFL; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233FieldElement.java new file mode 100644 index 00000000..73706517 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233FieldElement.java @@ -0,0 +1,221 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.raw.Nat256; +import org.bouncycastle.util.Arrays; + +public class SecT233FieldElement extends ECFieldElement +{ + protected long[] x; + + public SecT233FieldElement(BigInteger x) + { + if (x == null || x.signum() < 0 || x.bitLength() > 233) + { + throw new IllegalArgumentException("x value invalid for SecT233FieldElement"); + } + + this.x = SecT233Field.fromBigInteger(x); + } + + public SecT233FieldElement() + { + this.x = Nat256.create64(); + } + + protected SecT233FieldElement(long[] x) + { + this.x = x; + } + +// public int bitLength() +// { +// return x.degree(); +// } + + public boolean isOne() + { + return Nat256.isOne64(x); + } + + public boolean isZero() + { + return Nat256.isZero64(x); + } + + public boolean testBitZero() + { + return (x[0] & 1L) != 0L; + } + + public BigInteger toBigInteger() + { + return Nat256.toBigInteger64(x); + } + + public String getFieldName() + { + return "SecT233Field"; + } + + public int getFieldSize() + { + return 233; + } + + public ECFieldElement add(ECFieldElement b) + { + long[] z = Nat256.create64(); + SecT233Field.add(x, ((SecT233FieldElement)b).x, z); + return new SecT233FieldElement(z); + } + + public ECFieldElement addOne() + { + long[] z = Nat256.create64(); + SecT233Field.addOne(x, z); + return new SecT233FieldElement(z); + } + + public ECFieldElement subtract(ECFieldElement b) + { + // Addition and subtraction are the same in F2m + return add(b); + } + + public ECFieldElement multiply(ECFieldElement b) + { + long[] z = Nat256.create64(); + SecT233Field.multiply(x, ((SecT233FieldElement)b).x, z); + return new SecT233FieldElement(z); + } + + public ECFieldElement multiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + return multiplyPlusProduct(b, x, y); + } + + public ECFieldElement multiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + long[] ax = this.x, bx = ((SecT233FieldElement)b).x; + long[] xx = ((SecT233FieldElement)x).x, yx = ((SecT233FieldElement)y).x; + + long[] tt = Nat256.createExt64(); + SecT233Field.multiplyAddToExt(ax, bx, tt); + SecT233Field.multiplyAddToExt(xx, yx, tt); + + long[] z = Nat256.create64(); + SecT233Field.reduce(tt, z); + return new SecT233FieldElement(z); + } + + public ECFieldElement divide(ECFieldElement b) + { + return multiply(b.invert()); + } + + public ECFieldElement negate() + { + return this; + } + + public ECFieldElement square() + { + long[] z = Nat256.create64(); + SecT233Field.square(x, z); + return new SecT233FieldElement(z); + } + + public ECFieldElement squareMinusProduct(ECFieldElement x, ECFieldElement y) + { + return squarePlusProduct(x, y); + } + + public ECFieldElement squarePlusProduct(ECFieldElement x, ECFieldElement y) + { + long[] ax = this.x; + long[] xx = ((SecT233FieldElement)x).x, yx = ((SecT233FieldElement)y).x; + + long[] tt = Nat256.createExt64(); + SecT233Field.squareAddToExt(ax, tt); + SecT233Field.multiplyAddToExt(xx, yx, tt); + + long[] z = Nat256.create64(); + SecT233Field.reduce(tt, z); + return new SecT233FieldElement(z); + } + + public ECFieldElement squarePow(int pow) + { + if (pow < 1) + { + return this; + } + + long[] z = Nat256.create64(); + SecT233Field.squareN(x, pow, z); + return new SecT233FieldElement(z); + } + + public ECFieldElement invert() + { + long[] z = Nat256.create64(); + SecT233Field.invert(x, z); + return new SecT233FieldElement(z); + } + + public ECFieldElement sqrt() + { + long[] z = Nat256.create64(); + SecT233Field.sqrt(x, z); + return new SecT233FieldElement(z); + } + + public int getRepresentation() + { + return ECFieldElement.F2m.TPB; + } + + public int getM() + { + return 233; + } + + public int getK1() + { + return 74; + } + + public int getK2() + { + return 0; + } + + public int getK3() + { + return 0; + } + + public boolean equals(Object other) + { + if (other == this) + { + return true; + } + + if (!(other instanceof SecT233FieldElement)) + { + return false; + } + + SecT233FieldElement o = (SecT233FieldElement)other; + return Nat256.eq64(x, o.x); + } + + public int hashCode() + { + return 2330074 ^ Arrays.hashCode(x, 0, 4); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233K1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233K1Curve.java new file mode 100644 index 00000000..2e83d1c9 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233K1Curve.java @@ -0,0 +1,108 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECCurve.AbstractF2m; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECMultiplier; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.WTauNafMultiplier; +import org.bouncycastle.util.encoders.Hex; + +public class SecT233K1Curve extends AbstractF2m +{ + private static final int SecT233K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected SecT233K1Point infinity; + + public SecT233K1Curve() + { + super(233, 74, 0, 0); + + this.infinity = new SecT233K1Point(this, null, null); + + this.a = fromBigInteger(BigInteger.valueOf(0)); + this.b = fromBigInteger(BigInteger.valueOf(1)); + this.order = new BigInteger(1, Hex.decode("8000000000000000000000000000069D5BB915BCD46EFB1AD5F173ABDF")); + this.cofactor = BigInteger.valueOf(4); + + this.coord = SecT233K1_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecT233K1Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + protected ECMultiplier createDefaultMultiplier() + { + return new WTauNafMultiplier(); + } + + public int getFieldSize() + { + return 233; + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecT233FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecT233K1Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecT233K1Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } + + public boolean isKoblitz() + { + return true; + } + + public int getM() + { + return 233; + } + + public boolean isTrinomial() + { + return true; + } + + public int getK1() + { + return 74; + } + + public int getK2() + { + return 0; + } + + public int getK3() + { + return 0; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233K1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233K1Point.java new file mode 100644 index 00000000..90a9701c --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233K1Point.java @@ -0,0 +1,323 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECConstants; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.ECPoint.AbstractF2m; + +public class SecT233K1Point extends AbstractF2m +{ + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT233K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + { + this(curve, x, y, false); + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)} + */ + public SecT233K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecT233K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecT233K1Point(null, this.getAffineXCoord(), this.getAffineYCoord()); // earlier JDK + } + + public ECFieldElement getYCoord() + { + ECFieldElement X = x, L = y; + + 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); + + ECFieldElement Z = zs[0]; + if (!Z.isOne()) + { + Y = Y.divide(Z); + } + + return Y; + } + + protected boolean getCompressionYTilde() + { + ECFieldElement X = this.getRawXCoord(); + if (X.isZero()) + { + return false; + } + + ECFieldElement Y = this.getRawYCoord(); + + // Y is actually Lambda (X + Y/X) here + return Y.testBitZero() != X.testBitZero(); + } + + public ECPoint add(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + ECFieldElement X2 = b.getRawXCoord(); + + if (X1.isZero()) + { + if (X2.isZero()) + { + return curve.getInfinity(); + } + + return b.add(this); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0); + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.multiply(Z1); + S2 = S2.multiply(Z1); + } + + boolean 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 twice(); + } + + return curve.getInfinity(); + } + + ECFieldElement X3, L3, Z3; + if (X2.isZero()) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.normalize(); + X1 = p.getXCoord(); + ECFieldElement Y1 = p.getYCoord(); + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.add(Y2).divide(X1); + +// X3 = L.square().add(L).add(X1).add(curve.getA()); + X3 = L.square().add(L).add(X1); + if (X3.isZero()) + { +// return new SecT233K1Point(curve, X3, curve.getB().sqrt(), this.withCompression); + return new SecT233K1Point(curve, X3, curve.getB(), this.withCompression); + } + + ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1); + L3 = Y3.divide(X3).add(X3); + Z3 = curve.fromBigInteger(ECConstants.ONE); + } + else + { + B = B.square(); + + ECFieldElement AU1 = A.multiply(U1); + ECFieldElement AU2 = A.multiply(U2); + + X3 = AU1.multiply(AU2); + if (X3.isZero()) + { +// return new SecT233K1Point(curve, X3, curve.getB().sqrt(), this.withCompression); + return new SecT233K1Point(curve, X3, curve.getB(), this.withCompression); + } + + 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 SecT233K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return curve.getInfinity(); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square(); + ECFieldElement T; + if (Z1IsOne) + { + T = L1.square().add(L1); + } + else + { + T = L1.add(Z1).multiply(L1); + } + + if (T.isZero()) + { +// return new SecT233K1Point(curve, T, curve.getB().sqrt(), withCompression); + return new SecT233K1Point(curve, T, curve.getB(), withCompression); + } + + ECFieldElement X3 = T.square(); + ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq); + + ECFieldElement t1 = L1.add(X1).square(); + ECFieldElement t2 = Z1IsOne ? Z1 : Z1Sq.square(); + ECFieldElement L3 = t1.add(T).add(Z1Sq).multiply(t1).add(t2).add(X3).add(Z3); + + return new SecT233K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twicePlus(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return twice(); + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return b; + } + + // NOTE: twicePlus() only optimized for lambda-affine argument + ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0); + if (X2.isZero() || !Z2.isOne()) + { + return twice().add(b); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(); + + ECFieldElement X1Sq = X1.square(); + ECFieldElement L1Sq = L1.square(); + ECFieldElement Z1Sq = Z1.square(); + ECFieldElement L1Z1 = L1.multiply(Z1); + +// ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1); + ECFieldElement T = L1Sq.add(L1Z1); + ECFieldElement L2plus1 = L2.addOne(); +// ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement A = 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.getInfinity(); + } + + if (A.isZero()) + { +// return new SecT233K1Point(curve, A, curve.getB().sqrt(), withCompression); + return new SecT233K1Point(curve, A, curve.getB(), withCompression); + } + + 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 SecT233K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + ECFieldElement X = this.x; + if (X.isZero()) + { + return this; + } + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.y, Z = this.zs[0]; + return new SecT233K1Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233R1Curve.java new file mode 100644 index 00000000..7a9c5226 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233R1Curve.java @@ -0,0 +1,101 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECCurve.AbstractF2m; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; + +public class SecT233R1Curve extends AbstractF2m +{ + private static final int SecT233R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected SecT233R1Point infinity; + + public SecT233R1Curve() + { + super(233, 74, 0, 0); + + this.infinity = new SecT233R1Point(this, null, null); + + this.a = fromBigInteger(BigInteger.valueOf(1)); + this.b = fromBigInteger(new BigInteger(1, Hex.decode("0066647EDE6C332C7F8C0923BB58213B333B20E9CE4281FE115F7D8F90AD"))); + this.order = new BigInteger(1, Hex.decode("01000000000000000000000000000013E974E72F8A6922031D2603CFE0D7")); + this.cofactor = BigInteger.valueOf(2); + + this.coord = SecT233R1_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecT233R1Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + public int getFieldSize() + { + return 233; + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecT233FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecT233R1Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecT233R1Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } + + public boolean isKoblitz() + { + return false; + } + + public int getM() + { + return 233; + } + + public boolean isTrinomial() + { + return true; + } + + public int getK1() + { + return 74; + } + + public int getK2() + { + return 0; + } + + public int getK3() + { + return 0; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233R1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233R1Point.java new file mode 100644 index 00000000..2c01a583 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT233R1Point.java @@ -0,0 +1,309 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECConstants; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.ECPoint.AbstractF2m; + +public class SecT233R1Point extends AbstractF2m +{ + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT233R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + { + this(curve, x, y, false); + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)} + */ + public SecT233R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecT233R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecT233R1Point(null, getAffineXCoord(), getAffineYCoord()); + } + + public ECFieldElement getYCoord() + { + ECFieldElement X = x, L = y; + + 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); + + ECFieldElement Z = zs[0]; + if (!Z.isOne()) + { + Y = Y.divide(Z); + } + + return Y; + } + + protected boolean getCompressionYTilde() + { + ECFieldElement X = this.getRawXCoord(); + if (X.isZero()) + { + return false; + } + + ECFieldElement Y = this.getRawYCoord(); + + // Y is actually Lambda (X + Y/X) here + return Y.testBitZero() != X.testBitZero(); + } + + public ECPoint add(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + ECFieldElement X2 = b.getRawXCoord(); + + if (X1.isZero()) + { + if (X2.isZero()) + { + return curve.getInfinity(); + } + + return b.add(this); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0); + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.multiply(Z1); + S2 = S2.multiply(Z1); + } + + boolean 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 twice(); + } + + return curve.getInfinity(); + } + + ECFieldElement X3, L3, Z3; + if (X2.isZero()) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.normalize(); + X1 = p.getXCoord(); + ECFieldElement Y1 = p.getYCoord(); + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.add(Y2).divide(X1); + +// X3 = L.square().add(L).add(X1).add(curve.getA()); + X3 = L.square().add(L).add(X1).addOne(); + if (X3.isZero()) + { + return new SecT233R1Point(curve, X3, curve.getB().sqrt(), this.withCompression); + } + + ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1); + L3 = Y3.divide(X3).add(X3); + Z3 = curve.fromBigInteger(ECConstants.ONE); + } + else + { + B = B.square(); + + ECFieldElement AU1 = A.multiply(U1); + ECFieldElement AU2 = A.multiply(U2); + + X3 = AU1.multiply(AU2); + if (X3.isZero()) + { + return new SecT233R1Point(curve, X3, curve.getB().sqrt(), this.withCompression); + } + + 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 SecT233R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return curve.getInfinity(); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square(); + ECFieldElement T = L1.square().add(L1Z1).add(Z1Sq); + if (T.isZero()) + { + return new SecT233R1Point(curve, T, curve.getB().sqrt(), withCompression); + } + + ECFieldElement X3 = T.square(); + ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq); + + ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1); + ECFieldElement L3 = X1Z1.squarePlusProduct(T, L1Z1).add(X3).add(Z3); + + return new SecT233R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twicePlus(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return twice(); + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return b; + } + + ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0); + if (X2.isZero() || !Z2.isOne()) + { + return twice().add(b); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(); + + ECFieldElement X1Sq = X1.square(); + ECFieldElement L1Sq = L1.square(); + ECFieldElement Z1Sq = Z1.square(); + ECFieldElement L1Z1 = L1.multiply(Z1); + +// ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1); + ECFieldElement T = Z1Sq.add(L1Sq).add(L1Z1); + ECFieldElement L2plus1 = L2.addOne(); +// ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement A = L2.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.getInfinity(); + } + + if (A.isZero()) + { + return new SecT233R1Point(curve, A, curve.getB().sqrt(), withCompression); + } + + 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 SecT233R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + ECFieldElement X = this.x; + if (X.isZero()) + { + return this; + } + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.y, Z = this.zs[0]; + return new SecT233R1Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239Field.java new file mode 100644 index 00000000..5f5bf3fd --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239Field.java @@ -0,0 +1,329 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.raw.Interleave; +import org.bouncycastle.math.raw.Nat256; + +public class SecT239Field +{ + private static final long M47 = -1L >>> 17; + private static final long M60 = -1L >>> 4; + + public static void add(long[] x, long[] y, long[] z) + { + z[0] = x[0] ^ y[0]; + z[1] = x[1] ^ y[1]; + z[2] = x[2] ^ y[2]; + z[3] = x[3] ^ y[3]; + } + + public static void addExt(long[] xx, long[] yy, long[] zz) + { + zz[0] = xx[0] ^ yy[0]; + zz[1] = xx[1] ^ yy[1]; + zz[2] = xx[2] ^ yy[2]; + zz[3] = xx[3] ^ yy[3]; + zz[4] = xx[4] ^ yy[4]; + zz[5] = xx[5] ^ yy[5]; + zz[6] = xx[6] ^ yy[6]; + zz[7] = xx[7] ^ yy[7]; + } + + public static void addOne(long[] x, long[] z) + { + z[0] = x[0] ^ 1L; + z[1] = x[1]; + z[2] = x[2]; + z[3] = x[3]; + } + + public static long[] fromBigInteger(BigInteger x) + { + long[] z = Nat256.fromBigInteger64(x); + reduce17(z, 0); + return z; + } + + public static void invert(long[] x, long[] z) + { + if (Nat256.isZero64(x)) + { + throw new IllegalStateException(); + } + + // Itoh-Tsujii inversion + + long[] t0 = Nat256.create64(); + long[] t1 = Nat256.create64(); + + square(x, t0); + multiply(t0, x, t0); + square(t0, t0); + multiply(t0, x, t0); + squareN(t0, 3, t1); + multiply(t1, t0, t1); + square(t1, t1); + multiply(t1, x, t1); + squareN(t1, 7, t0); + multiply(t0, t1, t0); + squareN(t0, 14, t1); + multiply(t1, t0, t1); + square(t1, t1); + multiply(t1, x, t1); + squareN(t1, 29, t0); + multiply(t0, t1, t0); + square(t0, t0); + multiply(t0, x, t0); + squareN(t0, 59, t1); + multiply(t1, t0, t1); + square(t1, t1); + multiply(t1, x, t1); + squareN(t1, 119, t0); + multiply(t0, t1, t0); + square(t0, z); + } + + public static void multiply(long[] x, long[] y, long[] z) + { + long[] tt = Nat256.createExt64(); + implMultiply(x, y, tt); + reduce(tt, z); + } + + public static void multiplyAddToExt(long[] x, long[] y, long[] zz) + { + long[] tt = Nat256.createExt64(); + implMultiply(x, y, tt); + addExt(zz, tt, zz); + } + + public static void reduce(long[] xx, long[] z) + { + long x0 = xx[0], x1 = xx[1], x2 = xx[2], x3 = xx[3]; + long x4 = xx[4], x5 = xx[5], x6 = xx[6], x7 = xx[7]; + + x3 ^= (x7 << 17); + x4 ^= (x7 >>> 47); + x5 ^= (x7 << 47); + x6 ^= (x7 >>> 17); + + x2 ^= (x6 << 17); + x3 ^= (x6 >>> 47); + x4 ^= (x6 << 47); + x5 ^= (x6 >>> 17); + + x1 ^= (x5 << 17); + x2 ^= (x5 >>> 47); + x3 ^= (x5 << 47); + x4 ^= (x5 >>> 17); + + x0 ^= (x4 << 17); + x1 ^= (x4 >>> 47); + x2 ^= (x4 << 47); + x3 ^= (x4 >>> 17); + + long t = x3 >>> 47; + z[0] = x0 ^ t; + z[1] = x1; + z[2] = x2 ^ (t << 30); + z[3] = x3 & M47; + } + + public static void reduce17(long[] z, int zOff) + { + long z3 = z[zOff + 3], t = z3 >>> 47; + z[zOff ] ^= t; + z[zOff + 2] ^= (t << 30); + z[zOff + 3] = z3 & M47; + } + + public static void sqrt(long[] x, long[] z) + { + long u0, u1; + u0 = Interleave.unshuffle(x[0]); u1 = Interleave.unshuffle(x[1]); + long e0 = (u0 & 0x00000000FFFFFFFFL) | (u1 << 32); + long c0 = (u0 >>> 32) | (u1 & 0xFFFFFFFF00000000L); + + u0 = Interleave.unshuffle(x[2]); u1 = Interleave.unshuffle(x[3]); + long e1 = (u0 & 0x00000000FFFFFFFFL) | (u1 << 32); + long c1 = (u0 >>> 32) | (u1 & 0xFFFFFFFF00000000L); + + long c2, c3; + c3 = (c1 >>> 49); + c2 = (c0 >>> 49) | (c1 << 15); + c1 ^= (c0 << 15); + + long[] tt = Nat256.createExt64(); + + int[] shifts = { 39, 120 }; + for (int i = 0; i < shifts.length; ++i) + { + int w = shifts[i] >>> 6, s = shifts[i] & 63; +// assert s != 0; + tt[w ] ^= (c0 << s); + tt[w + 1] ^= (c1 << s) | (c0 >>> -s); + tt[w + 2] ^= (c2 << s) | (c1 >>> -s); + tt[w + 3] ^= (c3 << s) | (c2 >>> -s); + tt[w + 4] ^= (c3 >>> -s); + } + + reduce(tt, z); + + z[0] ^= e0; + z[1] ^= e1; + } + + public static void square(long[] x, long[] z) + { + long[] tt = Nat256.createExt64(); + implSquare(x, tt); + reduce(tt, z); + } + + public static void squareAddToExt(long[] x, long[] zz) + { + long[] tt = Nat256.createExt64(); + implSquare(x, tt); + addExt(zz, tt, zz); + } + + public static void squareN(long[] x, int n, long[] z) + { +// assert n > 0; + + long[] tt = Nat256.createExt64(); + implSquare(x, tt); + reduce(tt, z); + + while (--n > 0) + { + implSquare(z, tt); + reduce(tt, z); + } + } + + public static int trace(long[] x) + { + // Non-zero-trace bits: 0, 81, 162 + return (int)(x[0] ^ (x[1] >>> 17) ^ (x[2] >>> 34)) & 1; + } + + protected static void implCompactExt(long[] zz) + { + long z0 = zz[0], z1 = zz[1], z2 = zz[2], z3 = zz[3], z4 = zz[4], z5 = zz[5], z6 = zz[6], z7 = zz[7]; + zz[0] = z0 ^ (z1 << 60); + zz[1] = (z1 >>> 4) ^ (z2 << 56); + zz[2] = (z2 >>> 8) ^ (z3 << 52); + zz[3] = (z3 >>> 12) ^ (z4 << 48); + zz[4] = (z4 >>> 16) ^ (z5 << 44); + zz[5] = (z5 >>> 20) ^ (z6 << 40); + zz[6] = (z6 >>> 24) ^ (z7 << 36); + zz[7] = (z7 >>> 28); + } + + protected static void implExpand(long[] x, long[] z) + { + long x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3]; + z[0] = x0 & M60; + z[1] = ((x0 >>> 60) ^ (x1 << 4)) & M60; + z[2] = ((x1 >>> 56) ^ (x2 << 8)) & M60; + z[3] = ((x2 >>> 52) ^ (x3 << 12)); + } + + protected static void implMultiply(long[] x, long[] y, long[] zz) + { + /* + * "Two-level seven-way recursion" as described in "Batch binary Edwards", Daniel J. Bernstein. + */ + + long[] f = new long[4], g = new long[4]; + implExpand(x, f); + implExpand(y, g); + + implMulwAcc(f[0], g[0], zz, 0); + implMulwAcc(f[1], g[1], zz, 1); + implMulwAcc(f[2], g[2], zz, 2); + implMulwAcc(f[3], g[3], zz, 3); + + // U *= (1 - t^n) + for (int i = 5; i > 0; --i) + { + zz[i] ^= zz[i - 1]; + } + + implMulwAcc(f[0] ^ f[1], g[0] ^ g[1], zz, 1); + implMulwAcc(f[2] ^ f[3], g[2] ^ g[3], zz, 3); + + // V *= (1 - t^2n) + for (int i = 7; i > 1; --i) + { + zz[i] ^= zz[i - 2]; + } + + // Double-length recursion + { + long c0 = f[0] ^ f[2], c1 = f[1] ^ f[3]; + long d0 = g[0] ^ g[2], d1 = g[1] ^ g[3]; + implMulwAcc(c0 ^ c1, d0 ^ d1, zz, 3); + long[] t = new long[3]; + implMulwAcc(c0, d0, t, 0); + implMulwAcc(c1, d1, t, 1); + long t0 = t[0], t1 = t[1], t2 = t[2]; + zz[2] ^= t0; + zz[3] ^= t0 ^ t1; + zz[4] ^= t2 ^ t1; + zz[5] ^= t2; + } + + implCompactExt(zz); + } + + protected static void implMulwAcc(long x, long y, long[] z, int zOff) + { +// assert x >>> 60 == 0; +// assert y >>> 60 == 0; + + long[] u = new long[8]; +// u[0] = 0; + u[1] = y; + u[2] = u[1] << 1; + u[3] = u[2] ^ y; + u[4] = u[2] << 1; + u[5] = u[4] ^ y; + u[6] = u[3] << 1; + u[7] = u[6] ^ y; + + int j = (int)x; + long g, h = 0, l = u[j & 7] + ^ (u[(j >>> 3) & 7] << 3); + int k = 54; + do + { + j = (int)(x >>> k); + g = u[j & 7] + ^ u[(j >>> 3) & 7] << 3; + l ^= (g << k); + h ^= (g >>> -k); + } + while ((k -= 6) > 0); + + h ^= ((x & 0x0820820820820820L) & ((y << 4) >> 63)) >>> 5; + +// assert h >>> 55 == 0; + + z[zOff ] ^= l & M60; + z[zOff + 1] ^= (l >>> 60) ^ (h << 4); + } + + protected static void implSquare(long[] x, long[] zz) + { + Interleave.expand64To128(x[0], zz, 0); + Interleave.expand64To128(x[1], zz, 2); + Interleave.expand64To128(x[2], zz, 4); + + long x3 = x[3]; + zz[6] = Interleave.expand32to64((int)x3); + zz[7] = Interleave.expand16to32((int)(x3 >>> 32)) & 0xFFFFFFFFL; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239FieldElement.java new file mode 100644 index 00000000..f6553ba2 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239FieldElement.java @@ -0,0 +1,221 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.raw.Nat256; +import org.bouncycastle.util.Arrays; + +public class SecT239FieldElement extends ECFieldElement +{ + protected long[] x; + + public SecT239FieldElement(BigInteger x) + { + if (x == null || x.signum() < 0 || x.bitLength() > 239) + { + throw new IllegalArgumentException("x value invalid for SecT239FieldElement"); + } + + this.x = SecT239Field.fromBigInteger(x); + } + + public SecT239FieldElement() + { + this.x = Nat256.create64(); + } + + protected SecT239FieldElement(long[] x) + { + this.x = x; + } + +// public int bitLength() +// { +// return x.degree(); +// } + + public boolean isOne() + { + return Nat256.isOne64(x); + } + + public boolean isZero() + { + return Nat256.isZero64(x); + } + + public boolean testBitZero() + { + return (x[0] & 1L) != 0L; + } + + public BigInteger toBigInteger() + { + return Nat256.toBigInteger64(x); + } + + public String getFieldName() + { + return "SecT239Field"; + } + + public int getFieldSize() + { + return 239; + } + + public ECFieldElement add(ECFieldElement b) + { + long[] z = Nat256.create64(); + SecT239Field.add(x, ((SecT239FieldElement)b).x, z); + return new SecT239FieldElement(z); + } + + public ECFieldElement addOne() + { + long[] z = Nat256.create64(); + SecT239Field.addOne(x, z); + return new SecT239FieldElement(z); + } + + public ECFieldElement subtract(ECFieldElement b) + { + // Addition and subtraction are the same in F2m + return add(b); + } + + public ECFieldElement multiply(ECFieldElement b) + { + long[] z = Nat256.create64(); + SecT239Field.multiply(x, ((SecT239FieldElement)b).x, z); + return new SecT239FieldElement(z); + } + + public ECFieldElement multiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + return multiplyPlusProduct(b, x, y); + } + + public ECFieldElement multiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + long[] ax = this.x, bx = ((SecT239FieldElement)b).x; + long[] xx = ((SecT239FieldElement)x).x, yx = ((SecT239FieldElement)y).x; + + long[] tt = Nat256.createExt64(); + SecT239Field.multiplyAddToExt(ax, bx, tt); + SecT239Field.multiplyAddToExt(xx, yx, tt); + + long[] z = Nat256.create64(); + SecT239Field.reduce(tt, z); + return new SecT239FieldElement(z); + } + + public ECFieldElement divide(ECFieldElement b) + { + return multiply(b.invert()); + } + + public ECFieldElement negate() + { + return this; + } + + public ECFieldElement square() + { + long[] z = Nat256.create64(); + SecT239Field.square(x, z); + return new SecT239FieldElement(z); + } + + public ECFieldElement squareMinusProduct(ECFieldElement x, ECFieldElement y) + { + return squarePlusProduct(x, y); + } + + public ECFieldElement squarePlusProduct(ECFieldElement x, ECFieldElement y) + { + long[] ax = this.x; + long[] xx = ((SecT239FieldElement)x).x, yx = ((SecT239FieldElement)y).x; + + long[] tt = Nat256.createExt64(); + SecT239Field.squareAddToExt(ax, tt); + SecT239Field.multiplyAddToExt(xx, yx, tt); + + long[] z = Nat256.create64(); + SecT239Field.reduce(tt, z); + return new SecT239FieldElement(z); + } + + public ECFieldElement squarePow(int pow) + { + if (pow < 1) + { + return this; + } + + long[] z = Nat256.create64(); + SecT239Field.squareN(x, pow, z); + return new SecT239FieldElement(z); + } + + public ECFieldElement invert() + { + long[] z = Nat256.create64(); + SecT239Field.invert(x, z); + return new SecT239FieldElement(z); + } + + public ECFieldElement sqrt() + { + long[] z = Nat256.create64(); + SecT239Field.sqrt(x, z); + return new SecT239FieldElement(z); + } + + public int getRepresentation() + { + return ECFieldElement.F2m.TPB; + } + + public int getM() + { + return 239; + } + + public int getK1() + { + return 158; + } + + public int getK2() + { + return 0; + } + + public int getK3() + { + return 0; + } + + public boolean equals(Object other) + { + if (other == this) + { + return true; + } + + if (!(other instanceof SecT239FieldElement)) + { + return false; + } + + SecT239FieldElement o = (SecT239FieldElement)other; + return Nat256.eq64(x, o.x); + } + + public int hashCode() + { + return 23900158 ^ Arrays.hashCode(x, 0, 4); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239K1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239K1Curve.java new file mode 100644 index 00000000..074ff712 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239K1Curve.java @@ -0,0 +1,108 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECCurve.AbstractF2m; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECMultiplier; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.WTauNafMultiplier; +import org.bouncycastle.util.encoders.Hex; + +public class SecT239K1Curve extends AbstractF2m +{ + private static final int SecT239K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected SecT239K1Point infinity; + + public SecT239K1Curve() + { + super(239, 158, 0, 0); + + this.infinity = new SecT239K1Point(this, null, null); + + this.a = fromBigInteger(BigInteger.valueOf(0)); + this.b = fromBigInteger(BigInteger.valueOf(1)); + this.order = new BigInteger(1, Hex.decode("2000000000000000000000000000005A79FEC67CB6E91F1C1DA800E478A5")); + this.cofactor = BigInteger.valueOf(4); + + this.coord = SecT239K1_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecT239K1Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + protected ECMultiplier createDefaultMultiplier() + { + return new WTauNafMultiplier(); + } + + public int getFieldSize() + { + return 239; + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecT239FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecT239K1Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecT239K1Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } + + public boolean isKoblitz() + { + return true; + } + + public int getM() + { + return 239; + } + + public boolean isTrinomial() + { + return true; + } + + public int getK1() + { + return 158; + } + + public int getK2() + { + return 0; + } + + public int getK3() + { + return 0; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239K1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239K1Point.java new file mode 100644 index 00000000..39384dc2 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT239K1Point.java @@ -0,0 +1,324 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECConstants; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.ECPoint.AbstractF2m; + +public class SecT239K1Point extends AbstractF2m +{ + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT239K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + { + this(curve, x, y, false); + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)} + */ + public SecT239K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecT239K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecT239K1Point(null, this.getAffineXCoord(), this.getAffineYCoord()); // earlier JDK + } + + public ECFieldElement getYCoord() + { + ECFieldElement X = x, L = y; + + 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); + + ECFieldElement Z = zs[0]; + if (!Z.isOne()) + { + Y = Y.divide(Z); + } + + return Y; + } + + protected boolean getCompressionYTilde() + { + ECFieldElement X = this.getRawXCoord(); + if (X.isZero()) + { + return false; + } + + ECFieldElement Y = this.getRawYCoord(); + + // Y is actually Lambda (X + Y/X) here + return Y.testBitZero() != X.testBitZero(); + } + + public ECPoint add(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + ECFieldElement X2 = b.getRawXCoord(); + + if (X1.isZero()) + { + if (X2.isZero()) + { + return curve.getInfinity(); + } + + return b.add(this); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0); + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.multiply(Z1); + S2 = S2.multiply(Z1); + } + + boolean 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 twice(); + } + + return curve.getInfinity(); + } + + ECFieldElement X3, L3, Z3; + if (X2.isZero()) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.normalize(); + X1 = p.getXCoord(); + ECFieldElement Y1 = p.getYCoord(); + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.add(Y2).divide(X1); + +// X3 = L.square().add(L).add(X1).add(curve.getA()); + X3 = L.square().add(L).add(X1); + if (X3.isZero()) + { +// return new SecT239K1Point(curve, X3, curve.getB().sqrt(), this.withCompression); + return new SecT239K1Point(curve, X3, curve.getB(), this.withCompression); + } + + ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1); + L3 = Y3.divide(X3).add(X3); + Z3 = curve.fromBigInteger(ECConstants.ONE); + } + else + { + B = B.square(); + + ECFieldElement AU1 = A.multiply(U1); + ECFieldElement AU2 = A.multiply(U2); + + X3 = AU1.multiply(AU2); + if (X3.isZero()) + { +// return new SecT239K1Point(curve, X3, curve.getB().sqrt(), this.withCompression); + return new SecT239K1Point(curve, X3, curve.getB(), this.withCompression); + } + + 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 SecT239K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return curve.getInfinity(); + } + + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square(); + ECFieldElement T; + if (Z1IsOne) + { + T = L1.square().add(L1); + } + else + { + T = L1.add(Z1).multiply(L1); + } + + if (T.isZero()) + { +// return new SecT239K1Point(curve, T, curve.getB().sqrt(), withCompression); + return new SecT239K1Point(curve, T, curve.getB(), withCompression); + } + + ECFieldElement X3 = T.square(); + ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq); + + ECFieldElement t1 = L1.add(X1).square(); + ECFieldElement t2 = Z1IsOne ? Z1 : Z1Sq.square(); + ECFieldElement L3 = t1.add(T).add(Z1Sq).multiply(t1).add(t2).add(X3).add(Z3); + + return new SecT239K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twicePlus(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return twice(); + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return b; + } + + // NOTE: twicePlus() only optimized for lambda-affine argument + ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0); + if (X2.isZero() || !Z2.isOne()) + { + return twice().add(b); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(); + + ECFieldElement X1Sq = X1.square(); + ECFieldElement L1Sq = L1.square(); + ECFieldElement Z1Sq = Z1.square(); + ECFieldElement L1Z1 = L1.multiply(Z1); + +// ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1); + ECFieldElement T = L1Sq.add(L1Z1); + ECFieldElement L2plus1 = L2.addOne(); +// ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement A = 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.getInfinity(); + } + + if (A.isZero()) + { +// return new SecT239K1Point(curve, A, curve.getB().sqrt(), withCompression); + return new SecT239K1Point(curve, A, curve.getB(), withCompression); + } + + 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 SecT239K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + ECFieldElement X = this.x; + if (X.isZero()) + { + return this; + } + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.y, Z = this.zs[0]; + return new SecT239K1Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283Field.java new file mode 100644 index 00000000..7b6679bd --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283Field.java @@ -0,0 +1,404 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.raw.Interleave; +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat320; + +public class SecT283Field +{ + private static final long M27 = -1L >>> 37; + private static final long M57 = -1L >>> 7; + + private static final long[] ROOT_Z = new long[]{ 0x0C30C30C30C30808L, 0x30C30C30C30C30C3L, 0x820820820820830CL, 0x0820820820820820L, 0x2082082L }; + + public static void add(long[] x, long[] y, long[] z) + { + z[0] = x[0] ^ y[0]; + z[1] = x[1] ^ y[1]; + z[2] = x[2] ^ y[2]; + z[3] = x[3] ^ y[3]; + z[4] = x[4] ^ y[4]; + } + + public static void addExt(long[] xx, long[] yy, long[] zz) + { + zz[0] = xx[0] ^ yy[0]; + zz[1] = xx[1] ^ yy[1]; + zz[2] = xx[2] ^ yy[2]; + zz[3] = xx[3] ^ yy[3]; + zz[4] = xx[4] ^ yy[4]; + zz[5] = xx[5] ^ yy[5]; + zz[6] = xx[6] ^ yy[6]; + zz[7] = xx[7] ^ yy[7]; + zz[8] = xx[8] ^ yy[8]; + } + + public static void addOne(long[] x, long[] z) + { + z[0] = x[0] ^ 1L; + z[1] = x[1]; + z[2] = x[2]; + z[3] = x[3]; + z[4] = x[4]; + } + + public static long[] fromBigInteger(BigInteger x) + { + long[] z = Nat320.fromBigInteger64(x); + reduce37(z, 0); + return z; + } + + public static void invert(long[] x, long[] z) + { + if (Nat320.isZero64(x)) + { + throw new IllegalStateException(); + } + + // Itoh-Tsujii inversion + + long[] t0 = Nat320.create64(); + long[] t1 = Nat320.create64(); + + square(x, t0); + multiply(t0, x, t0); + squareN(t0, 2, t1); + multiply(t1, t0, t1); + squareN(t1, 4, t0); + multiply(t0, t1, t0); + squareN(t0, 8, t1); + multiply(t1, t0, t1); + square(t1, t1); + multiply(t1, x, t1); + squareN(t1, 17, t0); + multiply(t0, t1, t0); + square(t0, t0); + multiply(t0, x, t0); + squareN(t0, 35, t1); + multiply(t1, t0, t1); + squareN(t1, 70, t0); + multiply(t0, t1, t0); + square(t0, t0); + multiply(t0, x, t0); + squareN(t0, 141, t1); + multiply(t1, t0, t1); + square(t1, z); + } + + public static void multiply(long[] x, long[] y, long[] z) + { + long[] tt = Nat320.createExt64(); + implMultiply(x, y, tt); + reduce(tt, z); + } + + public static void multiplyAddToExt(long[] x, long[] y, long[] zz) + { + long[] tt = Nat320.createExt64(); + implMultiply(x, y, tt); + addExt(zz, tt, zz); + } + + public static void reduce(long[] xx, long[] z) + { + long x0 = xx[0], x1 = xx[1], x2 = xx[2], x3 = xx[3], x4 = xx[4]; + long x5 = xx[5], x6 = xx[6], x7 = xx[7], x8 = xx[8]; + + x3 ^= (x8 << 37) ^ (x8 << 42) ^ (x8 << 44) ^ (x8 << 49); + x4 ^= (x8 >>> 27) ^ (x8 >>> 22) ^ (x8 >>> 20) ^ (x8 >>> 15); + + x2 ^= (x7 << 37) ^ (x7 << 42) ^ (x7 << 44) ^ (x7 << 49); + x3 ^= (x7 >>> 27) ^ (x7 >>> 22) ^ (x7 >>> 20) ^ (x7 >>> 15); + + x1 ^= (x6 << 37) ^ (x6 << 42) ^ (x6 << 44) ^ (x6 << 49); + x2 ^= (x6 >>> 27) ^ (x6 >>> 22) ^ (x6 >>> 20) ^ (x6 >>> 15); + + x0 ^= (x5 << 37) ^ (x5 << 42) ^ (x5 << 44) ^ (x5 << 49); + x1 ^= (x5 >>> 27) ^ (x5 >>> 22) ^ (x5 >>> 20) ^ (x5 >>> 15); + + long t = x4 >>> 27; + z[0] = x0 ^ t ^ (t << 5) ^ (t << 7) ^ (t << 12); + z[1] = x1; + z[2] = x2; + z[3] = x3; + z[4] = x4 & M27; + } + + public static void reduce37(long[] z, int zOff) + { + long z4 = z[zOff + 4], t = z4 >>> 27; + z[zOff ] ^= t ^ (t << 5) ^ (t << 7) ^ (t << 12); + z[zOff + 4] = z4 & M27; + } + + public static void sqrt(long[] x, long[] z) + { + long[] odd = Nat320.create64(); + + long u0, u1; + u0 = Interleave.unshuffle(x[0]); u1 = Interleave.unshuffle(x[1]); + long e0 = (u0 & 0x00000000FFFFFFFFL) | (u1 << 32); + odd[0] = (u0 >>> 32) | (u1 & 0xFFFFFFFF00000000L); + + u0 = Interleave.unshuffle(x[2]); u1 = Interleave.unshuffle(x[3]); + long e1 = (u0 & 0x00000000FFFFFFFFL) | (u1 << 32); + odd[1] = (u0 >>> 32) | (u1 & 0xFFFFFFFF00000000L); + + u0 = Interleave.unshuffle(x[4]); + long e2 = (u0 & 0x00000000FFFFFFFFL); + odd[2] = (u0 >>> 32); + + multiply(odd, ROOT_Z, z); + + z[0] ^= e0; + z[1] ^= e1; + z[2] ^= e2; + } + + public static void square(long[] x, long[] z) + { + long[] tt = Nat.create64(9); + implSquare(x, tt); + reduce(tt, z); + } + + public static void squareAddToExt(long[] x, long[] zz) + { + long[] tt = Nat.create64(9); + implSquare(x, tt); + addExt(zz, tt, zz); + } + + public static void squareN(long[] x, int n, long[] z) + { +// assert n > 0; + + long[] tt = Nat.create64(9); + implSquare(x, tt); + reduce(tt, z); + + while (--n > 0) + { + implSquare(z, tt); + reduce(tt, z); + } + } + + public static int trace(long[] x) + { + // Non-zero-trace bits: 0, 271 + return (int)(x[0] ^ (x[4] >>> 15)) & 1; + } + + protected static void implCompactExt(long[] zz) + { + long z0 = zz[0], z1 = zz[1], z2 = zz[2], z3 = zz[3], z4 = zz[4]; + long z5 = zz[5], z6 = zz[6], z7 = zz[7], z8 = zz[8], z9 = zz[9]; + zz[0] = z0 ^ (z1 << 57); + zz[1] = (z1 >>> 7) ^ (z2 << 50); + zz[2] = (z2 >>> 14) ^ (z3 << 43); + zz[3] = (z3 >>> 21) ^ (z4 << 36); + zz[4] = (z4 >>> 28) ^ (z5 << 29); + zz[5] = (z5 >>> 35) ^ (z6 << 22); + zz[6] = (z6 >>> 42) ^ (z7 << 15); + zz[7] = (z7 >>> 49) ^ (z8 << 8); + zz[8] = (z8 >>> 56) ^ (z9 << 1); + zz[9] = (z9 >>> 63); // Zero! + } + + protected static void implExpand(long[] x, long[] z) + { + long x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4]; + z[0] = x0 & M57; + z[1] = ((x0 >>> 57) ^ (x1 << 7)) & M57; + z[2] = ((x1 >>> 50) ^ (x2 << 14)) & M57; + z[3] = ((x2 >>> 43) ^ (x3 << 21)) & M57; + z[4] = ((x3 >>> 36) ^ (x4 << 28)); + } + +// protected static void addMs(long[] zz, int zOff, long[] p, int... ms) +// { +// long t0 = 0, t1 = 0; +// for (int m : ms) +// { +// int i = (m - 1) << 1; +// t0 ^= p[i ]; +// t1 ^= p[i + 1]; +// } +// zz[zOff ] ^= t0; +// zz[zOff + 1] ^= t1; +// } + + protected static void implMultiply(long[] x, long[] y, long[] zz) + { + /* + * Formula (17) from "Some New Results on Binary Polynomial Multiplication", + * Murat Cenk and M. Anwar Hasan. + * + * The formula as given contained an error in the term t25, as noted below + */ + long[] a = new long[5], b = new long[5]; + implExpand(x, a); + implExpand(y, b); + + long[] p = new long[26]; + + implMulw(a[0], b[0], p, 0); // m1 + implMulw(a[1], b[1], p, 2); // m2 + implMulw(a[2], b[2], p, 4); // m3 + implMulw(a[3], b[3], p, 6); // m4 + implMulw(a[4], b[4], p, 8); // m5 + + long u0 = a[0] ^ a[1], v0 = b[0] ^ b[1]; + long u1 = a[0] ^ a[2], v1 = b[0] ^ b[2]; + long u2 = a[2] ^ a[4], v2 = b[2] ^ b[4]; + long u3 = a[3] ^ a[4], v3 = b[3] ^ b[4]; + + implMulw(u1 ^ a[3], v1 ^ b[3], p, 18); // m10 + implMulw(u2 ^ a[1], v2 ^ b[1], p, 20); // m11 + + long A4 = u0 ^ u3 , B4 = v0 ^ v3; + long A5 = A4 ^ a[2], B5 = B4 ^ b[2]; + + implMulw(A4, B4, p, 22); // m12 + implMulw(A5, B5, p, 24); // m13 + + implMulw(u0, v0, p, 10); // m6 + implMulw(u1, v1, p, 12); // m7 + implMulw(u2, v2, p, 14); // m8 + implMulw(u3, v3, p, 16); // m9 + + + // Original method, corresponding to formula (16) +// addMs(zz, 0, p, 1); +// addMs(zz, 1, p, 1, 2, 6); +// addMs(zz, 2, p, 1, 2, 3, 7); +// addMs(zz, 3, p, 1, 3, 4, 5, 8, 10, 12, 13); +// addMs(zz, 4, p, 1, 2, 4, 5, 6, 9, 10, 11, 13); +// addMs(zz, 5, p, 1, 2, 3, 5, 7, 11, 12, 13); +// addMs(zz, 6, p, 3, 4, 5, 8); +// addMs(zz, 7, p, 4, 5, 9); +// addMs(zz, 8, p, 5); + + // Improved method factors out common single-word terms + // NOTE: p1,...,p26 in the paper maps to p[0],...,p[25] here + + zz[0] = p[ 0]; + zz[9] = p[ 9]; + + long t1 = p[ 0] ^ p[ 1]; + long t2 = t1 ^ p[ 2]; + long t3 = t2 ^ p[10]; + + zz[1] = t3; + + long t4 = p[ 3] ^ p[ 4]; + long t5 = p[11] ^ p[12]; + long t6 = t4 ^ t5; + long t7 = t2 ^ t6; + + zz[2] = t7; + + long t8 = t1 ^ t4; + long t9 = p[ 5] ^ p[ 6]; + long t10 = t8 ^ t9; + long t11 = t10 ^ p[ 8]; + long t12 = p[13] ^ p[14]; + long t13 = t11 ^ t12; + long t14 = p[18] ^ p[22]; + long t15 = t14 ^ p[24]; + long t16 = t13 ^ t15; + + zz[3] = t16; + + long t17 = p[ 7] ^ p[ 8]; + long t18 = t17 ^ p[ 9]; + long t19 = t18 ^ p[17]; + + zz[8] = t19; + + long t20 = t18 ^ t9; + long t21 = p[15] ^ p[16]; + long t22 = t20 ^ t21; + + zz[7] = t22; + + long t23 = t22 ^ t3; + long t24 = p[19] ^ p[20]; +// long t25 = p[23] ^ p[24]; + long t25 = p[25] ^ p[24]; // Fixes an error in the paper: p[23] -> p{25] + long t26 = p[18] ^ p[23]; + long t27 = t24 ^ t25; + long t28 = t27 ^ t26; + long t29 = t28 ^ t23; + + zz[4] = t29; + + long t30 = t7 ^ t19; + long t31 = t27 ^ t30; + long t32 = p[21] ^ p[22]; + long t33 = t31 ^ t32; + + zz[5] = t33; + + long t34 = t11 ^ p[0]; + long t35 = t34 ^ p[9]; + long t36 = t35 ^ t12; + long t37 = t36 ^ p[21]; + long t38 = t37 ^ p[23]; + long t39 = t38 ^ p[25]; + + zz[6] = t39; + + implCompactExt(zz); + } + + protected static void implMulw(long x, long y, long[] z, int zOff) + { +// assert x >>> 57 == 0; +// assert y >>> 57 == 0; + + long[] u = new long[8]; +// u[0] = 0; + u[1] = y; + u[2] = u[1] << 1; + u[3] = u[2] ^ y; + u[4] = u[2] << 1; + u[5] = u[4] ^ y; + u[6] = u[3] << 1; + u[7] = u[6] ^ y; + + int j = (int)x; + long g, h = 0, l = u[j & 7]; + int k = 48; + do + { + j = (int)(x >>> k); + g = u[j & 7] + ^ u[(j >>> 3) & 7] << 3 + ^ u[(j >>> 6) & 7] << 6; + l ^= (g << k); + h ^= (g >>> -k); + } + while ((k -= 9) > 0); + + h ^= ((x & 0x0100804020100800L) & ((y << 7) >> 63)) >>> 8; + +// assert h >>> 49 == 0; + + z[zOff ] = l & M57; + z[zOff + 1] = (l >>> 57) ^ (h << 7); + } + + protected static void implSquare(long[] x, long[] zz) + { + for (int i = 0; i < 4; ++i) + { + Interleave.expand64To128(x[i], zz, i << 1); + } + zz[8] = Interleave.expand32to64((int)x[4]); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283FieldElement.java new file mode 100644 index 00000000..ee0024bc --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283FieldElement.java @@ -0,0 +1,222 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat320; +import org.bouncycastle.util.Arrays; + +public class SecT283FieldElement extends ECFieldElement +{ + protected long[] x; + + public SecT283FieldElement(BigInteger x) + { + if (x == null || x.signum() < 0 || x.bitLength() > 283) + { + throw new IllegalArgumentException("x value invalid for SecT283FieldElement"); + } + + this.x = SecT283Field.fromBigInteger(x); + } + + public SecT283FieldElement() + { + this.x = Nat320.create64(); + } + + protected SecT283FieldElement(long[] x) + { + this.x = x; + } + +// public int bitLength() +// { +// return x.degree(); +// } + + public boolean isOne() + { + return Nat320.isOne64(x); + } + + public boolean isZero() + { + return Nat320.isZero64(x); + } + + public boolean testBitZero() + { + return (x[0] & 1L) != 0L; + } + + public BigInteger toBigInteger() + { + return Nat320.toBigInteger64(x); + } + + public String getFieldName() + { + return "SecT283Field"; + } + + public int getFieldSize() + { + return 283; + } + + public ECFieldElement add(ECFieldElement b) + { + long[] z = Nat320.create64(); + SecT283Field.add(x, ((SecT283FieldElement)b).x, z); + return new SecT283FieldElement(z); + } + + public ECFieldElement addOne() + { + long[] z = Nat320.create64(); + SecT283Field.addOne(x, z); + return new SecT283FieldElement(z); + } + + public ECFieldElement subtract(ECFieldElement b) + { + // Addition and subtraction are the same in F2m + return add(b); + } + + public ECFieldElement multiply(ECFieldElement b) + { + long[] z = Nat320.create64(); + SecT283Field.multiply(x, ((SecT283FieldElement)b).x, z); + return new SecT283FieldElement(z); + } + + public ECFieldElement multiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + return multiplyPlusProduct(b, x, y); + } + + public ECFieldElement multiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + long[] ax = this.x, bx = ((SecT283FieldElement)b).x; + long[] xx = ((SecT283FieldElement)x).x, yx = ((SecT283FieldElement)y).x; + + long[] tt = Nat.create64(9); + SecT283Field.multiplyAddToExt(ax, bx, tt); + SecT283Field.multiplyAddToExt(xx, yx, tt); + + long[] z = Nat320.create64(); + SecT283Field.reduce(tt, z); + return new SecT283FieldElement(z); + } + + public ECFieldElement divide(ECFieldElement b) + { + return multiply(b.invert()); + } + + public ECFieldElement negate() + { + return this; + } + + public ECFieldElement square() + { + long[] z = Nat320.create64(); + SecT283Field.square(x, z); + return new SecT283FieldElement(z); + } + + public ECFieldElement squareMinusProduct(ECFieldElement x, ECFieldElement y) + { + return squarePlusProduct(x, y); + } + + public ECFieldElement squarePlusProduct(ECFieldElement x, ECFieldElement y) + { + long[] ax = this.x; + long[] xx = ((SecT283FieldElement)x).x, yx = ((SecT283FieldElement)y).x; + + long[] tt = Nat.create64(9); + SecT283Field.squareAddToExt(ax, tt); + SecT283Field.multiplyAddToExt(xx, yx, tt); + + long[] z = Nat320.create64(); + SecT283Field.reduce(tt, z); + return new SecT283FieldElement(z); + } + + public ECFieldElement squarePow(int pow) + { + if (pow < 1) + { + return this; + } + + long[] z = Nat320.create64(); + SecT283Field.squareN(x, pow, z); + return new SecT283FieldElement(z); + } + + public ECFieldElement invert() + { + long[] z = Nat320.create64(); + SecT283Field.invert(x, z); + return new SecT283FieldElement(z); + } + + public ECFieldElement sqrt() + { + long[] z = Nat320.create64(); + SecT283Field.sqrt(x, z); + return new SecT283FieldElement(z); + } + + public int getRepresentation() + { + return ECFieldElement.F2m.PPB; + } + + public int getM() + { + return 283; + } + + public int getK1() + { + return 5; + } + + public int getK2() + { + return 7; + } + + public int getK3() + { + return 12; + } + + public boolean equals(Object other) + { + if (other == this) + { + return true; + } + + if (!(other instanceof SecT283FieldElement)) + { + return false; + } + + SecT283FieldElement o = (SecT283FieldElement)other; + return Nat320.eq64(x, o.x); + } + + public int hashCode() + { + return 2831275 ^ Arrays.hashCode(x, 0, 5); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283K1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283K1Curve.java new file mode 100644 index 00000000..1c48a6fb --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283K1Curve.java @@ -0,0 +1,108 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECCurve.AbstractF2m; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECMultiplier; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.WTauNafMultiplier; +import org.bouncycastle.util.encoders.Hex; + +public class SecT283K1Curve extends AbstractF2m +{ + private static final int SecT283K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected SecT283K1Point infinity; + + public SecT283K1Curve() + { + super(283, 5, 7, 12); + + this.infinity = new SecT283K1Point(this, null, null); + + this.a = fromBigInteger(BigInteger.valueOf(0)); + this.b = fromBigInteger(BigInteger.valueOf(1)); + this.order = new BigInteger(1, Hex.decode("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9AE2ED07577265DFF7F94451E061E163C61")); + this.cofactor = BigInteger.valueOf(4); + + this.coord = SecT283K1_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecT283K1Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + protected ECMultiplier createDefaultMultiplier() + { + return new WTauNafMultiplier(); + } + + public int getFieldSize() + { + return 283; + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecT283FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecT283K1Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecT283K1Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } + + public boolean isKoblitz() + { + return true; + } + + public int getM() + { + return 283; + } + + public boolean isTrinomial() + { + return false; + } + + public int getK1() + { + return 5; + } + + public int getK2() + { + return 7; + } + + public int getK3() + { + return 12; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283K1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283K1Point.java new file mode 100644 index 00000000..f3b704e1 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283K1Point.java @@ -0,0 +1,324 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECConstants; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.ECPoint.AbstractF2m; + +public class SecT283K1Point extends AbstractF2m +{ + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT283K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + { + this(curve, x, y, false); + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)} + */ + public SecT283K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecT283K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecT283K1Point(null, this.getAffineXCoord(), this.getAffineYCoord()); // earlier JDK + } + + public ECFieldElement getYCoord() + { + ECFieldElement X = x, L = y; + + 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); + + ECFieldElement Z = zs[0]; + if (!Z.isOne()) + { + Y = Y.divide(Z); + } + + return Y; + } + + protected boolean getCompressionYTilde() + { + ECFieldElement X = this.getRawXCoord(); + if (X.isZero()) + { + return false; + } + + ECFieldElement Y = this.getRawYCoord(); + + // Y is actually Lambda (X + Y/X) here + return Y.testBitZero() != X.testBitZero(); + } + + public ECPoint add(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + ECFieldElement X2 = b.getRawXCoord(); + + if (X1.isZero()) + { + if (X2.isZero()) + { + return curve.getInfinity(); + } + + return b.add(this); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0); + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.multiply(Z1); + S2 = S2.multiply(Z1); + } + + boolean 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 twice(); + } + + return curve.getInfinity(); + } + + ECFieldElement X3, L3, Z3; + if (X2.isZero()) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.normalize(); + X1 = p.getXCoord(); + ECFieldElement Y1 = p.getYCoord(); + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.add(Y2).divide(X1); + +// X3 = L.square().add(L).add(X1).add(curve.getA()); + X3 = L.square().add(L).add(X1); + if (X3.isZero()) + { +// return new SecT283K1Point(curve, X3, curve.getB().sqrt(), this.withCompression); + return new SecT283K1Point(curve, X3, curve.getB(), this.withCompression); + } + + ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1); + L3 = Y3.divide(X3).add(X3); + Z3 = curve.fromBigInteger(ECConstants.ONE); + } + else + { + B = B.square(); + + ECFieldElement AU1 = A.multiply(U1); + ECFieldElement AU2 = A.multiply(U2); + + X3 = AU1.multiply(AU2); + if (X3.isZero()) + { +// return new SecT283K1Point(curve, X3, curve.getB().sqrt(), this.withCompression); + return new SecT283K1Point(curve, X3, curve.getB(), this.withCompression); + } + + 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 SecT283K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return curve.getInfinity(); + } + + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square(); + ECFieldElement T; + if (Z1IsOne) + { + T = L1.square().add(L1); + } + else + { + T = L1.add(Z1).multiply(L1); + } + + if (T.isZero()) + { +// return new SecT283K1Point(curve, T, curve.getB().sqrt(), withCompression); + return new SecT283K1Point(curve, T, curve.getB(), withCompression); + } + + ECFieldElement X3 = T.square(); + ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq); + + ECFieldElement t1 = L1.add(X1).square(); + ECFieldElement t2 = Z1IsOne ? Z1 : Z1Sq.square(); + ECFieldElement L3 = t1.add(T).add(Z1Sq).multiply(t1).add(t2).add(X3).add(Z3); + + return new SecT283K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twicePlus(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return twice(); + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return b; + } + + // NOTE: twicePlus() only optimized for lambda-affine argument + ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0); + if (X2.isZero() || !Z2.isOne()) + { + return twice().add(b); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(); + + ECFieldElement X1Sq = X1.square(); + ECFieldElement L1Sq = L1.square(); + ECFieldElement Z1Sq = Z1.square(); + ECFieldElement L1Z1 = L1.multiply(Z1); + +// ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1); + ECFieldElement T = L1Sq.add(L1Z1); + ECFieldElement L2plus1 = L2.addOne(); +// ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement A = 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.getInfinity(); + } + + if (A.isZero()) + { +// return new SecT283K1Point(curve, A, curve.getB().sqrt(), withCompression); + return new SecT283K1Point(curve, A, curve.getB(), withCompression); + } + + 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 SecT283K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + ECFieldElement X = this.x; + if (X.isZero()) + { + return this; + } + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.y, Z = this.zs[0]; + return new SecT283K1Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283R1Curve.java new file mode 100644 index 00000000..2909fca8 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283R1Curve.java @@ -0,0 +1,101 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECCurve.AbstractF2m; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; + +public class SecT283R1Curve extends AbstractF2m +{ + private static final int SecT283R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected SecT283R1Point infinity; + + public SecT283R1Curve() + { + super(283, 5, 7, 12); + + this.infinity = new SecT283R1Point(this, null, null); + + this.a = fromBigInteger(BigInteger.valueOf(1)); + this.b = fromBigInteger(new BigInteger(1, Hex.decode("027B680AC8B8596DA5A4AF8A19A0303FCA97FD7645309FA2A581485AF6263E313B79A2F5"))); + this.order = new BigInteger(1, Hex.decode("03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF90399660FC938A90165B042A7CEFADB307")); + this.cofactor = BigInteger.valueOf(2); + + this.coord = SecT283R1_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecT283R1Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + public int getFieldSize() + { + return 283; + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecT283FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecT283R1Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecT283R1Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } + + public boolean isKoblitz() + { + return false; + } + + public int getM() + { + return 283; + } + + public boolean isTrinomial() + { + return false; + } + + public int getK1() + { + return 5; + } + + public int getK2() + { + return 7; + } + + public int getK3() + { + return 12; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283R1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283R1Point.java new file mode 100644 index 00000000..7a95a734 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT283R1Point.java @@ -0,0 +1,309 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECConstants; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.ECPoint.AbstractF2m; + +public class SecT283R1Point extends AbstractF2m +{ + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT283R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + { + this(curve, x, y, false); + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)} + */ + public SecT283R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecT283R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecT283R1Point(null, getAffineXCoord(), getAffineYCoord()); + } + + public ECFieldElement getYCoord() + { + ECFieldElement X = x, L = y; + + 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); + + ECFieldElement Z = zs[0]; + if (!Z.isOne()) + { + Y = Y.divide(Z); + } + + return Y; + } + + protected boolean getCompressionYTilde() + { + ECFieldElement X = this.getRawXCoord(); + if (X.isZero()) + { + return false; + } + + ECFieldElement Y = this.getRawYCoord(); + + // Y is actually Lambda (X + Y/X) here + return Y.testBitZero() != X.testBitZero(); + } + + public ECPoint add(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + ECFieldElement X2 = b.getRawXCoord(); + + if (X1.isZero()) + { + if (X2.isZero()) + { + return curve.getInfinity(); + } + + return b.add(this); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0); + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.multiply(Z1); + S2 = S2.multiply(Z1); + } + + boolean 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 twice(); + } + + return curve.getInfinity(); + } + + ECFieldElement X3, L3, Z3; + if (X2.isZero()) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.normalize(); + X1 = p.getXCoord(); + ECFieldElement Y1 = p.getYCoord(); + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.add(Y2).divide(X1); + +// X3 = L.square().add(L).add(X1).add(curve.getA()); + X3 = L.square().add(L).add(X1).addOne(); + if (X3.isZero()) + { + return new SecT283R1Point(curve, X3, curve.getB().sqrt(), this.withCompression); + } + + ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1); + L3 = Y3.divide(X3).add(X3); + Z3 = curve.fromBigInteger(ECConstants.ONE); + } + else + { + B = B.square(); + + ECFieldElement AU1 = A.multiply(U1); + ECFieldElement AU2 = A.multiply(U2); + + X3 = AU1.multiply(AU2); + if (X3.isZero()) + { + return new SecT283R1Point(curve, X3, curve.getB().sqrt(), this.withCompression); + } + + 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 SecT283R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return curve.getInfinity(); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square(); + ECFieldElement T = L1.square().add(L1Z1).add(Z1Sq); + if (T.isZero()) + { + return new SecT283R1Point(curve, T, curve.getB().sqrt(), withCompression); + } + + ECFieldElement X3 = T.square(); + ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq); + + ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1); + ECFieldElement L3 = X1Z1.squarePlusProduct(T, L1Z1).add(X3).add(Z3); + + return new SecT283R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twicePlus(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return twice(); + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return b; + } + + ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0); + if (X2.isZero() || !Z2.isOne()) + { + return twice().add(b); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(); + + ECFieldElement X1Sq = X1.square(); + ECFieldElement L1Sq = L1.square(); + ECFieldElement Z1Sq = Z1.square(); + ECFieldElement L1Z1 = L1.multiply(Z1); + +// ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1); + ECFieldElement T = Z1Sq.add(L1Sq).add(L1Z1); + ECFieldElement L2plus1 = L2.addOne(); +// ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement A = L2.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.getInfinity(); + } + + if (A.isZero()) + { + return new SecT283R1Point(curve, A, curve.getB().sqrt(), withCompression); + } + + 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 SecT283R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + ECFieldElement X = this.x; + if (X.isZero()) + { + return this; + } + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.y, Z = this.zs[0]; + return new SecT283R1Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409Field.java new file mode 100644 index 00000000..9e58a2ba --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409Field.java @@ -0,0 +1,333 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.raw.Interleave; +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat448; + +public class SecT409Field +{ + private static final long M25 = -1L >>> 39; + private static final long M59 = -1L >>> 5; + + public static void add(long[] x, long[] y, long[] z) + { + z[0] = x[0] ^ y[0]; + z[1] = x[1] ^ y[1]; + z[2] = x[2] ^ y[2]; + z[3] = x[3] ^ y[3]; + z[4] = x[4] ^ y[4]; + z[5] = x[5] ^ y[5]; + z[6] = x[6] ^ y[6]; + } + + public static void addExt(long[] xx, long[] yy, long[] zz) + { + for (int i = 0; i < 13; ++i) + { + zz[i] = xx[i] ^ yy[i]; + } + } + + public static void addOne(long[] x, long[] z) + { + z[0] = x[0] ^ 1L; + 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 long[] fromBigInteger(BigInteger x) + { + long[] z = Nat448.fromBigInteger64(x); + reduce39(z, 0); + return z; + } + + public static void invert(long[] x, long[] z) + { + if (Nat448.isZero64(x)) + { + throw new IllegalStateException(); + } + + // Itoh-Tsujii inversion with bases { 2, 3 } + + long[] t0 = Nat448.create64(); + long[] t1 = Nat448.create64(); + long[] t2 = Nat448.create64(); + + square(x, t0); + + // 3 | 408 + squareN(t0, 1, t1); + multiply(t0, t1, t0); + squareN(t1, 1, t1); + multiply(t0, t1, t0); + + // 2 | 136 + squareN(t0, 3, t1); + multiply(t0, t1, t0); + + // 2 | 68 + squareN(t0, 6, t1); + multiply(t0, t1, t0); + + // 2 | 34 + squareN(t0, 12, t1); + multiply(t0, t1, t2); + + // ! {2,3} | 17 + squareN(t2, 24, t0); + squareN(t0, 24, t1); + multiply(t0, t1, t0); + + // 2 | 8 + squareN(t0, 48, t1); + multiply(t0, t1, t0); + + // 2 | 4 + squareN(t0, 96, t1); + multiply(t0, t1, t0); + + // 2 | 2 + squareN(t0, 192, t1); + multiply(t0, t1, t0); + + multiply(t0, t2, z); + } + + public static void multiply(long[] x, long[] y, long[] z) + { + long[] tt = Nat448.createExt64(); + implMultiply(x, y, tt); + reduce(tt, z); + } + + public static void multiplyAddToExt(long[] x, long[] y, long[] zz) + { + long[] tt = Nat448.createExt64(); + implMultiply(x, y, tt); + addExt(zz, tt, zz); + } + + public static void reduce(long[] xx, long[] z) + { + long x00 = xx[0], x01 = xx[1], x02 = xx[2], x03 = xx[3]; + long x04 = xx[4], x05 = xx[5], x06 = xx[6], x07 = xx[7]; + + long u = xx[12]; + x05 ^= (u << 39); + x06 ^= (u >>> 25) ^ (u << 62); + x07 ^= (u >>> 2); + + u = xx[11]; + x04 ^= (u << 39); + x05 ^= (u >>> 25) ^ (u << 62); + x06 ^= (u >>> 2); + + u = xx[10]; + x03 ^= (u << 39); + x04 ^= (u >>> 25) ^ (u << 62); + x05 ^= (u >>> 2); + + u = xx[9]; + x02 ^= (u << 39); + x03 ^= (u >>> 25) ^ (u << 62); + x04 ^= (u >>> 2); + + u = xx[8]; + x01 ^= (u << 39); + x02 ^= (u >>> 25) ^ (u << 62); + x03 ^= (u >>> 2); + + u = x07; + x00 ^= (u << 39); + x01 ^= (u >>> 25) ^ (u << 62); + x02 ^= (u >>> 2); + + long t = x06 >>> 25; + z[0] = x00 ^ t; + z[1] = x01 ^ (t << 23); + z[2] = x02; + z[3] = x03; + z[4] = x04; + z[5] = x05; + z[6] = x06 & M25; + } + + public static void reduce39(long[] z, int zOff) + { + long z6 = z[zOff + 6], t = z6 >>> 25; + z[zOff ] ^= t; + z[zOff + 1] ^= (t << 23); + z[zOff + 6] = z6 & M25; + } + + public static void sqrt(long[] x, long[] z) + { + long u0, u1; + u0 = Interleave.unshuffle(x[0]); u1 = Interleave.unshuffle(x[1]); + long e0 = (u0 & 0x00000000FFFFFFFFL) | (u1 << 32); + long c0 = (u0 >>> 32) | (u1 & 0xFFFFFFFF00000000L); + + u0 = Interleave.unshuffle(x[2]); u1 = Interleave.unshuffle(x[3]); + long e1 = (u0 & 0x00000000FFFFFFFFL) | (u1 << 32); + long c1 = (u0 >>> 32) | (u1 & 0xFFFFFFFF00000000L); + + u0 = Interleave.unshuffle(x[4]); u1 = Interleave.unshuffle(x[5]); + long e2 = (u0 & 0x00000000FFFFFFFFL) | (u1 << 32); + long c2 = (u0 >>> 32) | (u1 & 0xFFFFFFFF00000000L); + + u0 = Interleave.unshuffle(x[6]); + long e3 = (u0 & 0x00000000FFFFFFFFL); + long c3 = (u0 >>> 32); + + z[0] = e0 ^ (c0 << 44); + z[1] = e1 ^ (c1 << 44) ^ (c0 >>> 20); + z[2] = e2 ^ (c2 << 44) ^ (c1 >>> 20); + z[3] = e3 ^ (c3 << 44) ^ (c2 >>> 20) ^ (c0 << 13); + z[4] = (c3 >>> 20) ^ (c1 << 13) ^ (c0 >>> 51); + z[5] = (c2 << 13) ^ (c1 >>> 51); + z[6] = (c3 << 13) ^ (c2 >>> 51); + +// assert (c3 >>> 51) == 0; + } + + public static void square(long[] x, long[] z) + { + long[] tt = Nat.create64(13); + implSquare(x, tt); + reduce(tt, z); + } + + public static void squareAddToExt(long[] x, long[] zz) + { + long[] tt = Nat.create64(13); + implSquare(x, tt); + addExt(zz, tt, zz); + } + + public static void squareN(long[] x, int n, long[] z) + { +// assert n > 0; + + long[] tt = Nat.create64(13); + implSquare(x, tt); + reduce(tt, z); + + while (--n > 0) + { + implSquare(z, tt); + reduce(tt, z); + } + } + + public static int trace(long[] x) + { + // Non-zero-trace bits: 0 + return (int)(x[0]) & 1; + } + + protected static void implCompactExt(long[] zz) + { + long z00 = zz[ 0], z01 = zz[ 1], z02 = zz[ 2], z03 = zz[ 3], z04 = zz[ 4], z05 = zz[ 5], z06 = zz[ 6]; + long z07 = zz[ 7], z08 = zz[ 8], z09 = zz[ 9], z10 = zz[10], z11 = zz[11], z12 = zz[12], z13 = zz[13]; + zz[ 0] = z00 ^ (z01 << 59); + zz[ 1] = (z01 >>> 5) ^ (z02 << 54); + zz[ 2] = (z02 >>> 10) ^ (z03 << 49); + zz[ 3] = (z03 >>> 15) ^ (z04 << 44); + zz[ 4] = (z04 >>> 20) ^ (z05 << 39); + zz[ 5] = (z05 >>> 25) ^ (z06 << 34); + zz[ 6] = (z06 >>> 30) ^ (z07 << 29); + zz[ 7] = (z07 >>> 35) ^ (z08 << 24); + zz[ 8] = (z08 >>> 40) ^ (z09 << 19); + zz[ 9] = (z09 >>> 45) ^ (z10 << 14); + zz[10] = (z10 >>> 50) ^ (z11 << 9); + zz[11] = (z11 >>> 55) ^ (z12 << 4) + ^ (z13 << 63); + zz[12] = (z12 >>> 60) + ^ (z13 >>> 1); + zz[13] = 0; + } + + protected static void implExpand(long[] x, long[] z) + { + long x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6]; + z[0] = x0 & M59; + z[1] = ((x0 >>> 59) ^ (x1 << 5)) & M59; + z[2] = ((x1 >>> 54) ^ (x2 << 10)) & M59; + z[3] = ((x2 >>> 49) ^ (x3 << 15)) & M59; + z[4] = ((x3 >>> 44) ^ (x4 << 20)) & M59; + z[5] = ((x4 >>> 39) ^ (x5 << 25)) & M59; + z[6] = ((x5 >>> 34) ^ (x6 << 30)); + } + + protected static void implMultiply(long[] x, long[] y, long[] zz) + { + long[] a = new long[7], b = new long[7]; + implExpand(x, a); + implExpand(y, b); + + for (int i = 0; i < 7; ++i) + { + implMulwAcc(a, b[i], zz, i); + } + + implCompactExt(zz); + } + + protected static void implMulwAcc(long[] xs, long y, long[] z, int zOff) + { +// assert y >>> 59 == 0; + + long[] u = new long[8]; +// u[0] = 0; + u[1] = y; + u[2] = u[1] << 1; + u[3] = u[2] ^ y; + u[4] = u[2] << 1; + u[5] = u[4] ^ y; + u[6] = u[3] << 1; + u[7] = u[6] ^ y; + + for (int i = 0; i < 7; ++i) + { + long x = xs[i]; + +// assert x >>> 59 == 0; + + int j = (int)x; + long g, h = 0, l = u[j & 7] + ^ (u[(j >>> 3) & 7] << 3); + int k = 54; + do + { + j = (int)(x >>> k); + g = u[j & 7] + ^ u[(j >>> 3) & 7] << 3; + l ^= (g << k); + h ^= (g >>> -k); + } + while ((k -= 6) > 0); + +// assert h >>> 53 == 0; + + z[zOff + i ] ^= l & M59; + z[zOff + i + 1] ^= (l >>> 59) ^ (h << 5); + } + } + + protected static void implSquare(long[] x, long[] zz) + { + for (int i = 0; i < 6; ++i) + { + Interleave.expand64To128(x[i], zz, i << 1); + } + zz[12] = Interleave.expand32to64((int)x[6]); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409FieldElement.java new file mode 100644 index 00000000..eaf6fc90 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409FieldElement.java @@ -0,0 +1,222 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat448; +import org.bouncycastle.util.Arrays; + +public class SecT409FieldElement extends ECFieldElement +{ + protected long[] x; + + public SecT409FieldElement(BigInteger x) + { + if (x == null || x.signum() < 0 || x.bitLength() > 409) + { + throw new IllegalArgumentException("x value invalid for SecT409FieldElement"); + } + + this.x = SecT409Field.fromBigInteger(x); + } + + public SecT409FieldElement() + { + this.x = Nat448.create64(); + } + + protected SecT409FieldElement(long[] x) + { + this.x = x; + } + +// public int bitLength() +// { +// return x.degree(); +// } + + public boolean isOne() + { + return Nat448.isOne64(x); + } + + public boolean isZero() + { + return Nat448.isZero64(x); + } + + public boolean testBitZero() + { + return (x[0] & 1L) != 0L; + } + + public BigInteger toBigInteger() + { + return Nat448.toBigInteger64(x); + } + + public String getFieldName() + { + return "SecT409Field"; + } + + public int getFieldSize() + { + return 409; + } + + public ECFieldElement add(ECFieldElement b) + { + long[] z = Nat448.create64(); + SecT409Field.add(x, ((SecT409FieldElement)b).x, z); + return new SecT409FieldElement(z); + } + + public ECFieldElement addOne() + { + long[] z = Nat448.create64(); + SecT409Field.addOne(x, z); + return new SecT409FieldElement(z); + } + + public ECFieldElement subtract(ECFieldElement b) + { + // Addition and subtraction are the same in F2m + return add(b); + } + + public ECFieldElement multiply(ECFieldElement b) + { + long[] z = Nat448.create64(); + SecT409Field.multiply(x, ((SecT409FieldElement)b).x, z); + return new SecT409FieldElement(z); + } + + public ECFieldElement multiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + return multiplyPlusProduct(b, x, y); + } + + public ECFieldElement multiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + long[] ax = this.x, bx = ((SecT409FieldElement)b).x; + long[] xx = ((SecT409FieldElement)x).x, yx = ((SecT409FieldElement)y).x; + + long[] tt = Nat.create64(13); + SecT409Field.multiplyAddToExt(ax, bx, tt); + SecT409Field.multiplyAddToExt(xx, yx, tt); + + long[] z = Nat448.create64(); + SecT409Field.reduce(tt, z); + return new SecT409FieldElement(z); + } + + public ECFieldElement divide(ECFieldElement b) + { + return multiply(b.invert()); + } + + public ECFieldElement negate() + { + return this; + } + + public ECFieldElement square() + { + long[] z = Nat448.create64(); + SecT409Field.square(x, z); + return new SecT409FieldElement(z); + } + + public ECFieldElement squareMinusProduct(ECFieldElement x, ECFieldElement y) + { + return squarePlusProduct(x, y); + } + + public ECFieldElement squarePlusProduct(ECFieldElement x, ECFieldElement y) + { + long[] ax = this.x; + long[] xx = ((SecT409FieldElement)x).x, yx = ((SecT409FieldElement)y).x; + + long[] tt = Nat.create64(13); + SecT409Field.squareAddToExt(ax, tt); + SecT409Field.multiplyAddToExt(xx, yx, tt); + + long[] z = Nat448.create64(); + SecT409Field.reduce(tt, z); + return new SecT409FieldElement(z); + } + + public ECFieldElement squarePow(int pow) + { + if (pow < 1) + { + return this; + } + + long[] z = Nat448.create64(); + SecT409Field.squareN(x, pow, z); + return new SecT409FieldElement(z); + } + + public ECFieldElement invert() + { + long[] z = Nat448.create64(); + SecT409Field.invert(x, z); + return new SecT409FieldElement(z); + } + + public ECFieldElement sqrt() + { + long[] z = Nat448.create64(); + SecT409Field.sqrt(x, z); + return new SecT409FieldElement(z); + } + + public int getRepresentation() + { + return ECFieldElement.F2m.TPB; + } + + public int getM() + { + return 409; + } + + public int getK1() + { + return 87; + } + + public int getK2() + { + return 0; + } + + public int getK3() + { + return 0; + } + + public boolean equals(Object other) + { + if (other == this) + { + return true; + } + + if (!(other instanceof SecT409FieldElement)) + { + return false; + } + + SecT409FieldElement o = (SecT409FieldElement)other; + return Nat448.eq64(x, o.x); + } + + public int hashCode() + { + return 4090087 ^ Arrays.hashCode(x, 0, 7); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409K1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409K1Curve.java new file mode 100644 index 00000000..fb66cd8b --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409K1Curve.java @@ -0,0 +1,108 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECCurve.AbstractF2m; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECMultiplier; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.WTauNafMultiplier; +import org.bouncycastle.util.encoders.Hex; + +public class SecT409K1Curve extends AbstractF2m +{ + private static final int SecT409K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected SecT409K1Point infinity; + + public SecT409K1Curve() + { + super(409, 87, 0, 0); + + this.infinity = new SecT409K1Point(this, null, null); + + this.a = fromBigInteger(BigInteger.valueOf(0)); + this.b = fromBigInteger(BigInteger.valueOf(1)); + this.order = new BigInteger(1, Hex.decode("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5F83B2D4EA20400EC4557D5ED3E3E7CA5B4B5C83B8E01E5FCF")); + this.cofactor = BigInteger.valueOf(4); + + this.coord = SecT409K1_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecT409K1Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + protected ECMultiplier createDefaultMultiplier() + { + return new WTauNafMultiplier(); + } + + public int getFieldSize() + { + return 409; + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecT409FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecT409K1Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecT409K1Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } + + public boolean isKoblitz() + { + return true; + } + + public int getM() + { + return 409; + } + + public boolean isTrinomial() + { + return true; + } + + public int getK1() + { + return 87; + } + + public int getK2() + { + return 0; + } + + public int getK3() + { + return 0; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409K1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409K1Point.java new file mode 100644 index 00000000..4204923b --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409K1Point.java @@ -0,0 +1,324 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECConstants; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.ECPoint.AbstractF2m; + +public class SecT409K1Point extends AbstractF2m +{ + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT409K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + { + this(curve, x, y, false); + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)} + */ + public SecT409K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecT409K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecT409K1Point(null, this.getAffineXCoord(), this.getAffineYCoord()); // earlier JDK + } + + public ECFieldElement getYCoord() + { + ECFieldElement X = x, L = y; + + 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); + + ECFieldElement Z = zs[0]; + if (!Z.isOne()) + { + Y = Y.divide(Z); + } + + return Y; + } + + protected boolean getCompressionYTilde() + { + ECFieldElement X = this.getRawXCoord(); + if (X.isZero()) + { + return false; + } + + ECFieldElement Y = this.getRawYCoord(); + + // Y is actually Lambda (X + Y/X) here + return Y.testBitZero() != X.testBitZero(); + } + + public ECPoint add(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + ECFieldElement X2 = b.getRawXCoord(); + + if (X1.isZero()) + { + if (X2.isZero()) + { + return curve.getInfinity(); + } + + return b.add(this); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0); + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.multiply(Z1); + S2 = S2.multiply(Z1); + } + + boolean 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 twice(); + } + + return curve.getInfinity(); + } + + ECFieldElement X3, L3, Z3; + if (X2.isZero()) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.normalize(); + X1 = p.getXCoord(); + ECFieldElement Y1 = p.getYCoord(); + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.add(Y2).divide(X1); + +// X3 = L.square().add(L).add(X1).add(curve.getA()); + X3 = L.square().add(L).add(X1); + if (X3.isZero()) + { +// return new SecT409K1Point(curve, X3, curve.getB().sqrt(), this.withCompression); + return new SecT409K1Point(curve, X3, curve.getB(), this.withCompression); + } + + ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1); + L3 = Y3.divide(X3).add(X3); + Z3 = curve.fromBigInteger(ECConstants.ONE); + } + else + { + B = B.square(); + + ECFieldElement AU1 = A.multiply(U1); + ECFieldElement AU2 = A.multiply(U2); + + X3 = AU1.multiply(AU2); + if (X3.isZero()) + { +// return new SecT409K1Point(curve, X3, curve.getB().sqrt(), this.withCompression); + return new SecT409K1Point(curve, X3, curve.getB(), this.withCompression); + } + + 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 SecT409K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return curve.getInfinity(); + } + + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square(); + ECFieldElement T; + if (Z1IsOne) + { + T = L1.square().add(L1); + } + else + { + T = L1.add(Z1).multiply(L1); + } + + if (T.isZero()) + { +// return new SecT409K1Point(curve, T, curve.getB().sqrt(), withCompression); + return new SecT409K1Point(curve, T, curve.getB(), withCompression); + } + + ECFieldElement X3 = T.square(); + ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq); + + ECFieldElement t1 = L1.add(X1).square(); + ECFieldElement t2 = Z1IsOne ? Z1 : Z1Sq.square(); + ECFieldElement L3 = t1.add(T).add(Z1Sq).multiply(t1).add(t2).add(X3).add(Z3); + + return new SecT409K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twicePlus(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return twice(); + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return b; + } + + // NOTE: twicePlus() only optimized for lambda-affine argument + ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0); + if (X2.isZero() || !Z2.isOne()) + { + return twice().add(b); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(); + + ECFieldElement X1Sq = X1.square(); + ECFieldElement L1Sq = L1.square(); + ECFieldElement Z1Sq = Z1.square(); + ECFieldElement L1Z1 = L1.multiply(Z1); + +// ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1); + ECFieldElement T = L1Sq.add(L1Z1); + ECFieldElement L2plus1 = L2.addOne(); +// ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement A = 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.getInfinity(); + } + + if (A.isZero()) + { +// return new SecT409K1Point(curve, A, curve.getB().sqrt(), withCompression); + return new SecT409K1Point(curve, A, curve.getB(), withCompression); + } + + 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 SecT409K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + ECFieldElement X = this.x; + if (X.isZero()) + { + return this; + } + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.y, Z = this.zs[0]; + return new SecT409K1Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409R1Curve.java new file mode 100644 index 00000000..55a4b7dd --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409R1Curve.java @@ -0,0 +1,101 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECCurve.AbstractF2m; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; + +public class SecT409R1Curve extends AbstractF2m +{ + private static final int SecT409R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected SecT409R1Point infinity; + + public SecT409R1Curve() + { + super(409, 87, 0, 0); + + this.infinity = new SecT409R1Point(this, null, null); + + this.a = fromBigInteger(BigInteger.valueOf(1)); + this.b = fromBigInteger(new BigInteger(1, Hex.decode("0021A5C2C8EE9FEB5C4B9A753B7B476B7FD6422EF1F3DD674761FA99D6AC27C8A9A197B272822F6CD57A55AA4F50AE317B13545F"))); + this.order = new BigInteger(1, Hex.decode("010000000000000000000000000000000000000000000000000001E2AAD6A612F33307BE5FA47C3C9E052F838164CD37D9A21173")); + this.cofactor = BigInteger.valueOf(2); + + this.coord = SecT409R1_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecT409R1Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + public int getFieldSize() + { + return 409; + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecT409FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecT409R1Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecT409R1Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } + + public boolean isKoblitz() + { + return false; + } + + public int getM() + { + return 409; + } + + public boolean isTrinomial() + { + return true; + } + + public int getK1() + { + return 87; + } + + public int getK2() + { + return 0; + } + + public int getK3() + { + return 0; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409R1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409R1Point.java new file mode 100644 index 00000000..078935da --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT409R1Point.java @@ -0,0 +1,309 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECConstants; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.ECPoint.AbstractF2m; + +public class SecT409R1Point extends AbstractF2m +{ + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT409R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + { + this(curve, x, y, false); + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)} + */ + public SecT409R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecT409R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecT409R1Point(null, getAffineXCoord(), getAffineYCoord()); + } + + public ECFieldElement getYCoord() + { + ECFieldElement X = x, L = y; + + 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); + + ECFieldElement Z = zs[0]; + if (!Z.isOne()) + { + Y = Y.divide(Z); + } + + return Y; + } + + protected boolean getCompressionYTilde() + { + ECFieldElement X = this.getRawXCoord(); + if (X.isZero()) + { + return false; + } + + ECFieldElement Y = this.getRawYCoord(); + + // Y is actually Lambda (X + Y/X) here + return Y.testBitZero() != X.testBitZero(); + } + + public ECPoint add(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + ECFieldElement X2 = b.getRawXCoord(); + + if (X1.isZero()) + { + if (X2.isZero()) + { + return curve.getInfinity(); + } + + return b.add(this); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0); + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.multiply(Z1); + S2 = S2.multiply(Z1); + } + + boolean 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 twice(); + } + + return curve.getInfinity(); + } + + ECFieldElement X3, L3, Z3; + if (X2.isZero()) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.normalize(); + X1 = p.getXCoord(); + ECFieldElement Y1 = p.getYCoord(); + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.add(Y2).divide(X1); + +// X3 = L.square().add(L).add(X1).add(curve.getA()); + X3 = L.square().add(L).add(X1).addOne(); + if (X3.isZero()) + { + return new SecT409R1Point(curve, X3, curve.getB().sqrt(), this.withCompression); + } + + ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1); + L3 = Y3.divide(X3).add(X3); + Z3 = curve.fromBigInteger(ECConstants.ONE); + } + else + { + B = B.square(); + + ECFieldElement AU1 = A.multiply(U1); + ECFieldElement AU2 = A.multiply(U2); + + X3 = AU1.multiply(AU2); + if (X3.isZero()) + { + return new SecT409R1Point(curve, X3, curve.getB().sqrt(), this.withCompression); + } + + 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 SecT409R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return curve.getInfinity(); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square(); + ECFieldElement T = L1.square().add(L1Z1).add(Z1Sq); + if (T.isZero()) + { + return new SecT409R1Point(curve, T, curve.getB().sqrt(), withCompression); + } + + ECFieldElement X3 = T.square(); + ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq); + + ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1); + ECFieldElement L3 = X1Z1.squarePlusProduct(T, L1Z1).add(X3).add(Z3); + + return new SecT409R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twicePlus(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return twice(); + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return b; + } + + ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0); + if (X2.isZero() || !Z2.isOne()) + { + return twice().add(b); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(); + + ECFieldElement X1Sq = X1.square(); + ECFieldElement L1Sq = L1.square(); + ECFieldElement Z1Sq = Z1.square(); + ECFieldElement L1Z1 = L1.multiply(Z1); + +// ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1); + ECFieldElement T = Z1Sq.add(L1Sq).add(L1Z1); + ECFieldElement L2plus1 = L2.addOne(); +// ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement A = L2.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.getInfinity(); + } + + if (A.isZero()) + { + return new SecT409R1Point(curve, A, curve.getB().sqrt(), withCompression); + } + + 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 SecT409R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + ECFieldElement X = this.x; + if (X.isZero()) + { + return this; + } + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.y, Z = this.zs[0]; + return new SecT409R1Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571Field.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571Field.java new file mode 100644 index 00000000..68368081 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571Field.java @@ -0,0 +1,335 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.raw.Interleave; +import org.bouncycastle.math.raw.Nat; +import org.bouncycastle.math.raw.Nat576; + +public class SecT571Field +{ + private static final long M59 = -1L >>> 5; + + private static final long RM = 0xEF7BDEF7BDEF7BDEL; + + private static final long[] ROOT_Z = new long[]{ 0x2BE1195F08CAFB99L, 0x95F08CAF84657C23L, 0xCAF84657C232BE11L, 0x657C232BE1195F08L, + 0xF84657C2308CAF84L, 0x7C232BE1195F08CAL, 0xBE1195F08CAF8465L, 0x5F08CAF84657C232L, 0x784657C232BE119L }; + + public static void add(long[] x, long[] y, long[] z) + { + for (int i = 0; i < 9; ++i) + { + z[i] = x[i] ^ y[i]; + } + } + + private static void add(long[] x, int xOff, long[] y, int yOff, long[] z, int zOff) + { + for (int i = 0; i < 9; ++i) + { + z[zOff + i] = x[xOff + i] ^ y[yOff + i]; + } + } + + private static void addBothTo(long[] x, int xOff, long[] y, int yOff, long[] z, int zOff) + { + for (int i = 0; i < 9; ++i) + { + z[zOff + i] ^= x[xOff + i] ^ y[yOff + i]; + } + } + + public static void addExt(long[] xx, long[] yy, long[] zz) + { + for (int i = 0; i < 18; ++i) + { + zz[i] = xx[i] ^ yy[i]; + } + } + + public static void addOne(long[] x, long[] z) + { + z[0] = x[0] ^ 1L; + for (int i = 1; i < 9; ++i) + { + z[i] = x[i]; + } + } + + public static long[] fromBigInteger(BigInteger x) + { + long[] z = Nat576.fromBigInteger64(x); + reduce5(z, 0); + return z; + } + + public static void invert(long[] x, long[] z) + { + if (Nat576.isZero64(x)) + { + throw new IllegalStateException(); + } + + // Itoh-Tsujii inversion with bases { 2, 3, 5 } + + long[] t0 = Nat576.create64(); + long[] t1 = Nat576.create64(); + long[] t2 = Nat576.create64(); + + square(x, t2); + + // 5 | 570 + square(t2, t0); + square(t0, t1); + multiply(t0, t1, t0); + squareN(t0, 2, t1); + multiply(t0, t1, t0); + multiply(t0, t2, t0); + + // 3 | 114 + squareN(t0, 5, t1); + multiply(t0, t1, t0); + squareN(t1, 5, t1); + multiply(t0, t1, t0); + + // 2 | 38 + squareN(t0, 15, t1); + multiply(t0, t1, t2); + + // ! {2,3,5} | 19 + squareN(t2, 30, t0); + squareN(t0, 30, t1); + multiply(t0, t1, t0); + + // 3 | 9 + squareN(t0, 60, t1); + multiply(t0, t1, t0); + squareN(t1, 60, t1); + multiply(t0, t1, t0); + + // 3 | 3 + squareN(t0, 180, t1); + multiply(t0, t1, t0); + squareN(t1, 180, t1); + multiply(t0, t1, t0); + + multiply(t0, t2, z); + } + + public static void multiply(long[] x, long[] y, long[] z) + { + long[] tt = Nat576.createExt64(); + implMultiply(x, y, tt); + reduce(tt, z); + } + + public static void multiplyAddToExt(long[] x, long[] y, long[] zz) + { + long[] tt = Nat576.createExt64(); + implMultiply(x, y, tt); + addExt(zz, tt, zz); + } + + public static void reduce(long[] xx, long[] z) + { + long xx09 = xx[9]; + long u = xx[17], v = xx09; + + xx09 = v ^ (u >>> 59) ^ (u >>> 57) ^ (u >>> 54) ^ (u >>> 49); + v = xx[8] ^ (u << 5) ^ (u << 7) ^ (u << 10) ^ (u << 15); + + for (int i = 16; i >= 10; --i) + { + u = xx[i]; + z[i - 8] = v ^ (u >>> 59) ^ (u >>> 57) ^ (u >>> 54) ^ (u >>> 49); + v = xx[i - 9] ^ (u << 5) ^ (u << 7) ^ (u << 10) ^ (u << 15); + } + + u = xx09; + z[1] = v ^ (u >>> 59) ^ (u >>> 57) ^ (u >>> 54) ^ (u >>> 49); + v = xx[0] ^ (u << 5) ^ (u << 7) ^ (u << 10) ^ (u << 15); + + long x08 = z[8]; + long t = x08 >>> 59; + z[0] = v ^ t ^ (t << 2) ^ (t << 5) ^ (t << 10); + z[8] = x08 & M59; + } + + public static void reduce5(long[] z, int zOff) + { + long z8 = z[zOff + 8], t = z8 >>> 59; + z[zOff ] ^= t ^ (t << 2) ^ (t << 5) ^ (t << 10); + z[zOff + 8] = z8 & M59; + } + + public static void sqrt(long[] x, long[] z) + { + long[] evn = Nat576.create64(), odd = Nat576.create64(); + + int pos = 0; + for (int i = 0; i < 4; ++i) + { + long u0 = Interleave.unshuffle(x[pos++]); + long u1 = Interleave.unshuffle(x[pos++]); + evn[i] = (u0 & 0x00000000FFFFFFFFL) | (u1 << 32); + odd[i] = (u0 >>> 32) | (u1 & 0xFFFFFFFF00000000L); + } + { + long u0 = Interleave.unshuffle(x[pos]); + evn[4] = (u0 & 0x00000000FFFFFFFFL); + odd[4] = (u0 >>> 32); + } + + multiply(odd, ROOT_Z, z); + add(z, evn, z); + } + + public static void square(long[] x, long[] z) + { + long[] tt = Nat576.createExt64(); + implSquare(x, tt); + reduce(tt, z); + } + + public static void squareAddToExt(long[] x, long[] zz) + { + long[] tt = Nat576.createExt64(); + implSquare(x, tt); + addExt(zz, tt, zz); + } + + public static void squareN(long[] x, int n, long[] z) + { +// assert n > 0; + + long[] tt = Nat576.createExt64(); + implSquare(x, tt); + reduce(tt, z); + + while (--n > 0) + { + implSquare(z, tt); + reduce(tt, z); + } + } + + public static int trace(long[] x) + { + // Non-zero-trace bits: 0, 561, 569 + return (int)(x[0] ^ (x[8] >>> 49) ^ (x[8] >>> 57)) & 1; + } + + protected static void implMultiply(long[] x, long[] y, long[] zz) + { +// for (int i = 0; i < 9; ++i) +// { +// implMulwAcc(x, y[i], zz, i); +// } + + /* + * Precompute table of all 4-bit products of y + */ + long[] T0 = new long[9 << 4]; + System.arraycopy(y, 0, T0, 9, 9); +// reduce5(T0, 9); + int tOff = 0; + for (int i = 7; i > 0; --i) + { + tOff += 18; + Nat.shiftUpBit64(9, T0, tOff >>> 1, 0L, T0, tOff); + reduce5(T0, tOff); + add(T0, 9, T0, tOff, T0, tOff + 9); + } + + /* + * Second table with all 4-bit products of B shifted 4 bits + */ + long[] T1 = new long[T0.length]; + Nat.shiftUpBits64(T0.length, T0, 0, 4, 0L, T1, 0); + + int MASK = 0xF; + + /* + * Lopez-Dahab algorithm + */ + + for (int k = 56; k >= 0; k -= 8) + { + for (int j = 1; j < 9; j += 2) + { + int aVal = (int)(x[j] >>> k); + int u = aVal & MASK; + int v = (aVal >>> 4) & MASK; + addBothTo(T0, 9 * u, T1, 9 * v, zz, j - 1); + } + Nat.shiftUpBits64(16, zz, 0, 8, 0L); + } + + for (int k = 56; k >= 0; k -= 8) + { + for (int j = 0; j < 9; j += 2) + { + int aVal = (int)(x[j] >>> k); + int u = aVal & MASK; + int v = (aVal >>> 4) & MASK; + addBothTo(T0, 9 * u, T1, 9 * v, zz, j); + } + if (k > 0) + { + Nat.shiftUpBits64(18, zz, 0, 8, 0L); + } + } + } + + protected static void implMulwAcc(long[] xs, long y, long[] z, int zOff) + { + long[] u = new long[32]; +// u[0] = 0; + u[1] = y; + for (int i = 2; i < 32; i += 2) + { + u[i ] = u[i >>> 1] << 1; + u[i + 1] = u[i ] ^ y; + } + + long l = 0; + for (int i = 0; i < 9; ++i) + { + long x = xs[i]; + + int j = (int)x; + + l ^= u[j & 31]; + + long g, h = 0; + int k = 60; + do + { + j = (int)(x >>> k); + g = u[j & 31]; + l ^= (g << k); + h ^= (g >>> -k); + } + while ((k -= 5) > 0); + + for (int p = 0; p < 4; ++p) + { + x = (x & RM) >>> 1; + h ^= x & ((y << p) >> 63); + } + + z[zOff + i] ^= l; + + l = h; + } + z[zOff + 9] ^= l; + } + + protected static void implSquare(long[] x, long[] zz) + { + for (int i = 0; i < 9; ++i) + { + Interleave.expand64To128(x[i], zz, i << 1); + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571FieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571FieldElement.java new file mode 100644 index 00000000..31f5f5a2 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571FieldElement.java @@ -0,0 +1,221 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.raw.Nat576; +import org.bouncycastle.util.Arrays; + +public class SecT571FieldElement extends ECFieldElement +{ + protected long[] x; + + public SecT571FieldElement(BigInteger x) + { + if (x == null || x.signum() < 0 || x.bitLength() > 571) + { + throw new IllegalArgumentException("x value invalid for SecT571FieldElement"); + } + + this.x = SecT571Field.fromBigInteger(x); + } + + public SecT571FieldElement() + { + this.x = Nat576.create64(); + } + + protected SecT571FieldElement(long[] x) + { + this.x = x; + } + +// public int bitLength() +// { +// return x.degree(); +// } + + public boolean isOne() + { + return Nat576.isOne64(x); + } + + public boolean isZero() + { + return Nat576.isZero64(x); + } + + public boolean testBitZero() + { + return (x[0] & 1L) != 0L; + } + + public BigInteger toBigInteger() + { + return Nat576.toBigInteger64(x); + } + + public String getFieldName() + { + return "SecT571Field"; + } + + public int getFieldSize() + { + return 571; + } + + public ECFieldElement add(ECFieldElement b) + { + long[] z = Nat576.create64(); + SecT571Field.add(x, ((SecT571FieldElement)b).x, z); + return new SecT571FieldElement(z); + } + + public ECFieldElement addOne() + { + long[] z = Nat576.create64(); + SecT571Field.addOne(x, z); + return new SecT571FieldElement(z); + } + + public ECFieldElement subtract(ECFieldElement b) + { + // Addition and subtraction are the same in F2m + return add(b); + } + + public ECFieldElement multiply(ECFieldElement b) + { + long[] z = Nat576.create64(); + SecT571Field.multiply(x, ((SecT571FieldElement)b).x, z); + return new SecT571FieldElement(z); + } + + public ECFieldElement multiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + return multiplyPlusProduct(b, x, y); + } + + public ECFieldElement multiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y) + { + long[] ax = this.x, bx = ((SecT571FieldElement)b).x; + long[] xx = ((SecT571FieldElement)x).x, yx = ((SecT571FieldElement)y).x; + + long[] tt = Nat576.createExt64(); + SecT571Field.multiplyAddToExt(ax, bx, tt); + SecT571Field.multiplyAddToExt(xx, yx, tt); + + long[] z = Nat576.create64(); + SecT571Field.reduce(tt, z); + return new SecT571FieldElement(z); + } + + public ECFieldElement divide(ECFieldElement b) + { + return multiply(b.invert()); + } + + public ECFieldElement negate() + { + return this; + } + + public ECFieldElement square() + { + long[] z = Nat576.create64(); + SecT571Field.square(x, z); + return new SecT571FieldElement(z); + } + + public ECFieldElement squareMinusProduct(ECFieldElement x, ECFieldElement y) + { + return squarePlusProduct(x, y); + } + + public ECFieldElement squarePlusProduct(ECFieldElement x, ECFieldElement y) + { + long[] ax = this.x; + long[] xx = ((SecT571FieldElement)x).x, yx = ((SecT571FieldElement)y).x; + + long[] tt = Nat576.createExt64(); + SecT571Field.squareAddToExt(ax, tt); + SecT571Field.multiplyAddToExt(xx, yx, tt); + + long[] z = Nat576.create64(); + SecT571Field.reduce(tt, z); + return new SecT571FieldElement(z); + } + + public ECFieldElement squarePow(int pow) + { + if (pow < 1) + { + return this; + } + + long[] z = Nat576.create64(); + SecT571Field.squareN(x, pow, z); + return new SecT571FieldElement(z); + } + + public ECFieldElement invert() + { + long[] z = Nat576.create64(); + SecT571Field.invert(x, z); + return new SecT571FieldElement(z); + } + + public ECFieldElement sqrt() + { + long[] z = Nat576.create64(); + SecT571Field.sqrt(x, z); + return new SecT571FieldElement(z); + } + + public int getRepresentation() + { + return ECFieldElement.F2m.PPB; + } + + public int getM() + { + return 571; + } + + public int getK1() + { + return 2; + } + + public int getK2() + { + return 5; + } + + public int getK3() + { + return 10; + } + + public boolean equals(Object other) + { + if (other == this) + { + return true; + } + + if (!(other instanceof SecT571FieldElement)) + { + return false; + } + + SecT571FieldElement o = (SecT571FieldElement)other; + return Nat576.eq64(x, o.x); + } + + public int hashCode() + { + return 5711052 ^ Arrays.hashCode(x, 0, 9); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571K1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571K1Curve.java new file mode 100644 index 00000000..1fd2ce36 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571K1Curve.java @@ -0,0 +1,108 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECCurve.AbstractF2m; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECMultiplier; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.WTauNafMultiplier; +import org.bouncycastle.util.encoders.Hex; + +public class SecT571K1Curve extends AbstractF2m +{ + private static final int SecT571K1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected SecT571K1Point infinity; + + public SecT571K1Curve() + { + super(571, 2, 5, 10); + + this.infinity = new SecT571K1Point(this, null, null); + + this.a = fromBigInteger(BigInteger.valueOf(0)); + this.b = fromBigInteger(BigInteger.valueOf(1)); + this.order = new BigInteger(1, Hex.decode("020000000000000000000000000000000000000000000000000000000000000000000000131850E1F19A63E4B391A8DB917F4138B630D84BE5D639381E91DEB45CFE778F637C1001")); + this.cofactor = BigInteger.valueOf(4); + + this.coord = SecT571K1_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecT571K1Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + protected ECMultiplier createDefaultMultiplier() + { + return new WTauNafMultiplier(); + } + + public int getFieldSize() + { + return 571; + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecT571FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecT571K1Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecT571K1Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } + + public boolean isKoblitz() + { + return true; + } + + public int getM() + { + return 571; + } + + public boolean isTrinomial() + { + return false; + } + + public int getK1() + { + return 2; + } + + public int getK2() + { + return 5; + } + + public int getK3() + { + return 10; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571K1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571K1Point.java new file mode 100644 index 00000000..c9238cd8 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571K1Point.java @@ -0,0 +1,324 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECConstants; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.ECPoint.AbstractF2m; + +public class SecT571K1Point extends AbstractF2m +{ + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT571K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + { + this(curve, x, y, false); + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)} + */ + public SecT571K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecT571K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecT571K1Point(null, this.getAffineXCoord(), this.getAffineYCoord()); // earlier JDK + } + + public ECFieldElement getYCoord() + { + ECFieldElement X = x, L = y; + + 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); + + ECFieldElement Z = zs[0]; + if (!Z.isOne()) + { + Y = Y.divide(Z); + } + + return Y; + } + + protected boolean getCompressionYTilde() + { + ECFieldElement X = this.getRawXCoord(); + if (X.isZero()) + { + return false; + } + + ECFieldElement Y = this.getRawYCoord(); + + // Y is actually Lambda (X + Y/X) here + return Y.testBitZero() != X.testBitZero(); + } + + public ECPoint add(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + ECFieldElement X2 = b.getRawXCoord(); + + if (X1.isZero()) + { + if (X2.isZero()) + { + return curve.getInfinity(); + } + + return b.add(this); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0); + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.multiply(Z1); + S2 = S2.multiply(Z1); + } + + boolean 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 twice(); + } + + return curve.getInfinity(); + } + + ECFieldElement X3, L3, Z3; + if (X2.isZero()) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.normalize(); + X1 = p.getXCoord(); + ECFieldElement Y1 = p.getYCoord(); + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.add(Y2).divide(X1); + +// X3 = L.square().add(L).add(X1).add(curve.getA()); + X3 = L.square().add(L).add(X1).addOne(); + if (X3.isZero()) + { +// return new SecT571K1Point(curve, X3, curve.getB().sqrt(), this.withCompression); + return new SecT571K1Point(curve, X3, curve.getB(), this.withCompression); + } + + ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1); + L3 = Y3.divide(X3).add(X3); + Z3 = curve.fromBigInteger(ECConstants.ONE); + } + else + { + B = B.square(); + + ECFieldElement AU1 = A.multiply(U1); + ECFieldElement AU2 = A.multiply(U2); + + X3 = AU1.multiply(AU2); + if (X3.isZero()) + { +// return new SecT571K1Point(curve, X3, curve.getB().sqrt(), this.withCompression); + return new SecT571K1Point(curve, X3, curve.getB(), this.withCompression); + } + + 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 SecT571K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return curve.getInfinity(); + } + + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square(); + ECFieldElement T; + if (Z1IsOne) + { + T = L1.square().add(L1); + } + else + { + T = L1.add(Z1).multiply(L1); + } + + if (T.isZero()) + { +// return new SecT571K1Point(curve, T, curve.getB().sqrt(), withCompression); + return new SecT571K1Point(curve, T, curve.getB(), withCompression); + } + + ECFieldElement X3 = T.square(); + ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq); + + ECFieldElement t1 = L1.add(X1).square(); + ECFieldElement t2 = Z1IsOne ? Z1 : Z1Sq.square(); + ECFieldElement L3 = t1.add(T).add(Z1Sq).multiply(t1).add(t2).add(X3).add(Z3); + + return new SecT571K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twicePlus(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return twice(); + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return b; + } + + // NOTE: twicePlus() only optimized for lambda-affine argument + ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0); + if (X2.isZero() || !Z2.isOne()) + { + return twice().add(b); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(); + + ECFieldElement X1Sq = X1.square(); + ECFieldElement L1Sq = L1.square(); + ECFieldElement Z1Sq = Z1.square(); + ECFieldElement L1Z1 = L1.multiply(Z1); + +// ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1); + ECFieldElement T = L1Sq.add(L1Z1); + ECFieldElement L2plus1 = L2.addOne(); +// ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement A = 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.getInfinity(); + } + + if (A.isZero()) + { +// return new SecT571K1Point(curve, A, curve.getB().sqrt(), withCompression); + return new SecT571K1Point(curve, A, curve.getB(), withCompression); + } + + 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 SecT571K1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + ECFieldElement X = this.x; + if (X.isZero()) + { + return this; + } + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.y, Z = this.zs[0]; + return new SecT571K1Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571R1Curve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571R1Curve.java new file mode 100644 index 00000000..df39e502 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571R1Curve.java @@ -0,0 +1,105 @@ +package org.bouncycastle.math.ec.custom.sec; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECCurve.AbstractF2m; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; + +public class SecT571R1Curve extends AbstractF2m +{ + private static final int SecT571R1_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + protected SecT571R1Point infinity; + + static final SecT571FieldElement SecT571R1_B = new SecT571FieldElement( + new BigInteger(1, Hex.decode("02F40E7E2221F295DE297117B7F3D62F5C6A97FFCB8CEFF1CD6BA8CE4A9A18AD84FFABBD8EFA59332BE7AD6756A66E294AFD185A78FF12AA520E4DE739BACA0C7FFEFF7F2955727A"))); + static final SecT571FieldElement SecT571R1_B_SQRT = (SecT571FieldElement)SecT571R1_B.sqrt(); + + public SecT571R1Curve() + { + super(571, 2, 5, 10); + + this.infinity = new SecT571R1Point(this, null, null); + + this.a = fromBigInteger(BigInteger.valueOf(1)); + this.b = SecT571R1_B; + this.order = new BigInteger(1, Hex.decode("03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE661CE18FF55987308059B186823851EC7DD9CA1161DE93D5174D66E8382E9BB2FE84E47")); + this.cofactor = BigInteger.valueOf(2); + + this.coord = SecT571R1_DEFAULT_COORDS; + } + + protected ECCurve cloneCurve() + { + return new SecT571R1Curve(); + } + + public boolean supportsCoordinateSystem(int coord) + { + switch (coord) + { + case COORD_LAMBDA_PROJECTIVE: + return true; + default: + return false; + } + } + + public int getFieldSize() + { + return 571; + } + + public ECFieldElement fromBigInteger(BigInteger x) + { + return new SecT571FieldElement(x); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) + { + return new SecT571R1Point(this, x, y, withCompression); + } + + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + return new SecT571R1Point(this, x, y, zs, withCompression); + } + + public ECPoint getInfinity() + { + return infinity; + } + + public boolean isKoblitz() + { + return false; + } + + public int getM() + { + return 571; + } + + public boolean isTrinomial() + { + return false; + } + + public int getK1() + { + return 2; + } + + public int getK2() + { + return 5; + } + + public int getK3() + { + return 10; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571R1Point.java b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571R1Point.java new file mode 100644 index 00000000..921828f2 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/custom/sec/SecT571R1Point.java @@ -0,0 +1,313 @@ +package org.bouncycastle.math.ec.custom.sec; + +import org.bouncycastle.math.ec.ECConstants; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.ECPoint.AbstractF2m; + +public class SecT571R1Point extends AbstractF2m +{ + /** + * @deprecated Use ECCurve.createPoint to construct points + */ + public SecT571R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y) + { + this(curve, x, y, false); + } + + /** + * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)} + */ + public SecT571R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression) + { + super(curve, x, y); + + if ((x == null) != (y == null)) + { + throw new IllegalArgumentException("Exactly one of the field elements is null"); + } + + this.withCompression = withCompression; + } + + SecT571R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) + { + super(curve, x, y, zs); + + this.withCompression = withCompression; + } + + protected ECPoint detach() + { + return new SecT571R1Point(null, getAffineXCoord(), getAffineYCoord()); + } + + public ECFieldElement getYCoord() + { + ECFieldElement X = x, L = y; + + 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); + + ECFieldElement Z = zs[0]; + if (!Z.isOne()) + { + Y = Y.divide(Z); + } + + return Y; + } + + protected boolean getCompressionYTilde() + { + ECFieldElement X = this.getRawXCoord(); + if (X.isZero()) + { + return false; + } + + ECFieldElement Y = this.getRawYCoord(); + + // Y is actually Lambda (X + Y/X) here + return Y.testBitZero() != X.testBitZero(); + } + + public ECPoint add(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + ECFieldElement X2 = b.getRawXCoord(); + + if (X1.isZero()) + { + if (X2.isZero()) + { + return curve.getInfinity(); + } + + return b.add(this); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(), Z2 = b.getZCoord(0); + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement U2 = X2, S2 = L2; + if (!Z1IsOne) + { + U2 = U2.multiply(Z1); + S2 = S2.multiply(Z1); + } + + boolean 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 twice(); + } + + return curve.getInfinity(); + } + + ECFieldElement X3, L3, Z3; + if (X2.isZero()) + { + // TODO This can probably be optimized quite a bit + ECPoint p = this.normalize(); + X1 = p.getXCoord(); + ECFieldElement Y1 = p.getYCoord(); + + ECFieldElement Y2 = L2; + ECFieldElement L = Y1.add(Y2).divide(X1); + +// X3 = L.square().add(L).add(X1).add(curve.getA()); + X3 = L.square().add(L).add(X1).addOne(); + if (X3.isZero()) + { +// return new SecT571R1Point(curve, X3, curve.getB().sqrt(), this.withCompression); + return new SecT571R1Point(curve, X3, SecT571R1Curve.SecT571R1_B_SQRT, this.withCompression); + } + + ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1); + L3 = Y3.divide(X3).add(X3); + Z3 = curve.fromBigInteger(ECConstants.ONE); + } + else + { + B = B.square(); + + ECFieldElement AU1 = A.multiply(U1); + ECFieldElement AU2 = A.multiply(U2); + + X3 = AU1.multiply(AU2); + if (X3.isZero()) + { +// return new SecT571R1Point(curve, X3, curve.getB().sqrt(), this.withCompression); + return new SecT571R1Point(curve, X3, SecT571R1Curve.SecT571R1_B_SQRT, this.withCompression); + } + + 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 SecT571R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twice() + { + if (this.isInfinity()) + { + return this; + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return curve.getInfinity(); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + + boolean Z1IsOne = Z1.isOne(); + ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1); + ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square(); + ECFieldElement T = L1.square().add(L1Z1).add(Z1Sq); + if (T.isZero()) + { +// return new SecT571R1Point(curve, T, curve.getB().sqrt(), withCompression); + return new SecT571R1Point(curve, T, SecT571R1Curve.SecT571R1_B_SQRT, withCompression); + } + + ECFieldElement X3 = T.square(); + ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq); + + ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1); + ECFieldElement L3 = X1Z1.squarePlusProduct(T, L1Z1).add(X3).add(Z3); + + return new SecT571R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint twicePlus(ECPoint b) + { + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) + { + return twice(); + } + + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) + { + // A point with X == 0 is it's own additive inverse + return b; + } + + ECFieldElement X2 = b.getRawXCoord(), Z2 = b.getZCoord(0); + if (X2.isZero() || !Z2.isOne()) + { + return twice().add(b); + } + + ECFieldElement L1 = this.y, Z1 = this.zs[0]; + ECFieldElement L2 = b.getRawYCoord(); + + ECFieldElement X1Sq = X1.square(); + ECFieldElement L1Sq = L1.square(); + ECFieldElement Z1Sq = Z1.square(); + ECFieldElement L1Z1 = L1.multiply(Z1); + +// ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1); + ECFieldElement T = Z1Sq.add(L1Sq).add(L1Z1); + ECFieldElement L2plus1 = L2.addOne(); +// ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiplyPlusProduct(T, X1Sq, Z1Sq); + ECFieldElement A = L2.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.getInfinity(); + } + + if (A.isZero()) + { +// return new SecT571R1Point(curve, A, curve.getB().sqrt(), withCompression); + return new SecT571R1Point(curve, A, SecT571R1Curve.SecT571R1_B_SQRT, withCompression); + } + + 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 SecT571R1Point(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression); + } + + public ECPoint negate() + { + if (this.isInfinity()) + { + return this; + } + + ECFieldElement X = this.x; + if (X.isZero()) + { + return this; + } + + // L is actually Lambda (X + Y/X) here + ECFieldElement L = this.y, Z = this.zs[0]; + return new SecT571R1Point(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/tools/F2mSqrtOptimizer.java b/bcprov/src/main/java/org/bouncycastle/math/ec/tools/F2mSqrtOptimizer.java new file mode 100644 index 00000000..31bac8a3 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/tools/F2mSqrtOptimizer.java @@ -0,0 +1,72 @@ +package org.bouncycastle.math.ec.tools; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.bouncycastle.asn1.x9.ECNamedCurveTable; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.ec.CustomNamedCurves; +import org.bouncycastle.math.ec.ECAlgorithms; +import org.bouncycastle.math.ec.ECFieldElement; + +public class F2mSqrtOptimizer +{ + public static void main(String[] args) + { + SortedSet names = new TreeSet(enumToList(ECNamedCurveTable.getNames())); + names.addAll(enumToList(CustomNamedCurves.getNames())); + + Iterator it = names.iterator(); + while (it.hasNext()) + { + String name = (String)it.next(); + X9ECParameters x9 = CustomNamedCurves.getByName(name); + if (x9 == null) + { + x9 = ECNamedCurveTable.getByName(name); + } + if (x9 != null && ECAlgorithms.isF2mCurve(x9.getCurve())) + { + System.out.print(name + ":"); + implPrintRootZ(x9); + } + } + } + + public static void printRootZ(X9ECParameters x9) + { + if (!ECAlgorithms.isF2mCurve(x9.getCurve())) + { + throw new IllegalArgumentException("Sqrt optimization only defined over characteristic-2 fields"); + } + + implPrintRootZ(x9); + } + + private static void implPrintRootZ(X9ECParameters x9) + { + ECFieldElement z = x9.getCurve().fromBigInteger(BigInteger.valueOf(2)); + ECFieldElement rootZ = z.sqrt(); + + System.out.println(rootZ.toBigInteger().toString(16).toUpperCase()); + + if (!rootZ.square().equals(z)) + { + throw new IllegalStateException("Optimized-sqrt sanity check failed"); + } + } + + private static ArrayList enumToList(Enumeration en) + { + ArrayList rv = new ArrayList(); + while (en.hasMoreElements()) + { + rv.add(en.nextElement()); + } + return rv; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/tools/TraceOptimizer.java b/bcprov/src/main/java/org/bouncycastle/math/ec/tools/TraceOptimizer.java new file mode 100644 index 00000000..b21c9e0c --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/tools/TraceOptimizer.java @@ -0,0 +1,138 @@ +package org.bouncycastle.math.ec.tools; + +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.bouncycastle.asn1.x9.ECNamedCurveTable; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.ec.CustomNamedCurves; +import org.bouncycastle.math.ec.ECAlgorithms; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.util.Integers; + +public class TraceOptimizer +{ + private static final BigInteger ONE = BigInteger.valueOf(1); + + private static final SecureRandom R = new SecureRandom(); + + public static void main(String[] args) + { + SortedSet names = new TreeSet(enumToList(ECNamedCurveTable.getNames())); + names.addAll(enumToList(CustomNamedCurves.getNames())); + + Iterator it = names.iterator(); + while (it.hasNext()) + { + String name = (String)it.next(); + X9ECParameters x9 = CustomNamedCurves.getByName(name); + if (x9 == null) + { + x9 = ECNamedCurveTable.getByName(name); + } + if (x9 != null && ECAlgorithms.isF2mCurve(x9.getCurve())) + { + System.out.print(name + ":"); + implPrintNonZeroTraceBits(x9); + } + } + } + + public static void printNonZeroTraceBits(X9ECParameters x9) + { + if (!ECAlgorithms.isF2mCurve(x9.getCurve())) + { + throw new IllegalArgumentException("Trace only defined over characteristic-2 fields"); + } + + implPrintNonZeroTraceBits(x9); + } + + public static void implPrintNonZeroTraceBits(X9ECParameters x9) + { + ECCurve c = x9.getCurve(); + int m = c.getFieldSize(); + + ArrayList nonZeroTraceBits = new ArrayList(); + + /* + * Determine which of the bits contribute to the trace. + * + * See section 3.6.2 of "Guide to Elliptic Curve Cryptography" (Hankerson, Menezes, Vanstone) + */ + { + for (int i = 0; i < m; ++i) + { + BigInteger zi = ONE.shiftLeft(i); + ECFieldElement fe = c.fromBigInteger(zi); + int tr = calculateTrace(fe); + if (tr != 0) + { + nonZeroTraceBits.add(Integers.valueOf(i)); + System.out.print(" " + i); + } + } + System.out.println(); + } + + /* + * Check calculation based on the non-zero-trace bits against explicit calculation, for random field elements + */ + { + for (int i = 0; i < 1000; ++i) + { + BigInteger x = new BigInteger(m, R); + ECFieldElement fe = c.fromBigInteger(x); + int check = calculateTrace(fe); + + int tr = 0; + for (int j = 0; j < nonZeroTraceBits.size(); ++j) + { + int bit = ((Integer)nonZeroTraceBits.get(j)).intValue(); + if (x.testBit(bit)) + { + tr ^= 1; + } + } + + if (check != tr) + { + throw new IllegalStateException("Optimized-trace sanity check failed"); + } + } + } + } + + private static int calculateTrace(ECFieldElement fe) + { + int m = fe.getFieldSize(); + ECFieldElement tr = fe; + for (int i = 1; i < m; ++i) + { + fe = fe.square(); + tr = tr.add(fe); + } + BigInteger b = tr.toBigInteger(); + if (b.bitLength() > 1) + { + throw new IllegalStateException(); + } + return b.intValue(); + } + + private static ArrayList enumToList(Enumeration en) + { + ArrayList rv = new ArrayList(); + while (en.hasMoreElements()) + { + rv.add(en.nextElement()); + } + return rv; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/raw/Interleave.java b/bcprov/src/main/java/org/bouncycastle/math/raw/Interleave.java new file mode 100644 index 00000000..0d25a5e9 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/raw/Interleave.java @@ -0,0 +1,106 @@ +package org.bouncycastle.math.raw; + +public class Interleave +{ + private static final long M32 = 0x55555555L; + private static final long M64 = 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. + * + * NOTE: All entries are positive so sign-extension is not an issue. + */ +// private static final short[] INTERLEAVE2_TABLE = new short[] +// { +// 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 +// }; + + public static int expand8to16(int x) + { + x &= 0xFF; + x = (x | (x << 4)) & 0x0F0F; + x = (x | (x << 2)) & 0x3333; + x = (x | (x << 1)) & 0x5555; + return x; + } + + public static int expand16to32(int x) + { + x &= 0xFFFF; + x = (x | (x << 8)) & 0x00FF00FF; + x = (x | (x << 4)) & 0x0F0F0F0F; + x = (x | (x << 2)) & 0x33333333; + x = (x | (x << 1)) & 0x55555555; + return x; + } + + public static long expand32to64(int x) + { + // "shuffle" low half to even bits and high half to odd bits + int t; + t = (x ^ (x >>> 8)) & 0x0000FF00; x ^= (t ^ (t << 8)); + t = (x ^ (x >>> 4)) & 0x00F000F0; x ^= (t ^ (t << 4)); + t = (x ^ (x >>> 2)) & 0x0C0C0C0C; x ^= (t ^ (t << 2)); + t = (x ^ (x >>> 1)) & 0x22222222; x ^= (t ^ (t << 1)); + + return ((x >>> 1) & M32) << 32 | (x & M32); + } + + public static void expand64To128(long x, long[] z, int zOff) + { + // "shuffle" low half to even bits and high half to odd bits + long t; + t = (x ^ (x >>> 16)) & 0x00000000FFFF0000L; x ^= (t ^ (t << 16)); + t = (x ^ (x >>> 8)) & 0x0000FF000000FF00L; x ^= (t ^ (t << 8)); + t = (x ^ (x >>> 4)) & 0x00F000F000F000F0L; x ^= (t ^ (t << 4)); + t = (x ^ (x >>> 2)) & 0x0C0C0C0C0C0C0C0CL; x ^= (t ^ (t << 2)); + t = (x ^ (x >>> 1)) & 0x2222222222222222L; x ^= (t ^ (t << 1)); + + z[zOff ] = (x ) & M64; + z[zOff + 1] = (x >>> 1) & M64; + } + + public static long unshuffle(long x) + { + // "unshuffle" even bits to low half and odd bits to high half + long t; + t = (x ^ (x >>> 1)) & 0x2222222222222222L; x ^= (t ^ (t << 1)); + t = (x ^ (x >>> 2)) & 0x0C0C0C0C0C0C0C0CL; x ^= (t ^ (t << 2)); + t = (x ^ (x >>> 4)) & 0x00F000F000F000F0L; x ^= (t ^ (t << 4)); + t = (x ^ (x >>> 8)) & 0x0000FF000000FF00L; x ^= (t ^ (t << 8)); + t = (x ^ (x >>> 16)) & 0x00000000FFFF0000L; x ^= (t ^ (t << 16)); + return x; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat128.java b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat128.java new file mode 100644 index 00000000..8d621c17 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat128.java @@ -0,0 +1,866 @@ +package org.bouncycastle.math.raw; + +import java.math.BigInteger; + +import org.bouncycastle.util.Pack; + +public abstract class Nat128 +{ + private static final long M = 0xFFFFFFFFL; + + public static int add(int[] x, int[] y, int[] z) + { + long c = 0; + c += (x[0] & M) + (y[0] & M); + z[0] = (int)c; + c >>>= 32; + c += (x[1] & M) + (y[1] & M); + z[1] = (int)c; + c >>>= 32; + c += (x[2] & M) + (y[2] & M); + z[2] = (int)c; + c >>>= 32; + c += (x[3] & M) + (y[3] & M); + z[3] = (int)c; + c >>>= 32; + return (int)c; + } + + public static int addBothTo(int[] x, int[] y, int[] z) + { + long c = 0; + c += (x[0] & M) + (y[0] & M) + (z[0] & M); + z[0] = (int)c; + c >>>= 32; + c += (x[1] & M) + (y[1] & M) + (z[1] & M); + z[1] = (int)c; + c >>>= 32; + c += (x[2] & M) + (y[2] & M) + (z[2] & M); + z[2] = (int)c; + c >>>= 32; + c += (x[3] & M) + (y[3] & M) + (z[3] & M); + z[3] = (int)c; + c >>>= 32; + return (int)c; + } + + public static int addTo(int[] x, int[] z) + { + long c = 0; + c += (x[0] & M) + (z[0] & M); + z[0] = (int)c; + c >>>= 32; + c += (x[1] & M) + (z[1] & M); + z[1] = (int)c; + c >>>= 32; + c += (x[2] & M) + (z[2] & M); + z[2] = (int)c; + c >>>= 32; + c += (x[3] & M) + (z[3] & M); + z[3] = (int)c; + c >>>= 32; + return (int)c; + } + + public static int addTo(int[] x, int xOff, int[] z, int zOff, int cIn) + { + long c = cIn & M; + c += (x[xOff + 0] & M) + (z[zOff + 0] & M); + z[zOff + 0] = (int)c; + c >>>= 32; + c += (x[xOff + 1] & M) + (z[zOff + 1] & M); + z[zOff + 1] = (int)c; + c >>>= 32; + c += (x[xOff + 2] & M) + (z[zOff + 2] & M); + z[zOff + 2] = (int)c; + c >>>= 32; + c += (x[xOff + 3] & M) + (z[zOff + 3] & M); + z[zOff + 3] = (int)c; + c >>>= 32; + return (int)c; + } + + public static int addToEachOther(int[] u, int uOff, int[] v, int vOff) + { + long c = 0; + c += (u[uOff + 0] & M) + (v[vOff + 0] & M); + u[uOff + 0] = (int)c; + v[vOff + 0] = (int)c; + c >>>= 32; + c += (u[uOff + 1] & M) + (v[vOff + 1] & M); + u[uOff + 1] = (int)c; + v[vOff + 1] = (int)c; + c >>>= 32; + c += (u[uOff + 2] & M) + (v[vOff + 2] & M); + u[uOff + 2] = (int)c; + v[vOff + 2] = (int)c; + c >>>= 32; + c += (u[uOff + 3] & M) + (v[vOff + 3] & M); + u[uOff + 3] = (int)c; + v[vOff + 3] = (int)c; + c >>>= 32; + return (int)c; + } + + public static void copy(int[] x, int[] z) + { + z[0] = x[0]; + z[1] = x[1]; + z[2] = x[2]; + z[3] = x[3]; + } + + public static void copy64(long[] x, long[] z) + { + z[0] = x[0]; + z[1] = x[1]; + } + + public static int[] create() + { + return new int[4]; + } + + public static long[] create64() + { + return new long[2]; + } + + public static int[] createExt() + { + return new int[8]; + } + + public static long[] createExt64() + { + return new long[4]; + } + + public static boolean diff(int[] x, int xOff, int[] y, int yOff, int[] z, int zOff) + { + boolean 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 boolean eq(int[] x, int[] y) + { + for (int i = 3; i >= 0; --i) + { + if (x[i] != y[i]) + { + return false; + } + } + return true; + } + + public static boolean eq64(long[] x, long[] y) + { + for (int i = 1; i >= 0; --i) + { + if (x[i] != y[i]) + { + return false; + } + } + return true; + } + + public static int[] fromBigInteger(BigInteger x) + { + if (x.signum() < 0 || x.bitLength() > 128) + { + throw new IllegalArgumentException(); + } + + int[] z = create(); + int i = 0; + while (x.signum() != 0) + { + z[i++] = x.intValue(); + x = x.shiftRight(32); + } + return z; + } + + public static long[] fromBigInteger64(BigInteger x) + { + if (x.signum() < 0 || x.bitLength() > 128) + { + throw new IllegalArgumentException(); + } + + long[] z = create64(); + int i = 0; + while (x.signum() != 0) + { + z[i++] = x.longValue(); + x = x.shiftRight(64); + } + return z; + } + + public static int getBit(int[] x, int bit) + { + if (bit == 0) + { + return x[0] & 1; + } + int w = bit >> 5; + if (w < 0 || w >= 4) + { + return 0; + } + int b = bit & 31; + return (x[w] >>> b) & 1; + } + + public static boolean gte(int[] x, int[] y) + { + for (int i = 3; i >= 0; --i) + { + int x_i = x[i] ^ Integer.MIN_VALUE; + int y_i = y[i] ^ Integer.MIN_VALUE; + if (x_i < y_i) + return false; + if (x_i > y_i) + return true; + } + return true; + } + + public static boolean gte(int[] x, int xOff, int[] y, int yOff) + { + for (int i = 3; i >= 0; --i) + { + int x_i = x[xOff + i] ^ Integer.MIN_VALUE; + int y_i = y[yOff + i] ^ Integer.MIN_VALUE; + if (x_i < y_i) + return false; + if (x_i > y_i) + return true; + } + return true; + } + + public static boolean isOne(int[] x) + { + if (x[0] != 1) + { + return false; + } + for (int i = 1; i < 4; ++i) + { + if (x[i] != 0) + { + return false; + } + } + return true; + } + + public static boolean isOne64(long[] x) + { + if (x[0] != 1L) + { + return false; + } + for (int i = 1; i < 2; ++i) + { + if (x[i] != 0L) + { + return false; + } + } + return true; + } + + public static boolean isZero(int[] x) + { + for (int i = 0; i < 4; ++i) + { + if (x[i] != 0) + { + return false; + } + } + return true; + } + + public static boolean isZero64(long[] x) + { + for (int i = 0; i < 2; ++i) + { + if (x[i] != 0L) + { + return false; + } + } + return true; + } + + public static void mul(int[] x, int[] y, int[] zz) + { + long y_0 = y[0] & M; + long y_1 = y[1] & M; + long y_2 = y[2] & M; + long y_3 = y[3] & M; + + { + long c = 0, x_0 = x[0] & M; + c += x_0 * y_0; + zz[0] = (int)c; + c >>>= 32; + c += x_0 * y_1; + zz[1] = (int)c; + c >>>= 32; + c += x_0 * y_2; + zz[2] = (int)c; + c >>>= 32; + c += x_0 * y_3; + zz[3] = (int)c; + c >>>= 32; + zz[4] = (int)c; + } + + for (int i = 1; i < 4; ++i) + { + long c = 0, x_i = x[i] & M; + c += x_i * y_0 + (zz[i + 0] & M); + zz[i + 0] = (int)c; + c >>>= 32; + c += x_i * y_1 + (zz[i + 1] & M); + zz[i + 1] = (int)c; + c >>>= 32; + c += x_i * y_2 + (zz[i + 2] & M); + zz[i + 2] = (int)c; + c >>>= 32; + c += x_i * y_3 + (zz[i + 3] & M); + zz[i + 3] = (int)c; + c >>>= 32; + zz[i + 4] = (int)c; + } + } + + public static void mul(int[] x, int xOff, int[] y, int yOff, int[] zz, int zzOff) + { + long y_0 = y[yOff + 0] & M; + long y_1 = y[yOff + 1] & M; + long y_2 = y[yOff + 2] & M; + long y_3 = y[yOff + 3] & M; + + { + long c = 0, x_0 = x[xOff + 0] & M; + c += x_0 * y_0; + zz[zzOff + 0] = (int)c; + c >>>= 32; + c += x_0 * y_1; + zz[zzOff + 1] = (int)c; + c >>>= 32; + c += x_0 * y_2; + zz[zzOff + 2] = (int)c; + c >>>= 32; + c += x_0 * y_3; + zz[zzOff + 3] = (int)c; + c >>>= 32; + zz[zzOff + 4] = (int)c; + } + + for (int i = 1; i < 4; ++i) + { + ++zzOff; + long c = 0, x_i = x[xOff + i] & M; + c += x_i * y_0 + (zz[zzOff + 0] & M); + zz[zzOff + 0] = (int)c; + c >>>= 32; + c += x_i * y_1 + (zz[zzOff + 1] & M); + zz[zzOff + 1] = (int)c; + c >>>= 32; + c += x_i * y_2 + (zz[zzOff + 2] & M); + zz[zzOff + 2] = (int)c; + c >>>= 32; + c += x_i * y_3 + (zz[zzOff + 3] & M); + zz[zzOff + 3] = (int)c; + c >>>= 32; + zz[zzOff + 4] = (int)c; + } + } + + public static int mulAddTo(int[] x, int[] y, int[] zz) + { + long y_0 = y[0] & M; + long y_1 = y[1] & M; + long y_2 = y[2] & M; + long y_3 = y[3] & M; + + long zc = 0; + for (int i = 0; i < 4; ++i) + { + long c = 0, x_i = x[i] & M; + c += x_i * y_0 + (zz[i + 0] & M); + zz[i + 0] = (int)c; + c >>>= 32; + c += x_i * y_1 + (zz[i + 1] & M); + zz[i + 1] = (int)c; + c >>>= 32; + c += x_i * y_2 + (zz[i + 2] & M); + zz[i + 2] = (int)c; + c >>>= 32; + c += x_i * y_3 + (zz[i + 3] & M); + zz[i + 3] = (int)c; + c >>>= 32; + c += zc + (zz[i + 4] & M); + zz[i + 4] = (int)c; + zc = c >>> 32; + } + return (int)zc; + } + + public static int mulAddTo(int[] x, int xOff, int[] y, int yOff, int[] zz, int zzOff) + { + long y_0 = y[yOff + 0] & M; + long y_1 = y[yOff + 1] & M; + long y_2 = y[yOff + 2] & M; + long y_3 = y[yOff + 3] & M; + + long zc = 0; + for (int i = 0; i < 4; ++i) + { + long c = 0, x_i = x[xOff + i] & M; + c += x_i * y_0 + (zz[zzOff + 0] & M); + zz[zzOff + 0] = (int)c; + c >>>= 32; + c += x_i * y_1 + (zz[zzOff + 1] & M); + zz[zzOff + 1] = (int)c; + c >>>= 32; + c += x_i * y_2 + (zz[zzOff + 2] & M); + zz[zzOff + 2] = (int)c; + c >>>= 32; + c += x_i * y_3 + (zz[zzOff + 3] & M); + zz[zzOff + 3] = (int)c; + c >>>= 32; + c += zc + (zz[zzOff + 4] & M); + zz[zzOff + 4] = (int)c; + zc = c >>> 32; + ++zzOff; + } + return (int)zc; + } + + public static long mul33Add(int w, int[] x, int xOff, int[] y, int yOff, int[] z, int zOff) + { + // assert w >>> 31 == 0; + + long c = 0, wVal = w & M; + long x0 = x[xOff + 0] & M; + c += wVal * x0 + (y[yOff + 0] & M); + z[zOff + 0] = (int)c; + c >>>= 32; + long x1 = x[xOff + 1] & M; + c += wVal * x1 + x0 + (y[yOff + 1] & M); + z[zOff + 1] = (int)c; + c >>>= 32; + long x2 = x[xOff + 2] & M; + c += wVal * x2 + x1 + (y[yOff + 2] & M); + z[zOff + 2] = (int)c; + c >>>= 32; + long x3 = x[xOff + 3] & M; + c += wVal * x3 + x2 + (y[yOff + 3] & M); + z[zOff + 3] = (int)c; + c >>>= 32; + c += x3; + return c; + } + + public static int mulWordAddExt(int x, int[] yy, int yyOff, int[] zz, int zzOff) + { + // assert yyOff <= 4; + // assert zzOff <= 4; + long c = 0, xVal = x & M; + c += xVal * (yy[yyOff + 0] & M) + (zz[zzOff + 0] & M); + zz[zzOff + 0] = (int)c; + c >>>= 32; + c += xVal * (yy[yyOff + 1] & M) + (zz[zzOff + 1] & M); + zz[zzOff + 1] = (int)c; + c >>>= 32; + c += xVal * (yy[yyOff + 2] & M) + (zz[zzOff + 2] & M); + zz[zzOff + 2] = (int)c; + c >>>= 32; + c += xVal * (yy[yyOff + 3] & M) + (zz[zzOff + 3] & M); + zz[zzOff + 3] = (int)c; + c >>>= 32; + return (int)c; + } + + public static int mul33DWordAdd(int x, long y, int[] z, int zOff) + { + // assert x >>> 31 == 0; + // assert zOff <= 0; + + long c = 0, xVal = x & M; + long y00 = y & M; + c += xVal * y00 + (z[zOff + 0] & M); + z[zOff + 0] = (int)c; + c >>>= 32; + long y01 = y >>> 32; + c += xVal * y01 + y00 + (z[zOff + 1] & M); + z[zOff + 1] = (int)c; + c >>>= 32; + c += y01 + (z[zOff + 2] & M); + z[zOff + 2] = (int)c; + c >>>= 32; + c += (z[zOff + 3] & M); + z[zOff + 3] = (int)c; + c >>>= 32; + return (int)c; + } + + public static int mul33WordAdd(int x, int y, int[] z, int zOff) + { + // assert x >>> 31 == 0; + // assert zOff <= 1; + + long c = 0, xVal = x & M, yVal = y & M; + c += yVal * xVal + (z[zOff + 0] & M); + z[zOff + 0] = (int)c; + c >>>= 32; + c += yVal + (z[zOff + 1] & M); + z[zOff + 1] = (int)c; + c >>>= 32; + c += (z[zOff + 2] & M); + z[zOff + 2] = (int)c; + c >>>= 32; + return c == 0 ? 0 : Nat.incAt(4, z, zOff, 3); + } + + public static int mulWordDwordAdd(int x, long y, int[] z, int zOff) + { + // assert zOff <= 1; + long c = 0, xVal = x & M; + c += xVal * (y & M) + (z[zOff + 0] & M); + z[zOff + 0] = (int)c; + c >>>= 32; + c += xVal * (y >>> 32) + (z[zOff + 1] & M); + z[zOff + 1] = (int)c; + c >>>= 32; + c += (z[zOff + 2] & M); + z[zOff + 2] = (int)c; + c >>>= 32; + return c == 0 ? 0 : Nat.incAt(4, z, zOff, 3); + } + + public static int mulWordsAdd(int x, int y, int[] z, int zOff) + { + // assert zOff <= 2; + + long c = 0, xVal = x & M, yVal = y & M; + c += yVal * xVal + (z[zOff + 0] & M); + z[zOff + 0] = (int)c; + c >>>= 32; + c += (z[zOff + 1] & M); + z[zOff + 1] = (int)c; + c >>>= 32; + return c == 0 ? 0 : Nat.incAt(4, z, zOff, 2); + } + + public static int mulWord(int x, int[] y, int[] z, int zOff) + { + long c = 0, xVal = x & M; + int i = 0; + do + { + c += xVal * (y[i] & M); + z[zOff + i] = (int)c; + c >>>= 32; + } + while (++i < 4); + return (int)c; + } + + public static void square(int[] x, int[] zz) + { + long x_0 = x[0] & M; + long zz_1; + + int c = 0, w; + { + int i = 3, j = 8; + do + { + long xVal = (x[i--] & M); + long p = xVal * xVal; + zz[--j] = (c << 31) | (int)(p >>> 33); + zz[--j] = (int)(p >>> 1); + c = (int)p; + } + while (i > 0); + + { + long p = x_0 * x_0; + zz_1 = ((c << 31) & M) | (p >>> 33); + zz[0] = (int)p; + c = (int)(p >>> 32) & 1; + } + } + + long x_1 = x[1] & M; + long zz_2 = zz[2] & M; + + { + zz_1 += x_1 * x_0; + w = (int)zz_1; + zz[1] = (w << 1) | c; + c = w >>> 31; + zz_2 += zz_1 >>> 32; + } + + long x_2 = x[2] & M; + long zz_3 = zz[3] & M; + long zz_4 = zz[4] & M; + { + zz_2 += x_2 * x_0; + w = (int)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; + } + + long x_3 = x[3] & M; + long zz_5 = zz[5] & M; + long zz_6 = zz[6] & M; + { + zz_3 += x_3 * x_0; + w = (int)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_6 += zz_5 >>> 32; + zz_5 &= M; + } + + w = (int)zz_4; + zz[4] = (w << 1) | c; + c = w >>> 31; + w = (int)zz_5; + zz[5] = (w << 1) | c; + c = w >>> 31; + w = (int)zz_6; + zz[6] = (w << 1) | c; + c = w >>> 31; + w = zz[7] + (int)(zz_6 >> 32); + zz[7] = (w << 1) | c; + } + + public static void square(int[] x, int xOff, int[] zz, int zzOff) + { + long x_0 = x[xOff + 0] & M; + long zz_1; + + int c = 0, w; + { + int i = 3, j = 8; + do + { + long xVal = (x[xOff + i--] & M); + long p = xVal * xVal; + zz[zzOff + --j] = (c << 31) | (int)(p >>> 33); + zz[zzOff + --j] = (int)(p >>> 1); + c = (int)p; + } + while (i > 0); + + { + long p = x_0 * x_0; + zz_1 = ((c << 31) & M) | (p >>> 33); + zz[zzOff + 0] = (int)p; + c = (int)(p >>> 32) & 1; + } + } + + long x_1 = x[xOff + 1] & M; + long zz_2 = zz[zzOff + 2] & M; + + { + zz_1 += x_1 * x_0; + w = (int)zz_1; + zz[zzOff + 1] = (w << 1) | c; + c = w >>> 31; + zz_2 += zz_1 >>> 32; + } + + long x_2 = x[xOff + 2] & M; + long zz_3 = zz[zzOff + 3] & M; + long zz_4 = zz[zzOff + 4] & M; + { + zz_2 += x_2 * x_0; + w = (int)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; + } + + long x_3 = x[xOff + 3] & M; + long zz_5 = zz[zzOff + 5] & M; + long zz_6 = zz[zzOff + 6] & M; + { + zz_3 += x_3 * x_0; + w = (int)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_6 += zz_5 >>> 32; + } + + w = (int)zz_4; + zz[zzOff + 4] = (w << 1) | c; + c = w >>> 31; + w = (int)zz_5; + zz[zzOff + 5] = (w << 1) | c; + c = w >>> 31; + w = (int)zz_6; + zz[zzOff + 6] = (w << 1) | c; + c = w >>> 31; + w = zz[zzOff + 7] + (int)(zz_6 >> 32); + zz[zzOff + 7] = (w << 1) | c; + } + + public static int sub(int[] x, int[] y, int[] z) + { + long c = 0; + c += (x[0] & M) - (y[0] & M); + z[0] = (int)c; + c >>= 32; + c += (x[1] & M) - (y[1] & M); + z[1] = (int)c; + c >>= 32; + c += (x[2] & M) - (y[2] & M); + z[2] = (int)c; + c >>= 32; + c += (x[3] & M) - (y[3] & M); + z[3] = (int)c; + c >>= 32; + return (int)c; + } + + public static int sub(int[] x, int xOff, int[] y, int yOff, int[] z, int zOff) + { + long c = 0; + c += (x[xOff + 0] & M) - (y[yOff + 0] & M); + z[zOff + 0] = (int)c; + c >>= 32; + c += (x[xOff + 1] & M) - (y[yOff + 1] & M); + z[zOff + 1] = (int)c; + c >>= 32; + c += (x[xOff + 2] & M) - (y[yOff + 2] & M); + z[zOff + 2] = (int)c; + c >>= 32; + c += (x[xOff + 3] & M) - (y[yOff + 3] & M); + z[zOff + 3] = (int)c; + c >>= 32; + return (int)c; + } + + public static int subBothFrom(int[] x, int[] y, int[] z) + { + long c = 0; + c += (z[0] & M) - (x[0] & M) - (y[0] & M); + z[0] = (int)c; + c >>= 32; + c += (z[1] & M) - (x[1] & M) - (y[1] & M); + z[1] = (int)c; + c >>= 32; + c += (z[2] & M) - (x[2] & M) - (y[2] & M); + z[2] = (int)c; + c >>= 32; + c += (z[3] & M) - (x[3] & M) - (y[3] & M); + z[3] = (int)c; + c >>= 32; + return (int)c; + } + + public static int subFrom(int[] x, int[] z) + { + long c = 0; + c += (z[0] & M) - (x[0] & M); + z[0] = (int)c; + c >>= 32; + c += (z[1] & M) - (x[1] & M); + z[1] = (int)c; + c >>= 32; + c += (z[2] & M) - (x[2] & M); + z[2] = (int)c; + c >>= 32; + c += (z[3] & M) - (x[3] & M); + z[3] = (int)c; + c >>= 32; + return (int)c; + } + + public static int subFrom(int[] x, int xOff, int[] z, int zOff) + { + long c = 0; + c += (z[zOff + 0] & M) - (x[xOff + 0] & M); + z[zOff + 0] = (int)c; + c >>= 32; + c += (z[zOff + 1] & M) - (x[xOff + 1] & M); + z[zOff + 1] = (int)c; + c >>= 32; + c += (z[zOff + 2] & M) - (x[xOff + 2] & M); + z[zOff + 2] = (int)c; + c >>= 32; + c += (z[zOff + 3] & M) - (x[xOff + 3] & M); + z[zOff + 3] = (int)c; + c >>= 32; + return (int)c; + } + + public static BigInteger toBigInteger(int[] x) + { + byte[] bs = new byte[16]; + for (int i = 0; i < 4; ++i) + { + int x_i = x[i]; + if (x_i != 0) + { + Pack.intToBigEndian(x_i, bs, (3 - i) << 2); + } + } + return new BigInteger(1, bs); + } + + public static BigInteger toBigInteger64(long[] x) + { + byte[] bs = new byte[16]; + for (int i = 0; i < 2; ++i) + { + long x_i = x[i]; + if (x_i != 0L) + { + Pack.longToBigEndian(x_i, bs, (1 - i) << 3); + } + } + return new BigInteger(1, bs); + } + + public static void zero(int[] z) + { + z[0] = 0; + z[1] = 0; + z[2] = 0; + z[3] = 0; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat160.java b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat160.java new file mode 100644 index 00000000..55010dc3 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat160.java @@ -0,0 +1,878 @@ +package org.bouncycastle.math.raw; + +import java.math.BigInteger; + +import org.bouncycastle.util.Pack; + +public abstract class Nat160 +{ + private static final long M = 0xFFFFFFFFL; + + public static int add(int[] x, int[] y, int[] z) + { + long c = 0; + c += (x[0] & M) + (y[0] & M); + z[0] = (int)c; + c >>>= 32; + c += (x[1] & M) + (y[1] & M); + z[1] = (int)c; + c >>>= 32; + c += (x[2] & M) + (y[2] & M); + z[2] = (int)c; + c >>>= 32; + c += (x[3] & M) + (y[3] & M); + z[3] = (int)c; + c >>>= 32; + c += (x[4] & M) + (y[4] & M); + z[4] = (int)c; + c >>>= 32; + return (int)c; + } + + public static int addBothTo(int[] x, int[] y, int[] z) + { + long c = 0; + c += (x[0] & M) + (y[0] & M) + (z[0] & M); + z[0] = (int)c; + c >>>= 32; + c += (x[1] & M) + (y[1] & M) + (z[1] & M); + z[1] = (int)c; + c >>>= 32; + c += (x[2] & M) + (y[2] & M) + (z[2] & M); + z[2] = (int)c; + c >>>= 32; + c += (x[3] & M) + (y[3] & M) + (z[3] & M); + z[3] = (int)c; + c >>>= 32; + c += (x[4] & M) + (y[4] & M) + (z[4] & M); + z[4] = (int)c; + c >>>= 32; + return (int)c; + } + + public static int addTo(int[] x, int[] z) + { + long c = 0; + c += (x[0] & M) + (z[0] & M); + z[0] = (int)c; + c >>>= 32; + c += (x[1] & M) + (z[1] & M); + z[1] = (int)c; + c >>>= 32; + c += (x[2] & M) + (z[2] & M); + z[2] = (int)c; + c >>>= 32; + c += (x[3] & M) + (z[3] & M); + z[3] = (int)c; + c >>>= 32; + c += (x[4] & M) + (z[4] & M); + z[4] = (int)c; + c >>>= 32; + return (int)c; + } + + public static int addTo(int[] x, int xOff, int[] z, int zOff, int cIn) + { + long c = cIn & M; + c += (x[xOff + 0] & M) + (z[zOff + 0] & M); + z[zOff + 0] = (int)c; + c >>>= 32; + c += (x[xOff + 1] & M) + (z[zOff + 1] & M); + z[zOff + 1] = (int)c; + c >>>= 32; + c += (x[xOff + 2] & M) + (z[zOff + 2] & M); + z[zOff + 2] = (int)c; + c >>>= 32; + c += (x[xOff + 3] & M) + (z[zOff + 3] & M); + z[zOff + 3] = (int)c; + c >>>= 32; + c += (x[xOff + 4] & M) + (z[zOff + 4] & M); + z[zOff + 4] = (int)c; + c >>>= 32; + return (int)c; + } + + public static int addToEachOther(int[] u, int uOff, int[] v, int vOff) + { + long c = 0; + c += (u[uOff + 0] & M) + (v[vOff + 0] & M); + u[uOff + 0] = (int)c; + v[vOff + 0] = (int)c; + c >>>= 32; + c += (u[uOff + 1] & M) + (v[vOff + 1] & M); + u[uOff + 1] = (int)c; + v[vOff + 1] = (int)c; + c >>>= 32; + c += (u[uOff + 2] & M) + (v[vOff + 2] & M); + u[uOff + 2] = (int)c; + v[vOff + 2] = (int)c; + c >>>= 32; + c += (u[uOff + 3] & M) + (v[vOff + 3] & M); + u[uOff + 3] = (int)c; + v[vOff + 3] = (int)c; + c >>>= 32; + c += (u[uOff + 4] & M) + (v[vOff + 4] & M); + u[uOff + 4] = (int)c; + v[vOff + 4] = (int)c; + c >>>= 32; + return (int)c; + } + + public static void copy(int[] x, int[] z) + { + z[0] = x[0]; + z[1] = x[1]; + z[2] = x[2]; + z[3] = x[3]; + z[4] = x[4]; + } + + public static int[] create() + { + return new int[5]; + } + + public static int[] createExt() + { + return new int[10]; + } + + public static boolean diff(int[] x, int xOff, int[] y, int yOff, int[] z, int zOff) + { + boolean 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 boolean eq(int[] x, int[] y) + { + for (int i = 4; i >= 0; --i) + { + if (x[i] != y[i]) + { + return false; + } + } + return true; + } + + public static int[] fromBigInteger(BigInteger x) + { + if (x.signum() < 0 || x.bitLength() > 160) + { + throw new IllegalArgumentException(); + } + + int[] z = create(); + int i = 0; + while (x.signum() != 0) + { + z[i++] = x.intValue(); + x = x.shiftRight(32); + } + return z; + } + + public static int getBit(int[] x, int bit) + { + if (bit == 0) + { + return x[0] & 1; + } + int w = bit >> 5; + if (w < 0 || w >= 5) + { + return 0; + } + int b = bit & 31; + return (x[w] >>> b) & 1; + } + + public static boolean gte(int[] x, int[] y) + { + for (int i = 4; i >= 0; --i) + { + int x_i = x[i] ^ Integer.MIN_VALUE; + int y_i = y[i] ^ Integer.MIN_VALUE; + if (x_i < y_i) + return false; + if (x_i > y_i) + return true; + } + return true; + } + + public static boolean gte(int[] x, int xOff, int[] y, int yOff) + { + for (int i = 4; i >= 0; --i) + { + int x_i = x[xOff + i] ^ Integer.MIN_VALUE; + int y_i = y[yOff + i] ^ Integer.MIN_VALUE; + if (x_i < y_i) + return false; + if (x_i > y_i) + return true; + } + return true; + } + + public static boolean isOne(int[] x) + { + if (x[0] != 1) + { + return false; + } + for (int i = 1; i < 5; ++i) + { + if (x[i] != 0) + { + return false; + } + } + return true; + } + + public static boolean isZero(int[] x) + { + for (int i = 0; i < 5; ++i) + { + if (x[i] != 0) + { + return false; + } + } + return true; + } + + public static void mul(int[] x, int[] y, int[] zz) + { + long y_0 = y[0] & M; + long y_1 = y[1] & M; + long y_2 = y[2] & M; + long y_3 = y[3] & M; + long y_4 = y[4] & M; + + { + long c = 0, x_0 = x[0] & M; + c += x_0 * y_0; + zz[0] = (int)c; + c >>>= 32; + c += x_0 * y_1; + zz[1] = (int)c; + c >>>= 32; + c += x_0 * y_2; + zz[2] = (int)c; + c >>>= 32; + c += x_0 * y_3; + zz[3] = (int)c; + c >>>= 32; + c += x_0 * y_4; + zz[4] = (int)c; + c >>>= 32; + zz[5] = (int)c; + } + + for (int i = 1; i < 5; ++i) + { + long c = 0, x_i = x[i] & M; + c += x_i * y_0 + (zz[i + 0] & M); + zz[i + 0] = (int)c; + c >>>= 32; + c += x_i * y_1 + (zz[i + 1] & M); + zz[i + 1] = (int)c; + c >>>= 32; + c += x_i * y_2 + (zz[i + 2] & M); + zz[i + 2] = (int)c; + c >>>= 32; + c += x_i * y_3 + (zz[i + 3] & M); + zz[i + 3] = (int)c; + c >>>= 32; + c += x_i * y_4 + (zz[i + 4] & M); + zz[i + 4] = (int)c; + c >>>= 32; + zz[i + 5] = (int)c; + } + } + + public static void mul(int[] x, int xOff, int[] y, int yOff, int[] zz, int zzOff) + { + long y_0 = y[yOff + 0] & M; + long y_1 = y[yOff + 1] & M; + long y_2 = y[yOff + 2] & M; + long y_3 = y[yOff + 3] & M; + long y_4 = y[yOff + 4] & M; + + { + long c = 0, x_0 = x[xOff + 0] & M; + c += x_0 * y_0; + zz[zzOff + 0] = (int)c; + c >>>= 32; + c += x_0 * y_1; + zz[zzOff + 1] = (int)c; + c >>>= 32; + c += x_0 * y_2; + zz[zzOff + 2] = (int)c; + c >>>= 32; + c += x_0 * y_3; + zz[zzOff + 3] = (int)c; + c >>>= 32; + c += x_0 * y_4; + zz[zzOff + 4] = (int)c; + c >>>= 32; + zz[zzOff + 5] = (int)c; + } + + for (int i = 1; i < 5; ++i) + { + ++zzOff; + long c = 0, x_i = x[xOff + i] & M; + c += x_i * y_0 + (zz[zzOff + 0] & M); + zz[zzOff + 0] = (int)c; + c >>>= 32; + c += x_i * y_1 + (zz[zzOff + 1] & M); + zz[zzOff + 1] = (int)c; + c >>>= 32; + c += x_i * y_2 + (zz[zzOff + 2] & M); + zz[zzOff + 2] = (int)c; + c >>>= 32; + c += x_i * y_3 + (zz[zzOff + 3] & M); + zz[zzOff + 3] = (int)c; + c >>>= 32; + c += x_i * y_4 + (zz[zzOff + 4] & M); + zz[zzOff + 4] = (int)c; + c >>>= 32; + zz[zzOff + 5] = (int)c; + } + } + + public static int mulAddTo(int[] x, int[] y, int[] zz) + { + long y_0 = y[0] & M; + long y_1 = y[1] & M; + long y_2 = y[2] & M; + long y_3 = y[3] & M; + long y_4 = y[4] & M; + + long zc = 0; + for (int i = 0; i < 5; ++i) + { + long c = 0, x_i = x[i] & M; + c += x_i * y_0 + (zz[i + 0] & M); + zz[i + 0] = (int)c; + c >>>= 32; + c += x_i * y_1 + (zz[i + 1] & M); + zz[i + 1] = (int)c; + c >>>= 32; + c += x_i * y_2 + (zz[i + 2] & M); + zz[i + 2] = (int)c; + c >>>= 32; + c += x_i * y_3 + (zz[i + 3] & M); + zz[i + 3] = (int)c; + c >>>= 32; + c += x_i * y_4 + (zz[i + 4] & M); + zz[i + 4] = (int)c; + c >>>= 32; + c += zc + (zz[i + 5] & M); + zz[i + 5] = (int)c; + zc = c >>> 32; + } + return (int)zc; + } + + public static int mulAddTo(int[] x, int xOff, int[] y, int yOff, int[] zz, int zzOff) + { + long y_0 = y[yOff + 0] & M; + long y_1 = y[yOff + 1] & M; + long y_2 = y[yOff + 2] & M; + long y_3 = y[yOff + 3] & M; + long y_4 = y[yOff + 4] & M; + + long zc = 0; + for (int i = 0; i < 5; ++i) + { + long c = 0, x_i = x[xOff + i] & M; + c += x_i * y_0 + (zz[zzOff + 0] & M); + zz[zzOff + 0] = (int)c; + c >>>= 32; + c += x_i * y_1 + (zz[zzOff + 1] & M); + zz[zzOff + 1] = (int)c; + c >>>= 32; + c += x_i * y_2 + (zz[zzOff + 2] & M); + zz[zzOff + 2] = (int)c; + c >>>= 32; + c += x_i * y_3 + (zz[zzOff + 3] & M); + zz[zzOff + 3] = (int)c; + c >>>= 32; + c += x_i * y_4 + (zz[zzOff + 4] & M); + zz[zzOff + 4] = (int)c; + c >>>= 32; + c += zc + (zz[zzOff + 5] & M); + zz[zzOff + 5] = (int)c; + zc = c >>> 32; + ++zzOff; + } + return (int)zc; + } + + public static long mul33Add(int w, int[] x, int xOff, int[] y, int yOff, int[] z, int zOff) + { + // assert w >>> 31 == 0; + + long c = 0, wVal = w & M; + long x0 = x[xOff + 0] & M; + c += wVal * x0 + (y[yOff + 0] & M); + z[zOff + 0] = (int)c; + c >>>= 32; + long x1 = x[xOff + 1] & M; + c += wVal * x1 + x0 + (y[yOff + 1] & M); + z[zOff + 1] = (int)c; + c >>>= 32; + long x2 = x[xOff + 2] & M; + c += wVal * x2 + x1 + (y[yOff + 2] & M); + z[zOff + 2] = (int)c; + c >>>= 32; + long x3 = x[xOff + 3] & M; + c += wVal * x3 + x2 + (y[yOff + 3] & M); + z[zOff + 3] = (int)c; + c >>>= 32; + long x4 = x[xOff + 4] & M; + c += wVal * x4 + x3 + (y[yOff + 4] & M); + z[zOff + 4] = (int)c; + c >>>= 32; + c += x4; + return c; + } + + public static int mulWordAddExt(int x, int[] yy, int yyOff, int[] zz, int zzOff) + { + // assert yyOff <= 5; + // assert zzOff <= 5; + long c = 0, xVal = x & M; + c += xVal * (yy[yyOff + 0] & M) + (zz[zzOff + 0] & M); + zz[zzOff + 0] = (int)c; + c >>>= 32; + c += xVal * (yy[yyOff + 1] & M) + (zz[zzOff + 1] & M); + zz[zzOff + 1] = (int)c; + c >>>= 32; + c += xVal * (yy[yyOff + 2] & M) + (zz[zzOff + 2] & M); + zz[zzOff + 2] = (int)c; + c >>>= 32; + c += xVal * (yy[yyOff + 3] & M) + (zz[zzOff + 3] & M); + zz[zzOff + 3] = (int)c; + c >>>= 32; + c += xVal * (yy[yyOff + 4] & M) + (zz[zzOff + 4] & M); + zz[zzOff + 4] = (int)c; + c >>>= 32; + return (int)c; + } + + public static int mul33DWordAdd(int x, long y, int[] z, int zOff) + { + // assert x >>> 31 == 0; + // assert zOff <= 1; + + long c = 0, xVal = x & M; + long y00 = y & M; + c += xVal * y00 + (z[zOff + 0] & M); + z[zOff + 0] = (int)c; + c >>>= 32; + long y01 = y >>> 32; + c += xVal * y01 + y00 + (z[zOff + 1] & M); + z[zOff + 1] = (int)c; + c >>>= 32; + c += y01 + (z[zOff + 2] & M); + z[zOff + 2] = (int)c; + c >>>= 32; + c += (z[zOff + 3] & M); + z[zOff + 3] = (int)c; + c >>>= 32; + return c == 0 ? 0 : Nat.incAt(5, z, zOff, 4); + } + + public static int mul33WordAdd(int x, int y, int[] z, int zOff) + { + // assert x >>> 31 == 0; + // assert zOff <= 2; + + long c = 0, xVal = x & M, yVal = y & M; + c += yVal * xVal + (z[zOff + 0] & M); + z[zOff + 0] = (int)c; + c >>>= 32; + c += yVal + (z[zOff + 1] & M); + z[zOff + 1] = (int)c; + c >>>= 32; + c += (z[zOff + 2] & M); + z[zOff + 2] = (int)c; + c >>>= 32; + return c == 0 ? 0 : Nat.incAt(5, z, zOff, 3); + } + + public static int mulWordDwordAdd(int x, long y, int[] z, int zOff) + { + // assert zOff <= 2; + long c = 0, xVal = x & M; + c += xVal * (y & M) + (z[zOff + 0] & M); + z[zOff + 0] = (int)c; + c >>>= 32; + c += xVal * (y >>> 32) + (z[zOff + 1] & M); + z[zOff + 1] = (int)c; + c >>>= 32; + c += (z[zOff + 2] & M); + z[zOff + 2] = (int)c; + c >>>= 32; + return c == 0 ? 0 : Nat.incAt(5, z, zOff, 3); + } + + public static int mulWordsAdd(int x, int y, int[] z, int zOff) + { + // assert zOff <= 3; + + long c = 0, xVal = x & M, yVal = y & M; + c += yVal * xVal + (z[zOff + 0] & M); + z[zOff + 0] = (int)c; + c >>>= 32; + c += (z[zOff + 1] & M); + z[zOff + 1] = (int)c; + c >>>= 32; + return c == 0 ? 0 : Nat.incAt(5, z, zOff, 2); + } + + public static int mulWord(int x, int[] y, int[] z, int zOff) + { + long c = 0, xVal = x & M; + int i = 0; + do + { + c += xVal * (y[i] & M); + z[zOff + i] = (int)c; + c >>>= 32; + } + while (++i < 5); + return (int)c; + } + + public static void square(int[] x, int[] zz) + { + long x_0 = x[0] & M; + long zz_1; + + int c = 0, w; + { + int i = 4, j = 10; + do + { + long xVal = (x[i--] & M); + long p = xVal * xVal; + zz[--j] = (c << 31) | (int)(p >>> 33); + zz[--j] = (int)(p >>> 1); + c = (int)p; + } + while (i > 0); + + { + long p = x_0 * x_0; + zz_1 = ((c << 31) & M) | (p >>> 33); + zz[0] = (int)p; + c = (int)(p >>> 32) & 1; + } + } + + long x_1 = x[1] & M; + long zz_2 = zz[2] & M; + + { + zz_1 += x_1 * x_0; + w = (int)zz_1; + zz[1] = (w << 1) | c; + c = w >>> 31; + zz_2 += zz_1 >>> 32; + } + + long x_2 = x[2] & M; + long zz_3 = zz[3] & M; + long zz_4 = zz[4] & M; + { + zz_2 += x_2 * x_0; + w = (int)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; + } + + long x_3 = x[3] & M; + long zz_5 = zz[5] & M; + long zz_6 = zz[6] & M; + { + zz_3 += x_3 * x_0; + w = (int)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; + } + + long x_4 = x[4] & M; + long zz_7 = zz[7] & M; + long zz_8 = zz[8] & M; + { + zz_4 += x_4 * x_0; + w = (int)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_7 += (zz_6 >>> 32) + x_4 * x_3; + zz_8 += zz_7 >>> 32; + } + + w = (int)zz_5; + zz[5] = (w << 1) | c; + c = w >>> 31; + w = (int)zz_6; + zz[6] = (w << 1) | c; + c = w >>> 31; + w = (int)zz_7; + zz[7] = (w << 1) | c; + c = w >>> 31; + w = (int)zz_8; + zz[8] = (w << 1) | c; + c = w >>> 31; + w = zz[9] + (int)(zz_8 >> 32); + zz[9] = (w << 1) | c; + } + + public static void square(int[] x, int xOff, int[] zz, int zzOff) + { + long x_0 = x[xOff + 0] & M; + long zz_1; + + int c = 0, w; + { + int i = 4, j = 10; + do + { + long xVal = (x[xOff + i--] & M); + long p = xVal * xVal; + zz[zzOff + --j] = (c << 31) | (int)(p >>> 33); + zz[zzOff + --j] = (int)(p >>> 1); + c = (int)p; + } + while (i > 0); + + { + long p = x_0 * x_0; + zz_1 = ((c << 31) & M) | (p >>> 33); + zz[zzOff + 0] = (int)p; + c = (int)(p >>> 32) & 1; + } + } + + long x_1 = x[xOff + 1] & M; + long zz_2 = zz[zzOff + 2] & M; + + { + zz_1 += x_1 * x_0; + w = (int)zz_1; + zz[zzOff + 1] = (w << 1) | c; + c = w >>> 31; + zz_2 += zz_1 >>> 32; + } + + long x_2 = x[xOff + 2] & M; + long zz_3 = zz[zzOff + 3] & M; + long zz_4 = zz[zzOff + 4] & M; + { + zz_2 += x_2 * x_0; + w = (int)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; + } + + long x_3 = x[xOff + 3] & M; + long zz_5 = zz[zzOff + 5] & M; + long zz_6 = zz[zzOff + 6] & M; + { + zz_3 += x_3 * x_0; + w = (int)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; + } + + long x_4 = x[xOff + 4] & M; + long zz_7 = zz[zzOff + 7] & M; + long zz_8 = zz[zzOff + 8] & M; + { + zz_4 += x_4 * x_0; + w = (int)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_7 += (zz_6 >>> 32) + x_4 * x_3; + zz_8 += zz_7 >>> 32; + } + + w = (int)zz_5; + zz[zzOff + 5] = (w << 1) | c; + c = w >>> 31; + w = (int)zz_6; + zz[zzOff + 6] = (w << 1) | c; + c = w >>> 31; + w = (int)zz_7; + zz[zzOff + 7] = (w << 1) | c; + c = w >>> 31; + w = (int)zz_8; + zz[zzOff + 8] = (w << 1) | c; + c = w >>> 31; + w = zz[zzOff + 9] + (int)(zz_8 >> 32); + zz[zzOff + 9] = (w << 1) | c; + } + + public static int sub(int[] x, int[] y, int[] z) + { + long c = 0; + c += (x[0] & M) - (y[0] & M); + z[0] = (int)c; + c >>= 32; + c += (x[1] & M) - (y[1] & M); + z[1] = (int)c; + c >>= 32; + c += (x[2] & M) - (y[2] & M); + z[2] = (int)c; + c >>= 32; + c += (x[3] & M) - (y[3] & M); + z[3] = (int)c; + c >>= 32; + c += (x[4] & M) - (y[4] & M); + z[4] = (int)c; + c >>= 32; + return (int)c; + } + + public static int sub(int[] x, int xOff, int[] y, int yOff, int[] z, int zOff) + { + long c = 0; + c += (x[xOff + 0] & M) - (y[yOff + 0] & M); + z[zOff + 0] = (int)c; + c >>= 32; + c += (x[xOff + 1] & M) - (y[yOff + 1] & M); + z[zOff + 1] = (int)c; + c >>= 32; + c += (x[xOff + 2] & M) - (y[yOff + 2] & M); + z[zOff + 2] = (int)c; + c >>= 32; + c += (x[xOff + 3] & M) - (y[yOff + 3] & M); + z[zOff + 3] = (int)c; + c >>= 32; + c += (x[xOff + 4] & M) - (y[yOff + 4] & M); + z[zOff + 4] = (int)c; + c >>= 32; + return (int)c; + } + + public static int subBothFrom(int[] x, int[] y, int[] z) + { + long c = 0; + c += (z[0] & M) - (x[0] & M) - (y[0] & M); + z[0] = (int)c; + c >>= 32; + c += (z[1] & M) - (x[1] & M) - (y[1] & M); + z[1] = (int)c; + c >>= 32; + c += (z[2] & M) - (x[2] & M) - (y[2] & M); + z[2] = (int)c; + c >>= 32; + c += (z[3] & M) - (x[3] & M) - (y[3] & M); + z[3] = (int)c; + c >>= 32; + c += (z[4] & M) - (x[4] & M) - (y[4] & M); + z[4] = (int)c; + c >>= 32; + return (int)c; + } + + public static int subFrom(int[] x, int[] z) + { + long c = 0; + c += (z[0] & M) - (x[0] & M); + z[0] = (int)c; + c >>= 32; + c += (z[1] & M) - (x[1] & M); + z[1] = (int)c; + c >>= 32; + c += (z[2] & M) - (x[2] & M); + z[2] = (int)c; + c >>= 32; + c += (z[3] & M) - (x[3] & M); + z[3] = (int)c; + c >>= 32; + c += (z[4] & M) - (x[4] & M); + z[4] = (int)c; + c >>= 32; + return (int)c; + } + + public static int subFrom(int[] x, int xOff, int[] z, int zOff) + { + long c = 0; + c += (z[zOff + 0] & M) - (x[xOff + 0] & M); + z[zOff + 0] = (int)c; + c >>= 32; + c += (z[zOff + 1] & M) - (x[xOff + 1] & M); + z[zOff + 1] = (int)c; + c >>= 32; + c += (z[zOff + 2] & M) - (x[xOff + 2] & M); + z[zOff + 2] = (int)c; + c >>= 32; + c += (z[zOff + 3] & M) - (x[xOff + 3] & M); + z[zOff + 3] = (int)c; + c >>= 32; + c += (z[zOff + 4] & M) - (x[xOff + 4] & M); + z[zOff + 4] = (int)c; + c >>= 32; + return (int)c; + } + + public static BigInteger toBigInteger(int[] x) + { + byte[] bs = new byte[20]; + for (int i = 0; i < 5; ++i) + { + int x_i = x[i]; + if (x_i != 0) + { + Pack.intToBigEndian(x_i, bs, (4 - i) << 2); + } + } + return new BigInteger(1, bs); + } + + public static void zero(int[] z) + { + z[0] = 0; + z[1] = 0; + z[2] = 0; + z[3] = 0; + z[4] = 0; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat320.java b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat320.java new file mode 100644 index 00000000..764f7961 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat320.java @@ -0,0 +1,98 @@ +package org.bouncycastle.math.raw; + +import java.math.BigInteger; + +import org.bouncycastle.util.Pack; + +public abstract class Nat320 +{ + public static void copy64(long[] x, long[] z) + { + z[0] = x[0]; + z[1] = x[1]; + z[2] = x[2]; + z[3] = x[3]; + z[4] = x[4]; + } + + public static long[] create64() + { + return new long[5]; + } + + public static long[] createExt64() + { + return new long[10]; + } + + public static boolean eq64(long[] x, long[] y) + { + for (int i = 4; i >= 0; --i) + { + if (x[i] != y[i]) + { + return false; + } + } + return true; + } + + public static long[] fromBigInteger64(BigInteger x) + { + if (x.signum() < 0 || x.bitLength() > 320) + { + throw new IllegalArgumentException(); + } + + long[] z = create64(); + int i = 0; + while (x.signum() != 0) + { + z[i++] = x.longValue(); + x = x.shiftRight(64); + } + return z; + } + + public static boolean isOne64(long[] x) + { + if (x[0] != 1L) + { + return false; + } + for (int i = 1; i < 5; ++i) + { + if (x[i] != 0L) + { + return false; + } + } + return true; + } + + public static boolean isZero64(long[] x) + { + for (int i = 0; i < 5; ++i) + { + if (x[i] != 0L) + { + return false; + } + } + return true; + } + + public static BigInteger toBigInteger64(long[] x) + { + byte[] bs = new byte[40]; + for (int i = 0; i < 5; ++i) + { + long x_i = x[i]; + if (x_i != 0L) + { + Pack.longToBigEndian(x_i, bs, (4 - i) << 3); + } + } + return new BigInteger(1, bs); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat448.java b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat448.java new file mode 100644 index 00000000..29c18420 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat448.java @@ -0,0 +1,100 @@ +package org.bouncycastle.math.raw; + +import java.math.BigInteger; + +import org.bouncycastle.util.Pack; + +public abstract class Nat448 +{ + public static void copy64(long[] x, long[] 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 long[] create64() + { + return new long[7]; + } + + public static long[] createExt64() + { + return new long[14]; + } + + public static boolean eq64(long[] x, long[] y) + { + for (int i = 6; i >= 0; --i) + { + if (x[i] != y[i]) + { + return false; + } + } + return true; + } + + public static long[] fromBigInteger64(BigInteger x) + { + if (x.signum() < 0 || x.bitLength() > 448) + { + throw new IllegalArgumentException(); + } + + long[] z = create64(); + int i = 0; + while (x.signum() != 0) + { + z[i++] = x.longValue(); + x = x.shiftRight(64); + } + return z; + } + + public static boolean isOne64(long[] x) + { + if (x[0] != 1L) + { + return false; + } + for (int i = 1; i < 7; ++i) + { + if (x[i] != 0L) + { + return false; + } + } + return true; + } + + public static boolean isZero64(long[] x) + { + for (int i = 0; i < 7; ++i) + { + if (x[i] != 0L) + { + return false; + } + } + return true; + } + + public static BigInteger toBigInteger64(long[] x) + { + byte[] bs = new byte[56]; + for (int i = 0; i < 7; ++i) + { + long x_i = x[i]; + if (x_i != 0L) + { + Pack.longToBigEndian(x_i, bs, (6 - i) << 3); + } + } + return new BigInteger(1, bs); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/math/raw/Nat576.java b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat576.java new file mode 100644 index 00000000..d9e06a6b --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/raw/Nat576.java @@ -0,0 +1,102 @@ +package org.bouncycastle.math.raw; + +import java.math.BigInteger; + +import org.bouncycastle.util.Pack; + +public abstract class Nat576 +{ + public static void copy64(long[] x, long[] 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]; + z[8] = x[8]; + } + + public static long[] create64() + { + return new long[9]; + } + + public static long[] createExt64() + { + return new long[18]; + } + + public static boolean eq64(long[] x, long[] y) + { + for (int i = 8; i >= 0; --i) + { + if (x[i] != y[i]) + { + return false; + } + } + return true; + } + + public static long[] fromBigInteger64(BigInteger x) + { + if (x.signum() < 0 || x.bitLength() > 576) + { + throw new IllegalArgumentException(); + } + + long[] z = create64(); + int i = 0; + while (x.signum() != 0) + { + z[i++] = x.longValue(); + x = x.shiftRight(64); + } + return z; + } + + public static boolean isOne64(long[] x) + { + if (x[0] != 1L) + { + return false; + } + for (int i = 1; i < 9; ++i) + { + if (x[i] != 0L) + { + return false; + } + } + return true; + } + + public static boolean isZero64(long[] x) + { + for (int i = 0; i < 9; ++i) + { + if (x[i] != 0L) + { + return false; + } + } + return true; + } + + public static BigInteger toBigInteger64(long[] x) + { + byte[] bs = new byte[72]; + for (int i = 0; i < 9; ++i) + { + long x_i = x[i]; + if (x_i != 0L) + { + Pack.longToBigEndian(x_i, bs, (8 - i) << 3); + } + } + return new BigInteger(1, bs); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/util/Properties.java b/bcprov/src/main/java/org/bouncycastle/util/Properties.java new file mode 100644 index 00000000..96cef357 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/util/Properties.java @@ -0,0 +1,36 @@ +package org.bouncycastle.util; + +import java.security.AccessControlException; +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * Utility method for accessing system properties. + */ +public class Properties +{ + public static boolean isOverrideSet(final String propertyName) + { + try + { + return "true".equals(AccessController.doPrivileged(new PrivilegedAction() + { + // JDK 1.4 compatibility + public Object run() + { + String value = System.getProperty(propertyName); + if (value == null) + { + return null; + } + + return Strings.toLowerCase(value); + } + })); + } + catch (AccessControlException e) + { + return false; + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/util/test/TestRandomEntropySourceProvider.java b/bcprov/src/main/java/org/bouncycastle/util/test/TestRandomEntropySourceProvider.java new file mode 100644 index 00000000..e18dafe4 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/util/test/TestRandomEntropySourceProvider.java @@ -0,0 +1,57 @@ +package org.bouncycastle.util.test; + +import java.security.SecureRandom; + +import org.bouncycastle.crypto.prng.EntropySource; +import org.bouncycastle.crypto.prng.EntropySourceProvider; + +/** + * A class for returning "quick entropy" for testing purposes. + */ +public class TestRandomEntropySourceProvider + implements EntropySourceProvider +{ + private final SecureRandom _sr; + private final boolean _predictionResistant; + + /** + * Create a test entropy source provider. + * + * @param isPredictionResistant boolean indicating if the SecureRandom is based on prediction resistant entropy or not (true if it is). + */ + public TestRandomEntropySourceProvider(boolean isPredictionResistant) + { + _sr = new SecureRandom(); + _predictionResistant = isPredictionResistant; + } + + /** + * Return an entropy source that will create bitsRequired bits of entropy on + * each invocation of getEntropy(). + * + * @param bitsRequired size (in bits) of entropy to be created by the provided source. + * @return an EntropySource that generates bitsRequired bits of entropy on each call to its getEntropy() method. + */ + public EntropySource get(final int bitsRequired) + { + return new EntropySource() + { + public boolean isPredictionResistant() + { + return _predictionResistant; + } + + public byte[] getEntropy() + { + byte[] rv = new byte[(bitsRequired + 7) / 8]; + _sr.nextBytes(rv); + return rv; + } + + public int entropySize() + { + return bitsRequired; + } + }; + } +} |