1 // Copyright 2023 Google LLC 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.signature; 18 19 import com.google.crypto.tink.AccessesPartialKey; 20 import com.google.crypto.tink.Key; 21 import com.google.crypto.tink.internal.OutputPrefixUtil; 22 import com.google.crypto.tink.util.Bytes; 23 import com.google.errorprone.annotations.Immutable; 24 import com.google.errorprone.annotations.RestrictedApi; 25 import java.security.GeneralSecurityException; 26 import java.util.Objects; 27 import javax.annotation.Nullable; 28 29 /** Ed25519PublicKey represents the public portion of the Ed25519 signature primitive. */ 30 @Immutable 31 public final class Ed25519PublicKey extends SignaturePublicKey { 32 private final Ed25519Parameters parameters; 33 private final Bytes publicKeyBytes; 34 private final Bytes outputPrefix; 35 @Nullable private final Integer idRequirement; 36 Ed25519PublicKey( Ed25519Parameters parameters, Bytes publicKeyBytes, Bytes outputPrefix, @Nullable Integer idRequirement)37 private Ed25519PublicKey( 38 Ed25519Parameters parameters, 39 Bytes publicKeyBytes, 40 Bytes outputPrefix, 41 @Nullable Integer idRequirement) { 42 this.parameters = parameters; 43 this.publicKeyBytes = publicKeyBytes; 44 this.outputPrefix = outputPrefix; 45 this.idRequirement = idRequirement; 46 } 47 createOutputPrefix( Ed25519Parameters parameters, @Nullable Integer idRequirement)48 private static Bytes createOutputPrefix( 49 Ed25519Parameters parameters, @Nullable Integer idRequirement) { 50 if (parameters.getVariant() == Ed25519Parameters.Variant.NO_PREFIX) { 51 return OutputPrefixUtil.EMPTY_PREFIX; 52 } 53 if (parameters.getVariant() == Ed25519Parameters.Variant.CRUNCHY 54 || parameters.getVariant() == Ed25519Parameters.Variant.LEGACY) { 55 return OutputPrefixUtil.getLegacyOutputPrefix(idRequirement); 56 } 57 if (parameters.getVariant() == Ed25519Parameters.Variant.TINK) { 58 return OutputPrefixUtil.getTinkOutputPrefix(idRequirement); 59 } 60 throw new IllegalStateException("Unknown Variant: " + parameters.getVariant()); 61 } 62 63 @Override getOutputPrefix()64 public Bytes getOutputPrefix() { 65 return outputPrefix; 66 } 67 68 @RestrictedApi( 69 explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey", 70 link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys", 71 allowedOnPath = ".*Test\\.java", 72 allowlistAnnotations = {AccessesPartialKey.class}) 73 @AccessesPartialKey create(Bytes publicKeyBytes)74 public static Ed25519PublicKey create(Bytes publicKeyBytes) throws GeneralSecurityException { 75 return create(Ed25519Parameters.Variant.NO_PREFIX, publicKeyBytes, null); 76 } 77 78 @RestrictedApi( 79 explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey", 80 link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys", 81 allowedOnPath = ".*Test\\.java", 82 allowlistAnnotations = {AccessesPartialKey.class}) create( Ed25519Parameters.Variant variant, Bytes publicKeyBytes, @Nullable Integer idRequirement)83 public static Ed25519PublicKey create( 84 Ed25519Parameters.Variant variant, Bytes publicKeyBytes, @Nullable Integer idRequirement) 85 throws GeneralSecurityException { 86 Ed25519Parameters parameters = Ed25519Parameters.create(variant); 87 if (!variant.equals(Ed25519Parameters.Variant.NO_PREFIX) && idRequirement == null) { 88 throw new GeneralSecurityException( 89 "For given Variant " + variant + " the value of idRequirement must be non-null"); 90 } 91 92 if (variant.equals(Ed25519Parameters.Variant.NO_PREFIX) && idRequirement != null) { 93 throw new GeneralSecurityException( 94 "For given Variant NO_PREFIX the value of idRequirement must be null"); 95 } 96 if (publicKeyBytes.size() != 32) { 97 throw new GeneralSecurityException( 98 "Ed25519 key must be constructed with key of length 32 bytes, not " 99 + publicKeyBytes.size()); 100 } 101 102 return new Ed25519PublicKey( 103 parameters, publicKeyBytes, createOutputPrefix(parameters, idRequirement), idRequirement); 104 } 105 106 @RestrictedApi( 107 explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey", 108 link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys", 109 allowedOnPath = ".*Test\\.java", 110 allowlistAnnotations = {AccessesPartialKey.class}) getPublicKeyBytes()111 public Bytes getPublicKeyBytes() { 112 return publicKeyBytes; 113 } 114 115 @Override getParameters()116 public Ed25519Parameters getParameters() { 117 return parameters; 118 } 119 120 @Override 121 @Nullable getIdRequirementOrNull()122 public Integer getIdRequirementOrNull() { 123 return idRequirement; 124 } 125 126 @Override equalsKey(Key o)127 public boolean equalsKey(Key o) { 128 if (!(o instanceof Ed25519PublicKey)) { 129 return false; 130 } 131 Ed25519PublicKey that = (Ed25519PublicKey) o; 132 // Since outputPrefix is a function of parameters, we can ignore it here. 133 return that.parameters.equals(parameters) 134 && that.publicKeyBytes.equals(publicKeyBytes) 135 && Objects.equals(that.idRequirement, idRequirement); 136 } 137 } 138