// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////

package com.google.crypto.tink.subtle;

import com.google.crypto.tink.annotations.Alpha;
import java.security.InvalidKeyException;
import java.util.Arrays;

Defines the ECDH Curve25519 function, also known as the X25519 function.

This implementation is based on curve255-donna.

Warning

Do not use this API or any other APIs including fields and methods marked with the @Alpha annotation. They can be modified in any way, or even removed, at any time. They are in the package, but not for official, production release, but only for testing.

Usage

Alice:
byte[] privateKeyA = X25519.generatePrivateKey();
byte[] publicKeyA = X25519.publicFromPrivate(privateKeyA);
Bob:
byte[] privateKeyB = X25519.generatePrivateKey();
byte[] publicKeyB = X25519.publicFromPrivate(privateKeyB);
Alice sends publicKeyA to Bob and Bob sends publicKeyB to Alice.
Alice:
byte[] sharedSecretA = X25519.computeSharedSecret(privateKeyA, publicKeyB);
Bob:
byte[] sharedSecretB = X25519.computeSharedSecret(privateKeyB, publicKeyA);
such that sharedSecretA == sharedSecretB.
/** * Defines <a href="https://cr.yp.to/ecdh/curve25519-20060209.pdf">the ECDH Curve25519 function</a>, * also known as the X25519 function. * * <p>This implementation is based on <a * href="https://github.com/agl/curve25519-donna/blob/master/curve25519-donna.c">curve255-donna</a>. * * <h3>Warning</h3> * * <p>Do not use this API or any other APIs including fields and methods marked with the @Alpha * annotation. They can be modified in any way, or even removed, at any time. They are in the * package, but not for official, production release, but only for testing. * * <h3>Usage</h3> * * <pre> * Alice: * byte[] privateKeyA = X25519.generatePrivateKey(); * byte[] publicKeyA = X25519.publicFromPrivate(privateKeyA); * Bob: * byte[] privateKeyB = X25519.generatePrivateKey(); * byte[] publicKeyB = X25519.publicFromPrivate(privateKeyB); * * Alice sends publicKeyA to Bob and Bob sends publicKeyB to Alice. * Alice: * byte[] sharedSecretA = X25519.computeSharedSecret(privateKeyA, publicKeyB); * Bob: * byte[] sharedSecretB = X25519.computeSharedSecret(privateKeyB, publicKeyA); * such that sharedSecretA == sharedSecretB. * </pre> */
@Alpha public final class X25519 {
Returns a 32-byte private key for Curve25519.

Note from BoringSSL: All X25519 implementations should decode scalars correctly (see https://tools.ietf.org/html/rfc7748#section-5). However, if an implementation doesn't then it might interoperate with random keys a fraction of the time because they'll, randomly, happen to be correctly formed.

Thus we do the opposite of the masking here to make sure that our private keys are never correctly masked and so, hopefully, any incorrect implementations are deterministically broken.

This does not affect security because, although we're throwing away entropy, a valid implementation of computeSharedSecret should throw away the exact same bits anyway.

/** * Returns a 32-byte private key for Curve25519. * * <p>Note from BoringSSL: All X25519 implementations should decode scalars correctly (see * https://tools.ietf.org/html/rfc7748#section-5). However, if an implementation doesn't then it * might interoperate with random keys a fraction of the time because they'll, randomly, happen to * be correctly formed. * * <p>Thus we do the opposite of the masking here to make sure that our private keys are never * correctly masked and so, hopefully, any incorrect implementations are deterministically broken. * * <p>This does not affect security because, although we're throwing away entropy, a valid * implementation of computeSharedSecret should throw away the exact same bits anyway. */
@SuppressWarnings("NarrowingCompoundAssignment") public static byte[] generatePrivateKey() { byte[] privateKey = Random.randBytes(Field25519.FIELD_LEN); privateKey[0] |= 7; privateKey[31] &= 63; privateKey[31] |= 128; return privateKey; }
Returns the 32-byte shared key (i.e., privateKey·peersPublicValue on the curve).
Params:
  • privateKey – 32-byte private key
  • peersPublicValue – 32-byte public value
Throws:
Returns:the 32-byte shared key
/** * Returns the 32-byte shared key (i.e., privateKey·peersPublicValue on the curve). * * @param privateKey 32-byte private key * @param peersPublicValue 32-byte public value * @return the 32-byte shared key * @throws InvalidKeyException when {@code privateKey} is not 32-byte or {@code peersPublicValue} * is invalid. */
@SuppressWarnings("NarrowingCompoundAssignment") public static byte[] computeSharedSecret(byte[] privateKey, byte[] peersPublicValue) throws InvalidKeyException { if (privateKey.length != Field25519.FIELD_LEN) { throw new InvalidKeyException("Private key must have 32 bytes."); } long[] x = new long[Field25519.LIMB_CNT + 1]; byte[] e = Arrays.copyOf(privateKey, Field25519.FIELD_LEN); e[0] &= 248; e[31] &= 127; e[31] |= 64; Curve25519.curveMult(x, e, peersPublicValue); return Field25519.contract(x); }
Returns the 32-byte Diffie-Hellman public value based on the given privateKey (i.e., privateKey·[9] on the curve).
Params:
  • privateKey – 32-byte private key
Throws:
Returns:32-byte Diffie-Hellman public value
/** * Returns the 32-byte Diffie-Hellman public value based on the given {@code privateKey} (i.e., * {@code privateKey}·[9] on the curve). * * @param privateKey 32-byte private key * @return 32-byte Diffie-Hellman public value * @throws InvalidKeyException when the {@code privateKey} is not 32 bytes. */
public static byte[] publicFromPrivate(byte[] privateKey) throws InvalidKeyException { if (privateKey.length != Field25519.FIELD_LEN) { throw new InvalidKeyException("Private key must have 32 bytes."); } byte[] base = new byte[Field25519.FIELD_LEN]; base[0] = 9; return computeSharedSecret(privateKey, base); } }