Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8368841

X25519 implementation differs from the specification in RFC 7748

XMLWordPrintable

      ADDITIONAL SYSTEM INFORMATION :
      Windows 11

      A DESCRIPTION OF THE PROBLEM :
      According to the specification in [RFC 7748, Chapter 5][1], the implementation MUST ensure that the most significant bit of the input u-coordinate is cleared for X25519.
      This is currently not the case, which is why the current implementation does not satisfy the second test vector for X25519 in [RFC 7748, Chapter 5.2][2].
      If the most significant bit is explicitly cleared, the correct result is produced.

      Please note that there is an erratum for this test vector ([Errata ID 5568][3]). However, this has been rejected, meaning that the original test vector is valid. This is also confirmed by processing with other libraries such as BouncyCastle (Java) or pyca/cryptography (Python), which produce the correct result for this test vector.

      [1]: https://datatracker.ietf.org/doc/html/rfc7748#section-5
      [2]: https://datatracker.ietf.org/doc/html/rfc7748#section-5.2
      [3]: https://www.rfc-editor.org/errata/eid5568

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Step 1: Execute the code below for the input u-coordinate 0xe5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493, which corresponds to the official test vector from RFC 7748.
      Step 2: Set the most significant bit of the input u-coordinate to 0 (note that X25519 uses little endian), which changes the last byte of the input u-coordinate from 0x93 to 0x13: 0xe5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a413 and execute the code for this input u-coordinate again.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      In both cases, the output u-coordinate should be 0x95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957, which corresponds to the output u-coordinate of the official test vector from RFC 7748.
      ACTUAL -
      For step 1, the output u-coordinate is 0xd5f33573c9f6b8129483acce1e2534e95d3c41af6b00d0d30437b87cada57e4a, which does not correspond to the output u-coordinate of the official test vector from RFC 7748.
      For step 2, the output u-coordinate is 0x95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957, which corresponds to the output u-coordinate of the official test vector from RFC 7748.

      ---------- BEGIN SOURCE ----------
      import java.math.BigInteger;
      import java.security.KeyFactory;
      import java.security.PrivateKey;
      import java.security.PublicKey;
      import java.security.spec.NamedParameterSpec;
      import java.security.spec.XECPrivateKeySpec;
      import java.security.spec.XECPublicKeySpec;
      import java.util.HexFormat;

      import javax.crypto.KeyAgreement;

      public class Main {

      // https://openjdk.org/jeps/324
      public static void main(String[] args) throws Exception {

      byte[] publicKey = HexFormat.of().parseHex("e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493");
      //byte[] publicKey = HexFormat.of().parseHex("e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a413");
      byte[] privateKey = HexFormat.of().parseHex("4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d");
      NamedParameterSpec paramSpec = new NamedParameterSpec("X25519");

      KeyFactory kf = KeyFactory.getInstance("XDH");
      reverse(publicKey);
      BigInteger u = new BigInteger(1, publicKey);
      XECPublicKeySpec pubSpec = new XECPublicKeySpec(paramSpec, u);
      PublicKey pubKey = kf.generatePublic(pubSpec);

      XECPrivateKeySpec privSpec = new XECPrivateKeySpec(paramSpec, privateKey);
      PrivateKey privKey = kf.generatePrivate(privSpec);

      KeyAgreement ka = KeyAgreement.getInstance("XDH");
      ka.init(privKey);
      ka.doPhase(pubKey, true);
      byte[] secret = ka.generateSecret();

      System.out.println( HexFormat.of().formatHex(secret));
      }

      private static void reverse(byte[] array) {
      for(int i = 0; i < array.length / 2; i++)
      {
      byte temp = array[i];
      array[i] = array[array.length - i - 1];
      array[array.length - i - 1] = temp;
      }
      }
      }
      ---------- END SOURCE ----------

            jnimeh Jamil Nimeh
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated: