├── .github └── workflows │ └── codeql-analysis.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── SECURITY.md ├── pom.xml └── sike-java ├── pom.xml └── src ├── main └── java │ └── com │ └── wultra │ └── security │ └── pqc │ └── sike │ ├── crypto │ ├── KeyGenerator.java │ ├── RandomGenerator.java │ ├── Sidh.java │ └── Sike.java │ ├── math │ ├── api │ │ ├── Fp2Element.java │ │ ├── Fp2ElementFactory.java │ │ ├── Fp2Point.java │ │ ├── FpElement.java │ │ ├── Isogeny.java │ │ └── Montgomery.java │ ├── optimized │ │ ├── Fp2PointProjective.java │ │ ├── IsogenyProjective.java │ │ ├── MontgomeryProjective.java │ │ └── fp │ │ │ ├── Fp2ElementFactoryOpti.java │ │ │ ├── Fp2ElementOpti.java │ │ │ ├── FpElementOpti.java │ │ │ └── UnsignedLong.java │ └── reference │ │ ├── Fp2PointAffine.java │ │ ├── IsogenyAffine.java │ │ ├── MontgomeryAffine.java │ │ └── fp │ │ ├── Fp2ElementFactoryRef.java │ │ ├── Fp2ElementRef.java │ │ └── FpElementRef.java │ ├── model │ ├── EncapsulationResult.java │ ├── EncryptedMessage.java │ ├── EvaluatedCurve.java │ ├── ImplementationType.java │ ├── MontgomeryCurve.java │ ├── Party.java │ ├── SidhPrivateKey.java │ ├── SidhPublicKey.java │ └── optimized │ │ └── MontgomeryConstants.java │ ├── param │ ├── SikeParam.java │ ├── SikeParamP434.java │ ├── SikeParamP503.java │ ├── SikeParamP610.java │ └── SikeParamP751.java │ └── util │ ├── ByteEncoding.java │ ├── OctetEncoding.java │ ├── Sha3.java │ └── SideChannelUtil.java └── test ├── java └── com │ └── wultra │ └── security │ └── pqc │ └── sike │ ├── KeyConversionTest.java │ ├── OctetEncodingTest.java │ ├── SidhReferenceTest.java │ ├── SidhTest.java │ ├── SikeDeterministicTest.java │ ├── SikeRandomTest.java │ ├── kat │ ├── KatP434Test.java │ ├── KatP503Test.java │ ├── KatP610Test.java │ ├── KatP751Test.java │ ├── KatTester.java │ ├── model │ │ ├── KatRspFile.java │ │ └── KatRspRecord.java │ └── util │ │ └── CrtDrbgRandom.java │ └── math │ ├── Fp2MathTest.java │ ├── FpMathTest.java │ └── UnsignedLongTest.java └── resources └── kat ├── PQCkemKAT_374.rsp ├── PQCkemKAT_434.rsp ├── PQCkemKAT_524.rsp └── PQCkemKAT_644.rsp /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | name: "CodeQL" 7 | 8 | on: 9 | push: 10 | branches: [develop, master] 11 | pull_request: 12 | # The branches below must be a subset of the branches above 13 | branches: [develop] 14 | schedule: 15 | - cron: '0 3 * * 2' 16 | 17 | jobs: 18 | analyze: 19 | name: Analyze 20 | runs-on: ubuntu-latest 21 | 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | # Override automatic language detection by changing the below list 26 | # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] 27 | language: ['java'] 28 | # Learn more... 29 | # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection 30 | 31 | steps: 32 | - name: Checkout repository 33 | uses: actions/checkout@v2 34 | with: 35 | # We must fetch at least the immediate parents so that if this is 36 | # a pull request then we can checkout the head. 37 | fetch-depth: 2 38 | 39 | # If this run was triggered by a pull request event, then checkout 40 | # the head of the pull request instead of the merge commit. 41 | - run: git checkout HEAD^2 42 | if: ${{ github.event_name == 'pull_request' }} 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v1 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v1 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v1 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/.DS_Store 2 | **/target/ 3 | **/src/main/java/generated/ 4 | **/.springBeans 5 | 6 | ### NetBeans template 7 | **/nbproject/private/ 8 | **/nbproject/ 9 | 10 | ### Eclipse template 11 | **/.settings 12 | **/.project 13 | **/.classpath 14 | 15 | ### JetBrains template 16 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 17 | **/*.iml 18 | ## Directory-based project format: 19 | **/.idea/ 20 | **/.mvn 21 | **/dist/ 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - openjdk11 -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | The security fixes are provided for the following versions: 6 | 7 | | Version | Supported | 8 | | ------- | ------------------ | 9 | | 0.1.x | :white_check_mark: | 10 | 11 | ## Reporting a Vulnerability 12 | 13 | In case you find a security issue, please report the incident at [support@wultra.com](mailto:support@wultra.com). 14 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | sike-java-parent 7 | SIKE Parent Project 8 | 9 | com.wultra.security 10 | sike-java-parent 11 | 0.1.0 12 | pom 13 | 14 | 2020 15 | 16 | 17 | Wultra s.r.o. 18 | https://wultra.com/ 19 | 20 | 21 | 22 | 23 | GNU Affero General Public License v3.0 24 | https://www.gnu.org/licenses/agpl-3.0.en.html 25 | 26 | 27 | 28 | 29 | 30 | Roman Strobl 31 | roman.strobl@wultra.com 32 | 33 | developer 34 | 35 | 36 | 37 | 38 | 39 | scm:git:https://github.com/wultra/sike-java.git 40 | scm:git:https://github.com/wultra/sike-java.git 41 | https://github.com/wultra/sike-java 42 | 43 | 44 | 45 | Github 46 | https://github.com/wultra/sike-java/issues 47 | 48 | 49 | 50 | sike-java 51 | 52 | 53 | 54 | UTF-8 55 | 1.8 56 | 1.8 57 | 58 | 59 | 60 | 61 | 62 | org.apache.maven.plugins 63 | maven-source-plugin 64 | 3.2.1 65 | 66 | 67 | attach-sources 68 | verify 69 | 70 | jar-no-fork 71 | 72 | 73 | 74 | 75 | 76 | org.apache.maven.plugins 77 | maven-javadoc-plugin 78 | 3.1.1 79 | 80 | false 81 | 82 | 83 | 84 | attach-javadocs 85 | 86 | jar 87 | 88 | 89 | 90 | 91 | 92 | org.apache.maven.plugins 93 | maven-deploy-plugin 94 | 2.8.2 95 | 96 | 97 | 98 | 99 | 100 | 101 | release-sign-artifacts 102 | 103 | 104 | performRelease 105 | true 106 | 107 | 108 | 109 | 110 | 111 | org.apache.maven.plugins 112 | maven-gpg-plugin 113 | 1.6 114 | 115 | 116 | sign-artifacts 117 | verify 118 | 119 | sign 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | disable-java8-doclint 129 | 130 | [1.8,) 131 | 132 | 133 | -Xdoclint:none 134 | 135 | 136 | 137 | 138 | 139 | 140 | ossrh 141 | https://oss.sonatype.org/content/repositories/snapshots/ 142 | 143 | 144 | ossrh 145 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 146 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /sike-java/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | sike-java 8 | SIKE Java 9 | sike-java 10 | 0.1.0 11 | 12 | 13 | com.wultra.security 14 | sike-java-parent 15 | 0.1.0 16 | ../pom.xml 17 | 18 | 19 | jar 20 | 21 | 22 | 23 | org.bouncycastle 24 | bcprov-jdk15on 25 | 1.67 26 | 27 | 28 | org.junit.jupiter 29 | junit-jupiter 30 | 5.4.2 31 | test 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/crypto/KeyGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.crypto; 18 | 19 | import com.wultra.security.pqc.sike.model.ImplementationType; 20 | import com.wultra.security.pqc.sike.model.MontgomeryCurve; 21 | import com.wultra.security.pqc.sike.model.Party; 22 | import com.wultra.security.pqc.sike.model.SidhPrivateKey; 23 | import com.wultra.security.pqc.sike.param.SikeParam; 24 | import com.wultra.security.pqc.sike.util.ByteEncoding; 25 | 26 | import java.math.BigInteger; 27 | import java.security.*; 28 | 29 | /** 30 | * SIDH and SIKE key generator. 31 | * 32 | * @author Roman Strobl, roman.strobl@wultra.com 33 | */ 34 | public class KeyGenerator { 35 | 36 | private final SikeParam sikeParam; 37 | private final RandomGenerator randomGenerator; 38 | 39 | /** 40 | * Key generator constructor. 41 | * @param sikeParam SIKE parameters. 42 | */ 43 | public KeyGenerator(SikeParam sikeParam) { 44 | this.sikeParam = sikeParam; 45 | this.randomGenerator = new RandomGenerator(); 46 | } 47 | 48 | /** 49 | * Constructor for key generator with alternative random generator. 50 | * @param sikeParam SIKE parameters. 51 | * @param randomGenerator Alternative random generator. 52 | */ 53 | public KeyGenerator(SikeParam sikeParam, RandomGenerator randomGenerator) { 54 | this.sikeParam = sikeParam; 55 | this.randomGenerator = randomGenerator; 56 | } 57 | 58 | /** 59 | * Generate a key pair. 60 | * @param party Alice or Bob. 61 | * @return Generated key pair. 62 | * @throws GeneralSecurityException Thrown in case cryptography fails. 63 | */ 64 | public KeyPair generateKeyPair(Party party) throws GeneralSecurityException { 65 | PrivateKey privateKey = generatePrivateKey(party); 66 | PublicKey publicKey = derivePublicKey(party, privateKey); 67 | return new KeyPair(publicKey, privateKey); 68 | } 69 | 70 | /** 71 | * Generate a private key. 72 | * @param party Alice or Bob. 73 | * @return Generated key pair. 74 | * @throws GeneralSecurityException Thrown in case cryptography fails. 75 | */ 76 | public PrivateKey generatePrivateKey(Party party) throws GeneralSecurityException { 77 | byte[] s = randomGenerator.generateRandomBytes(sikeParam.getMessageBytes()); 78 | BigInteger randomKey = generateRandomKey(sikeParam, party); 79 | return new SidhPrivateKey(sikeParam, party, randomKey, s); 80 | } 81 | 82 | /** 83 | * Derive public key from a private key. 84 | * @param party Alice or Bob. 85 | * @param privateKey Private key. 86 | * @return Derived public key. 87 | * @throws InvalidKeyException Thrown in case key derivation fails. 88 | */ 89 | public PublicKey derivePublicKey(Party party, PrivateKey privateKey) throws InvalidKeyException { 90 | if (!(privateKey instanceof SidhPrivateKey)) { 91 | throw new InvalidKeyException("Invalid private key"); 92 | } 93 | SidhPrivateKey priv = (SidhPrivateKey) privateKey; 94 | MontgomeryCurve curve; 95 | if (sikeParam.getImplementationType() == ImplementationType.REFERENCE) { 96 | curve = new MontgomeryCurve(sikeParam, sikeParam.getA(), sikeParam.getB()); 97 | } else if (sikeParam.getImplementationType() == ImplementationType.OPTIMIZED) { 98 | curve = new MontgomeryCurve(sikeParam, sikeParam.getA()); 99 | } else { 100 | throw new InvalidParameterException("Unsupported implementation type"); 101 | } 102 | if (party == Party.ALICE) { 103 | return sikeParam.getIsogeny().isoGen2(curve, priv); 104 | } else if (party == Party.BOB) { 105 | return sikeParam.getIsogeny().isoGen3(curve, priv); 106 | } 107 | throw new InvalidParameterException("Invalid party"); 108 | } 109 | 110 | /** 111 | * Generate a random key. 112 | * @param sikeParam SIKE parameters. 113 | * @return Random BigInteger usable as a private key. 114 | * @throws GeneralSecurityException Thrown in case cryptography fails. 115 | */ 116 | private BigInteger generateRandomKey(SikeParam sikeParam, Party party) throws GeneralSecurityException { 117 | if (party == Party.ALICE) { 118 | // random value in [0, 2^eA - 1] 119 | int length = (sikeParam.getBitsA() + 7) / 8; 120 | byte[] randomBytes = randomGenerator.generateRandomBytes(length); 121 | randomBytes[randomBytes.length - 1] &= sikeParam.getMaskA(); 122 | return ByteEncoding.fromByteArray(randomBytes); 123 | } 124 | if (party == Party.BOB) { 125 | // random value in [0, 2^Floor(Log(2,3^eB)) - 1] 126 | int length = (sikeParam.getBitsB() - 1 + 7) / 8; 127 | byte[] randomBytes = randomGenerator.generateRandomBytes(length); 128 | randomBytes[randomBytes.length - 1] &= sikeParam.getMaskB(); 129 | return ByteEncoding.fromByteArray(randomBytes); 130 | } 131 | throw new InvalidParameterException("Invalid party"); 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/crypto/RandomGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.crypto; 18 | 19 | import java.security.NoSuchAlgorithmException; 20 | import java.security.NoSuchProviderException; 21 | import java.security.SecureRandom; 22 | 23 | /** 24 | * Random generator for key material. 25 | * 26 | * @author Roman Strobl, roman.strobl@wultra.com 27 | */ 28 | public class RandomGenerator { 29 | 30 | private volatile SecureRandom secureRandom; 31 | 32 | /** 33 | * Default random generator constructor. 34 | */ 35 | public RandomGenerator() { 36 | } 37 | 38 | /** 39 | * Random generator constructor with provided SecureRandom. 40 | * @param secureRandom SecureRandom implementation to use. 41 | */ 42 | public RandomGenerator(SecureRandom secureRandom) { 43 | this.secureRandom = secureRandom; 44 | } 45 | 46 | /** 47 | * Generate random bytes. 48 | * @param length Length of generated byte array. 49 | * @return Byte array with random bytes. 50 | * @throws NoSuchProviderException Thrown in case Bouncy Castle provider is not available. 51 | * @throws NoSuchAlgorithmException Thrown in case random generator algorithm is not available. 52 | */ 53 | public byte[] generateRandomBytes(int length) throws NoSuchProviderException, NoSuchAlgorithmException { 54 | // Double-check locking, see: https://rules.sonarsource.com/java/tag/multi-threading/RSPEC-2168 55 | SecureRandom localSecureRandom = secureRandom; 56 | 57 | if (localSecureRandom == null) { 58 | synchronized (this) { 59 | localSecureRandom = secureRandom; 60 | if (localSecureRandom == null) 61 | // Use SecureRandom implementation from Bouncy Castle library, it is slower, 62 | // however it reseeds periodically and it is quantum safe. The initialization is lazy 63 | // to allow dynamic Bouncy Castle provider initialization and to allow instantiation 64 | // of this class in fields. 65 | secureRandom = localSecureRandom = SecureRandom.getInstance("DEFAULT", "BC"); 66 | } 67 | } 68 | 69 | byte[] randomBytes = new byte[length]; 70 | localSecureRandom.nextBytes(randomBytes); 71 | return randomBytes; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/crypto/Sidh.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.crypto; 18 | 19 | import com.wultra.security.pqc.sike.math.api.Fp2Element; 20 | import com.wultra.security.pqc.sike.model.Party; 21 | import com.wultra.security.pqc.sike.model.SidhPrivateKey; 22 | import com.wultra.security.pqc.sike.model.SidhPublicKey; 23 | import com.wultra.security.pqc.sike.param.SikeParam; 24 | 25 | import java.security.*; 26 | 27 | /** 28 | * SIDH key exchange. 29 | * 30 | * @author Roman Strobl, roman.strobl@wultra.com 31 | */ 32 | public class Sidh { 33 | 34 | private final SikeParam sikeParam; 35 | 36 | /** 37 | * SIDH key exchange constructor. 38 | * @param sikeParam SIKE parameters. 39 | */ 40 | public Sidh(SikeParam sikeParam) { 41 | this.sikeParam = sikeParam; 42 | } 43 | 44 | /** 45 | * Generate a shared secret isogeny j-invariant. 46 | * @param party Alice or Bob. 47 | * @param privateKey Private key. 48 | * @param publicKey Public key. 49 | * @return Shared secret isogeny j-invariant. 50 | * @throws GeneralSecurityException Thrown in case cryptography fails. 51 | */ 52 | public Fp2Element generateSharedSecret(Party party, PrivateKey privateKey, PublicKey publicKey) throws GeneralSecurityException { 53 | if (!(privateKey instanceof SidhPrivateKey)) { 54 | throw new InvalidKeyException("Invalid private key"); 55 | } 56 | if (!(publicKey instanceof SidhPublicKey)) { 57 | throw new InvalidKeyException("Invalid public key"); 58 | } 59 | SidhPrivateKey priv = (SidhPrivateKey) privateKey; 60 | SidhPublicKey pub = (SidhPublicKey) publicKey; 61 | if (party == Party.ALICE) { 62 | return sikeParam.getIsogeny().isoEx2(sikeParam, priv.getKey(), pub.getPx(), pub.getQx(), pub.getRx()); 63 | } 64 | if (party == Party.BOB) { 65 | return sikeParam.getIsogeny().isoEx3(sikeParam, priv.getKey(), pub.getPx(), pub.getQx(), pub.getRx()); 66 | } 67 | throw new InvalidParameterException("Invalid party"); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/crypto/Sike.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.crypto; 18 | 19 | import com.wultra.security.pqc.sike.math.api.Fp2Element; 20 | import com.wultra.security.pqc.sike.model.*; 21 | import com.wultra.security.pqc.sike.param.SikeParam; 22 | import com.wultra.security.pqc.sike.util.ByteEncoding; 23 | import com.wultra.security.pqc.sike.util.Sha3; 24 | 25 | import java.math.BigInteger; 26 | import java.security.*; 27 | 28 | /** 29 | * SIKE key encapsulation. 30 | * 31 | * @author Roman Strobl, roman.strobl@wultra.com 32 | */ 33 | public class Sike { 34 | 35 | private final SikeParam sikeParam; 36 | private final RandomGenerator randomGenerator; 37 | private final KeyGenerator keyGenerator; 38 | private final Sidh sidh; 39 | 40 | /** 41 | * SIKE key encapsulation constructor. 42 | * @param sikeParam SIKE parameters. 43 | */ 44 | public Sike(SikeParam sikeParam) { 45 | this.sikeParam = sikeParam; 46 | this.randomGenerator = new RandomGenerator(); 47 | keyGenerator = new KeyGenerator(sikeParam); 48 | sidh = new Sidh(sikeParam); 49 | } 50 | 51 | /** 52 | * SIKE key encapsulation constructor with specified SecureRandom. 53 | * @param sikeParam SIKE parameters. 54 | * @param secureRandom SecureRandom to use. 55 | */ 56 | public Sike(SikeParam sikeParam, SecureRandom secureRandom) { 57 | this.sikeParam = sikeParam; 58 | this.randomGenerator = new RandomGenerator(secureRandom); 59 | keyGenerator = new KeyGenerator(sikeParam, randomGenerator); 60 | sidh = new Sidh(sikeParam); 61 | } 62 | 63 | /** 64 | * SIKE encapsulation. 65 | * @param pk3 Bob's public key. 66 | * @return Encapsulation result with shared secret and encrypted message. 67 | * @throws GeneralSecurityException Thrown in case cryptography fails. 68 | */ 69 | public EncapsulationResult encapsulate(PublicKey pk3) throws GeneralSecurityException { 70 | if (!(pk3 instanceof SidhPublicKey)) { 71 | throw new InvalidKeyException("Invalid public key"); 72 | } 73 | byte[] m = randomGenerator.generateRandomBytes(sikeParam.getMessageBytes()); 74 | byte[] r = generateR(m, pk3.getEncoded()); 75 | EncryptedMessage encrypted = encrypt(pk3, m, r); 76 | SidhPublicKey c0Key = (SidhPublicKey) encrypted.getC0(); 77 | byte[] k = generateK(m, c0Key.getEncoded(), encrypted.getC1()); 78 | return new EncapsulationResult(k, encrypted); 79 | } 80 | 81 | /** 82 | * SIKE decapsulation. 83 | * @param sk3 Bob's private key. 84 | * @param pk3 Bob's public key. 85 | * @param encrypted Encrypted message received from Alice. 86 | * @return Shared secret. 87 | * @throws GeneralSecurityException Thrown in case cryptography fails. 88 | */ 89 | public byte[] decapsulate(PrivateKey sk3, PublicKey pk3, EncryptedMessage encrypted) throws GeneralSecurityException { 90 | if (!(sk3 instanceof SidhPrivateKey)) { 91 | throw new InvalidKeyException("Invalid private key"); 92 | } 93 | if (!(pk3 instanceof SidhPublicKey)) { 94 | throw new InvalidKeyException("Invalid public key"); 95 | } 96 | if (encrypted == null) { 97 | throw new InvalidParameterException("Encrypted message is null"); 98 | } 99 | if (encrypted.getC0() == null) { 100 | throw new InvalidParameterException("Invalid parameter c0"); 101 | } 102 | if (encrypted.getC1() == null) { 103 | throw new InvalidParameterException("Invalid parameter c1"); 104 | } 105 | SidhPrivateKey priv3 = (SidhPrivateKey) sk3; 106 | if (priv3.getS() == null) { 107 | throw new InvalidParameterException("Private key cannot be used for decapsulation"); 108 | } 109 | byte[] m = decrypt(sk3, encrypted); 110 | byte[] r = generateR(m, pk3.getEncoded()); 111 | BigInteger modulo = new BigInteger("2").pow(sikeParam.getEA()); 112 | BigInteger key = ByteEncoding.fromByteArray(r).mod(modulo); 113 | PrivateKey rKey = new SidhPrivateKey(sikeParam, Party.ALICE, key); 114 | PublicKey c0Key = keyGenerator.derivePublicKey(Party.ALICE, rKey); 115 | byte[] k; 116 | // The public key equals method runs in constant time 117 | if (c0Key.equals(encrypted.getC0())) { 118 | k = generateK(m, c0Key.getEncoded(), encrypted.getC1()); 119 | } else { 120 | k = generateK(priv3.getS(), c0Key.getEncoded(), encrypted.getC1()); 121 | } 122 | return k; 123 | } 124 | 125 | /** 126 | * Encrypt a message. 127 | * @param pk3 Bob's public key. 128 | * @param m Message to encrypt, the message size must correspond to the SIKE parameter messageBytes. 129 | * @return Encrypted message. 130 | * @throws GeneralSecurityException Thrown in case cryptography fails. 131 | */ 132 | public EncryptedMessage encrypt(PublicKey pk3, byte[] m) throws GeneralSecurityException { 133 | return encrypt(pk3, m, null); 134 | } 135 | 136 | /** 137 | * Encrypt a message. 138 | * @param pk3 Bob's public key. 139 | * @param m Message to encrypt, the message size must correspond to the SIKE parameter messageBytes. 140 | * @param r Optional byte representation of Alice's private key used in SIKE encapsulation. 141 | * @return Encrypted message. 142 | * @throws GeneralSecurityException Thrown in case cryptography fails. 143 | */ 144 | private EncryptedMessage encrypt(PublicKey pk3, byte[] m, byte[] r) throws GeneralSecurityException { 145 | if (!(pk3 instanceof SidhPublicKey)) { 146 | throw new InvalidKeyException("Invalid public key"); 147 | } 148 | if (m == null || m.length != sikeParam.getMessageBytes()) { 149 | throw new InvalidParameterException("Invalid message"); 150 | } 151 | PrivateKey sk2; 152 | if (r == null) { 153 | // Generate ephemeral private key 154 | sk2 = keyGenerator.generatePrivateKey(Party.ALICE); 155 | } else { 156 | // Convert value r into private key 157 | BigInteger modulo = new BigInteger("2").pow(sikeParam.getEA()); 158 | BigInteger key = ByteEncoding.fromByteArray(r).mod(modulo); 159 | sk2 = new SidhPrivateKey(sikeParam, Party.ALICE, key); 160 | } 161 | PublicKey c0 = keyGenerator.derivePublicKey(Party.ALICE, sk2); 162 | Fp2Element j = sidh.generateSharedSecret(Party.ALICE, sk2, pk3); 163 | byte[] h = Sha3.shake256(j.getEncoded(), sikeParam.getMessageBytes()); 164 | byte[] c1 = new byte[sikeParam.getMessageBytes()]; 165 | for (int i = 0; i < sikeParam.getMessageBytes(); i++) { 166 | c1[i] = (byte) (h[i] ^ m[i]); 167 | } 168 | return new EncryptedMessage(c0, c1); 169 | } 170 | 171 | /** 172 | * Decrypt a message. 173 | * @param sk3 Bob's private key. 174 | * @param encrypted Encrypted message received from Alice. 175 | * @return Decrypted message. 176 | * @throws GeneralSecurityException Thrown in case cryptography fails. 177 | */ 178 | public byte[] decrypt(PrivateKey sk3, EncryptedMessage encrypted) throws GeneralSecurityException { 179 | if (!(sk3 instanceof SidhPrivateKey)) { 180 | throw new InvalidKeyException("Invalid private key"); 181 | } 182 | if (encrypted == null) { 183 | throw new InvalidParameterException("Encrypted message is null"); 184 | } 185 | PublicKey c0 = encrypted.getC0(); 186 | if (!(c0 instanceof SidhPublicKey)) { 187 | throw new InvalidKeyException("Invalid public key"); 188 | } 189 | byte[] c1 = encrypted.getC1(); 190 | if (c1 == null) { 191 | throw new InvalidParameterException("Invalid parameter c1"); 192 | } 193 | Fp2Element j = sidh.generateSharedSecret(Party.BOB, sk3, c0); 194 | byte[] h = Sha3.shake256(j.getEncoded(), sikeParam.getMessageBytes()); 195 | byte[] m = new byte[sikeParam.getMessageBytes()]; 196 | for (int i = 0; i < sikeParam.getMessageBytes(); i++) { 197 | m[i] = (byte) (h[i] ^ c1[i]); 198 | } 199 | return m; 200 | } 201 | 202 | /** 203 | * Generate the ephemeral private key r. 204 | * @param m Nonce. 205 | * @param pk3Enc Public key pk3 encoded in bytes. 206 | * @return Ephemeral private key r encoded in bytes. 207 | */ 208 | private byte[] generateR(byte[] m, byte[] pk3Enc) { 209 | byte[] dataR = new byte[(m.length + pk3Enc.length)]; 210 | System.arraycopy(m, 0, dataR, 0, m.length); 211 | System.arraycopy(pk3Enc, 0, dataR, m.length, pk3Enc.length); 212 | return Sha3.shake256(dataR, (sikeParam.getBitsA() + 7) / 8); 213 | } 214 | 215 | /** 216 | * Generate the shared secret K. 217 | * @param m Nonce. 218 | * @param c0 Public key bytes. 219 | * @param c1 Encrypted message bytes. 220 | * @return Shared secret bytes. 221 | */ 222 | private byte[] generateK(byte[] m, byte[] c0, byte[] c1) { 223 | byte[] dataK = new byte[(m.length + c0.length + c1.length)]; 224 | System.arraycopy(m, 0, dataK, 0, m.length); 225 | System.arraycopy(c0, 0, dataK, m.length, c0.length); 226 | System.arraycopy(c1, 0, dataK, m.length + c0.length, c1.length); 227 | return Sha3.shake256(dataK, sikeParam.getCryptoBytes()); 228 | } 229 | 230 | } 231 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/math/api/Fp2Element.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.math.api; 18 | 19 | import java.math.BigInteger; 20 | 21 | /** 22 | * Element of a quadratic extension field F(p^2): x0 + x1*i. 23 | * 24 | * @author Roman Strobl, roman.strobl@wultra.com 25 | */ 26 | public interface Fp2Element { 27 | 28 | /** 29 | * Get the real part of element. 30 | * @return Real part of element. 31 | */ 32 | FpElement getX0(); 33 | 34 | /** 35 | * Get the imaginary part of element. 36 | * @return Imaginary part of element. 37 | */ 38 | FpElement getX1(); 39 | 40 | /** 41 | * Add two elements. 42 | * @param y Other element. 43 | * @return Calculation result. 44 | */ 45 | Fp2Element add(Fp2Element y); 46 | 47 | /** 48 | * Subtract two elements. 49 | * @param y Other element. 50 | * @return Calculation result. 51 | */ 52 | Fp2Element subtract(Fp2Element y); 53 | 54 | /** 55 | * Multiply two elements. 56 | * @param y Other element. 57 | * @return Calculation result. 58 | */ 59 | Fp2Element multiply(Fp2Element y); 60 | 61 | /** 62 | * Multiply by the imaginary part of the element. 63 | * @return Calculation result. 64 | */ 65 | Fp2Element multiplyByI(); 66 | 67 | /** 68 | * Square the element. 69 | * @return Calculation result. 70 | */ 71 | Fp2Element square(); 72 | 73 | /** 74 | * Element exponentiation. 75 | * @param n Exponent 76 | * @return Calculation result. 77 | */ 78 | Fp2Element pow(BigInteger n); 79 | 80 | /** 81 | * Calculate the square root of the element. 82 | * @return Calculation result. 83 | */ 84 | Fp2Element sqrt(); 85 | 86 | /** 87 | * Invert the element. 88 | * @return Calculation result. 89 | */ 90 | Fp2Element inverse(); 91 | 92 | /** 93 | * Negate the element. 94 | * @return Calculation result. 95 | */ 96 | Fp2Element negate(); 97 | 98 | /** 99 | * Get whether the element is the zero element. 100 | * @return Whether the element is the zero element. 101 | */ 102 | boolean isZero(); 103 | 104 | /** 105 | * Copy the element. 106 | * @return Element copy. 107 | */ 108 | Fp2Element copy(); 109 | 110 | /** 111 | * Encode the element in bytes. 112 | * @return Encoded element in bytes. 113 | */ 114 | byte[] getEncoded(); 115 | 116 | /** 117 | * Convert element to octet string. 118 | * @return Octet string. 119 | */ 120 | String toOctetString(); 121 | 122 | } 123 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/math/api/Fp2ElementFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.math.api; 18 | 19 | import java.math.BigInteger; 20 | 21 | /** 22 | * Factory for elements of quadratic extension field F(p^2). 23 | * 24 | * @author Roman Strobl, roman.strobl@wultra.com 25 | */ 26 | public interface Fp2ElementFactory { 27 | 28 | /** 29 | * Construct the zero element 0 + 0*i. 30 | * @return Zero element. 31 | */ 32 | Fp2Element zero(); 33 | 34 | /** 35 | * Construct the one element 1 + 0*i. 36 | * @return One element. 37 | */ 38 | Fp2Element one(); 39 | 40 | /** 41 | * Generate an element with value x0r + 0*i. 42 | * @param x0r Integer value for the real part of element. 43 | * @return Generated element. 44 | */ 45 | Fp2Element generate(BigInteger x0r); 46 | 47 | /** 48 | * Generate an element with value x0r + x0i*i. 49 | * @param x0r Integer value for the real part of element. 50 | * @param x0i Integer value for the imaginary part of element. 51 | * @return Generated element. 52 | */ 53 | Fp2Element generate(BigInteger x0r, BigInteger x0i); 54 | } 55 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/math/api/Fp2Point.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.math.api; 18 | 19 | /** 20 | * Point in F(p^2) with unspecified coordinate system. 21 | * 22 | * @author Roman Strobl, roman.strobl@wultra.com 23 | */ 24 | public interface Fp2Point { 25 | 26 | /** 27 | * Get the x coordinate. 28 | * @return The x coordinate. 29 | */ 30 | Fp2Element getX(); 31 | 32 | /** 33 | * Get the y coordinate. 34 | * @return The y coordinate. 35 | */ 36 | Fp2Element getY(); 37 | 38 | /** 39 | * Get the z coordinate. 40 | * @return The z coordinate. 41 | */ 42 | Fp2Element getZ(); 43 | 44 | /** 45 | * Add two points. 46 | * @param o Other point. 47 | * @return Result of point addition. 48 | */ 49 | Fp2Point add(Fp2Point o); 50 | 51 | /** 52 | * Subtract two points. 53 | * @param o Other point. 54 | * @return Result of point subtraction. 55 | */ 56 | Fp2Point subtract(Fp2Point o); 57 | 58 | /** 59 | * Multiply two points. 60 | * @param o Other point. 61 | * @return Result of point multiplication. 62 | */ 63 | Fp2Point multiply(Fp2Point o); 64 | 65 | /** 66 | * Square a point. 67 | * @return Result of a square operation. 68 | */ 69 | Fp2Point square(); 70 | 71 | /** 72 | * Invert a point. 73 | * @return Result of an inversion operation. 74 | */ 75 | Fp2Point inverse(); 76 | 77 | /** 78 | * Negate a point. 79 | * @return Result of a negation operation. 80 | */ 81 | Fp2Point negate(); 82 | 83 | /** 84 | * Get whether this is a point at infinity. 85 | * @return Whether this is a point at infinity. 86 | */ 87 | boolean isInfinite(); 88 | 89 | /** 90 | * Copy the point. 91 | * @return Point copy. 92 | */ 93 | Fp2Point copy(); 94 | 95 | } 96 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/math/api/FpElement.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.math.api; 18 | 19 | import java.math.BigInteger; 20 | 21 | /** 22 | * Element of an F(p) field with a single coordinate x. 23 | * 24 | * @author Roman Strobl, roman.strobl@wultra.com 25 | */ 26 | public interface FpElement { 27 | 28 | /** 29 | * Get the element value. 30 | * @return the element value. 31 | */ 32 | BigInteger getX(); 33 | 34 | /** 35 | * Add two elements. 36 | * @param o Other element. 37 | * @return Calculation result. 38 | */ 39 | FpElement add(FpElement o); 40 | 41 | /** 42 | * Subtract two elements. 43 | * @param o Other element. 44 | * @return Calculation result. 45 | */ 46 | FpElement subtract(FpElement o); 47 | 48 | /** 49 | * Multiply two elements. 50 | * @param o Other element. 51 | * @return Calculation result. 52 | */ 53 | FpElement multiply(FpElement o); 54 | 55 | /** 56 | * Square the elements. 57 | * @return Calculation result. 58 | */ 59 | FpElement square(); 60 | /** 61 | * Invert the element. 62 | * @return Calculation result. 63 | */ 64 | FpElement inverse(); 65 | 66 | /** 67 | * Negate the element. 68 | * @return Calculation result. 69 | */ 70 | FpElement negate(); 71 | 72 | /** 73 | * Get whether the element is the zero element. 74 | * @return Whether the element is the zero element. 75 | */ 76 | boolean isZero(); 77 | 78 | /** 79 | * Copy the element. 80 | * @return Element copy. 81 | */ 82 | FpElement copy(); 83 | /** 84 | * Encode the element in bytes. 85 | * @return Encoded element in bytes. 86 | */ 87 | byte[] getEncoded(); 88 | 89 | /** 90 | * Convert element to octet string. 91 | * @return Octet string. 92 | */ 93 | String toOctetString(); 94 | 95 | } 96 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/math/api/Isogeny.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.math.api; 18 | 19 | import com.wultra.security.pqc.sike.model.EvaluatedCurve; 20 | import com.wultra.security.pqc.sike.model.MontgomeryCurve; 21 | import com.wultra.security.pqc.sike.model.SidhPrivateKey; 22 | import com.wultra.security.pqc.sike.model.SidhPublicKey; 23 | import com.wultra.security.pqc.sike.param.SikeParam; 24 | 25 | /** 26 | * Elliptic curve isogeny operations on Montgomery curves. 27 | * 28 | * @author Roman Strobl, roman.strobl@wultra.com 29 | */ 30 | public interface Isogeny { 31 | 32 | /** 33 | * Compute a 2-isogenous curve. 34 | * @param curve Current curve. 35 | * @param p2 Generator point with order equal to 2 on current curve. 36 | * @return A 2-isogenous curve. 37 | */ 38 | MontgomeryCurve curve2Iso(MontgomeryCurve curve, Fp2Point p2); 39 | 40 | /** 41 | * Compute a 3-isogenous curve. 42 | * @param curve Current curve. 43 | * @param p3 Generator point with order equal to 3 on current curve. 44 | * @return A 3-isogenous curve. 45 | */ 46 | MontgomeryCurve curve3Iso(MontgomeryCurve curve, Fp2Point p3); 47 | 48 | /** 49 | * Compute a 4-isogenous curve. 50 | * @param curve Current curve. 51 | * @param p4 Generator point with order equal to 4 on current curve. 52 | * @return A 4-isogenous curve. 53 | */ 54 | MontgomeryCurve curve4Iso(MontgomeryCurve curve, Fp2Point p4); 55 | 56 | /** 57 | * Evaluate a 2-isogeny at a point. 58 | * @param q Point to be evaluated. 59 | * @param p2 Generator point with order equal to 2 on current curve. 60 | * @return Evaluated point. 61 | */ 62 | Fp2Point eval2Iso(Fp2Point q, Fp2Point p2); 63 | 64 | /** 65 | * Evaluate a 3-isogeny at a point. 66 | * @param curve Current curve. 67 | * @param q Point to be evaluated. 68 | * @param p3 Generator point with order equal to 3 on current curve. 69 | * @return Evaluated point. 70 | */ 71 | Fp2Point eval3Iso(MontgomeryCurve curve, Fp2Point q, Fp2Point p3); 72 | 73 | /** 74 | * Evaluate a 4-isogeny at a point. 75 | * @param curve Current curve. 76 | * @param q Point to be evaluated. 77 | * @param p4 Generator point with order equal to 4 on current curve. 78 | * @return Evaluated point. 79 | */ 80 | Fp2Point eval4Iso(MontgomeryCurve curve, Fp2Point q, Fp2Point p4); 81 | 82 | /** 83 | * Compute a 2^eA-isogeny and evaluate points p and q on this isogeny. 84 | * @param curve Current curve. 85 | * @param s Generator point with order equal to 2^eA on current curve. 86 | * @param points Points to be evaluated on computed isogeny (optional). 87 | * @return Curve corresponding to a 2^eA-isogeny. 88 | */ 89 | EvaluatedCurve iso2e(MontgomeryCurve curve, Fp2Point s, Fp2Point ... points); 90 | 91 | /** 92 | * Compute a 3^eB-isogeny and evaluate points p and q on this isogeny. 93 | * @param curve Current curve. 94 | * @param s Generator point with order equal to 3^eB on current curve. 95 | * @param points Points to be evaluated on computed isogeny (optional). 96 | * @return Curve corresponding to a 3^eB-isogeny. 97 | */ 98 | EvaluatedCurve iso3e(MontgomeryCurve curve, Fp2Point s, Fp2Point ... points); 99 | 100 | /** 101 | * Derive a public key from a private key for Alice. 102 | * @param curve Starting curve. 103 | * @param privateKey Private key. 104 | * @return Derived public key. 105 | */ 106 | SidhPublicKey isoGen2(MontgomeryCurve curve, SidhPrivateKey privateKey); 107 | 108 | /** 109 | * Derive a public key from a private key for Bob. 110 | * @param curve Starting curve. 111 | * @param privateKey Private key. 112 | * @return Derived public key. 113 | */ 114 | SidhPublicKey isoGen3(MontgomeryCurve curve, SidhPrivateKey privateKey); 115 | 116 | /** 117 | * Compute a shared secret isogeny j-invariant in the 2-torsion. 118 | * @param sikeParam SIKE parameters. 119 | * @param sk2 Private key. 120 | * @param p2 The x coordinate of public point P. 121 | * @param q2 The x coordinate of public point Q. 122 | * @param r2 The x coordinate of public point R. 123 | * @return Shared secret isogeny j-invariant. 124 | */ 125 | Fp2Element isoEx2(SikeParam sikeParam, byte[] sk2, Fp2Element p2, Fp2Element q2, Fp2Element r2); 126 | 127 | /** 128 | * Compute a shared secret isogeny j-invariant in the 3-torsion. 129 | * @param sikeParam SIKE parameters. 130 | * @param sk3 Private key. 131 | * @param p3 The x coordinate of public point P. 132 | * @param q3 The x coordinate of public point Q. 133 | * @param r3 The x coordinate of public point R. 134 | * @return Shared secret isogeny j-invariant. 135 | */ 136 | Fp2Element isoEx3(SikeParam sikeParam, byte[] sk3, Fp2Element p3, Fp2Element q3, Fp2Element r3); 137 | 138 | } 139 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/math/api/Montgomery.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.math.api; 18 | 19 | import com.wultra.security.pqc.sike.model.MontgomeryCurve; 20 | import com.wultra.security.pqc.sike.param.SikeParam; 21 | 22 | /** 23 | * Elliptic curve mathematics on Montgomery curves. A common interface for all implementation types 24 | * 25 | * @author Roman Strobl, roman.strobl@wultra.com 26 | */ 27 | public interface Montgomery { 28 | 29 | /** 30 | * Double a point. 31 | * @param curve Current curve. 32 | * @param p Point on the curve. 33 | * @return Calculated new point. 34 | */ 35 | Fp2Point xDbl(MontgomeryCurve curve, Fp2Point p); 36 | 37 | /** 38 | * Triple a point. 39 | * @param curve Current curve. 40 | * @param p Point on the curve. 41 | * @return Calculated new point. 42 | */ 43 | Fp2Point xTpl(MontgomeryCurve curve, Fp2Point p); 44 | 45 | /** 46 | * Repeated doubling of a point. 47 | * @param curve Current curve. 48 | * @param p Point on the curve. 49 | * @param e Number of iterations. 50 | * @return Calculated new point. 51 | */ 52 | Fp2Point xDble(MontgomeryCurve curve, Fp2Point p, int e); 53 | 54 | /** 55 | * Repeated trippling of a point. 56 | * @param curve Current curve. 57 | * @param p Point on the curve. 58 | * @param e Number of iterations. 59 | * @return Calculated new point. 60 | */ 61 | Fp2Point xTple(MontgomeryCurve curve, Fp2Point p, int e); 62 | 63 | /** 64 | * Calculate a j-invariant of a curve. 65 | * @param curve Current curve. 66 | * @return Calculated j-invariant. 67 | */ 68 | Fp2Element jInv(MontgomeryCurve curve); 69 | 70 | /** 71 | * Recover the Montgomery curve coefficient a. 72 | * @param sikeParam SIKE parameters. 73 | * @param px The x coordinate of point P. 74 | * @param qx The x coordinate of point Q. 75 | * @param rx The x coordinate of point R. 76 | * @return Recovered coefficient a. 77 | */ 78 | Fp2Element getA(SikeParam sikeParam, Fp2Element px, Fp2Element qx, Fp2Element rx); 79 | 80 | } 81 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/math/optimized/Fp2PointProjective.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.math.optimized; 18 | 19 | import com.wultra.security.pqc.sike.math.api.Fp2Element; 20 | import com.wultra.security.pqc.sike.math.api.Fp2Point; 21 | 22 | import java.security.InvalidParameterException; 23 | import java.util.Objects; 24 | 25 | /** 26 | * Point with projective coordinates [x:z] in F(p^2). 27 | * 28 | * @author Roman Strobl, roman.strobl@wultra.com 29 | */ 30 | public class Fp2PointProjective implements Fp2Point { 31 | 32 | private final Fp2Element x; 33 | private final Fp2Element z; 34 | 35 | /** 36 | * Projective point constructor. 37 | * @param x The x element. 38 | * @param z The z element. 39 | */ 40 | public Fp2PointProjective(Fp2Element x, Fp2Element z) { 41 | this.x = x; 42 | this.z = z; 43 | } 44 | 45 | @Override 46 | public Fp2Element getX() { 47 | return x; 48 | } 49 | 50 | /** 51 | * The y coordinate is not defined in projective coordinate system. 52 | */ 53 | @Override 54 | public Fp2Element getY() { 55 | throw new InvalidParameterException("Invalid point coordinate"); 56 | } 57 | 58 | @Override 59 | public Fp2Element getZ() { 60 | return z; 61 | } 62 | 63 | @Override 64 | public Fp2Point add(Fp2Point o) { 65 | return new Fp2PointProjective(x.add(o.getX()), z.add(o.getY())); 66 | } 67 | 68 | @Override 69 | public Fp2Point subtract(Fp2Point o) { 70 | return new Fp2PointProjective(x.subtract(o.getX()), z.subtract(o.getY())); 71 | } 72 | 73 | @Override 74 | public Fp2Point multiply(Fp2Point o) { 75 | return new Fp2PointProjective(x.multiply(o.getX()), z.multiply(o.getY())); 76 | } 77 | 78 | @Override 79 | public Fp2Point square() { 80 | return multiply(this); 81 | } 82 | 83 | @Override 84 | public Fp2Point inverse() { 85 | return new Fp2PointProjective(x.inverse(), z.inverse()); 86 | } 87 | 88 | @Override 89 | public Fp2Point negate() { 90 | throw new RuntimeException("Not implemented yet"); 91 | } 92 | 93 | @Override 94 | public boolean isInfinite() { 95 | throw new RuntimeException("Not implemented yet"); 96 | } 97 | 98 | @Override 99 | public Fp2Point copy() { 100 | return new Fp2PointProjective(x.copy(), z.copy()); 101 | } 102 | 103 | @Override 104 | public String toString() { 105 | return "(" + x.toString() + ", " + z.toString() + ")"; 106 | } 107 | 108 | @Override 109 | public boolean equals(Object o) { 110 | if (this == o) return true; 111 | if (o == null || getClass() != o.getClass()) return false; 112 | Fp2PointProjective that = (Fp2PointProjective) o; 113 | // Use & to avoid timing attacks 114 | return x.equals(that.x) & z.equals(that.z); 115 | } 116 | 117 | @Override 118 | public int hashCode() { 119 | return Objects.hash(x, z); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/math/optimized/MontgomeryProjective.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.math.optimized; 18 | 19 | import com.wultra.security.pqc.sike.math.api.Fp2Element; 20 | import com.wultra.security.pqc.sike.math.api.Fp2Point; 21 | import com.wultra.security.pqc.sike.math.api.Montgomery; 22 | import com.wultra.security.pqc.sike.math.optimized.fp.FpElementOpti; 23 | import com.wultra.security.pqc.sike.model.MontgomeryCurve; 24 | import com.wultra.security.pqc.sike.model.optimized.MontgomeryConstants; 25 | import com.wultra.security.pqc.sike.param.SikeParam; 26 | 27 | /** 28 | * Optimized elliptic curve mathematics on Montgomery curves with projective coordinates. 29 | * 30 | * @author Roman Strobl, roman.strobl@wultra.com 31 | */ 32 | public class MontgomeryProjective implements Montgomery { 33 | 34 | @Override 35 | public Fp2Point xDbl(MontgomeryCurve curve, Fp2Point p) { 36 | MontgomeryConstants constants = curve.getOptimizedConstants(); 37 | Fp2Element a24plus = constants.getA24plus(); 38 | Fp2Element c24 = constants.getC24(); 39 | Fp2Element t0, t1, p2x, p2z; 40 | t0 = p.getX().subtract(p.getZ()); 41 | t1 = p.getX().add(p.getZ()); 42 | t0 = t0.square(); 43 | t1 = t1.square(); 44 | p2z = c24.multiply(t0); 45 | p2x = p2z.multiply(t1); 46 | t1 = t1.subtract(t0); 47 | t0 = a24plus.multiply(t1); 48 | p2z = p2z.add(t0); 49 | p2z = p2z.multiply(t1); 50 | return new Fp2PointProjective(p2x, p2z); 51 | } 52 | 53 | @Override 54 | public Fp2Point xTpl(MontgomeryCurve curve, Fp2Point p) { 55 | MontgomeryConstants constants = curve.getOptimizedConstants(); 56 | Fp2Element a24plus = constants.getA24plus(); 57 | Fp2Element a24minus = constants.getA24minus(); 58 | Fp2Element t0, t1, t2, t3, t4, t5, t6, p3x, p3z; 59 | t0 = p.getX().subtract(p.getZ()); 60 | t2 = t0.square(); 61 | t1 = p.getX().add(p.getZ()); 62 | t3 = t1.square(); 63 | t4 = t1.add(t0); 64 | t0 = t1.subtract(t0); 65 | t1 = t4.square(); 66 | t1 = t1.subtract(t3); 67 | t1 = t1.subtract(t2); 68 | // Multiplicands are swapped for faster computation as it is done in official C implementation. 69 | t5 = a24plus.multiply(t3); 70 | t3 = t5.multiply(t3); 71 | t6 = t2.multiply(a24minus); 72 | t2 = t2.multiply(t6); 73 | t3 = t2.subtract(t3); 74 | t2 = t5.subtract(t6); 75 | t1 = t2.multiply(t1); 76 | t2 = t3.add(t1); 77 | t2 = t2.square(); 78 | p3x = t2.multiply(t4); 79 | t1 = t3.subtract(t1); 80 | t1 = t1.square(); 81 | p3z = t1.multiply(t0); 82 | return new Fp2PointProjective(p3x, p3z); 83 | } 84 | 85 | @Override 86 | public Fp2Point xDble(MontgomeryCurve curve, Fp2Point p, int e) { 87 | Fp2Point pAp = p; 88 | for (int i = 0; i < e; i++) { 89 | pAp = xDbl(curve, pAp); 90 | } 91 | return pAp; 92 | } 93 | 94 | @Override 95 | public Fp2Point xTple(MontgomeryCurve curve, Fp2Point p, int e) { 96 | Fp2Point pAp = p; 97 | for (int i = 0; i < e; i++) { 98 | pAp = xTpl(curve, pAp); 99 | } 100 | return pAp; 101 | } 102 | 103 | @Override 104 | public Fp2Element jInv(MontgomeryCurve curve) { 105 | Fp2Element a = curve.getA(); 106 | MontgomeryConstants constants = curve.getOptimizedConstants(); 107 | Fp2Element c = constants.getC(); 108 | Fp2Element t0, t1, j; 109 | j = a.square(); 110 | t1 = c.square(); 111 | t0 = t1.add(t1); 112 | t0 = j.subtract(t0); 113 | t0 = t0.subtract(t1); 114 | j = t0.subtract(t1); 115 | t1 = t1.square(); 116 | j = j.multiply(t1); 117 | t0 = t0.add(t0); 118 | t0 = t0.add(t0); 119 | t1 = t0.square(); 120 | t0 = t0.multiply(t1); 121 | t0 = t0.add(t0); 122 | t0 = t0.add(t0); 123 | j = j.inverse(); 124 | j = t0.multiply(j); 125 | return j; 126 | } 127 | 128 | @Override 129 | public Fp2Element getA(SikeParam sikeParam, Fp2Element px, Fp2Element qx, Fp2Element rx) { 130 | Fp2Element t0, t1, ap; 131 | t1 = px.add(qx); 132 | t0 = px.multiply(qx); 133 | ap = rx.multiply(t1); 134 | ap = ap.add(t0); 135 | t0 = t0.multiply(rx); 136 | ap = ap.subtract(sikeParam.getFp2ElementFactory().one()); 137 | t0 = t0.add(t0); 138 | t1 = t1.add(rx); 139 | t0 = t0.add(t0); 140 | ap = ap.square(); 141 | t0 = t0.inverse(); 142 | ap = ap.multiply(t0); 143 | ap = ap.subtract(t1); 144 | return ap; 145 | } 146 | 147 | /** 148 | * Combined coordinate doubling and differential addition. 149 | * @param p Point P. 150 | * @param q Point Q. 151 | * @param r Point P - Q. 152 | * @return Points P2 and P + Q. 153 | */ 154 | private Fp2Point[] xDblAdd(Fp2Point p, Fp2Point q, Fp2Point r, Fp2Element a24plus) { 155 | Fp2Element t0, t1, t2, p2x, p2z, pqx, pqz; 156 | t0 = p.getX().add(p.getZ()); 157 | t1 = p.getX().subtract(p.getZ()); 158 | p2x = t0.square(); 159 | t2 = q.getX().subtract(q.getZ()); 160 | pqx = q.getX().add(q.getZ()); 161 | t0 = t0.multiply(t2); 162 | p2z = t1.square(); 163 | t1 = t1.multiply(pqx); 164 | t2 = p2x.subtract(p2z); 165 | p2x = p2x.multiply(p2z); 166 | pqx = a24plus.multiply(t2); 167 | pqz = t0.subtract(t1); 168 | p2z = pqx.add(p2z); 169 | pqx = t0.add(t1); 170 | p2z = p2z.multiply(t2); 171 | pqz = pqz.square(); 172 | pqx = pqx.square(); 173 | pqz = r.getX().multiply(pqz); 174 | pqx = r.getZ().multiply(pqx); 175 | Fp2PointProjective p2 = new Fp2PointProjective(p2x, p2z); 176 | Fp2PointProjective pq = new Fp2PointProjective(pqx, pqz); 177 | return new Fp2PointProjective[]{p2, pq}; 178 | } 179 | 180 | /** 181 | * Three point Montgomery ladder. 182 | * @param curve Current curve. 183 | * @param m Scalar value. 184 | * @param px The x coordinate of point P. 185 | * @param qx The x coordinate of point Q. 186 | * @param rx The x coordinate of point P - Q. 187 | * @param bits Number of bits in field elements. 188 | * @return Calculated new point. 189 | */ 190 | public Fp2Point ladder3Pt(MontgomeryCurve curve, byte[] m, Fp2Element px, Fp2Element qx, Fp2Element rx, int bits) { 191 | SikeParam sikeParam = curve.getSikeParam(); 192 | Fp2Element a = curve.getA(); 193 | Fp2Point r0 = new Fp2PointProjective(qx.copy(), sikeParam.getFp2ElementFactory().one()); 194 | Fp2Point r1 = new Fp2PointProjective(px.copy(), sikeParam.getFp2ElementFactory().one()); 195 | Fp2Point r2 = new Fp2PointProjective(rx.copy(), sikeParam.getFp2ElementFactory().one()); 196 | 197 | // Compute A + 2C / 4C 198 | Fp2Element c = curve.getOptimizedConstants().getC(); 199 | Fp2Element c2 = c.add(c); 200 | Fp2Element aPlus2c = a.add(c2); 201 | Fp2Element c4 = c2.add(c2); 202 | Fp2Element c4Inv = c4.inverse(); 203 | Fp2Element aPlus2cOver4c = aPlus2c.multiply(c4Inv); 204 | 205 | byte prevBit = 0; 206 | for (int i = 0; i < bits; i++) { 207 | byte bit = (byte) (m[i >>> 3] >>> (i & 7) & 1); 208 | byte swap = (byte) (prevBit ^ bit); 209 | prevBit = bit; 210 | condSwap(sikeParam, r1, r2, swap); 211 | Fp2Point[] points = xDblAdd(r0, r2, r1, aPlus2cOver4c); 212 | r0 = points[0].copy(); 213 | r2 = points[1].copy(); 214 | } 215 | condSwap(sikeParam, r1, r2, prevBit); 216 | return r1; 217 | } 218 | 219 | /** 220 | * Swap two points conditionally. 221 | * @param sikeParam SIKE parameters. 222 | * @param p Point p. 223 | * @param q Point q. 224 | * @param mask Swap condition, if zero swap is not performed. 225 | */ 226 | private void condSwap(SikeParam sikeParam, Fp2Point p, Fp2Point q, long mask) { 227 | FpElementOpti.conditionalSwap(sikeParam, (FpElementOpti) p.getX().getX0(), (FpElementOpti) q.getX().getX0(), mask); 228 | FpElementOpti.conditionalSwap(sikeParam, (FpElementOpti) p.getX().getX1(), (FpElementOpti) q.getX().getX1(), mask); 229 | FpElementOpti.conditionalSwap(sikeParam, (FpElementOpti) p.getZ().getX0(), (FpElementOpti) q.getZ().getX0(), mask); 230 | FpElementOpti.conditionalSwap(sikeParam, (FpElementOpti) p.getZ().getX1(), (FpElementOpti) q.getZ().getX1(), mask); 231 | } 232 | 233 | } 234 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/math/optimized/fp/Fp2ElementFactoryOpti.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.math.optimized.fp; 18 | 19 | import com.wultra.security.pqc.sike.math.api.Fp2Element; 20 | import com.wultra.security.pqc.sike.math.api.Fp2ElementFactory; 21 | import com.wultra.security.pqc.sike.param.SikeParam; 22 | 23 | import java.math.BigInteger; 24 | 25 | /** 26 | * Factory for optimized elements of quadratic extension field F(p^2). 27 | * 28 | * @author Roman Strobl, roman.strobl@wultra.com 29 | */ 30 | public class Fp2ElementFactoryOpti implements Fp2ElementFactory { 31 | 32 | private final SikeParam sikeParam; 33 | 34 | /** 35 | * Fp2Element factory constructor for optimized elements. 36 | * @param sikeParam SIKE parameters. 37 | */ 38 | public Fp2ElementFactoryOpti(SikeParam sikeParam) { 39 | this.sikeParam = sikeParam; 40 | } 41 | 42 | @Override 43 | public Fp2Element zero() { 44 | return new Fp2ElementOpti(sikeParam, BigInteger.ZERO, BigInteger.ZERO); 45 | } 46 | 47 | @Override 48 | public Fp2Element one() { 49 | return new Fp2ElementOpti(sikeParam, BigInteger.ONE, BigInteger.ZERO); 50 | } 51 | 52 | @Override 53 | public Fp2Element generate(BigInteger x0r) { 54 | return new Fp2ElementOpti(sikeParam, x0r, BigInteger.ZERO); 55 | } 56 | 57 | @Override 58 | public Fp2Element generate(BigInteger x0r, BigInteger x0i) { 59 | return new Fp2ElementOpti(sikeParam, x0r, x0i); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/math/optimized/fp/UnsignedLong.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.math.optimized.fp; 18 | 19 | /** 20 | * Mathematical functions for the 64-bit unsigned integer type. 21 | * All methods are constant time to prevent side channel attacks. 22 | * 23 | * @author Roman Strobl, roman.strobl@wultra.com 24 | */ 25 | public class UnsignedLong { 26 | 27 | private UnsignedLong() { 28 | 29 | } 30 | 31 | /** 32 | * Add two unsigned long values with carry. 33 | * @param x First unsigned long value. 34 | * @param y Second unsigned long value. 35 | * @param carry Carry set to 1 in case of overflow, otherwise 0. 36 | * @return Unsigned long addition result. 37 | */ 38 | public static long[] add(long x, long y, long carry) { 39 | long sum = x + y + carry; 40 | long carryOut = (((x & y) | ((x | y) & ~sum)) >>> 63); 41 | return new long[]{sum, carryOut}; 42 | } 43 | 44 | /** 45 | * Subtract two unsigned long values with borrow. 46 | * @param x First unsigned long value. 47 | * @param y Second unsigned long value. 48 | * @param borrow Borrow set to 1 in case of underflow, otherwise 0. 49 | * @return Unsigned long subtraction result. 50 | */ 51 | public static long[] sub(long x, long y, long borrow) { 52 | long sub = x - y; 53 | long borrowOut = (borrow & (1 ^ ((sub | -sub) >>> 63))) | (x ^ ((x ^ y) | ((x - y) ^ y))) >>> 63; 54 | long diff = sub - borrow; 55 | return new long[]{diff, borrowOut}; 56 | } 57 | 58 | /** 59 | * Multiply two unsigned long values. 60 | * @param x First unsigned long value. 61 | * @param y Second unsigned long value. 62 | * @return Result of multiplication of two unsigned longs, represented by their hi and lo values, each 64-bit. 63 | */ 64 | public static long[] mul(long x, long y) { 65 | long al, bl, ah, bh, albl, albh, ahbl, ahbh; 66 | long res1, res2, res3; 67 | long carry, temp; 68 | long maskL = 0L, maskH; 69 | long lo, hi; 70 | 71 | maskL = (~maskL) >>> 32; 72 | maskH = ~maskL; 73 | 74 | al = x & maskL; 75 | ah = x >>> 32; 76 | bl = y & maskL; 77 | bh = y >>> 32; 78 | 79 | albl = al * bl; 80 | albh = al * bh; 81 | ahbl = ah * bl; 82 | ahbh = ah * bh; 83 | lo = albl & maskL; 84 | 85 | res1 = albl >>> 32; 86 | res2 = ahbl & maskL; 87 | res3 = albh & maskL; 88 | temp = res1 + res2 + res3; 89 | carry = temp >>> 32; 90 | lo ^= temp << 32; 91 | 92 | res1 = ahbl >>> 32; 93 | res2 = albh >>> 32; 94 | res3 = ahbh & maskL; 95 | temp = res1 + res2 + res3 + carry; 96 | hi = temp & maskL; 97 | carry = temp & maskH; 98 | hi ^= (ahbh & maskH) + carry; 99 | return new long[]{hi, lo}; 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/math/reference/Fp2PointAffine.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.math.reference; 18 | 19 | import com.wultra.security.pqc.sike.math.api.Fp2Element; 20 | import com.wultra.security.pqc.sike.math.api.Fp2Point; 21 | import com.wultra.security.pqc.sike.param.SikeParam; 22 | 23 | import java.security.InvalidParameterException; 24 | import java.util.Objects; 25 | 26 | /** 27 | * Point with affine coordinates [x:y] in F(p^2). 28 | * 29 | * @author Roman Strobl, roman.strobl@wultra.com 30 | */ 31 | public class Fp2PointAffine implements Fp2Point { 32 | 33 | private final Fp2Element x; 34 | private final Fp2Element y; 35 | 36 | /** 37 | * Affine point constructor. 38 | * @param x The x element. 39 | * @param y The y element. 40 | */ 41 | public Fp2PointAffine(Fp2Element x, Fp2Element y) { 42 | this.x = x; 43 | this.y = y; 44 | } 45 | 46 | /** 47 | * Construct point at infinity. 48 | * @param sikeParam SIKE parameters. 49 | * @return Point at infinity. 50 | */ 51 | public static Fp2Point infinity(SikeParam sikeParam) { 52 | Fp2Element zero = sikeParam.getFp2ElementFactory().zero(); 53 | return new Fp2PointAffine(zero, zero); 54 | } 55 | 56 | @Override 57 | public Fp2Element getX() { 58 | return x; 59 | } 60 | 61 | @Override 62 | public Fp2Element getY() { 63 | return y; 64 | } 65 | 66 | /** 67 | * The z coordinate is not defined in affine coordinate system. 68 | */ 69 | @Override 70 | public Fp2Element getZ() { 71 | throw new InvalidParameterException("Invalid point coordinate"); 72 | } 73 | 74 | @Override 75 | public Fp2Point add(Fp2Point o) { 76 | return new Fp2PointAffine(x.add(o.getX()), y.add(o.getY())); 77 | } 78 | 79 | @Override 80 | public Fp2Point subtract(Fp2Point o) { 81 | return new Fp2PointAffine(x.subtract(o.getX()), y.subtract(o.getY())); 82 | } 83 | 84 | @Override 85 | public Fp2Point multiply(Fp2Point o) { 86 | return new Fp2PointAffine(x.multiply(o.getX()), y.multiply(o.getY())); 87 | } 88 | 89 | @Override 90 | public Fp2Point square() { 91 | return multiply(this); 92 | } 93 | 94 | @Override 95 | public Fp2Point inverse() { 96 | return new Fp2PointAffine(x.inverse(), y.inverse()); 97 | } 98 | 99 | @Override 100 | public Fp2Point negate() { 101 | return new Fp2PointAffine(x, y.negate()); 102 | } 103 | 104 | @Override 105 | public boolean isInfinite() { 106 | return y.isZero(); 107 | } 108 | 109 | @Override 110 | public Fp2Point copy() { 111 | return new Fp2PointAffine(x.copy(), y.copy()); 112 | } 113 | 114 | public String toString() { 115 | return "(" + x.toString() + ", " + y.toString() + ")"; 116 | } 117 | 118 | @Override 119 | public boolean equals(Object o) { 120 | if (this == o) return true; 121 | if (o == null || getClass() != o.getClass()) return false; 122 | Fp2PointAffine that = (Fp2PointAffine) o; 123 | return x.equals(that.x) && 124 | y.equals(that.y); 125 | } 126 | 127 | @Override 128 | public int hashCode() { 129 | return Objects.hash(x, y); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/math/reference/MontgomeryAffine.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.math.reference; 18 | 19 | import com.wultra.security.pqc.sike.math.api.Fp2Element; 20 | import com.wultra.security.pqc.sike.math.api.Fp2Point; 21 | import com.wultra.security.pqc.sike.math.api.Montgomery; 22 | import com.wultra.security.pqc.sike.model.EvaluatedCurve; 23 | import com.wultra.security.pqc.sike.model.MontgomeryCurve; 24 | import com.wultra.security.pqc.sike.param.SikeParam; 25 | 26 | import java.math.BigInteger; 27 | 28 | /** 29 | * Reference elliptic curve mathematics on Montgomery curves with affine coordinates. 30 | * 31 | * @author Roman Strobl, roman.strobl@wultra.com 32 | */ 33 | public class MontgomeryAffine implements Montgomery { 34 | 35 | @Override 36 | public Fp2Point xDbl(MontgomeryCurve curve, Fp2Point p) { 37 | if (p.isInfinite()) { 38 | return p; 39 | } 40 | 41 | Fp2Element t0, t1, t2, x2p, y2p; 42 | Fp2Element b = curve.getB(); 43 | SikeParam sikeParam = curve.getSikeParam(); 44 | 45 | t0 = p.getX().square(); 46 | t1 = t0.add(t0); 47 | t2 = sikeParam.getFp2ElementFactory().one(); 48 | t0 = t0.add(t1); 49 | t1 = curve.getA().multiply(p.getX()); 50 | t1 = t1.add(t1); 51 | t0 = t0.add(t1); 52 | t0 = t0.add(t2); 53 | t1 = b.multiply(p.getY()); 54 | t1 = t1.add(t1); 55 | t1 = t1.inverse(); 56 | t0 = t0.multiply(t1); 57 | t1 = t0.square(); 58 | t2 = b.multiply(t1); 59 | t2 = t2.subtract(curve.getA()); 60 | t2 = t2.subtract(p.getX()); 61 | t2 = t2.subtract(p.getX()); 62 | t1 = t0.multiply(t1); 63 | t1 = b.multiply(t1); 64 | t1 = t1.add(p.getY()); 65 | y2p = p.getX().add(p.getX()); 66 | y2p = y2p.add(p.getX()); 67 | y2p = y2p.add(curve.getA()); 68 | y2p = y2p.multiply(t0); 69 | y2p = y2p.subtract(t1); 70 | x2p = t2; 71 | return new Fp2PointAffine(x2p, y2p); 72 | } 73 | 74 | @Override 75 | public Fp2Point xTpl(MontgomeryCurve curve, Fp2Point p) { 76 | Fp2Point p2 = xDbl(curve, p); 77 | return xAdd(curve, p, p2); 78 | } 79 | 80 | @Override 81 | public Fp2Point xDble(MontgomeryCurve curve, Fp2Point p, int e) { 82 | Fp2Point pAp = p; 83 | for (int i = 0; i < e; i++) { 84 | pAp = xDbl(curve, pAp); 85 | } 86 | return pAp; 87 | } 88 | 89 | @Override 90 | public Fp2Point xTple(MontgomeryCurve curve, Fp2Point p, int e) { 91 | Fp2Point pAp = p; 92 | for (int i = 0; i < e; i++) { 93 | pAp = xTpl(curve, pAp); 94 | } 95 | return pAp; 96 | } 97 | 98 | @Override 99 | public Fp2Element jInv(MontgomeryCurve curve) { 100 | Fp2Element t0, t1, j; 101 | Fp2Element a = curve.getA(); 102 | SikeParam sikeParam = curve.getSikeParam(); 103 | t0 = a.square(); 104 | j = sikeParam.getFp2ElementFactory().generate(new BigInteger("3")); 105 | j = t0.subtract(j); 106 | t1 = j.square(); 107 | j = j.multiply(t1); 108 | j = j.add(j); 109 | j = j.add(j); 110 | j = j.add(j); 111 | j = j.add(j); 112 | j = j.add(j); 113 | j = j.add(j); 114 | j = j.add(j); 115 | j = j.add(j); 116 | t1 = sikeParam.getFp2ElementFactory().generate(new BigInteger("4")); 117 | t0 = t0.subtract(t1); 118 | t0 = t0.inverse(); 119 | j = j.multiply(t0); 120 | return j; 121 | } 122 | 123 | @Override 124 | public Fp2Element getA(SikeParam sikeParam, Fp2Element px, Fp2Element qx, Fp2Element rx) { 125 | Fp2Element t0, t1, a; 126 | t1 = px.add(qx); 127 | t0 = px.multiply(qx); 128 | a = rx.multiply(t1); 129 | a = a.add(t0); 130 | t0 = t0.multiply(rx); 131 | a = a.subtract(sikeParam.getFp2ElementFactory().one()); 132 | t0 = t0.add(t0); 133 | t1 = t1.add(rx); 134 | t0 = t0.add(t0); 135 | a = a.square(); 136 | t0 = t0.inverse(); 137 | a = a.multiply(t0); 138 | a = a.subtract(t1); 139 | return a; 140 | } 141 | 142 | /** 143 | * Double-and-add scalar multiplication. 144 | * @param curve Current curve. 145 | * @param m Scalar value. 146 | * @param p Point on the curve. 147 | * @param bits Number of bits in field elements. 148 | * @return Calculated new point. 149 | */ 150 | public Fp2Point doubleAndAdd(MontgomeryCurve curve, BigInteger m, Fp2Point p, int bits) { 151 | SikeParam sikeParam = curve.getSikeParam(); 152 | Fp2Point q = Fp2PointAffine.infinity(sikeParam); 153 | for (int i = bits - 1; i >= 0; i--) { 154 | q = xDbl(curve, q); 155 | if (m.testBit(i)) { 156 | q = xAdd(curve, q, p); 157 | } 158 | } 159 | return q; 160 | } 161 | 162 | /** 163 | * Adding of two points. 164 | * @param curve Current curve. 165 | * @param p First point on the curve. 166 | * @param q Second point on the curve. 167 | * @return Calculated new point. 168 | */ 169 | public Fp2Point xAdd(MontgomeryCurve curve, Fp2Point p, Fp2Point q) { 170 | if (p.isInfinite()) { 171 | return q; 172 | } 173 | if (q.isInfinite()) { 174 | return p; 175 | } 176 | if (p.equals(q)) { 177 | return xDbl(curve, p); 178 | } 179 | if (p.equals(q.negate())) { 180 | SikeParam sikeParam = curve.getSikeParam(); 181 | return Fp2PointAffine.infinity(sikeParam); 182 | } 183 | 184 | Fp2Element t0, t1, t2, xpq, ypq; 185 | Fp2Element b = curve.getB(); 186 | 187 | t0 = q.getY().subtract(p.getY()); 188 | t1 = q.getX().subtract(p.getX()); 189 | t1 = t1.inverse(); 190 | t0 = t0.multiply(t1); 191 | t1 = t0.square(); 192 | t2 = p.getX().add(p.getX()); 193 | t2 = t2.add(q.getX()); 194 | t2 = t2.add(curve.getA()); 195 | t2 = t2.multiply(t0); 196 | t0 = t0.multiply(t1); 197 | t0 = b.multiply(t0); 198 | t0 = t0.add(p.getY()); 199 | t0 = t2.subtract(t0); 200 | t1 = b.multiply(t1); 201 | t1 = t1.subtract(curve.getA()); 202 | t1 = t1.subtract(p.getX()); 203 | xpq = t1.subtract(q.getX()); 204 | ypq = t0; 205 | return new Fp2PointAffine(xpq, ypq); 206 | } 207 | 208 | /** 209 | * Recover the point R = P - Q. 210 | * @param curve Current curve. 211 | * @param p Point P. 212 | * @param q Point Q. 213 | * @return Calculated point R. 214 | */ 215 | public Fp2Point getXr(MontgomeryCurve curve, Fp2Point p, Fp2Point q) { 216 | Fp2Point qNeg = new Fp2PointAffine(q.getX(), q.getY().negate()); 217 | return xAdd(curve, p, qNeg); 218 | } 219 | 220 | /** 221 | * Recover the curve and points P and Q. 222 | * @param sikeParam SIKE parameters. 223 | * @param px The x coordinate of point P. 224 | * @param qx The x coordinate of point Q. 225 | * @param rx The x coordinate of point R. 226 | * @return A recovered curve and points P and Q. 227 | */ 228 | public EvaluatedCurve getYpYqAB(SikeParam sikeParam, Fp2Element px, Fp2Element qx, Fp2Element rx) { 229 | Fp2Element b, t1, t2, py, qy; 230 | Fp2Element a = getA(sikeParam, px, qx, rx); 231 | b = sikeParam.getFp2ElementFactory().one(); 232 | MontgomeryCurve curve = new MontgomeryCurve(sikeParam, a, b); 233 | t1 = px.square(); 234 | t2 = px.multiply(t1); 235 | t1 = a.multiply(t1); 236 | t1 = t2.add(t1); 237 | t1 = t1.add(px); 238 | py = t1.sqrt(); 239 | t1 = qx.square(); 240 | t2 = qx.multiply(t1); 241 | t1 = a.multiply(t1); 242 | t1 = t2.add(t1); 243 | t1 = t1.add(qx); 244 | qy = t1.sqrt(); 245 | Fp2Point p = new Fp2PointAffine(px, py); 246 | Fp2Point q1 = new Fp2PointAffine(qx, qy.negate()); 247 | Fp2Point t = xAdd(curve, p, q1); 248 | if (!t.getX().equals(rx)) { 249 | qy = qy.negate(); 250 | } 251 | Fp2Point q = new Fp2PointAffine(qx, qy); 252 | return new EvaluatedCurve(curve, p, q); 253 | } 254 | 255 | } 256 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/math/reference/fp/Fp2ElementFactoryRef.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.math.reference.fp; 18 | 19 | import com.wultra.security.pqc.sike.math.api.Fp2Element; 20 | import com.wultra.security.pqc.sike.math.api.Fp2ElementFactory; 21 | import com.wultra.security.pqc.sike.param.SikeParam; 22 | 23 | import java.math.BigInteger; 24 | 25 | /** 26 | * Factory for reference elements of quadratic extension field F(p^2). 27 | * 28 | * @author Roman Strobl, roman.strobl@wultra.com 29 | */ 30 | public class Fp2ElementFactoryRef implements Fp2ElementFactory { 31 | 32 | private final SikeParam sikeParam; 33 | 34 | /** 35 | * Fp2Element factory constructor for reference elements. 36 | * @param sikeParam SIKE parameters. 37 | */ 38 | public Fp2ElementFactoryRef(SikeParam sikeParam) { 39 | this.sikeParam = sikeParam; 40 | } 41 | 42 | @Override 43 | public Fp2Element zero() { 44 | return new Fp2ElementRef(sikeParam, BigInteger.ZERO, BigInteger.ZERO); 45 | } 46 | 47 | @Override 48 | public Fp2Element one() { 49 | return new Fp2ElementRef(sikeParam, BigInteger.ONE, BigInteger.ZERO); 50 | } 51 | 52 | @Override 53 | public Fp2Element generate(BigInteger x0r) { 54 | return new Fp2ElementRef(sikeParam, x0r, BigInteger.ZERO); 55 | } 56 | 57 | @Override 58 | public Fp2Element generate(BigInteger x0r, BigInteger x0i) { 59 | return new Fp2ElementRef(sikeParam, x0r, x0i); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/math/reference/fp/Fp2ElementRef.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.math.reference.fp; 18 | 19 | import com.wultra.security.pqc.sike.math.api.Fp2Element; 20 | import com.wultra.security.pqc.sike.math.api.FpElement; 21 | import com.wultra.security.pqc.sike.param.SikeParam; 22 | 23 | import java.math.BigInteger; 24 | import java.util.Objects; 25 | 26 | /** 27 | * Element of a quadratic extension field F(p^2): x0 + x1*i. 28 | * 29 | * @author Roman Strobl, roman.strobl@wultra.com 30 | */ 31 | public class Fp2ElementRef implements Fp2Element { 32 | 33 | private final FpElement x0; 34 | private final FpElement x1; 35 | 36 | private final SikeParam sikeParam; 37 | 38 | /** 39 | * The F(p^2) field element constructor for given F(p) elements. 40 | * @param sikeParam SIKE parameters. 41 | * @param x0 The x0 real F(p) element. 42 | * @param x1 The x1 imaginary F(p) element. 43 | */ 44 | public Fp2ElementRef(SikeParam sikeParam, FpElement x0, FpElement x1) { 45 | this.sikeParam = sikeParam; 46 | this.x0 = x0.copy(); 47 | this.x1 = x1.copy(); 48 | } 49 | 50 | /** 51 | * The F(p^2) field element constructor for given BigInteger values. 52 | * @param sikeParam SIKE parameters. 53 | * @param x0b The x0 real F(p) element. 54 | * @param x1b The x1 imaginary F(p) element. 55 | */ 56 | public Fp2ElementRef(SikeParam sikeParam, BigInteger x0b, BigInteger x1b) { 57 | this.sikeParam = sikeParam; 58 | this.x0 = new FpElementRef(sikeParam, x0b); 59 | this.x1 = new FpElementRef(sikeParam, x1b); 60 | } 61 | 62 | /** 63 | * Get the real part of element. 64 | * @return Real part of element. 65 | */ 66 | public FpElement getX0() { 67 | return x0; 68 | } 69 | 70 | /** 71 | * Get the imaginary part of element. 72 | * @return Imaginary part of element. 73 | */ 74 | public FpElement getX1() { 75 | return x1; 76 | } 77 | 78 | /** 79 | * Add two elements. 80 | * @param y Other element. 81 | * @return Calculation result. 82 | */ 83 | public Fp2Element add(Fp2Element y) { 84 | // y = (x0 + i*x1) + (y0 + i*y1) = x0 + y0 + i*(x1 + y1) 85 | FpElement r, i; 86 | 87 | r = x0.add(y.getX0()); 88 | i = x1.add(y.getX1()); 89 | return new Fp2ElementRef(sikeParam, r, i); 90 | } 91 | 92 | /** 93 | * Subtract two elements. 94 | * @param y Other element. 95 | * @return Calculation result. 96 | */ 97 | public Fp2Element subtract(Fp2Element y) { 98 | // y = (x0 + i*x1) - (y0 + i*y1) = x0 - y0 + i*(x1 - y1) 99 | FpElement r, i; 100 | 101 | r = x0.subtract(y.getX0()); 102 | i = x1.subtract(y.getX1()); 103 | return new Fp2ElementRef(sikeParam, r, i); 104 | } 105 | 106 | /** 107 | * Multiply two elements. 108 | * @param y Other element. 109 | * @return Calculation result. 110 | */ 111 | public Fp2Element multiply(Fp2Element y) { 112 | // y = (x0 + i*x1) * (y0 + i*y1) = x0y0 - x1y1 + i*(x0y1 + x1y0) 113 | FpElement r1, r2, r, i1, i2, i; 114 | 115 | r1 = x0.multiply(y.getX0()); 116 | r2 = x1.multiply(y.getX1()); 117 | r = r1.subtract(r2); 118 | 119 | i1 = x0.multiply(y.getX1()); 120 | i2 = x1.multiply(y.getX0()); 121 | i = i1.add(i2); 122 | 123 | return new Fp2ElementRef(sikeParam, r, i); 124 | } 125 | 126 | /** 127 | * Multiply by the imaginary part of the element. 128 | * @return Calculation result. 129 | */ 130 | public Fp2Element multiplyByI() { 131 | return new Fp2ElementRef(sikeParam, x1.negate(), x0.copy()); 132 | } 133 | 134 | /** 135 | * Square the element. 136 | * @return Calculation result. 137 | */ 138 | public Fp2Element square() { 139 | return multiply(this); 140 | } 141 | 142 | /** 143 | * Element exponentiation. 144 | * @param n Exponent 145 | * @return Calculation result. 146 | */ 147 | public Fp2Element pow(BigInteger n) { 148 | if (n.compareTo(BigInteger.ZERO) < 0) { 149 | throw new ArithmeticException("Negative exponent"); 150 | } 151 | if (n.compareTo(BigInteger.ZERO) == 0) { 152 | return sikeParam.getFp2ElementFactory().one(); 153 | } 154 | if (n.compareTo(BigInteger.ONE) == 0) { 155 | return copy(); 156 | } 157 | BigInteger e = n; 158 | Fp2Element base = copy(); 159 | Fp2Element result = sikeParam.getFp2ElementFactory().one(); 160 | while (e.compareTo(BigInteger.ZERO) > 0) { 161 | if (e.testBit(0)) { 162 | result = result.multiply(base); 163 | } 164 | e = e.shiftRight(1); 165 | base = base.square(); 166 | } 167 | return result; 168 | } 169 | 170 | /** 171 | * Calculate the square root of the element. 172 | * @return Calculation result. 173 | */ 174 | public Fp2Element sqrt() { 175 | // TODO - compare performance with reference C implementation, consider replacing algorithm 176 | if (isZero()) { 177 | return sikeParam.getFp2ElementFactory().zero(); 178 | } 179 | if (!isQuadraticResidue()) { 180 | throw new ArithmeticException("The square root of a quadratic non-residue cannot be computed"); 181 | } 182 | BigInteger prime = sikeParam.getPrime(); 183 | if (prime.mod(new BigInteger("4")).compareTo(new BigInteger("3")) != 0) { 184 | throw new ArithmeticException("Field prime mod 4 is not 3"); 185 | } 186 | Fp2Element a1, a2; 187 | Fp2Element neg1 = sikeParam.getFp2ElementFactory().one(); 188 | BigInteger p = prime; 189 | p = p.shiftRight(2); 190 | a1 = copy(); 191 | a1 = a1.pow(p); 192 | a2 = copy(); 193 | a2 = a2.multiply(a1); 194 | a1 = a1.multiply(a2); 195 | if (a1.equals(neg1)) { 196 | return a2.multiplyByI(); 197 | } 198 | p = prime; 199 | p = p.shiftRight(1); 200 | a1 = a1.add(sikeParam.getFp2ElementFactory().one()); 201 | a1 = a1.pow(p); 202 | return a1.multiply(a2); 203 | } 204 | 205 | /** 206 | * Get whether the element is a quadratic residue modulo prime. 207 | * @return Whether the element is a quadratic residue. 208 | */ 209 | public boolean isQuadraticResidue() { 210 | Fp2Element base = copy(); 211 | BigInteger p = sikeParam.getPrime(); 212 | p = p.multiply(p); 213 | p = p.subtract(BigInteger.ONE); 214 | p = p.shiftRight(1); 215 | base = base.pow(p); 216 | return base.equals(sikeParam.getFp2ElementFactory().one()); 217 | } 218 | 219 | /** 220 | * Invert the element. 221 | * @return Calculation result. 222 | */ 223 | public Fp2ElementRef inverse() { 224 | FpElement t0, t1, o0, o1; 225 | t0 = x0.square(); 226 | t1 = x1.square(); 227 | t0 = t0.add(t1); 228 | t0 = t0.inverse(); 229 | o1 = x1.negate(); 230 | o0 = x0.multiply(t0); 231 | o1 = o1.multiply(t0); 232 | return new Fp2ElementRef(sikeParam, o0, o1); 233 | } 234 | 235 | /** 236 | * Negate the element. 237 | * @return Calculation result. 238 | */ 239 | public Fp2Element negate() { 240 | return new Fp2ElementRef(sikeParam, x0.negate(), x1.negate()); 241 | } 242 | 243 | /** 244 | * Get whether the element is the zero element. 245 | * @return Whether the element is the zero element. 246 | */ 247 | public boolean isZero() { 248 | return x0.isZero() && x1.isZero(); 249 | } 250 | 251 | /** 252 | * Copy the element. 253 | * @return Element copy. 254 | */ 255 | public Fp2Element copy() { 256 | return new Fp2ElementRef(sikeParam, new FpElementRef(sikeParam, x0.getX()), new FpElementRef(sikeParam, x1.getX())); 257 | } 258 | 259 | /** 260 | * Encode the element in bytes. 261 | * @return Encoded element in bytes. 262 | */ 263 | public byte[] getEncoded() { 264 | byte[] x0Encoded = x0.getEncoded(); 265 | byte[] x1Encoded = x1.getEncoded(); 266 | byte[] encoded = new byte[x0Encoded.length + x1Encoded.length]; 267 | System.arraycopy(x0Encoded, 0, encoded, 0, x0Encoded.length); 268 | System.arraycopy(x1Encoded, 0, encoded, x0Encoded.length, x1Encoded.length); 269 | return encoded; 270 | } 271 | 272 | /** 273 | * Convert element to octet string. 274 | * @return Octet string. 275 | */ 276 | public String toOctetString() { 277 | return x0.toOctetString() + x1.toOctetString(); 278 | } 279 | 280 | @Override 281 | public String toString() { 282 | return x1 + "i" + " + " + x0; 283 | } 284 | 285 | @Override 286 | public boolean equals(Object o) { 287 | if (this == o) return true; 288 | if (o == null || getClass() != o.getClass()) return false; 289 | Fp2ElementRef that = (Fp2ElementRef) o; 290 | return sikeParam.getPrime().equals(that.sikeParam.getPrime()) 291 | && x0.equals(that.x0) 292 | && x1.equals(that.x1); 293 | } 294 | 295 | @Override 296 | public int hashCode() { 297 | return Objects.hash(sikeParam, x0, x1); 298 | } 299 | } 300 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/math/reference/fp/FpElementRef.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.math.reference.fp; 18 | 19 | import com.wultra.security.pqc.sike.math.api.FpElement; 20 | import com.wultra.security.pqc.sike.param.SikeParam; 21 | import com.wultra.security.pqc.sike.util.ByteEncoding; 22 | import com.wultra.security.pqc.sike.util.OctetEncoding; 23 | 24 | import java.math.BigInteger; 25 | import java.util.Objects; 26 | 27 | /** 28 | * Element of an F(p) field with a single coordinate x. 29 | * 30 | * @author Roman Strobl, roman.strobl@wultra.com 31 | */ 32 | public class FpElementRef implements FpElement { 33 | 34 | private final BigInteger x; 35 | 36 | private final SikeParam sikeParam; 37 | 38 | /** 39 | * The F(p^) field element constructor for given BigInteger value. 40 | * @param sikeParam Field prime. 41 | * @param x BigInteger value. 42 | */ 43 | public FpElementRef(SikeParam sikeParam, BigInteger x) { 44 | this.sikeParam = sikeParam; 45 | this.x = x.mod(sikeParam.getPrime()); 46 | } 47 | 48 | /** 49 | * Get the element value. 50 | * @return the element value. 51 | */ 52 | public BigInteger getX() { 53 | return x; 54 | } 55 | 56 | /** 57 | * Get the field prime. 58 | * @return Field prime. 59 | */ 60 | public BigInteger getPrime() { 61 | return sikeParam.getPrime(); 62 | } 63 | 64 | /** 65 | * Add two elements. 66 | * @param o Other element. 67 | * @return Calculation result. 68 | */ 69 | public FpElement add(FpElement o) { 70 | return new FpElementRef(sikeParam, x.add(o.getX()).mod(getPrime())); 71 | } 72 | 73 | /** 74 | * Subtract two elements. 75 | * @param o Other element. 76 | * @return Calculation result. 77 | */ 78 | public FpElement subtract(FpElement o) { 79 | return new FpElementRef(sikeParam, x.subtract(o.getX()).mod(getPrime())); 80 | } 81 | 82 | /** 83 | * Multiply two elements. 84 | * @param o Other element. 85 | * @return Calculation result. 86 | */ 87 | public FpElement multiply(FpElement o) { 88 | return new FpElementRef(sikeParam, x.multiply(o.getX()).mod(getPrime())); 89 | } 90 | 91 | /** 92 | * Square the elements. 93 | * @return Calculation result. 94 | */ 95 | public FpElement square() { 96 | return multiply(this); 97 | } 98 | 99 | /** 100 | * Invert the element. 101 | * @return Calculation result. 102 | */ 103 | public FpElement inverse() { 104 | return new FpElementRef(sikeParam, x.modInverse(getPrime())); 105 | } 106 | 107 | /** 108 | * Negate the element. 109 | * @return Calculation result. 110 | */ 111 | public FpElement negate() { 112 | return new FpElementRef(sikeParam, getPrime().subtract(x)); 113 | } 114 | 115 | /** 116 | * Get whether the element is the zero element. 117 | * @return Whether the element is the zero element. 118 | */ 119 | public boolean isZero() { 120 | return BigInteger.ZERO.equals(x); 121 | } 122 | 123 | /** 124 | * Copy the element. 125 | * @return Element copy. 126 | */ 127 | public FpElement copy() { 128 | return new FpElementRef(sikeParam, x); 129 | } 130 | 131 | /** 132 | * Encode the element in bytes. 133 | * @return Encoded element in bytes. 134 | */ 135 | public byte[] getEncoded() { 136 | int primeSize = (sikeParam.getPrime().bitLength() + 7) / 8; 137 | return ByteEncoding.toByteArray(x, primeSize); 138 | } 139 | 140 | /** 141 | * Convert element to octet string. 142 | * @return Octet string. 143 | */ 144 | public String toOctetString() { 145 | int primeSize = (sikeParam.getPrime().bitLength() + 7) / 8; 146 | return OctetEncoding.toOctetString(x, primeSize); 147 | } 148 | 149 | @Override 150 | public String toString() { 151 | return x.toString(); 152 | } 153 | 154 | @Override 155 | public boolean equals(Object o) { 156 | if (this == o) return true; 157 | if (o == null || getClass() != o.getClass()) return false; 158 | FpElementRef fpElement = (FpElementRef) o; 159 | return getPrime().equals(fpElement.getPrime()) 160 | && x.equals(fpElement.x); 161 | } 162 | 163 | @Override 164 | public int hashCode() { 165 | return Objects.hash(getPrime(), x); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/model/EncapsulationResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.model; 18 | 19 | import com.wultra.security.pqc.sike.util.SideChannelUtil; 20 | 21 | import java.util.Arrays; 22 | import java.util.Objects; 23 | 24 | /** 25 | * SIKE encapsulation result. 26 | * 27 | * @author Roman Strobl, roman.strobl@wultra.com 28 | */ 29 | public class EncapsulationResult { 30 | 31 | private final byte[] secret; 32 | private final EncryptedMessage encryptedMessage; 33 | 34 | /** 35 | * SIKE encapsulation result constructor. 36 | * @param secret Shared secret. 37 | * @param encryptedMessage Encrypted message to be sent to Bob. 38 | */ 39 | public EncapsulationResult(byte[] secret, EncryptedMessage encryptedMessage) { 40 | this.secret = secret; 41 | this.encryptedMessage = encryptedMessage; 42 | } 43 | 44 | /** 45 | * Get the shared secret. 46 | * @return Shared secret. 47 | */ 48 | public byte[] getSecret() { 49 | return secret; 50 | } 51 | 52 | /** 53 | * Get the encrypted message. 54 | * @return Encypted message. 55 | */ 56 | public EncryptedMessage getEncryptedMessage() { 57 | return encryptedMessage; 58 | } 59 | 60 | @Override 61 | public boolean equals(Object o) { 62 | if (this == o) return true; 63 | if (o == null || getClass() != o.getClass()) return false; 64 | EncapsulationResult that = (EncapsulationResult) o; 65 | // Use constant time comparison to avoid timing attacks 66 | return SideChannelUtil.constantTimeAreEqual(secret, that.secret) & 67 | encryptedMessage.equals(that.encryptedMessage); 68 | } 69 | 70 | @Override 71 | public int hashCode() { 72 | return Objects.hash(encryptedMessage, Arrays.hashCode(secret)); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/model/EncryptedMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.model; 18 | 19 | import com.wultra.security.pqc.sike.param.SikeParam; 20 | import com.wultra.security.pqc.sike.util.SideChannelUtil; 21 | 22 | import java.security.InvalidParameterException; 23 | import java.security.PublicKey; 24 | import java.util.Arrays; 25 | import java.util.Objects; 26 | 27 | /** 28 | * SIKE encrypted message. 29 | * 30 | * @author Roman Strobl, roman.strobl@wultra.com 31 | */ 32 | public class EncryptedMessage { 33 | 34 | private final PublicKey c0; 35 | private final byte[] c1; 36 | 37 | /** 38 | * SIKE encrypted message constructor from public key and encrypted data. 39 | * @param c0 Alice's public key. 40 | * @param c1 Encrypted data. 41 | */ 42 | public EncryptedMessage(PublicKey c0, byte[] c1) { 43 | this.c0 = c0; 44 | this.c1 = c1; 45 | } 46 | 47 | /** 48 | * SIKE encrypted message constructor from message encoded into byte array. 49 | * @param sikeParam SIKE parameters. 50 | * @param bytes Encrypted message encoded into byte array. 51 | */ 52 | public EncryptedMessage(SikeParam sikeParam, byte[] bytes) { 53 | if (sikeParam == null) { 54 | throw new InvalidParameterException("Invalid parameter sikeParam"); 55 | } 56 | int primeSize = (sikeParam.getPrime().bitLength() + 7) / 8; 57 | int pubKeySize = primeSize * 6; 58 | int messageSize = sikeParam.getMessageBytes(); 59 | int expectedSize = pubKeySize + messageSize; 60 | if (bytes == null || bytes.length != expectedSize) { 61 | throw new InvalidParameterException("Invalid parameter bytes"); 62 | } 63 | byte[] pubKeyBytes = new byte[pubKeySize]; 64 | System.arraycopy(bytes, 0, pubKeyBytes, 0, pubKeySize); 65 | this.c0 = new SidhPublicKey(sikeParam, pubKeyBytes); 66 | this.c1 = new byte[messageSize]; 67 | System.arraycopy(bytes, pubKeySize, this.c1, 0, messageSize); 68 | } 69 | 70 | /** 71 | * Get encrypted message encoded into byte array. 72 | * @return Encrypted message encoded into byte array. 73 | */ 74 | public byte[] getEncoded() { 75 | if (c0 == null || c1 == null) { 76 | return null; 77 | } 78 | byte[] pubKey = c0.getEncoded(); 79 | byte[] encoded = new byte[pubKey.length + c1.length]; 80 | System.arraycopy(pubKey, 0, encoded, 0, pubKey.length); 81 | System.arraycopy(c1, 0, encoded, pubKey.length, c1.length); 82 | return encoded; 83 | } 84 | 85 | /** 86 | * Get Alice's public key. 87 | * @return Public key. 88 | */ 89 | public PublicKey getC0() { 90 | return c0; 91 | } 92 | 93 | /** 94 | * Get encrypted data. 95 | * @return Encrypted data. 96 | */ 97 | public byte[] getC1() { 98 | return c1; 99 | } 100 | 101 | @Override 102 | public boolean equals(Object o) { 103 | if (this == o) return true; 104 | if (o == null || getClass() != o.getClass()) return false; 105 | EncryptedMessage that = (EncryptedMessage) o; 106 | // Use constant time comparison to avoid timing attacks 107 | return SideChannelUtil.constantTimeAreEqual(getEncoded(), that.getEncoded()); 108 | } 109 | 110 | @Override 111 | public int hashCode() { 112 | return Objects.hash(c0, Arrays.hashCode(c1)); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/model/EvaluatedCurve.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.model; 18 | 19 | import com.wultra.security.pqc.sike.math.api.Fp2Point; 20 | 21 | import java.util.Objects; 22 | 23 | /** 24 | * Evaluated curve and optional points. 25 | * 26 | * @author Roman Strobl, roman.strobl@wultra.com 27 | */ 28 | public class EvaluatedCurve { 29 | 30 | private final MontgomeryCurve curve; 31 | private final Fp2Point p; 32 | private final Fp2Point q; 33 | private final Fp2Point r; 34 | 35 | /** 36 | * Curve constructor. 37 | * @param curve Evaluated curve. 38 | * @param p Optional point P. 39 | * @param q Optional point Q. 40 | */ 41 | public EvaluatedCurve(MontgomeryCurve curve, Fp2Point p, Fp2Point q) { 42 | this.curve = curve; 43 | this.p = p; 44 | this.q = q; 45 | this.r = null; 46 | } 47 | 48 | /** 49 | * Curve constructor. 50 | * @param curve Evaluated curve. 51 | * @param p Optional point P. 52 | * @param q Optional point Q. 53 | * @param r Optional point R. 54 | */ 55 | public EvaluatedCurve(MontgomeryCurve curve, Fp2Point p, Fp2Point q, Fp2Point r) { 56 | this.curve = curve; 57 | this.p = p; 58 | this.q = q; 59 | this.r = r; 60 | } 61 | 62 | /** 63 | * Get the evaluated curve. 64 | * @return Evaluated curve. 65 | */ 66 | public MontgomeryCurve getCurve() { 67 | return curve; 68 | } 69 | 70 | /** 71 | * Get optional point P. 72 | * @return Optional point P. 73 | */ 74 | public Fp2Point getP() { 75 | return p; 76 | } 77 | 78 | /** 79 | * Get optional point Q. 80 | * @return Optional point Q. 81 | */ 82 | public Fp2Point getQ() { 83 | return q; 84 | } 85 | 86 | /** 87 | * Get optional point R. 88 | * @return Optional point R. 89 | */ 90 | public Fp2Point getR() { 91 | return r; 92 | } 93 | 94 | @Override 95 | public String toString() { 96 | return curve.toString(); 97 | } 98 | 99 | @Override 100 | public boolean equals(Object o) { 101 | if (this == o) return true; 102 | if (o == null || getClass() != o.getClass()) return false; 103 | EvaluatedCurve that = (EvaluatedCurve) o; 104 | // Use & to avoid timing attacks 105 | return curve.equals(that.curve) 106 | & Objects.equals(p, that.p) 107 | & Objects.equals(q, that.q) 108 | & Objects.equals(r, that.r); 109 | } 110 | 111 | @Override 112 | public int hashCode() { 113 | return Objects.hash(curve, p, q, r); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/model/ImplementationType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.model; 18 | 19 | /** 20 | * SIKE implementation type. 21 | * 22 | * @author Roman Strobl, roman.strobl@wultra.com 23 | */ 24 | public enum ImplementationType { 25 | REFERENCE, 26 | OPTIMIZED 27 | } 28 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/model/MontgomeryCurve.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.model; 18 | 19 | import com.wultra.security.pqc.sike.math.api.Fp2Element; 20 | import com.wultra.security.pqc.sike.model.optimized.MontgomeryConstants; 21 | import com.wultra.security.pqc.sike.param.SikeParam; 22 | 23 | import java.security.InvalidParameterException; 24 | import java.util.Objects; 25 | 26 | /** 27 | * Montgomery curve parameters. 28 | * 29 | * @author Roman Strobl, roman.strobl@wultra.com 30 | */ 31 | public class MontgomeryCurve { 32 | 33 | private final SikeParam sikeParam; 34 | private Fp2Element a; 35 | private Fp2Element b; 36 | private MontgomeryConstants optimizedConstants; 37 | 38 | /** 39 | * Montgomery curve constructor. 40 | * @param sikeParam SIKE parameters. 41 | */ 42 | public MontgomeryCurve(SikeParam sikeParam) { 43 | this.sikeParam = sikeParam; 44 | if (sikeParam.getImplementationType() == ImplementationType.OPTIMIZED) { 45 | optimizedConstants = new MontgomeryConstants(sikeParam); 46 | } 47 | } 48 | 49 | /** 50 | * Montgomery curve constructor. 51 | * @param sikeParam SIKE parameters. 52 | * @param a Montgomery curve coefficient a. 53 | */ 54 | public MontgomeryCurve(SikeParam sikeParam, Fp2Element a) { 55 | this.sikeParam = sikeParam; 56 | this.a = a; 57 | if (sikeParam.getImplementationType() == ImplementationType.OPTIMIZED) { 58 | optimizedConstants = new MontgomeryConstants(sikeParam, a); 59 | } 60 | } 61 | 62 | /** 63 | * Montgomery curve constructor. 64 | * @param sikeParam SIKE parameters. 65 | * @param a Montgomery curve coefficient a. 66 | * @param b Montgomery curve coefficient b. 67 | */ 68 | public MontgomeryCurve(SikeParam sikeParam, Fp2Element a, Fp2Element b) { 69 | this(sikeParam, a); 70 | this.b = b; 71 | } 72 | 73 | /** 74 | * Get SIKE parameters. 75 | * @return SIKE parameters. 76 | */ 77 | public SikeParam getSikeParam() { 78 | return sikeParam; 79 | } 80 | 81 | /** 82 | * Get Montgomery curve coefficient a. 83 | * @return Montgomery curve coefficient a. 84 | */ 85 | public Fp2Element getA() { 86 | return a; 87 | } 88 | 89 | /** 90 | * Set Montgomery curve coefficient a. 91 | * @param a Montgomery curve coefficient a. 92 | */ 93 | public void setA(Fp2Element a) { 94 | this.a = a; 95 | } 96 | 97 | /** 98 | * Get Montgomery curve coefficient b. 99 | * @return Montgomery curve coefficient b. 100 | */ 101 | public Fp2Element getB() { 102 | return b; 103 | } 104 | 105 | /** 106 | * Set Montgomery curve coefficient b. 107 | * @param b Montgomery curve coefficient b. 108 | */ 109 | public void setB(Fp2Element b) { 110 | this.b = b; 111 | } 112 | 113 | /** 114 | * Get optimized montgomery constants for this curve. 115 | * @return Optimized montgomery constants for this curve. 116 | */ 117 | public MontgomeryConstants getOptimizedConstants() { 118 | if (sikeParam.getImplementationType() != ImplementationType.OPTIMIZED) { 119 | throw new InvalidParameterException("Invalid implementation type"); 120 | } 121 | return optimizedConstants; 122 | } 123 | 124 | @Override 125 | public String toString() { 126 | if (sikeParam.getImplementationType() == ImplementationType.OPTIMIZED) { 127 | return "a = " + a + ", " + optimizedConstants; 128 | } 129 | return "a = " + a + ", b = " + b; 130 | } 131 | 132 | @Override 133 | public boolean equals(Object o) { 134 | if (this == o) return true; 135 | if (o == null || getClass() != o.getClass()) return false; 136 | MontgomeryCurve that = (MontgomeryCurve) o; 137 | return sikeParam.equals(that.sikeParam) && 138 | a.equals(that.a) && 139 | Objects.equals(b, that.b) && 140 | Objects.equals(optimizedConstants, that.optimizedConstants); 141 | } 142 | 143 | @Override 144 | public int hashCode() { 145 | return Objects.hash(sikeParam, a, b, optimizedConstants); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/model/Party.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.model; 18 | 19 | /** 20 | * A party involved in SIDH or SIKE communication. 21 | * 22 | * In case of SIKE, Bob is the client and Alice is the server. 23 | * 24 | * @author Roman Strobl, roman.strobl@wultra.com 25 | */ 26 | public enum Party { 27 | ALICE, 28 | BOB 29 | } 30 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/model/SidhPrivateKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.model; 18 | 19 | import com.wultra.security.pqc.sike.math.api.FpElement; 20 | import com.wultra.security.pqc.sike.param.SikeParam; 21 | import com.wultra.security.pqc.sike.util.ByteEncoding; 22 | import com.wultra.security.pqc.sike.util.OctetEncoding; 23 | import com.wultra.security.pqc.sike.util.SideChannelUtil; 24 | 25 | import java.math.BigInteger; 26 | import java.nio.charset.StandardCharsets; 27 | import java.security.InvalidParameterException; 28 | import java.security.PrivateKey; 29 | import java.util.Arrays; 30 | import java.util.Objects; 31 | 32 | /** 33 | * SIDH or SIKE private key. 34 | * 35 | * @author Roman Strobl, roman.strobl@wultra.com 36 | */ 37 | public class SidhPrivateKey implements PrivateKey { 38 | 39 | private final SikeParam sikeParam; 40 | private final Party party; 41 | private final byte[] key; 42 | private final byte[] s; 43 | 44 | /** 45 | * Construct private key from a number. 46 | * @param sikeParam SIKE parameters. 47 | * @param party Alice or Bob. 48 | * @param secret Secret value of the private key. 49 | */ 50 | public SidhPrivateKey(SikeParam sikeParam, Party party, BigInteger secret) { 51 | this.sikeParam = sikeParam; 52 | this.party = party; 53 | validatePrivateKey(secret); 54 | int keyLength = (sikeParam.getPrime().bitLength() + 7) / 8; 55 | this.key = ByteEncoding.toByteArray(secret, keyLength); 56 | this.s = new byte[sikeParam.getMessageBytes()]; 57 | } 58 | 59 | /** 60 | * Construct private key from bytes. 61 | * @param sikeParam SIKE parameters. 62 | * @param party Alice or Bob. 63 | * @param bytes Byte value of the private key. 64 | */ 65 | public SidhPrivateKey(SikeParam sikeParam, Party party, byte[] bytes) { 66 | this.sikeParam = sikeParam; 67 | this.party = party; 68 | int sLength = sikeParam.getMessageBytes(); 69 | int keyLength = (sikeParam.getPrime().bitLength() + 7) / 8; 70 | if (bytes == null || bytes.length != sLength + keyLength) { 71 | throw new InvalidParameterException("Invalid private key"); 72 | } 73 | byte[] s = new byte[sLength]; 74 | key = new byte[keyLength]; 75 | System.arraycopy(bytes, 0, s, 0, sLength); 76 | System.arraycopy(bytes, sLength, key, 0, keyLength); 77 | BigInteger secret = ByteEncoding.fromByteArray(key); 78 | validatePrivateKey(secret); 79 | this.s = s; 80 | } 81 | 82 | /** 83 | * Construct private key from octets. 84 | * @param sikeParam SIKE parameters. 85 | * @param party Alice or Bob. 86 | * @param octets Octet value of the private key. 87 | */ 88 | public SidhPrivateKey(SikeParam sikeParam, Party party, String octets) { 89 | this.sikeParam = sikeParam; 90 | this.party = party; 91 | int sLength = sikeParam.getMessageBytes(); 92 | int keyLength = getKeyLength(party); 93 | if (octets == null || octets.length() != (sLength + keyLength) * 2) { 94 | throw new InvalidParameterException("Invalid private key"); 95 | } 96 | byte[] bytes = octets.getBytes(StandardCharsets.UTF_8); 97 | byte[] s = new byte[sLength * 2]; 98 | byte[] key = new byte[keyLength * 2]; 99 | System.arraycopy(bytes, 0, s, 0, sLength * 2); 100 | System.arraycopy(bytes, sLength * 2, key, 0, keyLength * 2); 101 | BigInteger sVal = OctetEncoding.fromOctetString(new String(s)); 102 | BigInteger secret = OctetEncoding.fromOctetString(new String(key)); 103 | validatePrivateKey(secret); 104 | this.s = ByteEncoding.toByteArray(sVal, sLength); 105 | int primeSize = (sikeParam.getPrime().bitLength() + 7) / 8; 106 | this.key = ByteEncoding.toByteArray(secret, primeSize); 107 | } 108 | 109 | /** 110 | * Construct private key from bytes with specified parameter s for SIKE decapsulation. 111 | * @param sikeParam SIKE parameters. 112 | * @param party Alice or Bob. 113 | * @param key Byte value of the private key. 114 | * @param s Parameter s for SIKE decapsulation. 115 | */ 116 | public SidhPrivateKey(SikeParam sikeParam, Party party, BigInteger key, byte[] s) { 117 | this(sikeParam, party, key); 118 | System.arraycopy(s, 0, this.s, 0, s.length); 119 | } 120 | 121 | /** 122 | * Validate the BigInteger value representing the private key. 123 | * @param secret BigInteger value representing the private key. 124 | */ 125 | private void validatePrivateKey(BigInteger secret) { 126 | if (secret.compareTo(BigInteger.ZERO) <= 0) { 127 | throw new InvalidParameterException("Invalid secret"); 128 | } 129 | if (party == Party.ALICE) { 130 | if (secret.compareTo(sikeParam.getOrdA()) >= 0) { 131 | throw new InvalidParameterException("Invalid secret"); 132 | } 133 | } else if (party == Party.BOB) { 134 | if (secret.compareTo(sikeParam.getOrdB()) >= 0) { 135 | throw new InvalidParameterException("Invalid secret"); 136 | } 137 | } else { 138 | throw new InvalidParameterException("Invalid party"); 139 | } 140 | } 141 | 142 | /** 143 | * Get the private key length. 144 | * @param party Alice or Bob. 145 | * @return Private key length. 146 | */ 147 | private int getKeyLength(Party party) { 148 | if (party == Party.ALICE) { 149 | return (sikeParam.getBitsA() + 7) / 8; 150 | } else if (party == Party.BOB){ 151 | return (sikeParam.getBitsB() - 1 + 7) / 8; 152 | } else { 153 | throw new InvalidParameterException("Invalid party"); 154 | } 155 | } 156 | 157 | /** 158 | * Get the private key as byte array. 159 | * @return Private key as byte array. 160 | */ 161 | public byte[] getKey() { 162 | byte[] keyBytes = new byte[key.length]; 163 | System.arraycopy(key, 0, keyBytes, 0, key.length); 164 | return keyBytes; 165 | } 166 | 167 | /** 168 | * Get the private key as an F(p) element. 169 | * @return Private key as an F(p) element. 170 | */ 171 | public FpElement getFpElement() { 172 | BigInteger secret = ByteEncoding.fromByteArray(key); 173 | return sikeParam.getFp2ElementFactory().generate(secret).getX0(); 174 | } 175 | 176 | /** 177 | * Get the private key as a number. 178 | * @return Private key as a number. 179 | */ 180 | public BigInteger getM() { 181 | return ByteEncoding.fromByteArray(key); 182 | } 183 | 184 | /** 185 | * Get parameter s for decapsulation. 186 | * @return Parameter s for decapsulation. 187 | */ 188 | public byte[] getS() { 189 | return s; 190 | } 191 | 192 | @Override 193 | public String getAlgorithm() { 194 | return sikeParam.getName(); 195 | } 196 | 197 | @Override 198 | public String getFormat() { 199 | // ASN.1 encoding is not supported 200 | return null; 201 | } 202 | 203 | /** 204 | * Get the private key encoded as bytes. 205 | * @return Private key encoded as bytes. 206 | */ 207 | @Override 208 | public byte[] getEncoded() { 209 | byte[] output = new byte[s.length + key.length]; 210 | System.arraycopy(s, 0, output, 0, s.length); 211 | System.arraycopy(key, 0, output, s.length, key.length); 212 | return output; 213 | } 214 | 215 | /** 216 | * Convert private key into an octet string. 217 | * @return Octet string. 218 | */ 219 | public String toOctetString() { 220 | String prefix = OctetEncoding.toOctetString(s, sikeParam.getMessageBytes()); 221 | int length = getKeyLength(party); 222 | return prefix + OctetEncoding.toOctetString(key, length); 223 | } 224 | 225 | @Override 226 | public String toString() { 227 | return getM().toString(); 228 | } 229 | 230 | @Override 231 | public boolean equals(Object o) { 232 | if (this == o) return true; 233 | if (o == null || getClass() != o.getClass()) return false; 234 | SidhPrivateKey that = (SidhPrivateKey) o; 235 | // Use constant time comparison to avoid timing attacks 236 | return sikeParam.equals(that.sikeParam) 237 | && SideChannelUtil.constantTimeAreEqual(getEncoded(), that.getEncoded()); 238 | } 239 | 240 | @Override 241 | public int hashCode() { 242 | return Objects.hash(sikeParam, Arrays.hashCode(s), Arrays.hashCode(key)); 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/model/SidhPublicKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.model; 18 | 19 | import com.wultra.security.pqc.sike.math.api.Fp2Element; 20 | import com.wultra.security.pqc.sike.param.SikeParam; 21 | import com.wultra.security.pqc.sike.util.ByteEncoding; 22 | import com.wultra.security.pqc.sike.util.OctetEncoding; 23 | import org.bouncycastle.util.Arrays; 24 | 25 | import java.math.BigInteger; 26 | import java.nio.charset.StandardCharsets; 27 | import java.security.PublicKey; 28 | import java.util.Objects; 29 | 30 | /** 31 | * SIDH or SIKE public key. 32 | * 33 | * @author Roman Strobl, roman.strobl@wultra.com 34 | */ 35 | public class SidhPublicKey implements PublicKey { 36 | 37 | private final SikeParam sikeParam; 38 | 39 | private final Fp2Element px; 40 | private final Fp2Element qx; 41 | private final Fp2Element rx; 42 | 43 | /** 44 | * Public key constructor from F(p^2) Elements. 45 | * @param sikeParam SIKE parameters. 46 | * @param px The x coordinate of public point P. 47 | * @param qx The x coordinate of public point Q. 48 | * @param rx The x coordinate of public point R. 49 | */ 50 | public SidhPublicKey(SikeParam sikeParam, Fp2Element px, Fp2Element qx, Fp2Element rx) { 51 | this.sikeParam = sikeParam; 52 | this.px = px; 53 | this.qx = qx; 54 | this.rx = rx; 55 | } 56 | 57 | /** 58 | * Public key constructor from byte array representation. 59 | * @param sikeParam SIKE parameters. 60 | * @param bytes The x coordinates of public points P, Q and R. 61 | */ 62 | public SidhPublicKey(SikeParam sikeParam, byte[] bytes) { 63 | this.sikeParam = sikeParam; 64 | BigInteger prime = sikeParam.getPrime(); 65 | int primeSize = (prime.bitLength() + 7) / 8; 66 | if (bytes == null || bytes.length != 6 * primeSize) { 67 | throw new IllegalStateException("Invalid public key"); 68 | } 69 | BigInteger[] keyParts = new BigInteger[6]; 70 | for (int i = 0; i < 6; i++) { 71 | byte[] keyBytes = new byte[primeSize]; 72 | System.arraycopy(bytes, i * primeSize, keyBytes, 0, keyBytes.length); 73 | keyParts[i] = ByteEncoding.fromByteArray(keyBytes); 74 | } 75 | this.px = sikeParam.getFp2ElementFactory().generate(keyParts[0], keyParts[1]); 76 | this.qx = sikeParam.getFp2ElementFactory().generate(keyParts[2], keyParts[3]); 77 | this.rx = sikeParam.getFp2ElementFactory().generate(keyParts[4], keyParts[5]); 78 | } 79 | 80 | /** 81 | * Construct public key from octets. 82 | * @param sikeParam SIKE parameters. 83 | * @param octets Octet value of the private key. 84 | */ 85 | public SidhPublicKey(SikeParam sikeParam, String octets) { 86 | this.sikeParam = sikeParam; 87 | BigInteger prime = sikeParam.getPrime(); 88 | int primeSize = (prime.bitLength() + 7) / 8; 89 | if (octets == null || octets.length() != 12 * primeSize) { 90 | throw new IllegalStateException("Invalid public key"); 91 | } 92 | byte[] octetBytes = octets.getBytes(StandardCharsets.UTF_8); 93 | BigInteger[] keyParts = new BigInteger[6]; 94 | for (int i = 0; i < 6; i++) { 95 | byte[] keyBytes = new byte[primeSize * 2]; 96 | System.arraycopy(octetBytes, i * primeSize * 2, keyBytes, 0, keyBytes.length); 97 | keyParts[i] = OctetEncoding.fromOctetString(new String(keyBytes)); 98 | } 99 | this.px = sikeParam.getFp2ElementFactory().generate(keyParts[0], keyParts[1]); 100 | this.qx = sikeParam.getFp2ElementFactory().generate(keyParts[2], keyParts[3]); 101 | this.rx = sikeParam.getFp2ElementFactory().generate(keyParts[4], keyParts[5]); 102 | } 103 | 104 | /** 105 | * Get the x coordinate of public point P. 106 | * @return The x coordinate of public point P. 107 | */ 108 | public Fp2Element getPx() { 109 | return px; 110 | } 111 | 112 | /** 113 | * The x coordinate of public point Q. 114 | * @return The x coordinate of public point Q. 115 | */ 116 | public Fp2Element getQx() { 117 | return qx; 118 | } 119 | 120 | /** 121 | * The x coordinate of public point R. 122 | * @return The x coordinate of public point R. 123 | */ 124 | public Fp2Element getRx() { 125 | return rx; 126 | } 127 | 128 | @Override 129 | public String getAlgorithm() { 130 | return sikeParam.getName(); 131 | } 132 | 133 | @Override 134 | public String getFormat() { 135 | // ASN.1 encoding is not supported 136 | return null; 137 | } 138 | 139 | /** 140 | * Get the public key encoded as bytes. 141 | * @return Public key encoded as bytes. 142 | */ 143 | @Override 144 | public byte[] getEncoded() { 145 | byte[] pxEncoded = px.getEncoded(); 146 | byte[] qxEncoded = qx.getEncoded(); 147 | byte[] rxEncoded = rx.getEncoded(); 148 | byte[] encoded = new byte[pxEncoded.length + qxEncoded.length + rxEncoded.length]; 149 | System.arraycopy(pxEncoded, 0, encoded, 0, pxEncoded.length); 150 | System.arraycopy(qxEncoded, 0, encoded, pxEncoded.length, qxEncoded.length); 151 | System.arraycopy(rxEncoded, 0, encoded, pxEncoded.length + qxEncoded.length, rxEncoded.length); 152 | return encoded; 153 | } 154 | 155 | /** 156 | * Convert public key to octet string. 157 | * @return Octet string. 158 | */ 159 | public String toOctetString() { 160 | return px.toOctetString() + qx.toOctetString() + rx.toOctetString(); 161 | } 162 | 163 | @Override 164 | public String toString() { 165 | return "(" + px.toString() + ", " + qx.toString() + ", " + rx.toString() + ")"; 166 | } 167 | 168 | @Override 169 | public boolean equals(Object o) { 170 | if (this == o) return true; 171 | if (o == null || getClass() != o.getClass()) return false; 172 | SidhPublicKey that = (SidhPublicKey) o; 173 | // Use constant time comparison to avoid timing attacks 174 | return sikeParam.equals(that.sikeParam) 175 | && Arrays.constantTimeAreEqual(getEncoded(), that.getEncoded()); 176 | } 177 | 178 | @Override 179 | public int hashCode() { 180 | return Objects.hash(sikeParam, px, qx, rx); 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/model/optimized/MontgomeryConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.model.optimized; 18 | 19 | import com.wultra.security.pqc.sike.math.api.Fp2Element; 20 | import com.wultra.security.pqc.sike.param.SikeParam; 21 | 22 | /** 23 | * Montgomery curve constants for optimization in projective coordinates. 24 | * 25 | * @author Roman Strobl, roman.strobl@wultra.com 26 | */ 27 | public class MontgomeryConstants { 28 | 29 | private Fp2Element c; 30 | private Fp2Element a24plus; 31 | private Fp2Element a24minus; 32 | private Fp2Element c24; 33 | private Fp2Element k1; 34 | private Fp2Element k2; 35 | private Fp2Element k3; 36 | 37 | /** 38 | * Default optimized montgomery constants constructor. 39 | * @param sikeParam SIKE parameters. 40 | */ 41 | public MontgomeryConstants(SikeParam sikeParam) { 42 | c = sikeParam.getFp2ElementFactory().one(); 43 | } 44 | 45 | /** 46 | * Optimized montgomery constants constructor with calculation of constants. 47 | * @param sikeParam SIKE parameters. 48 | * @param a Montgomery curve coefficient a. 49 | */ 50 | public MontgomeryConstants(SikeParam sikeParam, Fp2Element a) { 51 | this(sikeParam); 52 | Fp2Element t1 = c.add(c); 53 | this.a24plus = a.add(t1); 54 | this.a24minus = a.subtract(t1); 55 | this.c24 = t1.add(t1); 56 | } 57 | 58 | /** 59 | * Get Montgomery curve constant c. 60 | * @return Montgomery constant c. 61 | */ 62 | public Fp2Element getC() { 63 | return c; 64 | } 65 | 66 | /** 67 | * Set Montgomery curve constant c. 68 | * @param c Montgomery curve constant c. 69 | */ 70 | public void setC(Fp2Element c) { 71 | this.c = c; 72 | } 73 | 74 | /** 75 | * Get Montgomery curve constant a24+. 76 | * @return Montgomery curve constant a24+. 77 | */ 78 | public Fp2Element getA24plus() { 79 | return a24plus; 80 | } 81 | 82 | /** 83 | * Set Montgomery curve constant a24+. 84 | * @param a24plus Montgomery curve constant a24+. 85 | */ 86 | public void setA24plus(Fp2Element a24plus) { 87 | this.a24plus = a24plus; 88 | } 89 | 90 | /** 91 | * Get Montgomery curve constant a24-. 92 | * @return Montgomery curve constant a24-. 93 | */ 94 | public Fp2Element getA24minus() { 95 | return a24minus; 96 | } 97 | 98 | /** 99 | * Set Montgomery curve constant a24-. 100 | * @param a24minus Montgomery curve constant a24-. 101 | */ 102 | public void setA24minus(Fp2Element a24minus) { 103 | this.a24minus = a24minus; 104 | } 105 | 106 | /** 107 | * Get Montgomery curve constant c24. 108 | * @return Montgomery curve constant c24. 109 | */ 110 | public Fp2Element getC24() { 111 | return c24; 112 | } 113 | 114 | /** 115 | * Set Montgomery curve constant c24. 116 | * @param c24 Montgomery curve constant c24. 117 | */ 118 | public void setC24(Fp2Element c24) { 119 | this.c24 = c24; 120 | } 121 | 122 | /** 123 | * Get Montgomery curve constant K1. 124 | * @return Montgomery curve constant K1. 125 | */ 126 | public Fp2Element getK1() { 127 | return k1; 128 | } 129 | 130 | /** 131 | * Set Montgomery curve constant K2. 132 | * @param k1 Montgomery curve constant K2. 133 | */ 134 | public void setK1(Fp2Element k1) { 135 | this.k1 = k1; 136 | } 137 | 138 | /** 139 | * Get Montgomery curve constant K2. 140 | * @return Montgomery curve constant K2. 141 | */ 142 | public Fp2Element getK2() { 143 | return k2; 144 | } 145 | 146 | /** 147 | * Set Montgomery curve constant K2. 148 | * @param k2 Montgomery curve constant K2. 149 | */ 150 | public void setK2(Fp2Element k2) { 151 | this.k2 = k2; 152 | } 153 | 154 | /** 155 | * Get Montgomery curve constant K3. 156 | * @return Montgomery curve constant K3. 157 | */ 158 | public Fp2Element getK3() { 159 | return k3; 160 | } 161 | 162 | /** 163 | * Set Montgomery curve constant K3. 164 | * @param k3 Montgomery curve constant K3. 165 | */ 166 | public void setK3(Fp2Element k3) { 167 | this.k3 = k3; 168 | } 169 | 170 | @Override 171 | public String toString() { 172 | return "MontgomeryConstants{" + 173 | "c=" + c + 174 | ", a24plus=" + a24plus + 175 | ", a24minus=" + a24minus + 176 | ", c24=" + c24 + 177 | ", k1=" + k1 + 178 | ", k2=" + k2 + 179 | ", k3=" + k3 + 180 | '}'; 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/param/SikeParam.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.param; 18 | 19 | import com.wultra.security.pqc.sike.math.api.*; 20 | import com.wultra.security.pqc.sike.math.optimized.fp.FpElementOpti; 21 | import com.wultra.security.pqc.sike.model.ImplementationType; 22 | 23 | import java.math.BigInteger; 24 | 25 | /** 26 | * SIKE parameters. 27 | * 28 | * @author Roman Strobl, roman.strobl@wultra.com 29 | */ 30 | public interface SikeParam { 31 | 32 | /** 33 | * Get implementation type. 34 | * @return Implementation type. 35 | */ 36 | ImplementationType getImplementationType(); 37 | 38 | /** 39 | * Factory for Fp2Elements. 40 | * @return Factory for Fp2Elements. 41 | */ 42 | Fp2ElementFactory getFp2ElementFactory(); 43 | 44 | /** 45 | * Get Montgomery curve math algorithms. 46 | * @return Montgomery curve math algorithms. 47 | */ 48 | Montgomery getMontgomery(); 49 | 50 | /** 51 | * Get Isogeny curve algorithms. 52 | * @return Isogeny curve algorithms. 53 | */ 54 | Isogeny getIsogeny(); 55 | 56 | /** 57 | * Get SIKE variant name. 58 | * @return SIKE variant name. 59 | */ 60 | String getName(); 61 | 62 | /** 63 | * Get Montgomery coefficient a for starting curve. 64 | * @return Montgomery coefficient a for starting curve. 65 | */ 66 | Fp2Element getA(); 67 | 68 | /** 69 | * Get Montgomery coefficient b for starting curve. 70 | * @return Montgomery coefficient b for starting curve. 71 | */ 72 | Fp2Element getB(); 73 | 74 | /** 75 | * Get parameter eA. 76 | * @return Parameter eA. 77 | */ 78 | int getEA(); 79 | 80 | /** 81 | * Get parameter eB. 82 | * @return Parameter eB. 83 | */ 84 | int getEB(); 85 | 86 | /** 87 | * Get factor of A. 88 | * @return Factor of A. 89 | */ 90 | BigInteger getOrdA(); 91 | 92 | /** 93 | * Get factor of B. 94 | * @return Factor of b. 95 | */ 96 | BigInteger getOrdB(); 97 | 98 | /** 99 | * Get most significant bit of A. 100 | * @return Most significant bit of A. 101 | */ 102 | int getBitsA(); 103 | 104 | /** 105 | * Get most significant bit of B. 106 | * @return Most significant bit of B. 107 | */ 108 | int getBitsB(); 109 | 110 | /** 111 | * Get mask used for key generation of A. 112 | * @return Mask used for key generation of A. 113 | */ 114 | byte getMaskA(); 115 | 116 | /** 117 | * Get mask used for key generation of B. 118 | * @return Mask used for key generation of B. 119 | */ 120 | byte getMaskB(); 121 | 122 | /** 123 | * Get field prime. 124 | * @return Field prime. 125 | */ 126 | BigInteger getPrime(); 127 | 128 | /** 129 | * Get point PA. 130 | * @return point PA. 131 | */ 132 | Fp2Point getPA(); 133 | 134 | /** 135 | * Get point QA. 136 | * @return point QA. 137 | */ 138 | Fp2Point getQA(); 139 | 140 | /** 141 | * Get point RA. 142 | * @return point RA. 143 | */ 144 | Fp2Point getRA(); 145 | 146 | /** 147 | * Get point PB. 148 | * @return point PB. 149 | */ 150 | Fp2Point getPB(); 151 | 152 | /** 153 | * Get point QB. 154 | * @return point QB. 155 | */ 156 | Fp2Point getQB(); 157 | 158 | /** 159 | * Get point RB. 160 | * @return point RB. 161 | */ 162 | Fp2Point getRB(); 163 | 164 | /** 165 | * Get the number of bytes used for cryptography operations. 166 | * @return Number of bytes used for cryptography operations. 167 | */ 168 | int getCryptoBytes(); 169 | 170 | /** 171 | * Get the number of bytes used for message operations. 172 | * @return Number of bytes used for message operations. 173 | */ 174 | int getMessageBytes(); 175 | 176 | /** 177 | * Get number of rows for optimized tree computations in the 2-isogeny graph. 178 | * @return Number of rows for optimized tree computations in the 2-isogeny graph. 179 | */ 180 | int getTreeRowsA(); 181 | 182 | /** 183 | * Get number of rows for optimized tree computations in the 3-isogeny graph. 184 | * @return Number of rows for optimized tree computations in the 3-isogeny graph. 185 | */ 186 | int getTreeRowsB(); 187 | 188 | /** 189 | * Get maximum number of points for optimized tree computations in the 2-isogeny graph. 190 | * @return Maxim number of points for optimized tree computations in the 2-isogeny graph. 191 | */ 192 | int getTreePointsA(); 193 | 194 | /** 195 | * Get maximum number of points for optimized tree computations in the 3-isogeny graph. 196 | * @return Maxim number of points for optimized tree computations in the 3-isogeny graph. 197 | */ 198 | int getTreePointsB(); 199 | 200 | /** 201 | * Get optimization strategy for tree computations in the 2-isogeny graph. 202 | * @return Optimization strategy for tree computations in the 2-isogeny graph. 203 | */ 204 | int[] getStrategyA(); 205 | 206 | /** 207 | * Get optimization strategy for tree computations in the 3-isogeny graph. 208 | * @return Optimization strategy for tree computations in the 3-isogeny graph. 209 | */ 210 | int[] getStrategyB(); 211 | 212 | /** 213 | * Get size of long array for optimized elements. 214 | * @return Size of long array. 215 | */ 216 | int getFpWords(); 217 | 218 | /** 219 | * Get number of 0 digits in the least significant part of p + 1. 220 | * @return Number of 0 digits in the least significant part of p + 1. 221 | */ 222 | int getZeroWords(); 223 | 224 | /** 225 | * Get optimized field prime p. 226 | * @return Field prime p. 227 | */ 228 | FpElementOpti getP(); 229 | 230 | /** 231 | * Get optimized value p + 1. 232 | * @return Value p + 1. 233 | */ 234 | FpElementOpti getP1(); 235 | 236 | /** 237 | * Get optimized value p * 2. 238 | * @return Value p * 2. 239 | */ 240 | FpElementOpti getPx2(); 241 | 242 | /** 243 | * Get optimized value pR2. 244 | * @return Optimized value pR2. 245 | */ 246 | FpElementOpti getPR2(); 247 | 248 | /** 249 | * Get the power strategy for the p34 algorithm. 250 | * @return Power strategy. 251 | */ 252 | int[] getPowStrategy(); 253 | 254 | /** 255 | * Get the multiplication strategy for the p34 algorithm. 256 | * @return Multiplication strategy. 257 | */ 258 | int[] getMulStrategy(); 259 | 260 | /** 261 | * Get initial multiplication value for the p34 algorithm. 262 | * @return Initial multiplication value for the p34 algorithm 263 | */ 264 | int getInitialMul(); 265 | 266 | } 267 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/util/ByteEncoding.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.util; 18 | 19 | import org.bouncycastle.util.BigIntegers; 20 | 21 | import java.math.BigInteger; 22 | import java.security.InvalidParameterException; 23 | 24 | /** 25 | * Converter for byte encoding for compatibility with the GMP library. 26 | * 27 | * @author Roman Strobl, roman.strobl@wultra.com 28 | */ 29 | public class ByteEncoding { 30 | 31 | private ByteEncoding() { 32 | 33 | } 34 | 35 | /** 36 | * Convert unsigned number from byte array. The byte array is stored in big-endian ordering. 37 | * 38 | * @param data Byte array representing the number. 39 | * @return Converted number. 40 | */ 41 | public static BigInteger fromByteArray(byte[] data) { 42 | return BigIntegers.fromUnsignedByteArray(reverse(data)); 43 | } 44 | 45 | /** 46 | * Convert unsigned number into a byte array. The byte array is stored in big-endian ordering. 47 | * @param n Number to convert. 48 | * @param length Length of byte array. 49 | * @return Byte array representing converted number. 50 | */ 51 | public static byte[] toByteArray(BigInteger n, int length) { 52 | byte[] encoded = reverse(BigIntegers.asUnsignedByteArray(n)); 53 | if (encoded.length > length) { 54 | throw new InvalidParameterException("Number is too large"); 55 | } 56 | if (encoded.length == length) { 57 | return encoded; 58 | } 59 | byte[] padded = new byte[length]; 60 | System.arraycopy(encoded, 0, padded, 0, encoded.length); 61 | return padded; 62 | } 63 | 64 | /** 65 | * Reverse byte array. 66 | * @param data Source byte array. 67 | * @return Reversed byte array. 68 | */ 69 | private static byte[] reverse(byte[] data) { 70 | byte[] out = new byte[data.length]; 71 | for(int i = 0; i < data.length; i++) { 72 | out[i] = data[data.length - i - 1]; 73 | } 74 | return out; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/util/OctetEncoding.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.util; 18 | 19 | import java.math.BigInteger; 20 | import java.security.InvalidParameterException; 21 | 22 | /** 23 | * Converter for octet encoding specified in SIKE specification. 24 | * 25 | * @author Roman Strobl, roman.strobl@wultra.com 26 | */ 27 | public class OctetEncoding { 28 | 29 | private OctetEncoding() { 30 | 31 | } 32 | 33 | /** 34 | * Convert a BigInteger into octet string. 35 | * 36 | * @param n A non-negative number to convert. 37 | * @param length Length of generated octet string specified as number of octets. 38 | * @return Converted octet string. 39 | */ 40 | public static String toOctetString(BigInteger n, int length) { 41 | if (n.signum() == -1) { 42 | throw new InvalidParameterException("Number is negative"); 43 | } 44 | String hex = n.toString(16).toUpperCase(); 45 | if (hex.length() % 2 == 1) { 46 | hex = "0" + hex; 47 | } 48 | char[] chars = hex.toCharArray(); 49 | int expectedLength = length * 2; 50 | if (chars.length > expectedLength) { 51 | throw new InvalidParameterException("Number is too large, length: " + chars.length + ", expected: " + expectedLength); 52 | } 53 | StringBuilder sb = new StringBuilder(); 54 | for (int i = hex.length() - 1; i >= 0; i -= 2) { 55 | sb.append(chars[i - 1]); 56 | sb.append(chars[i]); 57 | } 58 | for (int i = 0; i < expectedLength - chars.length; i++) { 59 | sb.append("0"); 60 | } 61 | return sb.toString(); 62 | } 63 | 64 | /** 65 | * Convert a byte array representing a number into an octet string. 66 | * 67 | * @param data Byte array representing a number. 68 | * @param length Length of generated octet string specified as number of octets. 69 | * @return Converted octet string. 70 | */ 71 | public static String toOctetString(byte[] data, int length) { 72 | return toOctetString(ByteEncoding.fromByteArray(data), length); 73 | } 74 | 75 | 76 | /** 77 | * Convert an octet string into BigInteger. 78 | * 79 | * @param str Octet string. 80 | * @return Converted BigInteger value. 81 | */ 82 | public static BigInteger fromOctetString(String str) { 83 | if (str == null || str.length() % 2 == 1) { 84 | throw new InvalidParameterException("Invalid octet string"); 85 | } 86 | char[] chars = str.toCharArray(); 87 | StringBuilder sb = new StringBuilder(); 88 | for (int i = chars.length - 1; i >= 0; i -= 2) { 89 | sb.append(chars[i - 1]); 90 | sb.append(chars[i]); 91 | } 92 | return new BigInteger(sb.toString(), 16); 93 | } 94 | 95 | /** 96 | * Convert an octet string to byte array. 97 | * @param str Octet string. 98 | * @param length Expected length of byte array. 99 | * @return Converted byte array value. 100 | */ 101 | public static byte[] fromOctetString(String str, int length) { 102 | return ByteEncoding.toByteArray(fromOctetString(str), length); 103 | } 104 | } -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/util/Sha3.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.util; 18 | 19 | import org.bouncycastle.crypto.digests.SHAKEDigest; 20 | 21 | /** 22 | * SHA-3 hash function SHAKE256 with variable output length. 23 | * 24 | * @author Roman Strobl, roman.strobl@wultra.com 25 | */ 26 | public class Sha3 { 27 | 28 | private Sha3() { 29 | 30 | } 31 | 32 | /** 33 | * Hash data using SHAKE256. 34 | * @param data Data to hash. 35 | * @param outputLen Output length. 36 | * @return Hashed data. 37 | */ 38 | public static byte[] shake256(byte[] data, int outputLen) { 39 | SHAKEDigest shake256 = new SHAKEDigest(256); 40 | shake256.update(data, 0, data.length); 41 | byte[] hashed = new byte[outputLen]; 42 | // Squeeze output to required message length 43 | shake256.doFinal(hashed, 0, outputLen); 44 | return hashed; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /sike-java/src/main/java/com/wultra/security/pqc/sike/util/SideChannelUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.util; 18 | 19 | import org.bouncycastle.util.Arrays; 20 | 21 | /** 22 | * Utilities for preventing side channel attacks. 23 | * 24 | * @author Roman Strobl, roman.strobl@wultra.com 25 | */ 26 | public class SideChannelUtil { 27 | 28 | private SideChannelUtil() { 29 | 30 | } 31 | 32 | /** 33 | * Compare two byte arrays in constant time. 34 | * @param bytes1 First byte array. 35 | * @param bytes2 Second byte array. 36 | * @return Whether byte arrays are equal. 37 | */ 38 | public static boolean constantTimeAreEqual(byte[] bytes1, byte[] bytes2) { 39 | return Arrays.constantTimeAreEqual(bytes1, bytes2); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /sike-java/src/test/java/com/wultra/security/pqc/sike/KeyConversionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike; 18 | 19 | import com.wultra.security.pqc.sike.crypto.KeyGenerator; 20 | import com.wultra.security.pqc.sike.model.ImplementationType; 21 | import com.wultra.security.pqc.sike.model.Party; 22 | import com.wultra.security.pqc.sike.model.SidhPrivateKey; 23 | import com.wultra.security.pqc.sike.model.SidhPublicKey; 24 | import com.wultra.security.pqc.sike.param.SikeParam; 25 | import com.wultra.security.pqc.sike.param.SikeParamP434; 26 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 27 | import org.junit.jupiter.api.Test; 28 | 29 | import java.security.*; 30 | 31 | import static org.junit.jupiter.api.Assertions.assertEquals; 32 | 33 | /** 34 | * Test of key conversions. 35 | * 36 | * @author Roman Strobl, roman.strobl@wultra.com 37 | */ 38 | class KeyConversionTest { 39 | 40 | static { 41 | Security.addProvider(new BouncyCastleProvider()); 42 | } 43 | 44 | @Test 45 | void testConversionToByteArray() throws GeneralSecurityException { 46 | SikeParam sikeParam = new SikeParamP434(ImplementationType.OPTIMIZED); 47 | KeyGenerator keyGenerator = new KeyGenerator(sikeParam); 48 | KeyPair keyPairA = keyGenerator.generateKeyPair(Party.ALICE); 49 | KeyPair keyPairB = keyGenerator.generateKeyPair(Party.BOB); 50 | byte[] privKeyBytesA = keyPairA.getPrivate().getEncoded(); 51 | byte[] pubKeyBytesA = keyPairA.getPublic().getEncoded(); 52 | byte[] privKeyBytesB = keyPairB.getPrivate().getEncoded(); 53 | byte[] pubKeyBytesB = keyPairB.getPublic().getEncoded(); 54 | PrivateKey privKeyA = new SidhPrivateKey(sikeParam, Party.ALICE, privKeyBytesA); 55 | PublicKey pubKeyA = new SidhPublicKey(sikeParam, pubKeyBytesA); 56 | PrivateKey privKeyB = new SidhPrivateKey(sikeParam, Party.BOB, privKeyBytesB); 57 | PublicKey pubKeyB = new SidhPublicKey(sikeParam, pubKeyBytesB); 58 | assertEquals(privKeyA, keyPairA.getPrivate()); 59 | assertEquals(pubKeyA, keyPairA.getPublic()); 60 | assertEquals(privKeyB, keyPairB.getPrivate()); 61 | assertEquals(pubKeyB, keyPairB.getPublic()); 62 | } 63 | 64 | @Test 65 | void testConversionToOctets() throws GeneralSecurityException { 66 | SikeParam sikeParam = new SikeParamP434(ImplementationType.OPTIMIZED); 67 | KeyGenerator keyGenerator = new KeyGenerator(sikeParam); 68 | KeyPair keyPairA = keyGenerator.generateKeyPair(Party.ALICE); 69 | KeyPair keyPairB = keyGenerator.generateKeyPair(Party.BOB); 70 | String privKeyOctetsA = ((SidhPrivateKey) keyPairA.getPrivate()).toOctetString(); 71 | String pubKeyOctetsA = ((SidhPublicKey) keyPairA.getPublic()).toOctetString(); 72 | String privKeyOctetsB = ((SidhPrivateKey) keyPairB.getPrivate()).toOctetString(); 73 | String pubKeyOctetsB = ((SidhPublicKey) keyPairB.getPublic()).toOctetString(); 74 | PrivateKey privKeyA = new SidhPrivateKey(sikeParam, Party.ALICE, privKeyOctetsA); 75 | PublicKey pubKeyA = new SidhPublicKey(sikeParam, pubKeyOctetsA); 76 | PrivateKey privKeyB = new SidhPrivateKey(sikeParam, Party.BOB, privKeyOctetsB); 77 | PublicKey pubKeyB = new SidhPublicKey(sikeParam, pubKeyOctetsB); 78 | assertEquals(privKeyA, keyPairA.getPrivate()); 79 | assertEquals(pubKeyA, keyPairA.getPublic()); 80 | assertEquals(privKeyB, keyPairB.getPrivate()); 81 | assertEquals(pubKeyB, keyPairB.getPublic()); 82 | } 83 | 84 | } -------------------------------------------------------------------------------- /sike-java/src/test/java/com/wultra/security/pqc/sike/OctetEncodingTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike; 18 | 19 | import com.wultra.security.pqc.sike.math.optimized.fp.FpElementOpti; 20 | import com.wultra.security.pqc.sike.model.ImplementationType; 21 | import com.wultra.security.pqc.sike.param.*; 22 | import com.wultra.security.pqc.sike.util.OctetEncoding; 23 | import org.junit.jupiter.api.Test; 24 | 25 | import java.math.BigInteger; 26 | import java.security.SecureRandom; 27 | 28 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 29 | import static org.junit.jupiter.api.Assertions.assertEquals; 30 | 31 | /** 32 | * Test of octet encoding. 33 | * 34 | * @author Roman Strobl, roman.strobl@wultra.com 35 | */ 36 | class OctetEncodingTest { 37 | 38 | @Test 39 | void testConversionToString() { 40 | assertEquals("000000", OctetEncoding.toOctetString(BigInteger.ZERO, 3)); 41 | assertEquals("010000000000", OctetEncoding.toOctetString(BigInteger.ONE, 6)); 42 | assertEquals("02010000000000000000", OctetEncoding.toOctetString(new BigInteger("258"), 10)); 43 | assertEquals("F5D2847EB0AD708A836E3A42DCF8A06DD6A442E58584EEFCFB93E3643370A9D2B2D7A1FBD32FCA430B640DF746789886F55B0F9123AB00", OctetEncoding.toOctetString(new BigInteger("7414245793236218931759067416133656081091359987964242955586558871517284227355106165661062279519102988920114614398938767562664891125"), 55)); 44 | assertEquals("CF6121E44395DFAE78152A1162C49E3196B6065E8781FA4FC53F971E1766818E81388CB3BE212446A06AFBCEC958BE4E6539A590619100", OctetEncoding.toOctetString(new BigInteger("6298340736269152096363706376601736707467340295126624700837062780723222842097560939735075361277996916624015901459649381975340442063"), 55)); 45 | } 46 | 47 | @Test 48 | void testConversionFromString() { 49 | assertEquals(BigInteger.ZERO, OctetEncoding.fromOctetString("000000")); 50 | assertEquals(BigInteger.ONE, OctetEncoding.fromOctetString("010000000000")); 51 | assertEquals(new BigInteger("258"), OctetEncoding.fromOctetString("02010000000000000000")); 52 | assertEquals(new BigInteger("7414245793236218931759067416133656081091359987964242955586558871517284227355106165661062279519102988920114614398938767562664891125"), OctetEncoding.fromOctetString("F5D2847EB0AD708A836E3A42DCF8A06DD6A442E58584EEFCFB93E3643370A9D2B2D7A1FBD32FCA430B640DF746789886F55B0F9123AB00")); 53 | assertEquals(new BigInteger("6298340736269152096363706376601736707467340295126624700837062780723222842097560939735075361277996916624015901459649381975340442063"), OctetEncoding.fromOctetString("CF6121E44395DFAE78152A1162C49E3196B6065E8781FA4FC53F971E1766818E81388CB3BE212446A06AFBCEC958BE4E6539A590619100")); 54 | } 55 | 56 | @Test 57 | void testConversions() { 58 | SecureRandom secureRandom = new SecureRandom(); 59 | for (int i = 0; i < 100; i++) { 60 | BigInteger r = new BigInteger(i, secureRandom); 61 | int mlen = i / 8 + 1; 62 | assertEquals(r, OctetEncoding.fromOctetString(OctetEncoding.toOctetString(r, mlen))); 63 | } 64 | } 65 | 66 | @Test 67 | void testConversionToMontgomeryP434One() { 68 | SikeParam sikeParam = new SikeParamP434(ImplementationType.OPTIMIZED); 69 | FpElementOpti one = new FpElementOpti(sikeParam, new BigInteger("1")); 70 | assertArrayEquals(new long[]{ 71 | 0x000000000000742CL, 72 | 0x0000000000000000L, 73 | 0x0000000000000000L, 74 | 0xB90FF404FC000000L, 75 | 0xD801A4FB559FACD4L, 76 | 0xE93254545F77410CL, 77 | 0x0000ECEEA7BD2EDAL 78 | }, one.getValue()); 79 | String octetString = one.toOctetString(); 80 | assertEquals("01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", octetString); 81 | assertEquals(new BigInteger("1"), OctetEncoding.fromOctetString(octetString)); 82 | } 83 | 84 | @Test 85 | void testConversionFromMontgomeryP434() { 86 | SikeParam sikeParam = new SikeParamP434(ImplementationType.OPTIMIZED); 87 | FpElementOpti six = new FpElementOpti(sikeParam, new long[]{ 88 | 0x000000000002B90AL, 89 | 0x0000000000000000L, 90 | 0x0000000000000000L, 91 | 0x5ADCCB2822000000L, 92 | 0x187D24F39F0CAFB4L, 93 | 0x9D353A4D394145A0L, 94 | 0x00012559A0403298L 95 | }); 96 | String octetString = six.toOctetString(); 97 | assertEquals("06000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", octetString); 98 | assertEquals(new BigInteger("6"), OctetEncoding.fromOctetString(octetString)); 99 | } 100 | 101 | @Test 102 | void testConversionToMontgomeryP434Six() { 103 | SikeParam sikeParam = new SikeParamP434(ImplementationType.OPTIMIZED); 104 | FpElementOpti six = new FpElementOpti(sikeParam, new BigInteger("6")); 105 | assertArrayEquals(new long[]{ 106 | 0x000000000002B90AL, 107 | 0x0000000000000000L, 108 | 0x0000000000000000L, 109 | 0x5ADCCB2822000000L, 110 | 0x187D24F39F0CAFB4L, 111 | 0x9D353A4D394145A0L, 112 | 0x00012559A0403298L 113 | }, six.getValue()); 114 | String octetString = six.toOctetString(); 115 | assertEquals("06000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", octetString); 116 | assertEquals(new BigInteger("6"), OctetEncoding.fromOctetString(octetString)); 117 | } 118 | 119 | @Test 120 | void testConversionFromMontgomeryP503() { 121 | SikeParam sikeParam = new SikeParamP503(ImplementationType.OPTIMIZED); 122 | FpElementOpti six = new FpElementOpti(sikeParam, new long[]{ 123 | 0x00000000000017D8L, 124 | 0x0000000000000000L, 125 | 0x0000000000000000L, 126 | 0xE000000000000000L, 127 | 0x30B1E6E3A51520FAL, 128 | 0xB13BC3BF6FFB3992L, 129 | 0x8045412EEB3E3DEDL, 130 | 0x0069182E2159DBB8L 131 | }); 132 | String octetString = six.toOctetString(); 133 | assertEquals("060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", octetString); 134 | assertEquals(new BigInteger("6"), OctetEncoding.fromOctetString(octetString)); 135 | } 136 | 137 | @Test 138 | void testConversionFromMontgomeryP610() { 139 | SikeParam sikeParam = new SikeParamP610(ImplementationType.OPTIMIZED); 140 | FpElementOpti one = new FpElementOpti(sikeParam, new long[]{ 141 | 0x00000000670CC8E6L, 142 | 0x0000000000000000L, 143 | 0x0000000000000000L, 144 | 0x0000000000000000L, 145 | 0x9A34000000000000L, 146 | 0x4D99C2BD28717A3FL, 147 | 0x0A4A1839A323D41CL, 148 | 0xD2B62215D06AD1E2L, 149 | 0x1369026E862CAF3DL, 150 | 0x000000010894E964L 151 | }); 152 | String octetString = one.toOctetString(); 153 | assertEquals("0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", octetString); 154 | assertEquals(BigInteger.ONE, OctetEncoding.fromOctetString(octetString)); 155 | } 156 | 157 | @Test 158 | void testConversionFromMontgomeryP751() { 159 | SikeParam sikeParam = new SikeParamP751(ImplementationType.OPTIMIZED); 160 | FpElementOpti one = new FpElementOpti(sikeParam, new long[]{ 161 | 0x00000000000249ADL, 162 | 0x0000000000000000L, 163 | 0x0000000000000000L, 164 | 0x0000000000000000L, 165 | 0x0000000000000000L, 166 | 0x8310000000000000L, 167 | 0x5527B1E4375C6C66L, 168 | 0x697797BF3F4F24D0L, 169 | 0xC89DB7B2AC5C4E2EL, 170 | 0x4CA4B439D2076956L, 171 | 0x10F7926C7512C7E9L, 172 | 0x00002D5B24BCE5E2L 173 | }); 174 | String octetString = one.toOctetString(); 175 | assertEquals("01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", octetString); 176 | assertEquals(new BigInteger("1"), OctetEncoding.fromOctetString(octetString)); 177 | } 178 | 179 | // TODO - more conversion tests 180 | 181 | } 182 | -------------------------------------------------------------------------------- /sike-java/src/test/java/com/wultra/security/pqc/sike/SidhTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike; 18 | 19 | import com.wultra.security.pqc.sike.crypto.KeyGenerator; 20 | import com.wultra.security.pqc.sike.crypto.Sidh; 21 | import com.wultra.security.pqc.sike.math.api.Fp2Element; 22 | import com.wultra.security.pqc.sike.model.ImplementationType; 23 | import com.wultra.security.pqc.sike.model.Party; 24 | import com.wultra.security.pqc.sike.param.SikeParam; 25 | import com.wultra.security.pqc.sike.param.SikeParamP434; 26 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 27 | import org.junit.jupiter.api.Test; 28 | 29 | import java.security.GeneralSecurityException; 30 | import java.security.KeyPair; 31 | import java.security.Security; 32 | 33 | import static org.junit.jupiter.api.Assertions.assertTrue; 34 | 35 | /** 36 | * Test of SIDH key exchange. 37 | * 38 | * @author Roman Strobl, roman.strobl@wultra.com 39 | */ 40 | class SidhTest { 41 | 42 | static { 43 | Security.addProvider(new BouncyCastleProvider()); 44 | } 45 | 46 | @Test 47 | void testSidh() throws GeneralSecurityException { 48 | SikeParam sikeParam = new SikeParamP434(ImplementationType.OPTIMIZED); 49 | System.out.println("Prime: " + sikeParam.getPrime()); 50 | KeyGenerator keyGenerator = new KeyGenerator(sikeParam); 51 | Sidh sidh = new Sidh(sikeParam); 52 | int loopCount = 10; 53 | long timeTotalA = 0L; 54 | long timeTotalB = 0L; 55 | for (int i = 0; i < loopCount; i++) { 56 | System.out.println("----------------------------------------"); 57 | long timeStartA = System.currentTimeMillis(); 58 | KeyPair keyPairA = keyGenerator.generateKeyPair(Party.ALICE); 59 | System.out.println("Alice's keypair:"); 60 | System.out.println("Private key: " + keyPairA.getPrivate()); 61 | System.out.println("Public key: " + keyPairA.getPublic()); 62 | long timeEndA = System.currentTimeMillis(); 63 | timeTotalA += (timeEndA - timeStartA); 64 | 65 | long timeStartB = System.currentTimeMillis(); 66 | KeyPair keyPairB = keyGenerator.generateKeyPair(Party.BOB); 67 | System.out.println("Bob's keypair:"); 68 | System.out.println("Private key: " + keyPairB.getPrivate()); 69 | System.out.println("Public key: " + keyPairB.getPublic()); 70 | long timeEndB = System.currentTimeMillis(); 71 | timeTotalB += (timeEndB - timeStartB); 72 | 73 | // Bob's public key is sent to Alice 74 | timeStartA = System.currentTimeMillis(); 75 | Fp2Element secretA = sidh.generateSharedSecret(Party.ALICE, keyPairA.getPrivate(), keyPairB.getPublic()); 76 | System.out.println("Shared secret generated by Alice: " + secretA); 77 | timeEndA = System.currentTimeMillis(); 78 | timeTotalA += (timeEndA - timeStartA); 79 | 80 | // Alice's public key is sent to Bob 81 | timeStartB = System.currentTimeMillis(); 82 | Fp2Element secretB = sidh.generateSharedSecret(Party.BOB, keyPairB.getPrivate(), keyPairA.getPublic()); 83 | System.out.println("Shared secret generated by Bob: " + secretB); 84 | timeEndB = System.currentTimeMillis(); 85 | timeTotalB += (timeEndB - timeStartB); 86 | 87 | boolean match = secretA.equals(secretB); 88 | System.out.println("Secrets match: " + match); 89 | assertTrue(match, "Secrets do not match"); 90 | } 91 | System.out.println("Average execution time (Alice): " + ((double)timeTotalA / loopCount) + " ms"); 92 | System.out.println("Average execution time (Bob): " + ((double)timeTotalB / loopCount) + " ms"); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /sike-java/src/test/java/com/wultra/security/pqc/sike/SikeDeterministicTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike; 18 | 19 | import com.wultra.security.pqc.sike.crypto.KeyGenerator; 20 | import com.wultra.security.pqc.sike.crypto.RandomGenerator; 21 | import com.wultra.security.pqc.sike.crypto.Sike; 22 | import com.wultra.security.pqc.sike.kat.util.CrtDrbgRandom; 23 | import com.wultra.security.pqc.sike.model.*; 24 | import com.wultra.security.pqc.sike.param.SikeParam; 25 | import com.wultra.security.pqc.sike.param.SikeParamP434; 26 | import com.wultra.security.pqc.sike.util.OctetEncoding; 27 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 28 | import org.bouncycastle.util.encoders.Base64; 29 | import org.bouncycastle.util.encoders.Hex; 30 | import org.junit.jupiter.api.Test; 31 | 32 | import java.security.GeneralSecurityException; 33 | import java.security.KeyPair; 34 | import java.security.Security; 35 | import java.util.Arrays; 36 | 37 | import static org.junit.jupiter.api.Assertions.assertEquals; 38 | import static org.junit.jupiter.api.Assertions.assertTrue; 39 | 40 | /** 41 | * Test of randomness using seed provided to a CRT DRBG random generator in a known SIKE decapsulation. 42 | * 43 | * @author Roman Strobl, roman.strobl@wultra.com 44 | */ 45 | class SikeDeterministicTest { 46 | 47 | private static final String SEED = "061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1"; 48 | 49 | static { 50 | Security.addProvider(new BouncyCastleProvider()); 51 | } 52 | 53 | @Test 54 | void testDeterministicSike() throws GeneralSecurityException { 55 | System.out.println("----------------------------------------"); 56 | SikeParam sikeParam = new SikeParamP434(ImplementationType.OPTIMIZED); 57 | System.out.println("Prime: " + sikeParam.getPrime()); 58 | byte[] seedBytes = Hex.decode(SEED); 59 | CrtDrbgRandom drbgRandom = new CrtDrbgRandom(seedBytes); 60 | KeyGenerator keyGenerator = new KeyGenerator(sikeParam, new RandomGenerator(drbgRandom)); 61 | KeyPair keyPair = keyGenerator.generateKeyPair(Party.BOB); 62 | SidhPrivateKey priv = (SidhPrivateKey) keyPair.getPrivate(); 63 | SidhPublicKey pub = (SidhPublicKey) keyPair.getPublic(); 64 | assertEquals("7C9935A0B07694AA0C6D10E4DB6B1ADD91282214654CB55E7C2CACD53919604D5BAC7B23EEF4B315FEEF5E01", priv.toOctetString()); 65 | assertEquals("4484D7AADB44B40CC180DC568B2C142A60E6E2863F5988614A6215254B2F5F6F79B48F329AD1A2DED20B7ABAB10F7DBF59C3E20B59A700093060D2A44ACDC0083A53CF0808E0B3A827C45176BEE0DC6EC7CC16461E38461C12451BB95191407C1E942BB50D4C7B25A49C644B630159E6C403653838E689FBF4A7ADEA693ED0657BA4A724786AF7953F7BA6E15F9BBF9F5007FB711569E72ACAB05D3463A458536CAB647F00C205D27D5311B2A5113D4B26548000DB237515931A040804E769361F94FF0167C78353D2630A1E6F595A1F80E87F6A5BCD679D7A64C5006F6191D4ADEFA1EA67F6388B7017D453F4FE2DFE80CCC709000B52175BFC3ADE52ECCB0CEBE1654F89D39131C357EACB61E5F13C80AB0165B7714D6BE6DF65F8DE73FF47B7F3304639F0903653ECCFA252F6E2104C4ABAD3C33AF24FD0E56F58DB92CC66859766035419AB2DF600", pub.toOctetString()); 66 | 67 | Sike sike = new Sike(sikeParam, drbgRandom); 68 | EncapsulationResult encapsulationResult = sike.encapsulate(keyPair.getPublic()); 69 | EncryptedMessage encrypted = encapsulationResult.getEncryptedMessage(); 70 | System.out.println("Alice's shared secret: " + new String(Base64.encode(encapsulationResult.getSecret()))); 71 | 72 | SidhPublicKey c0 = (SidhPublicKey) encrypted.getC0(); 73 | byte[] c1 = encrypted.getC1(); 74 | assertEquals("0FDEB26DBD96E0CD272283CA5BDD1435BC9A7F9AB7FC24F83CA926DEED038AE4E47F39F9886E0BD7EEBEAACD12AB435CC92AA3383B2C01E6B9E02BC3BEF9C6C2719014562A96A0F3E784E3FA44E5C62ED8CEA79E1108B6FECD5BF8836BF2DAE9FEB1863C4C8B3429220E2797F601FB4B8EBAFDD4F17355508D259CA60721D167F6E5480B5133E824F76D3240E97F31325DBB9A53E9A3EEE2E0712734825615A027857E2000D4D00E11988499A738452C93DA895BFA0E10294895CCF25E3C261CBE38F5D7E19ABE4E322094CB8DEC5BF7484902BABDE33CC69595F6013B20AABA9698C1DEA2BC6F65D57519294E6FEEA3B549599D480948374D2D21B643573C276E1A5B0745301F648D7982AB46A3065639960182BF365819EFC0D4E61E87D2820DBC0E849E99E875B21501D1CA7588A1D458CD70C7DF793D4993B9B1679886CAE8013A8DD854F010A100", c0.toOctetString()); 75 | assertEquals("C9933FA642DC0AEA9985786ED36B98D3", OctetEncoding.toOctetString(c1, 16)); 76 | 77 | byte[] secretDecaps = sike.decapsulate(keyPair.getPrivate(), keyPair.getPublic(), encrypted); 78 | System.out.println("Bob's shared secret: " + new String(Base64.encode(secretDecaps))); 79 | assertEquals("35F7F8FF388714DEDC41F139078CEDC9", OctetEncoding.toOctetString(secretDecaps, 16)); 80 | 81 | boolean match = Arrays.equals(encapsulationResult.getSecret(), secretDecaps); 82 | System.out.println("Shared secrets match: " + match); 83 | assertTrue(match, "Decapsulation failed"); 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /sike-java/src/test/java/com/wultra/security/pqc/sike/SikeRandomTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike; 18 | 19 | import com.wultra.security.pqc.sike.crypto.KeyGenerator; 20 | import com.wultra.security.pqc.sike.crypto.Sike; 21 | import com.wultra.security.pqc.sike.model.EncapsulationResult; 22 | import com.wultra.security.pqc.sike.model.EncryptedMessage; 23 | import com.wultra.security.pqc.sike.model.ImplementationType; 24 | import com.wultra.security.pqc.sike.model.Party; 25 | import com.wultra.security.pqc.sike.param.SikeParam; 26 | import com.wultra.security.pqc.sike.param.SikeParamP434; 27 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 28 | import org.bouncycastle.util.encoders.Base64; 29 | import org.junit.jupiter.api.Test; 30 | 31 | import java.security.GeneralSecurityException; 32 | import java.security.KeyPair; 33 | import java.security.Security; 34 | import java.util.Arrays; 35 | 36 | import static org.junit.jupiter.api.Assertions.assertTrue; 37 | 38 | /** 39 | * Test of SIKE key encapsulation. 40 | * 41 | * @author Roman Strobl, roman.strobl@wultra.com 42 | */ 43 | class SikeRandomTest { 44 | 45 | private Sike sike; 46 | private SikeParam sikeParam; 47 | private KeyPair keyPair; 48 | 49 | static { 50 | Security.addProvider(new BouncyCastleProvider()); 51 | } 52 | 53 | void initSike() throws GeneralSecurityException { 54 | sikeParam = new SikeParamP434(ImplementationType.OPTIMIZED); 55 | KeyGenerator keyGenerator = new KeyGenerator(sikeParam); 56 | sike = new Sike(sikeParam); 57 | System.out.println("Prime: " + sikeParam.getPrime()); 58 | // Enable key decapsulation 59 | keyPair = keyGenerator.generateKeyPair(Party.BOB); 60 | System.out.println("Bob's keypair:"); 61 | System.out.println("Private key: " + keyPair.getPrivate()); 62 | System.out.println("Public key: " + keyPair.getPublic()); 63 | } 64 | 65 | @Test 66 | void testSikeEncryption() throws GeneralSecurityException { 67 | System.out.println("----------------------------------------"); 68 | initSike(); 69 | System.out.println("Testing SIKE encryption/decryption"); 70 | byte[] message = "test123456789012".getBytes(); 71 | System.out.println("Message to encrypt: " + new String(message)); 72 | EncryptedMessage encrypted = sike.encrypt(keyPair.getPublic(), message); 73 | // Encrypted message is sent to Bob 74 | byte[] decrypted = sike.decrypt(keyPair.getPrivate(), encrypted); 75 | System.out.println("Decrypted message: " + new String(decrypted)); 76 | boolean match = Arrays.equals(message, decrypted); 77 | System.out.println("Messages match: " + match); 78 | assertTrue(match, "Messages do not match"); 79 | } 80 | 81 | @Test 82 | void testSikeEncapsulation() throws GeneralSecurityException { 83 | System.out.println("----------------------------------------"); 84 | initSike(); 85 | System.out.println("Testing SIKE encapsulation/decapsulation"); 86 | EncapsulationResult encapsulationResult = sike.encapsulate(keyPair.getPublic()); 87 | System.out.println("Alice's shared secret: " + new String(Base64.encode(encapsulationResult.getSecret()))); 88 | // Encrypted message is sent to Bob 89 | EncryptedMessage encrypted = encapsulationResult.getEncryptedMessage(); 90 | byte[] secretDecaps = sike.decapsulate(keyPair.getPrivate(), keyPair.getPublic(), encrypted); 91 | System.out.println("Bob's shared secret: " + new String(Base64.encode(secretDecaps))); 92 | boolean match = Arrays.equals(encapsulationResult.getSecret(), secretDecaps); 93 | System.out.println("Shared secrets match: " + match); 94 | assertTrue(match, "Decapsulation failed"); 95 | } 96 | 97 | @Test 98 | void testSikeEncapsulationWithMessageTransport() throws GeneralSecurityException { 99 | System.out.println("----------------------------------------"); 100 | initSike(); 101 | System.out.println("Testing SIKE encapsulation/decapsulation with message transport"); 102 | EncapsulationResult encapsulationResult = sike.encapsulate(keyPair.getPublic()); 103 | System.out.println("Alice's shared secret: " + new String(Base64.encode(encapsulationResult.getSecret()))); 104 | // Encrypted message is serialized, transported over the network and deserialized 105 | EncryptedMessage encrypted = encapsulationResult.getEncryptedMessage(); 106 | byte[] encodedMessage = encrypted.getEncoded(); 107 | EncryptedMessage transported = new EncryptedMessage(sikeParam, encodedMessage); 108 | byte[] secretDecaps = sike.decapsulate(keyPair.getPrivate(), keyPair.getPublic(), transported); 109 | System.out.println("Bob's shared secret: " + new String(Base64.encode(secretDecaps))); 110 | boolean match = Arrays.equals(encapsulationResult.getSecret(), secretDecaps); 111 | System.out.println("Shared secrets match: " + match); 112 | assertTrue(match, "Decapsulation failed"); 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /sike-java/src/test/java/com/wultra/security/pqc/sike/kat/KatP434Test.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.kat; 18 | 19 | import com.wultra.security.pqc.sike.model.ImplementationType; 20 | import com.wultra.security.pqc.sike.param.SikeParam; 21 | import com.wultra.security.pqc.sike.param.SikeParamP434; 22 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 23 | import org.junit.jupiter.api.Test; 24 | 25 | import java.io.FileNotFoundException; 26 | import java.security.GeneralSecurityException; 27 | import java.security.Security; 28 | 29 | /** 30 | * Test of KAT responses for SIKEp434. 31 | */ 32 | class KatP434Test { 33 | 34 | static { 35 | Security.addProvider(new BouncyCastleProvider()); 36 | } 37 | 38 | @Test 39 | void testKatP434() throws FileNotFoundException, GeneralSecurityException { 40 | SikeParam sikeParam = new SikeParamP434(ImplementationType.OPTIMIZED); 41 | KatTester.run(sikeParam, "kat/PQCkemKAT_374.rsp"); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /sike-java/src/test/java/com/wultra/security/pqc/sike/kat/KatP503Test.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.kat; 18 | 19 | import com.wultra.security.pqc.sike.model.ImplementationType; 20 | import com.wultra.security.pqc.sike.param.SikeParam; 21 | import com.wultra.security.pqc.sike.param.SikeParamP503; 22 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 23 | import org.junit.jupiter.api.Test; 24 | 25 | import java.io.FileNotFoundException; 26 | import java.security.GeneralSecurityException; 27 | import java.security.Security; 28 | 29 | /** 30 | * Test of KAT responses for SIKEp503. 31 | */ 32 | class KatP503Test { 33 | 34 | static { 35 | Security.addProvider(new BouncyCastleProvider()); 36 | } 37 | 38 | @Test 39 | void testKatP503() throws FileNotFoundException, GeneralSecurityException { 40 | SikeParam sikeParam = new SikeParamP503(ImplementationType.OPTIMIZED); 41 | KatTester.run(sikeParam, "kat/PQCkemKAT_434.rsp"); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /sike-java/src/test/java/com/wultra/security/pqc/sike/kat/KatP610Test.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.kat; 18 | 19 | import com.wultra.security.pqc.sike.model.ImplementationType; 20 | import com.wultra.security.pqc.sike.param.SikeParam; 21 | import com.wultra.security.pqc.sike.param.SikeParamP610; 22 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 23 | import org.junit.jupiter.api.Test; 24 | 25 | import java.io.FileNotFoundException; 26 | import java.security.GeneralSecurityException; 27 | import java.security.Security; 28 | 29 | /** 30 | * Test of KAT responses for SIKEp610. 31 | */ 32 | class KatP610Test { 33 | 34 | static { 35 | Security.addProvider(new BouncyCastleProvider()); 36 | } 37 | 38 | @Test 39 | void testKatP610() throws FileNotFoundException, GeneralSecurityException { 40 | SikeParam sikeParam = new SikeParamP610(ImplementationType.OPTIMIZED); 41 | KatTester.run(sikeParam, "kat/PQCkemKAT_524.rsp"); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /sike-java/src/test/java/com/wultra/security/pqc/sike/kat/KatP751Test.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.kat; 18 | 19 | import com.wultra.security.pqc.sike.model.ImplementationType; 20 | import com.wultra.security.pqc.sike.param.SikeParam; 21 | import com.wultra.security.pqc.sike.param.SikeParamP751; 22 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 23 | import org.junit.jupiter.api.Test; 24 | 25 | import java.io.FileNotFoundException; 26 | import java.security.GeneralSecurityException; 27 | import java.security.Security; 28 | 29 | /** 30 | * Test of KAT responses for SIKEp710. 31 | */ 32 | class KatP751Test { 33 | 34 | static { 35 | Security.addProvider(new BouncyCastleProvider()); 36 | } 37 | 38 | @Test 39 | void testKatP710() throws FileNotFoundException, GeneralSecurityException { 40 | SikeParam sikeParam = new SikeParamP751(ImplementationType.OPTIMIZED); 41 | KatTester.run(sikeParam, "kat/PQCkemKAT_644.rsp"); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /sike-java/src/test/java/com/wultra/security/pqc/sike/kat/KatTester.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.kat; 18 | 19 | import com.wultra.security.pqc.sike.crypto.KeyGenerator; 20 | import com.wultra.security.pqc.sike.crypto.RandomGenerator; 21 | import com.wultra.security.pqc.sike.crypto.Sike; 22 | import com.wultra.security.pqc.sike.kat.model.KatRspFile; 23 | import com.wultra.security.pqc.sike.kat.model.KatRspRecord; 24 | import com.wultra.security.pqc.sike.kat.util.CrtDrbgRandom; 25 | import com.wultra.security.pqc.sike.model.*; 26 | import com.wultra.security.pqc.sike.param.SikeParam; 27 | import com.wultra.security.pqc.sike.util.OctetEncoding; 28 | import org.bouncycastle.util.encoders.Hex; 29 | 30 | import java.io.File; 31 | import java.io.FileNotFoundException; 32 | import java.net.URL; 33 | import java.security.GeneralSecurityException; 34 | import java.security.KeyPair; 35 | import java.util.Arrays; 36 | import java.util.List; 37 | 38 | import static org.junit.jupiter.api.Assertions.*; 39 | 40 | /** 41 | * Universal KAT tester. 42 | */ 43 | public class KatTester { 44 | 45 | public static void run(SikeParam sikeParam, String katFileName) throws FileNotFoundException, GeneralSecurityException { 46 | String runAllKatTests = System.getProperty("runAllKatTests"); 47 | boolean runAllTests = false; 48 | if ("true".equals(runAllKatTests)) { 49 | runAllTests = true; 50 | } 51 | System.out.println("----------------------------------------"); 52 | System.out.println("KAT tests for " + sikeParam.getName()); 53 | ClassLoader classLoader = KatTester.class.getClassLoader(); 54 | URL fileName = classLoader.getResource(katFileName); 55 | assertNotNull(fileName); 56 | File file = new File(fileName.getFile()); 57 | KatRspFile katFile = new KatRspFile(file); 58 | List katRecords = katFile.getKatRecords(); 59 | 60 | System.out.println("Prime: " + sikeParam.getPrime()); 61 | 62 | for (KatRspRecord kat: katRecords) { 63 | if (!runAllTests && kat.getCount() > 9) { 64 | System.out.println("Additional tests were skipped, use -DrunAllKatTests=true to run all KAT tests"); 65 | break; 66 | } 67 | System.out.println("Record: " + kat.getCount()); 68 | byte[] seedBytes = Hex.decode(kat.getSeed()); 69 | CrtDrbgRandom drbgRandom = new CrtDrbgRandom(seedBytes); 70 | KeyGenerator keyGenerator = new KeyGenerator(sikeParam, new RandomGenerator(drbgRandom)); 71 | KeyPair keyPair = keyGenerator.generateKeyPair(Party.BOB); 72 | SidhPrivateKey priv = (SidhPrivateKey) keyPair.getPrivate(); 73 | SidhPublicKey pub = (SidhPublicKey) keyPair.getPublic(); 74 | assertEquals(kat.getSk(), priv.toOctetString() + pub.toOctetString()); 75 | assertEquals(kat.getPk(), pub.toOctetString()); 76 | 77 | Sike sike = new Sike(sikeParam, drbgRandom); 78 | EncapsulationResult encapsulationResult = sike.encapsulate(keyPair.getPublic()); 79 | EncryptedMessage encrypted = encapsulationResult.getEncryptedMessage(); 80 | SidhPublicKey c0 = (SidhPublicKey) encrypted.getC0(); 81 | String ssA = OctetEncoding.toOctetString(encapsulationResult.getSecret(), sikeParam.getMessageBytes()); 82 | System.out.println("Alice's shared secret: " + ssA); 83 | byte[] c1 = encrypted.getC1(); 84 | assertEquals(kat.getCt(), c0.toOctetString() + OctetEncoding.toOctetString(c1, sikeParam.getMessageBytes())); 85 | 86 | byte[] secretDecaps = sike.decapsulate(keyPair.getPrivate(), keyPair.getPublic(), encrypted); 87 | String ssB = OctetEncoding.toOctetString(secretDecaps, sikeParam.getMessageBytes()); 88 | System.out.println("Bob's shared secret: " + ssB); 89 | assertEquals(kat.getSs(), ssB); 90 | 91 | boolean match = Arrays.equals(encapsulationResult.getSecret(), secretDecaps); 92 | System.out.println("Shared secrets match: " + match); 93 | assertTrue(match, "Decapsulation failed"); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /sike-java/src/test/java/com/wultra/security/pqc/sike/kat/model/KatRspFile.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.kat.model; 18 | 19 | import java.io.File; 20 | import java.io.FileNotFoundException; 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | import java.util.Scanner; 24 | 25 | /** 26 | * Class representing a KAT response file. 27 | * 28 | * @author Roman Strobl, roman.strobl@wultra.com 29 | */ 30 | public class KatRspFile { 31 | 32 | private final List katRecords = new ArrayList<>(); 33 | 34 | /** 35 | * KAT file constructor. 36 | * @param file File to read. 37 | */ 38 | public KatRspFile(File file) throws FileNotFoundException { 39 | readFile(file); 40 | } 41 | 42 | /** 43 | * Get parsed KAT records. 44 | * @return KAT records. 45 | */ 46 | public List getKatRecords() { 47 | return katRecords; 48 | } 49 | 50 | /** 51 | * Add a KAT record. 52 | * @param katRecord KAT record. 53 | */ 54 | public void add(KatRspRecord katRecord) { 55 | katRecords.add(katRecord); 56 | } 57 | 58 | /** 59 | * Read the KAT response file. 60 | * @throws FileNotFoundException Thrown when file is not found. 61 | */ 62 | private void readFile(File file) throws FileNotFoundException { 63 | Scanner scanner = new Scanner(file); 64 | KatRspRecord record = new KatRspRecord(); 65 | while (scanner.hasNextLine()) { 66 | String line = scanner.nextLine(); 67 | if (!line.contains(" = ")) { 68 | continue; 69 | } 70 | String[] parts = line.split(" = "); 71 | if (parts.length != 2) { 72 | continue; 73 | } 74 | String key = parts[0]; 75 | String value = parts[1]; 76 | switch(key) { 77 | case "count": 78 | record = new KatRspRecord(); 79 | record.setCount(Integer.parseInt(value)); 80 | break; 81 | case "seed": 82 | record.setSeed(value); 83 | break; 84 | case "pk": 85 | record.setPk(value); 86 | break; 87 | case "sk": 88 | record.setSk(value); 89 | break; 90 | case "ct": 91 | record.setCt(value); 92 | break; 93 | case "ss": 94 | record.setSs(value); 95 | add(record); 96 | break; 97 | } 98 | } 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /sike-java/src/test/java/com/wultra/security/pqc/sike/kat/model/KatRspRecord.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.kat.model; 18 | 19 | import java.util.Objects; 20 | 21 | /** 22 | * Class representing a KAT response record. 23 | * 24 | * @author Roman Strobl, roman.strobl@wultra.com 25 | */ 26 | public class KatRspRecord { 27 | 28 | private int count; 29 | private String seed; 30 | private String pk; 31 | private String sk; 32 | private String ct; 33 | private String ss; 34 | 35 | /** 36 | * Default constructor. 37 | */ 38 | public KatRspRecord() { 39 | } 40 | 41 | /** 42 | * Constructor with all details. 43 | * @param count Counter. 44 | * @param seed Seed. 45 | * @param pk Public key. 46 | * @param sk Private key. 47 | * @param ct Encrypted message. 48 | * @param ss Shared secret. 49 | */ 50 | public KatRspRecord(int count, String seed, String pk, String sk, String ct, String ss) { 51 | this.count = count; 52 | this.seed = seed; 53 | this.pk = pk; 54 | this.sk = sk; 55 | this.ct = ct; 56 | this.ss = ss; 57 | } 58 | 59 | /** 60 | * Get the counter. 61 | * @return Counter. 62 | */ 63 | public int getCount() { 64 | return count; 65 | } 66 | 67 | /** 68 | * Set the counter. 69 | * @param count Counter. 70 | */ 71 | public void setCount(int count) { 72 | this.count = count; 73 | } 74 | 75 | /** 76 | * Get the seed. 77 | * @return Seed. 78 | */ 79 | public String getSeed() { 80 | return seed; 81 | } 82 | 83 | /** 84 | * Set the seed. 85 | * @param seed Seed. 86 | */ 87 | public void setSeed(String seed) { 88 | this.seed = seed; 89 | } 90 | 91 | /** 92 | * Get the public key. 93 | * @return Public key. 94 | */ 95 | public String getPk() { 96 | return pk; 97 | } 98 | 99 | /** 100 | * Set the public key. 101 | * @param pk Public key. 102 | */ 103 | public void setPk(String pk) { 104 | this.pk = pk; 105 | } 106 | 107 | /** 108 | * Get the private key. 109 | * @return Private key. 110 | */ 111 | public String getSk() { 112 | return sk; 113 | } 114 | 115 | /** 116 | * Set the private key. 117 | * @param sk Private key. 118 | */ 119 | public void setSk(String sk) { 120 | this.sk = sk; 121 | } 122 | 123 | /** 124 | * Get the encrypted message. 125 | * @return Encrypted message. 126 | */ 127 | public String getCt() { 128 | return ct; 129 | } 130 | 131 | /** 132 | * Set the encrypted message. 133 | * @param ct Encrypted message. 134 | */ 135 | public void setCt(String ct) { 136 | this.ct = ct; 137 | } 138 | 139 | /** 140 | * Get the shared secret. 141 | * @return Shared secret. 142 | */ 143 | public String getSs() { 144 | return ss; 145 | } 146 | 147 | /** 148 | * Set the shared secret. 149 | * @param ss Shared secret. 150 | */ 151 | public void setSs(String ss) { 152 | this.ss = ss; 153 | } 154 | 155 | @Override 156 | public boolean equals(Object o) { 157 | if (this == o) return true; 158 | if (o == null || getClass() != o.getClass()) return false; 159 | KatRspRecord katRecord = (KatRspRecord) o; 160 | return seed.equals(katRecord.seed) && 161 | pk.equals(katRecord.pk) && 162 | ct.equals(katRecord.ct) && 163 | ss.equals(katRecord.ss); 164 | } 165 | 166 | @Override 167 | public int hashCode() { 168 | return Objects.hash(seed, pk, ct, ss); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /sike-java/src/test/java/com/wultra/security/pqc/sike/kat/util/CrtDrbgRandom.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.kat.util; 18 | 19 | import javax.crypto.Cipher; 20 | import javax.crypto.spec.SecretKeySpec; 21 | import java.security.GeneralSecurityException; 22 | import java.security.SecureRandom; 23 | 24 | /** 25 | * A simple SP800-90A based CTR DRBG SecureRandom with fixed parameters and without DF. 26 | * 27 | * This random generator is not secure and is only used for deterministic generation of cryptographic material 28 | * used for KAT tests. 29 | * 30 | * @author Roman Strobl, roman.strobl@wultra.com 31 | */ 32 | public class CrtDrbgRandom extends SecureRandom { 33 | 34 | private static final int KEY_SIZE = 32; 35 | private static final int VALUE_SIZE = 16; 36 | private static final int SEED_SIZE = KEY_SIZE + VALUE_SIZE; 37 | 38 | private final Cipher cipher; 39 | private byte[] key; 40 | private byte[] value; 41 | 42 | /** 43 | * CTR DRBG SecureRandom constructor. 44 | * @param seed Seed for deterministic random generation. 45 | * @throws GeneralSecurityException Throw in case encryption fails. 46 | */ 47 | public CrtDrbgRandom(byte[] seed) throws GeneralSecurityException { 48 | cipher = Cipher.getInstance("AES/ECB/NoPadding", "BC"); 49 | ctrDrbgInstantiateAlgorithm(seed); 50 | } 51 | 52 | /** 53 | * Generate random bytes. 54 | * @param bytes Byte array to fill with random bytes. 55 | */ 56 | public void nextBytes(byte[] bytes) { 57 | synchronized (this) { 58 | try { 59 | generate(bytes); 60 | } catch (GeneralSecurityException e) { 61 | // Ignored, AES algorithm is available in case this class was successfully constructed 62 | } 63 | } 64 | } 65 | 66 | /** 67 | * Instantiate CTR DRBG algorithm without derivation function. 68 | * @param seed Seed for deterministic random generation. 69 | * @throws GeneralSecurityException Throw in case encryption fails. 70 | */ 71 | private void ctrDrbgInstantiateAlgorithm(byte[] seed) throws GeneralSecurityException { 72 | key = new byte[KEY_SIZE]; 73 | value = new byte[VALUE_SIZE]; 74 | ctrDrbgUpdate(seed); 75 | } 76 | 77 | /** 78 | * Generate random bytes. 79 | * @param output Output byte array. 80 | * @throws GeneralSecurityException Throw in case encryption fails. 81 | */ 82 | private void generate(byte[] output) throws GeneralSecurityException { 83 | byte[] val = new byte[VALUE_SIZE]; 84 | cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES")); 85 | 86 | for (int i = 0; i <= output.length / val.length; i++) { 87 | int bytesToCopy = Math.min((output.length - i * VALUE_SIZE), VALUE_SIZE); 88 | 89 | if (bytesToCopy != 0) { 90 | addOne(value); 91 | cipher.doFinal(value, 0, value.length, val); 92 | System.arraycopy(val, 0, output, i * val.length, bytesToCopy); 93 | } 94 | } 95 | 96 | ctrDrbgUpdate(null); 97 | 98 | } 99 | 100 | /** 101 | * Update CTR DRBG state. 102 | * @param data Input data. 103 | * @throws GeneralSecurityException Thrown in case encryption fails. 104 | */ 105 | private void ctrDrbgUpdate(byte[] data) throws GeneralSecurityException { 106 | byte[] temp = new byte[SEED_SIZE]; 107 | byte[] outputBlock = new byte[cipher.getBlockSize()]; 108 | int i = 0; 109 | int outLen = cipher.getBlockSize(); 110 | 111 | cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES")); 112 | 113 | while (i * outLen < SEED_SIZE) { 114 | addOne(value); 115 | cipher.doFinal(value, 0, 16, outputBlock); 116 | int bytesToCopy = Math.min((temp.length - i * outLen), outLen); 117 | System.arraycopy(outputBlock, 0, temp, i * outLen, bytesToCopy); 118 | i++; 119 | } 120 | 121 | if (data != null) { 122 | temp = xor(data, temp); 123 | } 124 | 125 | System.arraycopy(temp, 0, key, 0, KEY_SIZE); 126 | System.arraycopy(temp, KEY_SIZE, value, 0, VALUE_SIZE); 127 | } 128 | 129 | /** 130 | * Add number one to a byte array. 131 | * @param bytes Byte array to update. 132 | */ 133 | private void addOne(byte[] bytes) { 134 | int carry = 1; 135 | for (int i = 1; i <= bytes.length; i++) { 136 | int res = (bytes[bytes.length - i] & 0xff) + carry; 137 | carry = (res > 0xff) ? 1 : 0; 138 | bytes[bytes.length - i] = (byte) res; 139 | } 140 | } 141 | 142 | /** 143 | * XOR two byte arrays. 144 | * @param a First byte array. 145 | * @param b Second byte array. 146 | * @return Byte array with result. 147 | */ 148 | private byte[] xor(byte[] a, byte[] b) { 149 | byte[] result = new byte[SEED_SIZE]; 150 | for (int i = 0; i < SEED_SIZE; i++) { 151 | result[i] = (byte) (a[i] ^ b[i]); 152 | } 153 | return result; 154 | } 155 | 156 | } 157 | -------------------------------------------------------------------------------- /sike-java/src/test/java/com/wultra/security/pqc/sike/math/Fp2MathTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.math; 18 | 19 | import com.wultra.security.pqc.sike.math.api.Fp2Element; 20 | import com.wultra.security.pqc.sike.math.api.FpElement; 21 | import com.wultra.security.pqc.sike.math.optimized.fp.Fp2ElementOpti; 22 | import com.wultra.security.pqc.sike.math.optimized.fp.FpElementOpti; 23 | import com.wultra.security.pqc.sike.model.ImplementationType; 24 | import com.wultra.security.pqc.sike.param.SikeParam; 25 | import com.wultra.security.pqc.sike.param.SikeParamP434; 26 | import org.junit.jupiter.api.Test; 27 | 28 | import static org.junit.jupiter.api.Assertions.assertEquals; 29 | 30 | /** 31 | * Test of Fp2Element mathematics. 32 | * 33 | * @author Roman Strobl, roman.strobl@wultra.com 34 | */ 35 | class Fp2MathTest { 36 | 37 | private final SikeParam sikeParam = new SikeParamP434(ImplementationType.OPTIMIZED); 38 | 39 | @Test 40 | void testAddZero() { 41 | FpElement x = new FpElementOpti(sikeParam); 42 | Fp2Element x2 = new Fp2ElementOpti(sikeParam, x, x); 43 | Fp2Element result = x2.add(x2); 44 | assertEquals(x2, result); 45 | } 46 | 47 | @Test 48 | void testAddOneAndOne() { 49 | FpElementOpti x = new FpElementOpti(sikeParam); 50 | x.getValue()[x.size() - 1] = 1L; 51 | Fp2Element x2 = new Fp2ElementOpti(sikeParam, x, x); 52 | Fp2Element result = x2.add(x2); 53 | FpElementOpti expected = new FpElementOpti(sikeParam); 54 | expected.getValue()[expected.size() - 1] = 2L; 55 | Fp2Element expected2 = new Fp2ElementOpti(sikeParam, expected, expected); 56 | assertEquals(expected2, result); 57 | } 58 | 59 | @Test 60 | void testSubZero() { 61 | FpElement x = new FpElementOpti(sikeParam); 62 | Fp2Element x2 = new Fp2ElementOpti(sikeParam, x, x); 63 | Fp2Element result = x2.subtract(x2); 64 | assertEquals(x2, result); 65 | } 66 | 67 | // TODO more Fp2Element math tests 68 | } 69 | -------------------------------------------------------------------------------- /sike-java/src/test/java/com/wultra/security/pqc/sike/math/UnsignedLongTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Wultra s.r.o. 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU Affero General Public License as published 6 | * by the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU Affero General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package com.wultra.security.pqc.sike.math; 18 | 19 | import com.wultra.security.pqc.sike.math.optimized.fp.UnsignedLong; 20 | import org.junit.jupiter.api.Test; 21 | 22 | import static org.junit.jupiter.api.Assertions.assertEquals; 23 | 24 | /** 25 | * Test of unsigned 64-bit integer arithmetic. 26 | * 27 | * @author Roman Strobl, roman.strobl@wultra.com 28 | */ 29 | class UnsignedLongTest { 30 | 31 | @Test 32 | void testZeroAdd() { 33 | long x = 0L; 34 | long y = 0L; 35 | long carry = 0L; 36 | long[] result = UnsignedLong.add(x, y, carry); 37 | assertEquals(0L, result[0]); 38 | assertEquals(0L, result[1]); 39 | } 40 | 41 | @Test 42 | void testZeroOneAdd() { 43 | long x = 0L; 44 | long y = 1L; 45 | long carry = 0L; 46 | long[] result = UnsignedLong.add(x, y, carry); 47 | assertEquals(1L, result[0]); 48 | assertEquals(0L, result[1]); 49 | } 50 | 51 | @Test 52 | void testZeroOneAddAndCarry() { 53 | long x = 0L; 54 | long y = 1L; 55 | long carry = 1L; 56 | long[] result = UnsignedLong.add(x, y, carry); 57 | assertEquals(2L, result[0]); 58 | assertEquals(0L, result[1]); 59 | } 60 | 61 | @Test 62 | void testMaxValueAddZero() { 63 | long x = Long.parseUnsignedLong("18446744073709551615"); 64 | long y = 0L; 65 | long carry = 0L; 66 | long[] result = UnsignedLong.add(x, y, carry); 67 | assertEquals(Long.parseUnsignedLong("18446744073709551615"), result[0]); 68 | assertEquals(0L, result[1]); 69 | } 70 | 71 | @Test 72 | void testMaxValueAddOne() { 73 | long x = Long.parseUnsignedLong("18446744073709551615"); 74 | long y = 1L; 75 | long carry = 0L; 76 | long[] result = UnsignedLong.add(x, y, carry); 77 | assertEquals(0L, result[0]); 78 | assertEquals(1L, result[1]); 79 | } 80 | 81 | @Test 82 | void testMaxValueAddTwo() { 83 | long x = Long.parseUnsignedLong("18446744073709551615"); 84 | long y = 2L; 85 | long carry = 0L; 86 | long[] result = UnsignedLong.add(x, y, carry); 87 | assertEquals(1L, result[0]); 88 | assertEquals(1L, result[1]); 89 | } 90 | 91 | @Test 92 | void testMaxValueAddZeroAndCarry() { 93 | long x = Long.parseUnsignedLong("18446744073709551615"); 94 | long y = 0L; 95 | long carry = 1L; 96 | long[] result = UnsignedLong.add(x, y, carry); 97 | assertEquals(0L, result[0]); 98 | assertEquals(1L, result[1]); 99 | } 100 | 101 | @Test 102 | void testMaxValueAddOneAndCarry() { 103 | long x = Long.parseUnsignedLong("18446744073709551615"); 104 | long y = 1L; 105 | long carry = 1L; 106 | long[] result = UnsignedLong.add(x, y, carry); 107 | assertEquals(1L, result[0]); 108 | assertEquals(1L, result[1]); 109 | } 110 | 111 | @Test 112 | void testMaxValueMinusOneAddZeroAndCarry() { 113 | long x = Long.parseUnsignedLong("18446744073709551614"); 114 | long y = 0L; 115 | long carry = 1L; 116 | long[] result = UnsignedLong.add(x, y, carry); 117 | assertEquals(Long.parseUnsignedLong("18446744073709551615"), result[0]); 118 | assertEquals(0L, result[1]); 119 | } 120 | 121 | @Test 122 | void testZeroSub() { 123 | long x = 0L; 124 | long y = 0L; 125 | long borrow = 0L; 126 | long[] result = UnsignedLong.sub(x, y, borrow); 127 | assertEquals(0L, result[0]); 128 | assertEquals(0L, result[1]); 129 | } 130 | 131 | @Test 132 | void testOneZeroSub() { 133 | long x = 1L; 134 | long y = 0L; 135 | long borrow = 0L; 136 | long[] result = UnsignedLong.sub(x, y, borrow); 137 | assertEquals(1L, result[0]); 138 | assertEquals(0L, result[1]); 139 | } 140 | 141 | @Test 142 | void testOneZeroSubBorrow() { 143 | long x = 1L; 144 | long y = 0L; 145 | long borrow = 1L; 146 | long[] result = UnsignedLong.sub(x, y, borrow); 147 | assertEquals(0L, result[0]); 148 | assertEquals(0L, result[1]); 149 | } 150 | 151 | @Test 152 | void testZeroSubOne() { 153 | long x = 0L; 154 | long y = 1L; 155 | long borrow = 0L; 156 | long[] result = UnsignedLong.sub(x, y, borrow); 157 | assertEquals(Long.parseUnsignedLong("18446744073709551615"), result[0]); 158 | assertEquals(1L, result[1]); 159 | } 160 | 161 | @Test 162 | void testZeroSubZeroBorrow() { 163 | long x = 0L; 164 | long y = 0L; 165 | long borrow = 1L; 166 | long[] result = UnsignedLong.sub(x, y, borrow); 167 | assertEquals(Long.parseUnsignedLong("18446744073709551615"), result[0]); 168 | assertEquals(1L, result[1]); 169 | } 170 | 171 | @Test 172 | void testZeroSubOneBorrow() { 173 | long x = 0L; 174 | long y = 1L; 175 | long borrow = 1L; 176 | long[] result = UnsignedLong.sub(x, y, borrow); 177 | assertEquals(Long.parseUnsignedLong("18446744073709551614"), result[0]); 178 | assertEquals(1L, result[1]); 179 | } 180 | 181 | @Test 182 | void testMaxSubMax() { 183 | long x = Long.parseUnsignedLong("18446744073709551615"); 184 | long y = Long.parseUnsignedLong("18446744073709551615"); 185 | long borrow = 0L; 186 | long[] result = UnsignedLong.sub(x, y, borrow); 187 | assertEquals(0L, result[0]); 188 | assertEquals(0L, result[1]); 189 | } 190 | 191 | @Test 192 | void testMaxSubMaxMinusOneBorrow() { 193 | long x = Long.parseUnsignedLong("18446744073709551615"); 194 | long y = Long.parseUnsignedLong("18446744073709551614"); 195 | long borrow = 1L; 196 | long[] result = UnsignedLong.sub(x, y, borrow); 197 | assertEquals(0L, result[0]); 198 | assertEquals(0L, result[1]); 199 | } 200 | 201 | @Test 202 | void testMultiplyZeroByZero() { 203 | long x = 0L; 204 | long y = 0L; 205 | long[] result = UnsignedLong.mul(x, y); 206 | assertEquals(0L, result[0]); 207 | assertEquals(0L, result[1]); 208 | } 209 | 210 | @Test 211 | void testMultiplyOneByOne() { 212 | long x = 1L; 213 | long y = 1L; 214 | long[] result = UnsignedLong.mul(x, y); 215 | assertEquals(0L, result[0]); 216 | assertEquals(1L, result[1]); 217 | } 218 | 219 | @Test 220 | void testMultiplyBigValueByTwo() { 221 | long x = Long.parseUnsignedLong("9223372036854775807"); 222 | long y = 2L; 223 | long[] result = UnsignedLong.mul(x, y); 224 | assertEquals(0L, result[0]); 225 | assertEquals(Long.parseUnsignedLong("18446744073709551614"), result[1]); 226 | } 227 | 228 | @Test 229 | void testMultiplyMaxByMax() { 230 | long x = Long.parseUnsignedLong("18446744073709551615"); 231 | long y = Long.parseUnsignedLong("18446744073709551615"); 232 | long[] result = UnsignedLong.mul(x, y); 233 | // ((2^64)-1)*((2^64)-1) / (2^64) = 18446744073709551614 234 | assertEquals(Long.parseUnsignedLong("18446744073709551614"), result[0]); 235 | // ((2^64)-1)*((2^64)-1) mod 2^64 = 1 236 | assertEquals(1L, result[1]); 237 | } 238 | 239 | @Test 240 | void testMultiplyHiLo1() { 241 | long x = Long.parseUnsignedLong("123456789123456789"); 242 | long y = Long.parseUnsignedLong("987654321987654321"); 243 | long[] result = UnsignedLong.mul(x, y); 244 | assertEquals(Long.parseUnsignedLong("6609981190679600"), result[0]); 245 | assertEquals(Long.parseUnsignedLong("14369616054794401669"), result[1]); 246 | } 247 | 248 | @Test 249 | void testMultiplyHiLo2() { 250 | long x = Long.parseUnsignedLong("11111111111"); 251 | long y = Long.parseUnsignedLong("99999999999"); 252 | long[] result = UnsignedLong.mul(x, y); 253 | assertEquals(Long.parseUnsignedLong("60"), result[0]); 254 | assertEquals(Long.parseUnsignedLong("4306466666315791929"), result[1]); 255 | } 256 | 257 | @Test 258 | void testMultiplyHiLo3() { 259 | long x = Long.parseUnsignedLong("555555555"); 260 | long y = Long.parseUnsignedLong("555555555"); 261 | long[] result = UnsignedLong.mul(x, y); 262 | assertEquals(0L, result[0]); 263 | assertEquals(Long.parseUnsignedLong("308641974691358025"), result[1]); 264 | } 265 | 266 | } 267 | --------------------------------------------------------------------------------