• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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