1 // Copyright 2017 Google Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 //////////////////////////////////////////////////////////////////////////////// 16 17 package com.google.crypto.tink.subtle; 18 19 import com.google.crypto.tink.AccessesPartialKey; 20 import com.google.crypto.tink.InsecureSecretKeyAccess; 21 import com.google.crypto.tink.PublicKeySign; 22 import com.google.crypto.tink.config.internal.TinkFipsUtil; 23 import com.google.crypto.tink.internal.Ed25519; 24 import com.google.crypto.tink.internal.Field25519; 25 import com.google.crypto.tink.signature.Ed25519Parameters; 26 import com.google.crypto.tink.signature.Ed25519PrivateKey; 27 import java.security.GeneralSecurityException; 28 import java.util.Arrays; 29 30 /** 31 * Ed25519 signing. 32 * 33 * <h3>Usage</h3> 34 * 35 * <pre>{@code 36 * Ed25519Sign.KeyPair keyPair = Ed25519Sign.KeyPair.newKeyPair(); 37 * // securely store keyPair and share keyPair.getPublicKey() 38 * Ed25519Sign signer = new Ed25519Sign(keyPair.getPrivateKey()); 39 * byte[] signature = signer.sign(message); 40 * }</pre> 41 * 42 * @since 1.1.0 43 */ 44 public final class Ed25519Sign implements PublicKeySign { 45 public static final TinkFipsUtil.AlgorithmFipsCompatibility FIPS = 46 TinkFipsUtil.AlgorithmFipsCompatibility.ALGORITHM_NOT_FIPS; 47 48 public static final int SECRET_KEY_LEN = Field25519.FIELD_LEN; 49 50 private final byte[] hashedPrivateKey; 51 private final byte[] publicKey; 52 private final byte[] outputPrefix; 53 private final byte[] messageSuffix; 54 55 @AccessesPartialKey create(Ed25519PrivateKey key)56 public static PublicKeySign create(Ed25519PrivateKey key) throws GeneralSecurityException { 57 return new Ed25519Sign( 58 key.getPrivateKeyBytes().toByteArray(InsecureSecretKeyAccess.get()), 59 key.getOutputPrefix().toByteArray(), 60 key.getParameters().getVariant().equals(Ed25519Parameters.Variant.LEGACY) 61 ? new byte[] {0} 62 : new byte[0]); 63 } 64 Ed25519Sign( final byte[] privateKey, final byte[] outputPrefix, final byte[] messageSuffix)65 private Ed25519Sign( 66 final byte[] privateKey, final byte[] outputPrefix, final byte[] messageSuffix) 67 throws GeneralSecurityException { 68 if (!FIPS.isCompatible()) { 69 throw new GeneralSecurityException("Can not use Ed25519 in FIPS-mode."); 70 } 71 72 if (privateKey.length != SECRET_KEY_LEN) { 73 throw new IllegalArgumentException( 74 String.format("Given private key's length is not %s", SECRET_KEY_LEN)); 75 } 76 77 this.hashedPrivateKey = Ed25519.getHashedScalar(privateKey); 78 this.publicKey = Ed25519.scalarMultWithBaseToBytes(this.hashedPrivateKey); 79 this.outputPrefix = outputPrefix; 80 this.messageSuffix = messageSuffix; 81 } 82 83 /** 84 * Constructs a Ed25519Sign with the {@code privateKey}. 85 * 86 * @param privateKey 32-byte random sequence. 87 * @throws GeneralSecurityException if there is no SHA-512 algorithm defined in {@link 88 * EngineFactory}.MESSAGE_DIGEST. 89 */ Ed25519Sign(final byte[] privateKey)90 public Ed25519Sign(final byte[] privateKey) throws GeneralSecurityException { 91 this(privateKey, new byte[0], new byte[0]); 92 } 93 noPrefixSign(final byte[] data)94 private byte[] noPrefixSign(final byte[] data) throws GeneralSecurityException { 95 return Ed25519.sign(data, publicKey, hashedPrivateKey); 96 } 97 98 @Override sign(final byte[] data)99 public byte[] sign(final byte[] data) throws GeneralSecurityException { 100 byte[] signature; 101 if (messageSuffix.length == 0) { 102 signature = noPrefixSign(data); 103 } else { 104 signature = noPrefixSign(Bytes.concat(data, messageSuffix)); 105 } 106 if (outputPrefix.length == 0) { 107 return signature; 108 } else { 109 return Bytes.concat(outputPrefix, signature); 110 } 111 } 112 113 /** Defines the KeyPair consisting of a private key and its corresponding public key. */ 114 public static final class KeyPair { 115 116 private final byte[] publicKey; 117 private final byte[] privateKey; 118 KeyPair(final byte[] publicKey, final byte[] privateKey)119 private KeyPair(final byte[] publicKey, final byte[] privateKey) { 120 this.publicKey = publicKey; 121 this.privateKey = privateKey; 122 } 123 getPublicKey()124 public byte[] getPublicKey() { 125 return Arrays.copyOf(publicKey, publicKey.length); 126 } 127 getPrivateKey()128 public byte[] getPrivateKey() { 129 return Arrays.copyOf(privateKey, privateKey.length); 130 } 131 132 /** Returns a new <publicKey, privateKey> KeyPair. */ newKeyPair()133 public static KeyPair newKeyPair() throws GeneralSecurityException { 134 return newKeyPairFromSeed(Random.randBytes(Field25519.FIELD_LEN)); 135 } 136 137 /** Returns a new <publicKey, privateKey> KeyPair generated from a seed. */ newKeyPairFromSeed(byte[] secretSeed)138 public static KeyPair newKeyPairFromSeed(byte[] secretSeed) throws GeneralSecurityException { 139 if (secretSeed.length != Field25519.FIELD_LEN) { 140 throw new IllegalArgumentException( 141 String.format("Given secret seed length is not %s", Field25519.FIELD_LEN)); 142 } 143 byte[] privateKey = secretSeed; 144 byte[] publicKey = Ed25519.scalarMultWithBaseToBytes(Ed25519.getHashedScalar(privateKey)); 145 return new KeyPair(publicKey, privateKey); 146 } 147 } 148 } 149